LEPTON CMS 7.0.0
feel free to keep it strictly simple...
Loading...
Searching...
No Matches
lepton_login.php
Go to the documentation of this file.
1<?php
2
3
22{
23 private string $USERS_TABLE = TABLE_PREFIX."users";
24 private string $GROUPS_TABLE = TABLE_PREFIX."groups";
25
26 private int $user_id = -1;
27 private string $username = '';
28 private string $password = '';
29 private string $url = '';
30 public string $message = '';
31
32 private string $username_fieldname = "";
33 private string $password_fieldname = "";
34
35 private int $max_attempts = MAX_ATTEMPTS;
36
37 private string $warning_url = "";
38 private string $login_url = "";
39 public string $redirect_url = ""; // must be public
40
41 private string $template_dir = THEME_PATH."/templates";
42 private string $template_file = "";
43
44 public bool $frontend = false;
45 private string $forgotten_details_app = ADMIN_URL."/login/forgot/index.php";
46
47 // Private var that holds the length of the given username
48 private int $username_len = 0;
49
50 // Private var that holds the length of the given password
51 private int $password_len = 0;
52
57 public static $instance;
58
64 const FINGERPRINT_HASH_ALGO = "haval128,4";
65
72 public static function getInstance(array $config_array = [] ): object
73 {
74 if (null === static::$instance)
75 {
76 static::$instance = new static($config_array);
77 }
78 return static::$instance;
79 }
80
84 public function __construct(array $config_array = [])
85 {
87
88 $MESSAGE = LEPTON_core::getGlobal("MESSAGE");
89
90 if (NULL === self::$instance)
91 {
92 self::$instance = $this;
93 }
94
95 // Get configuration values
96 // [2.1]
97 if (isset($config_array['USERS_TABLE']))
98 {
99 $this->USERS_TABLE = $config_array['USERS_TABLE'];
100 }
101 // [2.2]
102 if (isset($config_array['GROUPS_TABLE']))
103 {
104 $this->GROUPS_TABLE = $config_array['GROUPS_TABLE'];
105 }
106 // [2.3]
107 if (isset($config_array['USERNAME_FIELDNAME']))
108 {
109 $this->username_fieldname = $config_array['USERNAME_FIELDNAME'];
110 }
111 // [2.4]
112 if (isset($config_array['PASSWORD_FIELDNAME']))
113 {
114 $this->password_fieldname = $config_array['PASSWORD_FIELDNAME'];
115 }
116 // [2.5]
117 if (isset($config_array['MAX_ATTEMPTS']))
118 {
119 $this->max_attempts = $config_array['MAX_ATTEMPTS'];
120 }
121 // [2.6]
122 if (isset($config_array['WARNING_URL']))
123 {
124 $this->warning_url = $config_array['WARNING_URL'];
125 }
126 // [2.7]
127 if (isset($config_array['LOGIN_URL']))
128 {
129 $this->login_url = $config_array['LOGIN_URL'];
130 }
131 // [2.8]
132 if (isset($config_array['TEMPLATE_DIR']))
133 {
134 $this->template_dir = $config_array['TEMPLATE_DIR'];
135 }
136 // [2.9]
137 if (isset($config_array['TEMPLATE_FILE']))
138 {
139 $this->template_file = $config_array['TEMPLATE_FILE'];
140 }
141 // [2.10]
142 if (isset($config_array['FRONTEND']))
143 {
144 $this->frontend = $config_array['FRONTEND'];
145 }
146 // [2.11]
147 if (isset($config_array['frontend']))
148 {
149 $this->frontend = $config_array['frontend'];
150 }
151 // [2.12]
152 if (isset($config_array['FORGOTTEN_DETAILS_APP']))
153 {
154 $this->forgotten_details_app = $config_array['FORGOTTEN_DETAILS_APP'];
155 }
156 // [2.13]
157 if (isset($config_array['REDIRECT_URL']))
158 {
159 $this->redirect_url = $config_array['REDIRECT_URL'];
160 }
161 // [2.13.2]
162 else
163 {
164 $sTempURL = ($_POST['redirect'] ?? "");
165 if( $sTempURL == "" )
166 {
167 $sTempURL = ( $_GET['redirect'] ?? "");
168 }
169 $this->redirect_url = htmlspecialchars(strip_tags($sTempURL, ""));
170 }
171
172 // Get the supplied username and password
173 if ($this->get_post('username_fieldname') != null)
174 {
175 $username_fieldname = $this->get_post('username_fieldname');
176 $password_fieldname = $this->get_post('password_fieldname');
177 }
178 else
179 {
180 $username_fieldname = 'username';
181 $password_fieldname = 'password';
182 }
183
184 if ($this->get_post($username_fieldname) != null)
185 {
186 $this->username = htmlspecialchars($this->get_post($username_fieldname), ENT_QUOTES);
187 }
188 else
189 {
190 $this->username = '';
191 }
192
193 $this->password = $this->get_post($password_fieldname) ?? "";
194
195 // Get the length of the supplied username and password
196 if ($this->get_post($username_fieldname) != null)
197 {
198 $this->username_len = strlen($this->username);
199 $this->password_len = strlen($this->password);
200 }
201 // If the url is blank, set it to the default url
202 $this->url = $this->get_post('url') ?? "";
203 if ($this->redirect_url != '')
204 {
205 $this->url = $this->redirect_url;
206 }
207
208 if (strlen($this->url) < 2)
209 {
210 $token = (!LEPTOKEN_LIFETIME) ? '' : '?leptoken=' . $this->createLepToken();
211 $this->url = ($config_array['DEFAULT_URL'] ?? "") . $token;
212 }
213
214 if ($this->is_authenticated() === true)
215 {
216 // User already logged-in, so redirect to default url
217 header('Location: '.$this->url);
218 exit();
219 }
220 elseif (($this->username == '') && ($this->password == ''))
221 {
222 $this->message = $MESSAGE['LOGIN_BOTH_BLANK'];
223 $this->increase_attempts();
224 }
225 elseif ($this->username == '')
226 {
227 $this->message = $MESSAGE['LOGIN_USERNAME_BLANK'];
228 $this->increase_attempts();
229 }
230 elseif ($this->password == '')
231 {
232 $this->message = $MESSAGE['LOGIN_PASSWORD_BLANK'];
233 $this->increase_attempts();
234 }
235 elseif ($this->username_len < $config_array['MIN_USERNAME_LEN'])
236 {
237 $this->message = $MESSAGE['LOGIN_USERNAME_TOO_SHORT'];
238 $this->increase_attempts();
239 }
240 elseif ($this->password_len < $config_array['MIN_PASSWORD_LEN'])
241 {
242 $this->message = $MESSAGE['LOGIN_PASSWORD_TOO_SHORT'];
243 $this->increase_attempts();
244 }
245 else
246 {
247 if ($this->authenticate())
248 {
249 // Authentication successful
250 $token = (!LEPTOKEN_LIFETIME) ? '' : '?leptoken=' . $this->createLepToken();
251
255 $browser_fingerprint = hash( self::FINGERPRINT_HASH_ALGO, $_SERVER['HTTP_USER_AGENT'] );
256 $ip_fingerprint = hash(self::FINGERPRINT_HASH_ALGO, $_SERVER['REMOTE_ADDR'] );
257
258 $fields = [
259 'temp_active' => 1,
260 'temp_count' => 0,
261 'temp_time' => TIME()
262 ];
263
264 $database->build_and_execute(
265 'update',
266 TABLE_PREFIX."temp",
267 $fields,
268 "`temp_ip`='".$ip_fingerprint."' AND `temp_browser`='".$browser_fingerprint."'"
269 );
270 // End: reset
271
272
273 // check for tfa use local PIN
274 if (TFA === 'local' && LEPTOKEN_LIFETIME > 0) // second step in process set vars
275 {
276 $oTFA = LEPTON_tfa::getInstance();
277 $oTFA->initialize(intval($_SESSION['USER_ID']));
278
279 if (isset($_REQUEST['redirect'])) // frontend account
280 {
281 if ($oTFA->key_new === true)
282 {
283 if (isset($_POST['submit']) )
284 {
285 $oTFA->set_fe_pin('create');
286 exit();
287 }
288 else
289 {
290 header('Location: '.LEPTON_URL.'/account/logout.php');
291 exit();
292 }
293 }
294 else
295 {
296 if (isset($_POST['submit']))
297 {
298 $oTFA->display_fe_pin('display');
299 exit();
300 }
301 else
302 {
303 header('Location: '.LEPTON_URL.'/account/logout.php');
304 exit();
305 }
306 }
307 }
308 else // backend directory
309 {
310 if ($oTFA->key_new === true)
311 {
312 if (isset($_POST['submit']))
313 {
314 $oTFA->set_be_pin('create');
315 exit();
316 }
317 else
318 {
319 header('Location: '.ADMIN_URL.'/logout/index.php');
320 exit();
321 }
322 }
323 else
324 {
325 if (!isset($_POST['lkey']))
326 {
327 $oTFA->display_be_pin('display');
328 exit();
329 }
330 else
331 {
332 header('Location: '.ADMIN_URL.'/logout/index.php');
333 exit();
334 }
335 }
336 }
337 }
338
339
340 // check for tfa to send TAN via mail
341 if (TFA === 'mail' && LEPTOKEN_LIFETIME > 0)
342 {
343 $oTFA = LEPTON_tfa::getInstance();
344 $oTFA->initialize(intval($_SESSION['USER_ID']));
345 if(isset($_REQUEST['redirect'])) // frontend account
346 {
347 // send PIN via mail to user
348 $oTFA->display_fe_pin('display');
349 exit();
350 }
351 else // backend directory
352 {
353 if (!isset($_POST['lkey']) )
354 {
355 // send PIN via mail to user
356 $oTFA->display_be_pin('display');
357 exit();
358 }
359 else
360 {
361 header('Location: '.ADMIN_URL.'/logout/index.php');
362 exit();
363 }
364
365 }
366 }
367 // end check for tfa
368
369 header("Location: ".$this->url . $token);
370 exit(0);
371 }
372 else
373 {
374 $this->message = $MESSAGE['LOGIN_AUTHENTICATION_FAILED'];
375 $this->increase_attempts();
376 }
377 }
378 }
379
380 // Authenticate the user (check if they exist in the database)
381
385 public function authenticate(): bool
386 {
388
389 // Get user information
390 $loginName = (preg_match('/[\;\=\&\|<> ]/', $this->username) ? '' : $this->username);
391 $results_array = [];
392 $database->execute_query(
393 'SELECT `password` FROM `'.$this->USERS_TABLE.'` WHERE `username` = "'.$loginName.'" AND `active` = 1',
394 true,
395 $results_array,
396 false
397 );
398
399 if (!empty($results_array))
400 {
401 $check = password_verify($this->password,$results_array['password']);
402 if ($check != 1)
403 {
404 return false;
405 }
406 // -------- [1]
407 $authenticated_user = [];
408 $database->execute_query(
409 'SELECT * FROM `'.$this->USERS_TABLE.'` WHERE `username` = "'.$loginName.'" AND `active` = 1',
410 true,
411 $authenticated_user,
412 false
413 );
414
415 $this->user_id = intval($authenticated_user['user_id']);
416 $_SESSION['USER_ID'] =$this->user_id;
417 $_SESSION['GROUP_ID'] = $authenticated_user['group_id'];
418 $_SESSION['GROUPS_ID'] = $authenticated_user['groups_id'];
419 $_SESSION['USERNAME'] = $authenticated_user['username'];
420 $_SESSION['DISPLAY_NAME'] = $authenticated_user['display_name'];
421 $_SESSION['EMAIL'] = $authenticated_user['email'];
422 $_SESSION['HOME_FOLDER'] = $authenticated_user['home_folder'];
423
424 // Set language
425 if ($authenticated_user['language'] != '')
426 {
427 $_SESSION['LANGUAGE'] = $authenticated_user['language'];
428 }
429
430 // Set timezone
431 if ($authenticated_user['timezone_string'] != '')
432 {
433 $_SESSION['TIMEZONE_STRING'] = $authenticated_user['timezone_string'];
434 }
435 $timezone_string = ($_SESSION['TIMEZONE_STRING'] ?? DEFAULT_TIMEZONE_STRING);
436 date_default_timezone_set($timezone_string);
437
438 // Set date format
439 if ($authenticated_user['date_format'] != '')
440 {
441 $_SESSION['DATE_FORMAT'] = $authenticated_user['date_format'];
442 }
443 else
444 {
445 // Set a session var so apps can tell user is using default date format
446 $_SESSION['USE_DEFAULT_DATE_FORMAT'] = true;
447 }
448 // Set time format
449 if ($authenticated_user['time_format'] != '')
450 {
451 $_SESSION['TIME_FORMAT'] = $authenticated_user['time_format'];
452 }
453 else
454 {
455 // Set a session var so apps can tell user is using default time format
456 $_SESSION['USE_DEFAULT_TIME_FORMAT'] = true;
457 }
458
459 // Get group information
460 $_SESSION['SYSTEM_PERMISSIONS'] = [];
461 $_SESSION['MODULE_PERMISSIONS'] = [];
462 $_SESSION['TEMPLATE_PERMISSIONS'] = [];
463 $_SESSION['GROUP_NAME'] = [];
464
465 $first_group = true;
466 foreach (explode(",", $this->get_session('GROUPS_ID')) as $cur_group_id)
467 {
468 $results_array_2 = [];
469 $database->execute_query(
470 "SELECT * FROM ".$this->GROUPS_TABLE." WHERE group_id = ".$cur_group_id,
471 true,
472 $results_array_2,
473 false
474 );
475
476 if (empty($results_array_2))
477 {
478 continue;
479 }
480
481 $_SESSION['GROUP_NAME'][$cur_group_id] = $results_array_2['name'];
482
483 // Set system permissions
484 $_SESSION['SYSTEM_PERMISSIONS'] = array_merge($_SESSION['SYSTEM_PERMISSIONS'], explode(',', $results_array_2['system_permissions']));
485
486 // Set module permissions
487 if ($first_group)
488 {
489 $_SESSION['MODULE_PERMISSIONS'] = explode(',', $results_array_2['module_permissions']);
490 }
491 else
492 {
493 $_SESSION['MODULE_PERMISSIONS'] = array_merge($_SESSION['MODULE_PERMISSIONS'], explode(',', $results_array_2['module_permissions']));
494 }
495
496 // Set template permissions
497 if ($first_group)
498 {
499 $_SESSION['TEMPLATE_PERMISSIONS'] = explode(',', $results_array_2['template_permissions']);
500 }
501 else
502 {
503 $_SESSION['TEMPLATE_PERMISSIONS'] = array_intersect($_SESSION['TEMPLATE_PERMISSIONS'], explode(',', $results_array_2['template_permissions']));
504 }
505 $first_group = false;
506 }
507
508 if (false === $this->frontend)
509 {
510 // prevent users with no backend access
511 $aTempGroups = explode(",",$authenticated_user['groups_id']);
512 $bGotBackendAccess = false;
513 foreach($aTempGroups as $tempGroupId)
514 {
515 $check_backend = intval( $database->get_one("SELECT `backend_permission` FROM `".$this->GROUPS_TABLE."` WHERE `group_id` = ".$tempGroupId ) );
516 if( 1 === $check_backend)
517 {
518 $bGotBackendAccess = true;
519 $_SESSION['SYSTEM_PERMISSIONS'][] = "backend_permission";
520 break;
521 }
522 }
523 if (false === $bGotBackendAccess)
524 {
526 return false;
527 }
528 }
529 // -------- end [1]
530 // Update the users table with current ip and timestamp
531 $fields= [
532 "login_when" => time(),
533 "login_ip" => $_SERVER['REMOTE_ADDR']
534 ];
535
536 $database->build_and_execute(
537 "update",
538 $this->USERS_TABLE,
539 $fields,
540 "user_id = ".$authenticated_user['user_id']
541 );
542
543 return true;
544 }
545 else
546 {
547 // User doesn't exists
548 return false;
549 }
550 }
551
552 // Increase the count for login attempts
553
557 public function increase_attempts(): void
558 {
559 $this->test_attempts();
560
561 if (!isset($_SESSION['ATTEMPS']))
562 {
563 $_SESSION['ATTEMPS'] = 0;
564 }
565 else
566 {
567 $_SESSION['ATTEMPS']++;
568 }
569 $this->display_login();
570 }
571
572 // Display the login screen
573
577 public function display_login(): void
578 {
579 // Get language vars
580 global $MESSAGE;
581 global $MENU;
582 global $TEXT;
583
584 // If attempts more than allowed, warn the user
585 if ($_SESSION['ATTEMPS'] > $this->max_attempts)
586 {
587 $this->warn();
588 }
589
590 // Show the login form
591 if ($this->frontend === false)
592 {
593 $login_values = [
594 'ACTION_URL' => $this->login_url,
595 'ATTEMPS' => $this->get_session('ATTEMPS'),
596 'USERNAME' => $this->username,
597 'USERNAME_FIELDNAME' => $this->username_fieldname,
598 'PASSWORD_FIELDNAME' => $this->password_fieldname,
599 'MESSAGE' => $this->message,
600 'LEPTON_URL' => LEPTON_URL,
601 'THEME_URL' => THEME_URL,
602 'LEPTON_VERSION' => LEPTON_VERSION,
603 'LANGUAGE' => strtolower(LANGUAGE),
604 'FORGOTTEN_DETAILS_APP' => $this->forgotten_details_app,
605 'TEXT_FORGOTTEN_DETAILS' => $TEXT['FORGOTTEN_DETAILS'],
606 'TEXT_USERNAME' => $TEXT['USERNAME'],
607 'TEXT_PASSWORD' => $TEXT['PASSWORD'],
608 'TEXT_LOGIN' => $MENU['LOGIN'],
609 'TEXT_HOME' => $TEXT['HOME'],
610 'PAGES_DIRECTORY' => PAGES_DIRECTORY,
611 'SECTION_LOGIN' => $MENU['LOGIN'],
612 'CHARSET' => (defined('DEFAULT_CHARSET')) ? DEFAULT_CHARSET : "utf-8"
613 ];
614
615 $oTWIG = lib_twig_box::getInstance();
616 $oTWIG->loader->prependPath( $this->template_dir, "backend" );
617
618 echo $oTWIG->render(
619 "@backend/".$this->template_file,
620 $login_values
621 );
622 }
623 }
624
625 // Warn user that they have had to many login attempts
626 public function warn()
627 {
628 header('Location: '.$this->warning_url);
629 exit();
630 }
631
639 private function test_attempts()
640 {
641 global $database;
642
643 $database->simple_query("DELETE from `".TABLE_PREFIX."temp` WHERE `temp_time` < '".(time()-3600)."'");
644
645 $browser_fingerprint = hash(self::FINGERPRINT_HASH_ALGO, $_SERVER['HTTP_USER_AGENT'] );
646 $ip_fingerprint = hash(self::FINGERPRINT_HASH_ALGO, $_SERVER['REMOTE_ADDR'] );
647
648 $info = [];
649 $database->execute_query(
650 "SELECT * FROM `" . TABLE_PREFIX . "temp` WHERE `temp_ip` = '" . $ip_fingerprint . "'",
651 true,
652 $info,
653 false
654 );
655
656 if (empty($info))
657 {
658 // no entry for this ip
659 $fields = [
660 'temp_ip' => $ip_fingerprint,
661 'temp_browser' => $browser_fingerprint,
662 'temp_time' => TIME(),
663 'temp_count' => 1,
664 'temp_active' => 1
665 ];
666
667 $database->build_and_execute(
668 'insert',
669 TABLE_PREFIX."temp",
670 $fields
671 );
672
673 }
674 else
675 {
676 // is active?
677 if (intval($info['temp_active']) == 0)
678 {
679 if ($info['temp_time'] + 3600 <= time())
680 {
681 // zeit abgelaufen ... counter wieder auf 1
682 $fields = [
683 'temp_active' => 1,
684 'temp_count' => 1,
685 'temp_time' => TIME()
686 ];
687
688 $database->build_and_execute(
689 'update',
690 TABLE_PREFIX."temp",
691 $fields,
692 "`temp_id`='" . $info['temp_id'] . "'"
693 );
694
695 }
696 else
697 {
698 // In the time-range!
699 $this->warn();
700 }
701
702 }
703 else
704 {
705 $actual_count = ++$info['temp_count'];
706
707 if ($actual_count > $this->max_attempts)
708 {
709 // Too mutch attempts
710 $fields = [
711 'temp_active' => 0,
712 'temp_time' => TIME()
713 ];
714
715 $database->build_and_execute(
716 'update',
717 TABLE_PREFIX."temp",
718 $fields,
719 "`temp_id`='" . $info['temp_id'] . "'"
720 );
721
722 $this->warn();
723
724 }
725 else
726 {
727 // Insert the actual couter with the current time
728 $fields = [
729 'temp_count' => $actual_count,
730 'temp_time' => TIME()
731 ];
732
733 $database->build_and_execute(
734 'update',
735 TABLE_PREFIX."temp",
736 $fields,
737 "`temp_id`='" . $info['temp_id'] . "'"
738 );
739 }
740 }
741 }
742 }
743}
lib_twig_box $oTWIG
static getInstance(array &$settings=[])
const FINGERPRINT_HASH_ALGO
__construct(array $config_array=[])
string $redirect_url
static getInstance(array $config_array=[])
static getInstance()
const THEME_PATH
$database
Definition constants.php:52