ILIAS  trunk Revision v11.0_alpha-1769-g99a433fe2dc
All Data Structures Namespaces Files Functions Variables Enumerations Enumerator Modules Pages
class.ilAuthFrontend.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
26 //TODO check why authfront does not include frontendInterface
28 {
29  public const MIG_EXTERNAL_ACCOUNT = 'mig_ext_account';
30  public const MIG_TRIGGER_AUTHMODE = 'mig_trigger_auth_mode';
31  public const MIG_DESIRED_AUTHMODE = 'mig_desired_auth_mode';
32 
33  private ilLogger $logger;
35  private ilLanguage $lng;
36 
40  private array $providers;
43 
45 
46  private bool $authenticated = false;
47 
54  public function __construct(ilAuthSession $session, ilAuthStatus $status, ilAuthCredentials $credentials, array $providers)
55  {
56  global $DIC;
57  $this->logger = $DIC->logger()->auth();
58  $this->settings = $DIC->settings();
59  $this->lng = $DIC->language();
60  $this->ilAppEventHandler = $DIC->event();
61 
62  $this->auth_session = $session;
63  $this->credentials = $credentials;
64  $this->status = $status;
65  $this->providers = $providers;
66 
67  $this->user_profile = new ilUserProfile();
68  }
69 
73  public function getAuthSession(): ilAuthSession
74  {
75  return $this->auth_session;
76  }
77 
81  public function getCredentials(): ilAuthCredentials
82  {
83  return $this->credentials;
84  }
85 
90  public function getProviders(): array
91  {
92  return $this->providers;
93  }
94 
98  public function getStatus(): ilAuthStatus
99  {
100  return $this->status;
101  }
102 
106  public function resetStatus(): void
107  {
108  $this->getStatus()->setStatus(ilAuthStatus::STATUS_UNDEFINED);
109  $this->getStatus()->setReason('');
110  $this->getStatus()->setAuthenticatedUserId(ANONYMOUS_USER_ID);
111  }
112 
117  public function migrateAccount(ilAuthSession $session): bool
118  {
119  if (!$session->isAuthenticated()) {
120  $this->logger->warning('Desired user account is not authenticated');
121  return false;
122  }
123  $user = ilObjectFactory::getInstanceByObjId($session->getUserId(), false);
124 
125  if (!$user instanceof ilObjUser) {
126  $this->logger->info('Cannot instantiate user account for account migration: ' . $session->getUserId());
127  return false;
128  }
129 
130  $user->setAuthMode(ilSession::get(static::MIG_DESIRED_AUTHMODE));
131 
132  $this->logger->debug('new auth mode is: ' . ilSession::get(self::MIG_DESIRED_AUTHMODE));
133 
134  $user->setExternalAccount(ilSession::get(static::MIG_EXTERNAL_ACCOUNT));
135  $user->update();
136 
137  foreach ($this->getProviders() as $provider) {
138  if (!$provider instanceof ilAuthProviderAccountMigrationInterface) {
139  $this->logger->warning('Provider: ' . get_class($provider) . ' does not support account migration.');
140  throw new InvalidArgumentException('Invalid auth provider given.');
141  }
142  $this->getCredentials()->setUsername(ilSession::get(static::MIG_EXTERNAL_ACCOUNT));
143  $provider->migrateAccount($this->getStatus());
145  return $this->handleAuthenticationSuccess($provider);
146  }
147  }
148  return $this->handleAuthenticationFail();
149  }
150 
154  public function migrateAccountNew(): bool
155  {
156  foreach ($this->providers as $provider) {
157  if (!$provider instanceof ilAuthProviderAccountMigrationInterface) {
158  $this->logger->warning('Provider: ' . get_class($provider) . ' does not support account migration.');
159  throw new InvalidArgumentException('Invalid auth provider given.');
160  }
161  $provider->createNewAccount($this->getStatus());
162 
163  if ($provider instanceof ilAuthProviderInterface &&
165  return $this->handleAuthenticationSuccess($provider);
166  }
167  }
168  return $this->handleAuthenticationFail();
169  }
170 
171 
172 
176  public function authenticate(): bool
177  {
178  foreach ($this->getProviders() as $provider) {
179  $this->resetStatus();
180 
181  $this->logger->debug('Trying authentication against: ' . get_class($provider));
182 
183  $provider->doAuthentication($this->getStatus());
184 
185  $this->logger->debug('Authentication user id: ' . $this->getStatus()->getAuthenticatedUserId());
186 
187  switch ($this->getStatus()->getStatus()) {
189  return $this->handleAuthenticationSuccess($provider);
190 
192  $this->logger->notice("Account migration required.");
193  if ($provider instanceof ilAuthProviderAccountMigrationInterface) {
194  return $this->handleAccountMigration($provider);
195  }
196 
197  $this->logger->error('Authentication migratittion required but provider does not support interface' . get_class($provider));
198  break;
200  default:
201  $this->logger->debug('Authentication failed against: ' . get_class($provider));
202  break;
203  }
204  }
205  return $this->handleAuthenticationFail();
206  }
207 
213  {
214  $this->logger->debug('Trigger auth mode: ' . $provider->getTriggerAuthMode());
215  $this->logger->debug('Desired auth mode: ' . $provider->getUserAuthModeName());
216  $this->logger->debug('External account: ' . $provider->getExternalAccountName());
217 
218  $this->getStatus()->setAuthenticatedUserId(ANONYMOUS_USER_ID);
219  #$this->getStatus()->setStatus(ilAuthStatus::STATUS_AUTHENTICATED);
220 
221  ilSession::set(static::MIG_TRIGGER_AUTHMODE, $provider->getTriggerAuthMode());
222  ilSession::set(static::MIG_DESIRED_AUTHMODE, $provider->getUserAuthModeName());
223  ilSession::set(static::MIG_EXTERNAL_ACCOUNT, $provider->getExternalAccountName());
224 
226 
227  return true;
228  }
229 
234  {
235  $user = ilObjectFactory::getInstanceByObjId($this->getStatus()->getAuthenticatedUserId(), false);
236 
237  $this->getStatus()->setReason('auth_err_invalid_user_account');
238  // reset expired status
239  $this->getAuthSession()->setExpired(false);
240 
241  if (!$user instanceof ilObjUser) {
242  $this->logger->error('Cannot instantiate user account with id: ' . $this->getStatus()->getAuthenticatedUserId());
244  $this->getStatus()->setAuthenticatedUserId(ANONYMOUS_USER_ID);
245  return false;
246  }
247 
248  if (!$this->checkExceededLoginAttempts($user)) {
249  $this->logger->info('Authentication failed for inactive user with id and too may login attempts: ' . $this->getStatus()->getAuthenticatedUserId());
251  $this->getStatus()->setAuthenticatedUserId(ANONYMOUS_USER_ID);
252  return false;
253  }
254 
255  if (!$this->checkActivation($user)) {
256  $this->logger->info('Authentication failed for inactive user with id: ' . $this->getStatus()->getAuthenticatedUserId());
258  $this->getStatus()->setAuthenticatedUserId(ANONYMOUS_USER_ID);
259  return false;
260  }
261 
262  // time limit
263  if (!$this->checkTimeLimit($user)) {
264  $this->logger->info('Authentication failed (time limit restriction) for user with id: ' . $this->getStatus()->getAuthenticatedUserId());
265 
266  if ($this->settings->get('user_reactivate_code')) {
267  $this->logger->debug('Accout reactivation codes are active');
269  } else {
270  $this->logger->debug('Accout reactivation codes are inactive');
272  $this->getStatus()->setAuthenticatedUserId(ANONYMOUS_USER_ID);
273  }
274  return false;
275  }
276 
277  // ip check
278  if (!$this->checkIp($user)) {
279  $this->logger->info('Authentication failed (wrong ip) for user with id: ' . $this->getStatus()->getAuthenticatedUserId());
281  $this->getStatus()->setAuthenticatedUserId(ANONYMOUS_USER_ID);
282  return false;
283  }
284 
285  // check simultaneos logins
286  $this->logger->debug('Check simutaneous login');
287  if (!$this->checkSimultaneousLogins($user)) {
288  $this->logger->info('Authentication failed: simultaneous logins forbidden for user: ' . $this->getStatus()->getAuthenticatedUserId());
290  $this->getStatus()->setAuthenticatedUserId(ANONYMOUS_USER_ID);
291  return false;
292  }
293 
294  // check if profile is complete
295  if (
296  $this->user_profile->isProfileIncomplete($user) &&
299  ) {
300  ilLoggerFactory::getLogger('auth')->info('User profile is incomplete.');
301  $user->setProfileIncomplete(true);
302  $user->update();
303  }
304 
305  // redirects in case of error (session pool limit reached)
306  ilSessionControl::handleLoginEvent($user->getLogin(), $this->getAuthSession());
307 
308 
309  // @todo move to event handling
310  ilOnlineTracking::addUser($user->getId());
311 
312  $security_settings = ilSecuritySettings::_getInstance();
313 
314  // determine first login of user for setting an indicator
315  // which still is available in PersonalDesktop, Repository, ...
316  // (last login date is set to current date in next step)
317  if (
318  $security_settings->isPasswordChangeOnFirstLoginEnabled() &&
319  $user->getLastLogin() === ''
320  ) {
321  $user->resetLastPasswordChange();
322  }
323  $user->refreshLogin();
324 
325  if ($user->getLoginAttempts() > 0) {
326  $user->setLoginAttempts(0);
327  $user->update();
328  }
329 
330 
331  $this->logger->info('Successfully authenticated: ' . ilObjUser::_lookupLogin($this->getStatus()->getAuthenticatedUserId()));
332  $this->getAuthSession()->setAuthenticated(true, $this->getStatus()->getAuthenticatedUserId());
333 
335 
336  ilSession::set('orig_request_target', '');
337 
338 
339  // --- anonymous/registered user
340  if (PHP_SAPI !== "cli") {
341  $this->logger->info(
342  'logged in as ' . $user->getLogin() .
343  ', remote:' . $_SERVER['REMOTE_ADDR'] . ':' . $_SERVER['REMOTE_PORT'] .
344  ', server:' . $_SERVER['SERVER_ADDR'] . ':' . $_SERVER['SERVER_PORT']
345  );
346  } else {
347  $this->logger->info(
348  'logged in as ' . $user->getLogin() . ' from CLI'
349  );
350  }
351 
352  // finally raise event
353  $this->ilAppEventHandler->raise(
354  'components/ILIAS/Authentication',
355  'afterLogin',
356  array(
357  'username' => $user->getLogin())
358  );
359 
360  $this->getStatus()->setReason('');
361  return true;
362  }
363 
367  protected function checkActivation(ilObjUser $user): bool
368  {
369  return $user->getActive();
370  }
371 
372  protected function checkExceededLoginAttempts(ilObjUser $user): bool
373  {
374  if ($user->getId() === ANONYMOUS_USER_ID) {
375  return true;
376  }
377 
378  $isInactive = !$user->getActive();
379  if (!$isInactive) {
380  return true;
381  }
382 
383  $security = ilSecuritySettings::_getInstance();
384  $maxLoginAttempts = $security->getLoginMaxAttempts();
385 
386  if (!$maxLoginAttempts) {
387  return true;
388  }
389 
390  $numLoginAttempts = \ilObjUser::_getLoginAttempts($user->getId());
391 
392  return $numLoginAttempts < $maxLoginAttempts;
393  }
394 
398  protected function checkTimeLimit(ilObjUser $user): bool
399  {
400  return $user->checkTimeLimit();
401  }
402 
406  protected function checkIp(ilObjUser $user): bool
407  {
408  $clientip = $user->getClientIP();
409  if (trim($clientip) !== "") {
410  $clientip = preg_replace("/[^0-9.?*,:]+/", "", $clientip);
411  $clientip = str_replace([".", "?", "*", ","], ["\\.", "[0-9]", "[0-9]*", "|"], $clientip);
412 
413  ilLoggerFactory::getLogger('auth')->debug('Check ip ' . $clientip . ' against ' . $_SERVER['REMOTE_ADDR']);
414 
415  if (!preg_match("/^" . $clientip . "$/", $_SERVER["REMOTE_ADDR"])) {
416  return false;
417  }
418  }
419  return true;
420  }
421 
425  protected function checkSimultaneousLogins(ilObjUser $user): bool
426  {
427  $this->logger->debug('Setting prevent simultaneous session is: ' . $this->settings->get('ps_prevent_simultaneous_logins'));
428  return !($this->settings->get('ps_prevent_simultaneous_logins') &&
429  ilObjUser::hasActiveSession($user->getId(), $this->getAuthSession()->getId()));
430  }
431 
432  protected function handleAuthenticationFail(): bool
433  {
434  $this->logger->debug('Authentication failed for all authentication methods.');
435 
436  $this->handleLoginAttempts();
437 
438  return false;
439  }
440 
441  protected function handleLoginAttempts(): void
442  {
443  $security = ilSecuritySettings::_getInstance();
444  $max_attempts = $security->getLoginMaxAttempts();
445  if ($max_attempts < 1) {
446  return;
447  }
448 
449  $auth_determination = ilAuthModeDetermination::_getInstance();
450  if ($this->getCredentials()->getAuthMode() !== '') {
451  $auth_modes = [
452  $this->getCredentials()->getAuthMode()
453  ];
454  } else {
455  $auth_modes = $auth_determination->getAuthModeSequence($this->getCredentials()->getUsername());
456  }
457 
458  $usr_id_candidates = [];
459  foreach (array_filter($auth_modes) as $auth_mode) {
460  if ((int) $auth_mode === ilAuthUtils::AUTH_LOCAL) {
461  $usr_id_candidates[] = ilObjUser::_lookupId($this->getCredentials()->getUsername());
462  continue;
463  }
464 
466  ilAuthUtils::_getAuthModeName($auth_mode),
467  $this->getCredentials()->getUsername(),
468  false
469  );
470  if (!is_string($login) || $login === '') {
471  continue;
472  }
473 
474  $usr_id_candidates[] = ilObjUser::_lookupId($login);
475  }
476 
477  $usr_id_candidates = array_values(array_unique(array_filter($usr_id_candidates, intval(...))));
478  $num_deacticated_accounts = 0;
479  foreach ($usr_id_candidates as $usr_id) {
480  if ($usr_id === ANONYMOUS_USER_ID) {
481  continue;
482  }
483 
484  $num_login_attempts = ilObjUser::_getLoginAttempts($usr_id);
485 
486  if ($num_login_attempts <= $max_attempts) {
488  $this->logger->notice(
489  sprintf(
490  'Incremented login attempts for user %s with id %s.',
491  $this->getCredentials()->getUsername(),
492  $usr_id
493  )
494  );
495  }
496 
497  if ($num_login_attempts >= $max_attempts) {
499 
500  ++$num_deacticated_accounts;
501  $this->logger->warning(
502  sprintf(
503  'User account %s with id %s set to inactive due to exceeded login attempts.',
504  $this->getCredentials()->getUsername(),
505  $usr_id
506  )
507  );
508  }
509  }
510 
511  if ($num_deacticated_accounts > 0) {
512  $this->getStatus()->setReason('auth_err_login_attempts_deactivation');
513  }
514  }
515 }
static hasActiveSession(int $a_user_id, string $a_session_id)
migrateAccount(ilAuthSession $session)
Migrate Account to existing user account.
static get(string $a_var)
Global event handler.
static _getLoginAttempts(int $a_usr_id)
getProviders()
Get providers.
static dumpToString()
checkIp(ilObjUser $user)
Check ip.
Interface of auth credentials.
const ANONYMOUS_USER_ID
Definition: constants.php:27
static getLogger(string $a_component_id)
Get component logger.
const STATUS_AUTHENTICATION_FAILED
handleAuthenticationSuccess(ilAuthProviderInterface $provider)
Handle successful authentication.
checkSimultaneousLogins(ilObjUser $user)
Check simultaneous logins.
Class ilUserProfile.
const CONTEXT_LTI_PROVIDER
isAuthenticated()
Check if session is authenticated.
static _lookupId($a_user_str)
handleAccountMigration(ilAuthProviderAccountMigrationInterface $provider)
Handle account migration.
resetStatus()
Reset status.
checkExceededLoginAttempts(ilObjUser $user)
static _checkExternalAuthAccount(string $a_auth, string $a_account, bool $tryFallback=true)
check whether external account and authentication method matches with a user
static _getAuthModeName($a_auth_key)
getCredentials()
Get auth credentials.
migrateAccountNew()
Create new user account.
ilAuthSession $auth_session
getUserId()
Get authenticated user id.
$provider
Definition: ltitoken.php:80
Standard interface for auth provider implementations.
authenticate()
Try to authenticate user.
static addUser(int $a_user_id)
checkTimeLimit(ilObjUser $user)
Check time limit.
checkActivation(ilObjUser $user)
Check activation.
static _setUserInactive(int $a_usr_id)
$_SERVER['HTTP_HOST']
Definition: raiseError.php:26
getTriggerAuthMode()
Get auth mode which triggered the account migration 2_1 for ldap account migration with server id 1 1...
global $DIC
Definition: shib_login.php:22
ilUserProfile $user_profile
const CONTEXT_ECS
Calendar authentication with auth token.
ilAuthCredentials $credentials
static handleLoginEvent(string $a_login, ilAuthSession $auth_session)
when current session is allowed to be created it marks it with type regarding to the sessions user co...
static getInstanceByObjId(?int $obj_id, bool $stop_on_error=true)
get an instance of an Ilias object by object id
ilAppEventHandler $ilAppEventHandler
const STATUS_CODE_ACTIVATION_REQUIRED
static _incrementLoginAttempts(int $a_usr_id)
__construct(ilAuthSession $session, ilAuthStatus $status, ilAuthCredentials $credentials, array $providers)
static getType()
Get context type.
static initUserAccount()
Init user with current account id.
getAuthSession()
Get auth session.
getExternalAccountName()
Get external account name.
Auth status implementation.
static _getInstance()
Get instance of ilSecuritySettings.
const STATUS_ACCOUNT_MIGRATION_REQUIRED
static set(string $a_var, $a_val)
Set a value.
getUserAuthModeName()
Get user auth mode name ldap_1 for ldap account migration with server id 1 apache for apache auth...
raise(string $a_component, string $a_event, array $a_parameter=[])
Raise an event.
static _lookupLogin(int $a_user_id)