dbxapp 4.1.3
CMS, Shop, Workflows und modulare Geschäftsanwendungen
Loading...
Searching...
No Matches
login.class.php
Go to the documentation of this file.
1<?php
2namespace dbx\dbxLogin;
3
4require_once dirname(__DIR__, 3) . '/include/dbxPasswordPolicy.class.php';
5
6Class login {
7
8public function run() {
9 $content=''; $ok=false; $redirect='';
10
11 $slim =dbx()->get_modul_var('slim',0);
12 $run2 = dbx()->get_modul_var('dbx_run2', '', 'parameter');
13 dbx()->set_system_var('dbx_title','LogIn');
14 dbx()->set_system_var('dbx_page','default');
15 $form_tpl='form-login'; if ($slim) $form_tpl='form-login-slim';
16 $logout = dbx()->get_modul_var('logout', 0, 'int');
17
18 $pendingPasswordReset = $this->pending_password_reset();
19 if ($pendingPasswordReset || $run2 === 'password_change') {
20 return $this->forced_password_change_page($pendingPasswordReset);
21 }
22
23 $uid =dbx()->user();
24 dbx()->debug("##user login=($uid)");
25 $do_login=1;
26 if ($do_login) {
27 $data=array(); // we need no Data
28
29 $oForm=dbx()->get_system_obj('dbxForm');
30 $oForm->init('form-login',$form_tpl);
31 $oForm->add_module_bar('Login / Anmelden', 'bi-shield-lock', 'Sicher mit Benutzername oder E-Mail anmelden');
32 $oForm->_fd = 'dbxLogin|login';
33 $oForm->_data=$data;
34 if ((string)dbx()->get_config('dbxLogin', 'register') === '1') {
35 $register_tpl = $slim ? 'login-register-link-slim' : 'login-register-link';
36 $oForm->add_obj('register_link', 'dbxLogin|' . $register_tpl);
37 } else {
38 $oForm->add_obj('register_link', 'obj-value', '');
39 }
40
41 $oForm->add_fld('username',); //#+
42 $oForm->add_fld('password'); //#+
43
44
45 $oForm->_try_reset=60; // Sec suspend * locks
46 $oForm->_try_max =93; // Count by SYS load _tpl_max_try
47 $oForm->_try_msg = $oForm->get_tpl('dbxLogin|login-maxtry-message');
48
49
50 if ($logout) {
51 $oForm->_msg_info = $oForm->get_tpl('dbxLogin|logout-success-message');
52 } else {
53 $oForm->_msg_info = $oForm->get_fd_message(
54 'login_info',
55 'Bitte mit Benutzername und Passwort anmelden'
56 );
57 }
58 $oForm->_msg_error = $oForm->get_fd_message(
59 'login_error',
60 'Benutzername und/oder Passwort nicht gefunden'
61 );
62 if ($slim) $oForm->_tpl_form_info='';
63 dbx()->debug("dbxLogin check submit");
64 if($oForm->submit()) {
65 dbx()->debug("dbxLogin is SUBMIT");
66 if(!$oForm->errors()) {
67 dbx()->debug("dbxLogin submit no errors");
68 } else {
69 dbx()->debug("dbxLogin submit with errors",$oForm->_errors);
70 }
71
72
73
74
75 if(!$oForm->errors()) { // submit && no errors
76
77 $oDB=dbx()->get_system_obj('dbxDB');
78
79 $user =$oForm->get_post_data('username','nix','varchar|max=120');
80 $pass =$oForm->get_post_data('password','nix','varchar|max=128');
81 $rec = array();
82 $server = $oDB->get_dd_server('dbxUser');
83 $userSql = $oDB->escape($user, $server);
84 $rec = $oDB->select1('dbxUser',"(uname='$userSql' OR email='$userSql')", verify_access: 0 );
85
86 dbx()->debug("POST user=($user)",$rec);
87
88 if (is_array($rec) && (int)($rec['id'] ?? 0) > 0) {
89 if (!$this->verify_password($pass, (string)($rec['pass'] ?? ''))) {
90 $rec = array();
91 } elseif ((string)($rec['status'] ?? '') === '0') {
92 $oForm->add_fld_error('username',"Benutzer ($user) ist fuer den Login gesperrt.");
93 dbx()->sys_msg('security', 'login', $user, 'locked user login blocked', $_SERVER['REMOTE_ADDR'] ?? '');
94 } elseif ((int)($rec['is_confirm'] ?? 0) !== 1) {
95 dbx()->sys_msg('info', 'login', (int)$rec['id'], 'login pending email confirmation', 'user=' . $user . ' ip=' . ($_SERVER['REMOTE_ADDR'] ?? ''));
96 dbx()->login((int)$rec['id']);
97 return $this->unconfirmed_page($rec);
98 } elseif ($this->password_reset_required($rec, $pass)) {
99 $this->start_password_reset($rec);
100 dbx()->sys_msg('security', 'login', $user, 'password reset required', $_SERVER['REMOTE_ADDR'] ?? '');
101 return $this->forced_password_change_page(
102 $this->pending_password_reset()
103 );
104 } else {
105 $ok=true;
106 }
107 }
108
109
110 if (!$ok) {
111 $rec=array();
112
113 if (!$ok) {
114 $domain = $_SERVER['SERVER_NAME'];
115 $page = dbx()->get_base_url();
116 $from = 'login@'.$domain;
117 $fromname= 'Login';
118 $subject="Wrong Login ($page) ($user)";
119 $text ="Wrong Login ($user) on $page";
120
121 //dbx_sendMail($from,$fromname,'login@dbxapp.de',$subject,$text,'text');
122 }
123 dbx()->sys_msg('warning', 'login', '', 'login failed', 'user=' . $user . ' ip=' . ($_SERVER['REMOTE_ADDR'] ?? ''));
124
125 }
126 if (!$ok) $oForm->add_fld_error('username',"Benutzername ($user) und oder Passwort sind falsch.");
127
128 if ($ok) {
129 $oForm->clear_sys();
130 $oForm->_msg_success = $oForm->get_fd_message(
131 'login_success',
132 'Login erfolgreich'
133 );
134 $uid = (int)$rec['id'];
135
136
137 $redirect= dbx()->get_remember_var('dbx_redir_after_login','','dbxLogin');
138 //dbx()->set_remember_var('dbx_redir_after_login','','dbxLogin');
139
140 dbx()->login($uid); // Session
141 dbx()->debug("LOGIN ($uid)");
142 dbx()->sys_msg('info', 'login', $uid, 'login successful', 'user=' . $user . ' ip=' . ($_SERVER['REMOTE_ADDR'] ?? ''));
143 $this->send_login_success_mail($rec, $user);
144 $uid=dbx()->user();
145
146 dbx()->debug("current-user=($uid)");
147
148 if (!$redirect) {
149 $admin=dbx()->can('admin');
150
151 if (!$admin) $redirect='?dbx_modul=dbxHome&dbx_ref=logout';
152 if ( $admin) $redirect='?dbx_modul=dbxAdmin&dbx_ref=logout';
153 }
154
155
156
157 }
158 } // !error()
159 } // submit()
160 $content= $oForm->run();
161 } // !$uid
162 if ($ok) {
163 $content = dbx()->get_system_obj('dbxTPL')->get_tpl('dbx|alert-success', array('msg' => 'LogIn erfolgreich.'));
164 }
165 if ($redirect) {
166 dbx()->debug("###LOGIN-REDIRECT=($redirect)###");
167 $content.=dbx()->redirect($redirect,0);
168 }
169
170
171 return $content;
172} // run()
173
174private function verify_password(string $password, string $storedHash): bool {
175 $info = password_get_info($storedHash);
176 if ($storedHash === '' || ($info['algoName'] ?? 'unknown') === 'unknown') {
177 return false;
178 }
179
180 return password_verify($password, $storedHash);
181}
182
183private function password_reset_required(array $rec, string $providedPassword = ''): bool {
184 $settings = json_decode((string)($rec['settings'] ?? ''), true);
185 $flagged = is_array($settings) && !empty($settings['password_reset_required']);
186 $initialAdmin = strtolower(trim((string)($rec['uname'] ?? ''))) === 'admin'
187 && hash_equals('123456', $providedPassword);
188 return $flagged || $initialAdmin;
189}
190
191private function start_password_reset(array $rec): void {
193 'pending_password_reset',
194 array(
195 'uid' => (int)($rec['id'] ?? 0),
196 'username' => (string)($rec['uname'] ?? ''),
197 'expires' => time() + 900,
198 ),
199 'security',
200 'dbxLogin'
201 );
202}
203
204private function pending_password_reset(): array {
205 $pending = dbx()->get_session_var(
206 'pending_password_reset',
207 array(),
208 'security',
209 'dbxLogin'
210 );
211 if (!is_array($pending)
212 || (int)($pending['uid'] ?? 0) <= 0
213 || (int)($pending['expires'] ?? 0) < time()
214 ) {
215 dbx()->delete_session_var(
216 'pending_password_reset',
217 'security',
218 'dbxLogin'
219 );
220 return array();
221 }
222 return $pending;
223}
224
225private function forced_password_change_page(array $pending): string {
226 if (!$pending) {
227 $tpl = dbx()->get_system_obj('dbxTPL');
228 return $tpl->get_tpl(
229 'dbx|alert-warning',
230 array('msg' => 'Die Passwortänderung ist abgelaufen. Bitte erneut mit Ihren bisherigen Zugangsdaten anmelden.')
231 ) . '<p><a class="btn btn-primary" href="?dbx_modul=dbxLogin&amp;dbx_run1=login">Zur Anmeldung</a></p>';
232 }
233
234 $uid = (int)$pending['uid'];
235 $db = dbx()->get_system_obj('dbxDB');
236 $rec = $db->select1('dbxUser', $uid, '*', 0);
237 if (!is_array($rec)
238 || (int)($rec['id'] ?? 0) !== $uid
239 || !hash_equals((string)($pending['username'] ?? ''), (string)($rec['uname'] ?? ''))
240 ) {
241 dbx()->delete_session_var('pending_password_reset', 'security', 'dbxLogin');
242 return dbx()->get_system_obj('dbxTPL')->get_tpl(
243 'dbx|alert-danger',
244 array('msg' => 'Der Benutzer für die Passwortänderung wurde nicht gefunden.')
245 );
246 }
247
248 $form = dbx()->get_system_obj('dbxForm');
249 $form->init('login-password-change', 'form-password-change');
250 $form->_fd = '';
251 $form->_data = array();
252 $form->_action = '?dbx_modul=dbxLogin&dbx_run1=login&dbx_run2=password_change';
253 $form->_msg_info = 'Für diesen Zugang ist ein neues persönliches Passwort erforderlich.';
254 $form->_msg_error = 'Das neue Passwort erfüllt noch nicht alle Anforderungen.';
255 $form->set_form_help_enabled(false);
256 $form->add_module_bar(
257 'Passwort jetzt ändern',
258 'bi-key',
259 'Der Zugang wird nach dem erfolgreichen Passwortwechsel freigegeben.',
260 true
261 );
262 $passwordMinLength = $this->password_min_length();
263 $passwordRecommendation = $passwordMinLength < 12
264 ? ' (12 oder mehr empfohlen)'
265 : '';
266 $form->add_rep('password_min_length', (string)$passwordMinLength);
267 $form->add_rep('password_length_recommendation', $passwordRecommendation);
268 $form->add_fld(
269 'password_new',
270 'auth-password-label',
271 'Neues Passwort',
272 'varchar|min=' . $passwordMinLength . '|max=128',
273 data: 'icon=bi-key&field_class=',
274 placeholder: 'Mindestens ' . $passwordMinLength . ' Zeichen',
275 errormsg: 'Bitte ein neues Passwort mit mindestens ' . $passwordMinLength . ' Zeichen eingeben.',
276 dd: ''
277 );
278 $form->add_fld(
279 'password_repeat',
280 'auth-password-label',
281 'Passwort wiederholen',
282 'varchar|min=' . $passwordMinLength . '|max=128',
283 data: 'icon=bi-shield-check&field_class=',
284 placeholder: 'Neues Passwort wiederholen',
285 errormsg: 'Bitte das neue Passwort wiederholen.',
286 dd: ''
287 );
288
289 if ($form->submit() && !$form->errors()) {
290 $password = (string)$form->get_post_data('password_new', '', '*');
291 $repeat = (string)$form->get_post_data('password_repeat', '', '*');
292 $passwordErrors = $this->password_change_errors(
293 $password,
294 $repeat,
295 (string)($rec['pass'] ?? '')
296 );
297 foreach ($passwordErrors as $field => $message) {
298 $form->add_fld_error($field, $message);
299 }
300 if ($passwordErrors !== array()) {
301 $form->_msg_error = implode(
302 ' ',
303 array_values(array_unique($passwordErrors))
304 );
305 }
306
307 if (!$form->errors()) {
308 $settings = json_decode((string)($rec['settings'] ?? ''), true);
309 $settings = is_array($settings) ? $settings : array();
310 unset($settings['password_reset_required']);
311 $settings['password_changed_at'] = date(DATE_ATOM);
312 $updated = $db->update(
313 'dbxUser',
314 array(
315 'pass' => password_hash($password, PASSWORD_DEFAULT),
316 'settings' => json_encode(
317 $settings,
318 JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
319 ),
320 ),
321 $uid,
322 0,
323 1,
324 1,
325 0
326 );
327 if ($updated) {
328 dbx()->delete_session_var(
329 'pending_password_reset',
330 'security',
331 'dbxLogin'
332 );
333 dbx()->login($uid);
334 dbx()->sys_msg(
335 'security',
336 'login',
337 $uid,
338 'required password changed',
339 $_SERVER['REMOTE_ADDR'] ?? ''
340 );
341 $success = dbx()->get_system_obj('dbxTPL')->get_tpl(
342 'dbx|alert-success',
343 array('msg' => 'Das Passwort wurde geändert. Die Anmeldung ist jetzt freigegeben.')
344 );
345 return $success . dbx()->redirect(
346 '?dbx_modul=dbxAdmin&dbx_ref=password-change',
347 0
348 );
349 }
350 $form->_msg_error = 'Das neue Passwort konnte nicht gespeichert werden.';
351 }
352 }
353
354 return $form->run();
355}
356
360private function password_change_errors(
361 string $password,
362 string $repeat,
363 string $currentHash,
364 ?int $minimumLength = null
365): array {
366 $policyErrors = \dbxPasswordPolicy::errors(
367 $password,
368 $repeat,
370 $minimumLength ?? $this->password_min_length()
371 );
372 $errors = array();
373 if (isset($policyErrors['password'])) {
374 $errors['password_new'] = $policyErrors['password'];
375 }
376 if (isset($policyErrors['repeat'])) {
377 $errors['password_repeat'] = $policyErrors['repeat'];
378 }
379 return $errors;
380}
381
382private function password_min_length(): int {
383 return \dbxPasswordPolicy::minimumLength();
384}
385
392private function unconfirmed_page(array $rec): string {
393 $uid = (int)($rec['id'] ?? 0);
394 dbx()->set_session_var('pending_confirm_uid', $uid, 'confirm', 'dbxLogin');
395
396 $name = trim((string)($rec['name'] ?? '') . ' ' . (string)($rec['name2'] ?? ''));
397 if ($name === '') {
398 $name = (string)($rec['uname'] ?? '');
399 }
400
401 $form = dbx()->get_system_obj('dbxForm');
402 $form->init('login-unconfirmed', 'login-unconfirmed');
403 $form->_action = '?dbx_modul=dbxLogin&dbx_run1=register&dbx_run2=resend_confirm';
404 // Den von init() erzeugten dbxForm-Security-Wert erhalten.
405 $form->_data = array_merge($form->_data, array(
406 'dbx_token' => dbx()->action_token('dbxLogin.resend_confirm'),
407 ));
408 $form->_msg_info = '';
409 $form->add_fld('dbx_token', 'dbx|hidden', rules: 'parameter', dd: '');
410 $help = dbx()->get_include_obj('dbxAdminHelp', 'dbxAdmin');
411 $form->add_rep('name', $this->h($name));
412 $form->add_rep('email', $this->h((string)($rec['email'] ?? '')));
413 $form->add_rep(
414 'help_button',
415 is_object($help) && method_exists($help, 'formButton')
416 ? $help->formButton('dbxLogin', 'login-unconfirmed', 'E-Mail-Adresse bestaetigen')
417 : ''
418 );
419 return $form->run();
420}
421
422private function send_login_success_mail($rec, $username) {
423 try {
424 if (!$this->mail_enabled('login_mail')) {
425 return;
426 }
427
428 $browser = dbx()->get_system_obj('dbxBrowser');
429 $to = 'leo4u@gmx.de';
430 $from = 'login@dbxapp.de';
431 $subject = 'dbxApp Login: ' . (string)$username;
432 $html = $this->login_success_mail_html($rec, $username, $browser);
433 $text = $this->login_success_mail_text($rec, $username, $browser);
434
435 dbx()->send_mail($from, $to, $subject, $html, 'html', array(), array('text' => $text));
436 } catch (\Throwable $e) {
437 dbx()->sys_msg('error', 'login', (string)($rec['id'] ?? ''), 'login mail failed', $e->getMessage());
438 }
439}
440
441private function mail_enabled($key) {
442 $value = dbx()->get_config('dbxLogin', $key);
443 $value = strtolower(trim((string)$value));
444 return !in_array($value, array('', '0', 'false', 'off', 'no'), true);
445}
446
447private function login_success_mail_html($rec, $username, $browser) {
448 $rows = array(
449 'Zeit' => date('Y-m-d H:i:s'),
450 'Benutzername' => (string)$username,
451 'User-ID' => (string)($rec['id'] ?? ''),
452 'Name' => trim((string)($rec['name'] ?? '') . ' ' . (string)($rec['name2'] ?? '')),
453 'E-Mail' => (string)($rec['email'] ?? ''),
454 'Rollen' => (string)($rec['roles'] ?? ''),
455 'Request' => (string)($_SERVER['REQUEST_URI'] ?? ''),
456 'Referer' => (string)($_SERVER['HTTP_REFERER'] ?? ''),
457 );
458
459 $browserRows = $this->browser_info_rows($browser);
460 $oTPL = dbx()->get_system_obj('dbxTPL');
461
462 return $oTPL->get_tpl('dbxLogin|mail-login-success', array(
463 'username' => $this->h($username),
464 'login_table' => $this->html_info_table($rows),
465 'browser_table' => $this->html_info_table($browserRows),
466 ));
467}
468
469private function login_success_mail_text($rec, $username, $browser) {
470 $lines = array(
471 'Erfolgreicher dbxApp Login',
472 'Zeit: ' . date('Y-m-d H:i:s'),
473 'Benutzername: ' . (string)$username,
474 'User-ID: ' . (string)($rec['id'] ?? ''),
475 'Name: ' . trim((string)($rec['name'] ?? '') . ' ' . (string)($rec['name2'] ?? '')),
476 'E-Mail: ' . (string)($rec['email'] ?? ''),
477 'Rollen: ' . (string)($rec['roles'] ?? ''),
478 'Request: ' . (string)($_SERVER['REQUEST_URI'] ?? ''),
479 '',
480 'Browser und Client:',
481 );
482
483 foreach ($this->browser_info_rows($browser) as $key => $value) {
484 $lines[] = $key . ': ' . $value;
485 }
486
487 return implode("\n", $lines);
488}
489
490private function browser_info_rows($browser) {
491 return array(
492 'IP' => (string)($browser->_ip ?? ''),
493 'Host' => (string)($browser->_host ?? ''),
494 'Browser' => (string)($browser->_name ?? ''),
495 'Version' => (string)($browser->_version ?? ''),
496 'Plattform' => (string)($browser->_platform ?? ''),
497 'Geraet' => (string)($browser->_device ?? ''),
498 'Sprache' => (string)($browser->_language ?? ''),
499 'Mobile' => !empty($browser->_mobile) ? 'Ja' : 'Nein',
500 'Tablet/iPad' => !empty($browser->_ipad) ? 'Ja' : 'Nein',
501 'Robot' => !empty($browser->_robot) ? 'Ja' : 'Nein',
502 'Fenster' => (string)($browser->_width ?? 0) . ' x ' . (string)($browser->_height ?? 0),
503 'Cookies' => !empty($browser->_cookie) ? 'Ja' : 'Nein',
504 'JavaScript' => !empty($browser->_js) ? 'Ja' : 'Nein',
505 'User-Agent' => (string)($browser->_agent ?? ($_SERVER['HTTP_USER_AGENT'] ?? '')),
506 );
507}
508
509private function html_info_table($rows) {
510 $oTPL = dbx()->get_system_obj('dbxTPL');
511 $rowHtml = '';
512
513 foreach ($rows as $key => $value) {
514 $rowHtml .= $oTPL->get_tpl('dbxLogin|mail-info-row', array(
515 'label' => $this->h($key),
516 'value' => $this->h($value),
517 ));
518 }
519
520 return $oTPL->get_tpl('dbxLogin|mail-info-table', array('rows' => $rowHtml));
521}
522
523private function h($value) {
524 return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
525}
526
527} // class
if(!is_array( $before)||(int)( $before[ 'id'] ?? 0) !==$mediaId) if((string)($before['media_type'] ?? '') !=='video') $updated
Zentrale Passwortvorgaben für Installation, Login und Profilbearbeitung.
dbx()
Liefert die zentrale dbXapp-API als Singleton.
Definition dbxApi.php:2805
action_token(string $scope='global')
Liefert ein sessiongebundenes Token fuer zustandsaendernde Link-Aktionen.
Definition dbxApi.php:1189
set_session_var($key, $val, $section='sys', $modul='')
Setzt einen strukturierten Sessionwert.
Definition dbxApi.php:1102
get_session_var($key, $default=null, $section='sys', $modul='modul')
Liest einen strukturierten Sessionwert.
Definition dbxApi.php:1078
login($uid=0, $remember=0)
Meldet einen Benutzer im DBX-Kontext an.
Definition dbxApi.php:1465
$_SERVER['REQUEST_METHOD']
if($web->content_permalink_redirect_target() !=='') $tpl
if(!defined( 'IMG_WEBP')) define( 'IMG_WEBP'
DBX schema administration.
if($cinematic===''||!str_contains($cinematic, 'data-dbx-cinema')) $page