ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
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 return true;
319 }
320
321 protected function checkActivation(ilObjUser $user): bool
322 {
323 return $user->getActive();
324 }
325
326 protected function checkExceededLoginAttempts(ilObjUser $user): bool
327 {
328 if ($user->getId() === ANONYMOUS_USER_ID) {
329 return true;
330 }
331
332 $isInactive = !$user->getActive();
333 if (!$isInactive) {
334 return true;
335 }
336
338 $maxLoginAttempts = $security->getLoginMaxAttempts();
339
340 if (!$maxLoginAttempts) {
341 return true;
342 }
343
344 $numLoginAttempts = \ilObjUser::_getLoginAttempts($user->getId());
345
346 return $numLoginAttempts < $maxLoginAttempts;
347 }
348
349 protected function checkTimeLimit(ilObjUser $user): bool
350 {
351 return $user->checkTimeLimit();
352 }
353
354 protected function checkIp(ilObjUser $user): bool
355 {
356 $clientip = $user->getClientIP();
357 if (trim($clientip) !== '') {
358 $clientip = preg_replace('/[^0-9.?*,:]+/', '', $clientip);
359 $clientip = str_replace(['.', '?', '*', ','], ["\\.", '[0-9]', '[0-9]*', '|'], $clientip);
360
361 ilLoggerFactory::getLogger('auth')->debug('Check ip ' . $clientip . ' against ' . $_SERVER['REMOTE_ADDR']);
362
363 if (!preg_match('/^' . $clientip . '$/', $_SERVER['REMOTE_ADDR'])) {
364 return false;
365 }
366 }
367 return true;
368 }
369
370 protected function checkSimultaneousLogins(ilObjUser $user): bool
371 {
372 $this->logger->debug('Setting prevent simultaneous session is: ' . $this->settings->get('ps_prevent_simultaneous_logins'));
373 return !($this->settings->get('ps_prevent_simultaneous_logins') &&
374 ilObjUser::hasActiveSession($user->getId(), $this->getAuthSession()->getId()));
375 }
376
377 protected function handleAuthenticationFail(): bool
378 {
379 $this->logger->debug('Authentication failed for all authentication methods.');
380
381 $this->handleLoginAttempts();
382
383 return false;
384 }
385
386 protected function handleLoginAttempts(): void
387 {
389 $max_attempts = $security->getLoginMaxAttempts();
390 if ($max_attempts < 1) {
391 return;
392 }
393
394 $auth_determination = ilAuthModeDetermination::_getInstance();
395 if ($this->getCredentials()->getAuthMode() !== '') {
396 $auth_modes = [
397 $this->getCredentials()->getAuthMode()
398 ];
399 } else {
400 $auth_modes = $auth_determination->getAuthModeSequence($this->getCredentials()->getUsername());
401 }
402
403 $usr_id_candidates = [];
404 foreach (array_filter($auth_modes) as $auth_mode) {
405 if ((int) $auth_mode === ilAuthUtils::AUTH_LOCAL) {
406 $usr_id_candidates[] = ilObjUser::_lookupId($this->getCredentials()->getUsername());
407 continue;
408 }
409
412 $this->getCredentials()->getUsername(),
413 false
414 );
415 if (!is_string($login) || $login === '') {
416 continue;
417 }
418
419 $usr_id_candidates[] = ilObjUser::_lookupId($login);
420 }
421
422 $usr_id_candidates = array_values(array_unique(array_filter($usr_id_candidates, intval(...))));
423 $num_deacticated_accounts = 0;
424 foreach ($usr_id_candidates as $usr_id) {
425 if ($usr_id === ANONYMOUS_USER_ID) {
426 continue;
427 }
428
429 $num_login_attempts = ilObjUser::_getLoginAttempts($usr_id);
430
431 if ($num_login_attempts <= $max_attempts) {
433 $this->logger->notice(
434 sprintf(
435 'Incremented login attempts for user %s with id %s.',
436 $this->getCredentials()->getUsername(),
437 $usr_id
438 )
439 );
440 }
441
442 if ($num_login_attempts >= $max_attempts) {
444
445 ++$num_deacticated_accounts;
446 $this->logger->warning(
447 sprintf(
448 'User account %s with id %s set to inactive due to exceeded login attempts.',
449 $this->getCredentials()->getUsername(),
450 $usr_id
451 )
452 );
453 }
454 }
455
456 if ($num_deacticated_accounts > 0) {
457 $this->getStatus()->setReason('auth_err_login_attempts_deactivation');
458 }
459 }
460}
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