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