ILIAS  trunk Revision v12.0_alpha-377-g3641b37b9db
class.ilAuthFrontend.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
24{
25 public const string MIG_EXTERNAL_ACCOUNT = 'mig_ext_account';
26 public const string MIG_TRIGGER_AUTHMODE = 'mig_trigger_auth_mode';
27 public const string MIG_DESIRED_AUTHMODE = 'mig_desired_auth_mode';
28
32
36 private array $providers;
39
41
46 {
47 global $DIC;
48 $this->logger = $DIC->logger()->auth();
49 $this->settings = $DIC->settings();
50 $this->lng = $DIC->language();
51 $this->ilAppEventHandler = $DIC->event();
52
53 $this->auth_session = $session;
54 $this->credentials = $credentials;
55 $this->status = $status;
56 $this->providers = $providers;
57
58 $this->user_profile = $DIC['user']->getProfile();
59 }
60
61 public function getAuthSession(): ilAuthSession
62 {
64 }
65
67 {
68 return $this->credentials;
69 }
70
74 public function getProviders(): array
75 {
76 return $this->providers;
77 }
78
79 public function getStatus(): ilAuthStatus
80 {
81 return $this->status;
82 }
83
84 public function resetStatus(): void
85 {
86 $this->getStatus()->setStatus(ilAuthStatus::STATUS_UNDEFINED);
87 $this->getStatus()->setReason('');
88 $this->getStatus()->setAuthenticatedUserId(ANONYMOUS_USER_ID);
89 }
90
91 public function migrateAccount(ilAuthSession $session): bool
92 {
93 if (!$session->isAuthenticated()) {
94 $this->logger->warning('Desired user account is not authenticated');
95 return false;
96 }
97 $user = ilObjectFactory::getInstanceByObjId($session->getUserId(), false);
98
99 if (!$user instanceof ilObjUser) {
100 $this->logger->info('Cannot instantiate user account for account migration: ' . $session->getUserId());
101 return false;
102 }
103
104 $user->setAuthMode(ilSession::get(static::MIG_DESIRED_AUTHMODE));
105
106 $this->logger->debug('new auth mode is: ' . ilSession::get(self::MIG_DESIRED_AUTHMODE));
107
108 $user->setExternalAccount(ilSession::get(static::MIG_EXTERNAL_ACCOUNT));
109 $user->update();
110
111 foreach ($this->getProviders() as $provider) {
113 $this->logger->warning('Provider: ' . get_class($provider) . ' does not support account migration.');
114 throw new InvalidArgumentException('Invalid auth provider given.');
115 }
116 $this->getCredentials()->setUsername(ilSession::get(static::MIG_EXTERNAL_ACCOUNT));
117 $provider->migrateAccount($this->getStatus());
119 return $this->handleAuthenticationSuccess($provider);
120 }
121 }
122 return $this->handleAuthenticationFail();
123 }
124
125 public function migrateAccountNew(): bool
126 {
127 foreach ($this->providers as $provider) {
129 $this->logger->warning('Provider: ' . get_class($provider) . ' does not support account migration.');
130 throw new InvalidArgumentException('Invalid auth provider given.');
131 }
132 $provider->createNewAccount($this->getStatus());
133
134 if ($provider instanceof ilAuthProviderInterface &&
136 return $this->handleAuthenticationSuccess($provider);
137 }
138 }
139 return $this->handleAuthenticationFail();
140 }
141
142
143 public function authenticate(): bool
144 {
145 foreach ($this->getProviders() as $provider) {
146 $this->resetStatus();
147
148 $this->logger->debug('Trying authentication against: ' . get_class($provider));
149
150 $provider->doAuthentication($this->getStatus());
151
152 $this->logger->debug('Authentication user id: ' . $this->getStatus()->getAuthenticatedUserId());
153
154 switch ($this->getStatus()->getStatus()) {
156 return $this->handleAuthenticationSuccess($provider);
157
159 $this->logger->notice('Account migration required.');
160 if ($provider instanceof ilAuthProviderAccountMigrationInterface) {
161 return $this->handleAccountMigration($provider);
162 }
163
164 $this->logger->error('Authentication migratittion required but provider does not support interface' . get_class($provider));
165 break;
167 default:
168 $this->logger->debug('Authentication failed against: ' . get_class($provider));
169 break;
170 }
171 }
172 return $this->handleAuthenticationFail();
173 }
174
176 {
177 $this->logger->debug('Trigger auth mode: ' . $provider->getTriggerAuthMode());
178 $this->logger->debug('Desired auth mode: ' . $provider->getUserAuthModeName());
179 $this->logger->debug('External account: ' . $provider->getExternalAccountName());
180
181 $this->getStatus()->setAuthenticatedUserId(ANONYMOUS_USER_ID);
182 #$this->getStatus()->setStatus(ilAuthStatus::STATUS_AUTHENTICATED);
183
184 ilSession::set(static::MIG_TRIGGER_AUTHMODE, $provider->getTriggerAuthMode());
185 ilSession::set(static::MIG_DESIRED_AUTHMODE, $provider->getUserAuthModeName());
186 ilSession::set(static::MIG_EXTERNAL_ACCOUNT, $provider->getExternalAccountName());
187
189
190 return true;
191 }
192
194 {
195 $user = ilObjectFactory::getInstanceByObjId($this->getStatus()->getAuthenticatedUserId(), false);
196
197 $this->getStatus()->setReason('auth_err_invalid_user_account');
198 // reset expired status
199 $this->getAuthSession()->setExpired(false);
200
201 if (!$user instanceof ilObjUser) {
202 $this->logger->error('Cannot instantiate user account with id: ' . $this->getStatus()->getAuthenticatedUserId());
204 $this->getStatus()->setAuthenticatedUserId(ANONYMOUS_USER_ID);
205 return false;
206 }
207
208 if (!$this->checkExceededLoginAttempts($user)) {
209 $this->logger->info('Authentication failed for inactive user with id and too may login attempts: ' . $this->getStatus()->getAuthenticatedUserId());
211 $this->getStatus()->setAuthenticatedUserId(ANONYMOUS_USER_ID);
212 return false;
213 }
214
215 if (!$this->checkActivation($user)) {
216 $this->logger->info('Authentication failed for inactive user with id: ' . $this->getStatus()->getAuthenticatedUserId());
218 $this->getStatus()->setAuthenticatedUserId(ANONYMOUS_USER_ID);
219 return false;
220 }
221
222 // time limit
223 if (!$this->checkTimeLimit($user)) {
224 $this->logger->info('Authentication failed (time limit restriction) for user with id: ' . $this->getStatus()->getAuthenticatedUserId());
225
226 if ($this->settings->get('user_reactivate_code')) {
227 $this->logger->debug('Accout reactivation codes are active');
229 } else {
230 $this->logger->debug('Accout reactivation codes are inactive');
232 $this->getStatus()->setAuthenticatedUserId(ANONYMOUS_USER_ID);
233 }
234 return false;
235 }
236
237 // ip check
238 if (!$this->checkIp($user)) {
239 $this->logger->info('Authentication failed (wrong ip) for user with id: ' . $this->getStatus()->getAuthenticatedUserId());
241 $this->getStatus()->setAuthenticatedUserId(ANONYMOUS_USER_ID);
242 return false;
243 }
244
245 // check simultaneos logins
246 $this->logger->debug('Check simutaneous login');
247 if (!$this->checkSimultaneousLogins($user)) {
248 $this->logger->info('Authentication failed: simultaneous logins forbidden for user: ' . $this->getStatus()->getAuthenticatedUserId());
250 $this->getStatus()->setAuthenticatedUserId(ANONYMOUS_USER_ID);
251 return false;
252 }
253
254 // check if profile is complete
255 if ($this->user_profile->isProfileIncomplete($user)
258 ilLoggerFactory::getLogger('auth')->info('User profile is incomplete.');
259 $user->setProfileIncomplete(true);
260 }
261
262 // redirects in case of error (session pool limit reached)
263 ilSessionControl::handleLoginEvent($user->getLogin(), $this->getAuthSession());
264
265
266 // @todo move to event handling
267 ilOnlineTracking::addUser($user->getId());
268
269 $security_settings = ilSecuritySettings::_getInstance();
270
271 // determine first login of user for setting an indicator
272 // which still is available in PersonalDesktop, Repository, ...
273 // (last login date is set to current date in next step)
274 if (
275 $security_settings->isPasswordChangeOnFirstLoginEnabled() &&
276 $user->getLastLogin() === ''
277 ) {
278 $user->resetLastPasswordChange();
279 }
280
281 if ($user->getLoginAttempts() > 0) {
282 $user->setLoginAttempts(0);
283 }
284 $user->refreshLogin();
285 $user->update();
286
287 $this->logger->info('Successfully authenticated: ' . ilObjUser::_lookupLogin($this->getStatus()->getAuthenticatedUserId()));
288 $this->getAuthSession()->setAuthenticated(true, $this->getStatus()->getAuthenticatedUserId());
289
291
292 ilSession::set('orig_request_target', '');
293
294
295 // --- anonymous/registered user
296 if (PHP_SAPI !== 'cli') {
297 $this->logger->info(
298 'logged in as ' . $user->getLogin() .
299 ', remote:' . $_SERVER['REMOTE_ADDR'] . ':' . $_SERVER['REMOTE_PORT'] .
300 ', server:' . $_SERVER['SERVER_ADDR'] . ':' . $_SERVER['SERVER_PORT']
301 );
302 } else {
303 $this->logger->info(
304 'logged in as ' . $user->getLogin() . ' from CLI'
305 );
306 }
307
308 // finally raise event
309 $this->ilAppEventHandler->raise(
310 'components/ILIAS/Authentication',
311 'afterLogin',
312 [
313 'username' => $user->getLogin()
314 ]
315 );
316
317 $this->getStatus()->setReason('');
318
319 return true;
320 }
321
322 protected function checkActivation(ilObjUser $user): bool
323 {
324 return $user->getActive();
325 }
326
327 protected function checkExceededLoginAttempts(ilObjUser $user): bool
328 {
329 if ($user->getId() === ANONYMOUS_USER_ID) {
330 return true;
331 }
332
333 $isInactive = !$user->getActive();
334 if (!$isInactive) {
335 return true;
336 }
337
339 $maxLoginAttempts = $security->getLoginMaxAttempts();
340
341 if (!$maxLoginAttempts) {
342 return true;
343 }
344
345 $numLoginAttempts = \ilObjUser::_getLoginAttempts($user->getId());
346
347 return $numLoginAttempts < $maxLoginAttempts;
348 }
349
350 protected function checkTimeLimit(ilObjUser $user): bool
351 {
352 return $user->checkTimeLimit();
353 }
354
355 protected function checkIp(ilObjUser $user): bool
356 {
357 $clientip = $user->getClientIP();
358 if (trim($clientip) !== '') {
359 $clientip = preg_replace('/[^0-9.?*,:]+/', '', $clientip);
360 $clientip = str_replace(['.', '?', '*', ','], ["\\.", '[0-9]', '[0-9]*', '|'], $clientip);
361
362 ilLoggerFactory::getLogger('auth')->debug('Check ip ' . $clientip . ' against ' . $_SERVER['REMOTE_ADDR']);
363
364 if (!preg_match('/^' . $clientip . '$/', $_SERVER['REMOTE_ADDR'])) {
365 return false;
366 }
367 }
368 return true;
369 }
370
371 protected function checkSimultaneousLogins(ilObjUser $user): bool
372 {
373 $this->logger->debug('Setting prevent simultaneous session is: ' . $this->settings->get('ps_prevent_simultaneous_logins'));
374 return !($this->settings->get('ps_prevent_simultaneous_logins') &&
375 ilObjUser::hasActiveSession($user->getId(), $this->getAuthSession()->getId()));
376 }
377
378 protected function handleAuthenticationFail(): bool
379 {
380 $this->logger->debug('Authentication failed for all authentication methods.');
381
382 $this->handleLoginAttempts();
383
384 return false;
385 }
386
387 protected function handleLoginAttempts(): void
388 {
390 $max_attempts = $security->getLoginMaxAttempts();
391 if ($max_attempts < 1) {
392 return;
393 }
394
395 $auth_determination = ilAuthModeDetermination::_getInstance();
396 if ($this->getCredentials()->getAuthMode() !== '') {
397 $auth_modes = [
398 $this->getCredentials()->getAuthMode()
399 ];
400 } else {
401 $auth_modes = $auth_determination->getAuthModeSequence($this->getCredentials()->getUsername());
402 }
403
404 $usr_id_candidates = [];
405 foreach (array_filter($auth_modes) as $auth_mode) {
406 if ((int) $auth_mode === ilAuthUtils::AUTH_LOCAL) {
407 $usr_id_candidates[] = ilObjUser::_lookupId($this->getCredentials()->getUsername());
408 continue;
409 }
410
413 $this->getCredentials()->getUsername(),
414 false
415 );
416 if (!is_string($login) || $login === '') {
417 continue;
418 }
419
420 $usr_id_candidates[] = ilObjUser::_lookupId($login);
421 }
422
423 $usr_id_candidates = array_values(array_unique(array_filter($usr_id_candidates, intval(...))));
424 $num_deacticated_accounts = 0;
425 foreach ($usr_id_candidates as $usr_id) {
426 if ($usr_id === ANONYMOUS_USER_ID) {
427 continue;
428 }
429
430 $num_login_attempts = ilObjUser::_getLoginAttempts($usr_id);
431
432 if ($num_login_attempts <= $max_attempts) {
434 $this->logger->notice(
435 sprintf(
436 'Incremented login attempts for user %s with id %s.',
437 $this->getCredentials()->getUsername(),
438 $usr_id
439 )
440 );
441 }
442
443 if ($num_login_attempts >= $max_attempts) {
445
446 ++$num_deacticated_accounts;
447 $this->logger->warning(
448 sprintf(
449 'User account %s with id %s set to inactive due to exceeded login attempts.',
450 $this->getCredentials()->getUsername(),
451 $usr_id
452 )
453 );
454 }
455 }
456
457 if ($num_deacticated_accounts > 0) {
458 $this->getStatus()->setReason('auth_err_invalid_user_account');
459 }
460 }
461}
Global event handler.
raise(string $a_component, string $a_event, array $a_parameter=[])
Raise an event.
const int CONTEXT_ECS
Calendar authentication with auth token.
ilAppEventHandler $ilAppEventHandler
const string MIG_TRIGGER_AUTHMODE
const string MIG_DESIRED_AUTHMODE
checkActivation(ilObjUser $user)
checkIp(ilObjUser $user)
checkExceededLoginAttempts(ilObjUser $user)
checkTimeLimit(ilObjUser $user)
handleAccountMigration(ilAuthProviderAccountMigrationInterface $provider)
ilAuthSession $auth_session
__construct(ilAuthSession $session, ilAuthStatus $status, ilAuthCredentials $credentials, array $providers)
const string MIG_EXTERNAL_ACCOUNT
checkSimultaneousLogins(ilObjUser $user)
ilAuthCredentials $credentials
migrateAccount(ilAuthSession $session)
handleAuthenticationSuccess(ilAuthProviderInterface $provider)
getUserId()
Get authenticated user id.
isAuthenticated()
Check if session is authenticated.
const int STATUS_UNDEFINED
const int STATUS_AUTHENTICATION_FAILED
const int STATUS_CODE_ACTIVATION_REQUIRED
const int STATUS_ACCOUNT_MIGRATION_REQUIRED
const int STATUS_AUTHENTICATED
const int AUTH_LOCAL
static _getAuthModeName($a_auth_key)
static getType()
Get context type.
const CONTEXT_LTI_PROVIDER
static initUserAccount()
Init user with current account id.
language handling
static getLogger(string $a_component_id)
Get component logger.
Component logger with individual log levels by component id.
User class.
static _getLoginAttempts(int $a_usr_id)
static _incrementLoginAttempts(int $a_usr_id)
static _lookupId(string|array $a_user_str)
static hasActiveSession(int $a_user_id, string $a_session_id)
static _lookupLogin(int $a_user_id)
static _checkExternalAuthAccount(string $a_auth, string $a_account, bool $tryFallback=true)
check whether external account and authentication method matches with a user
static _setUserInactive(int $a_usr_id)
static getInstanceByObjId(?int $obj_id, bool $stop_on_error=true)
get an instance of an Ilias object by object id
static addUser(int $a_user_id)
static _getInstance()
Get instance of ilSecuritySettings.
static handleLoginEvent(string $a_login, ilAuthSession $auth_session)
static get(string $a_var)
static dumpToString()
static set(string $a_var, $a_val)
Set a value.
ILIAS Setting Class.
const ANONYMOUS_USER_ID
Definition: constants.php:27
getExternalAccountName()
Get external account name.
getTriggerAuthMode()
Get auth mode which triggered the account migration 2_1 for ldap account migration with server id 1 1...
getUserAuthModeName()
Get user auth mode name ldap_1 for ldap account migration with server id 1 apache for apache auth.
$provider
Definition: ltitoken.php:80
$_SERVER['HTTP_HOST']
Definition: raiseError.php:26
global $DIC
Definition: shib_login.php:26