ILIAS  release_8 Revision v8.24
class.ilAuthProviderLTI.php
Go to the documentation of this file.
1<?php
2
3declare(strict_types=1);
4
28{
29 public const AUTH_MODE_PREFIX = 'lti';
31 private string $lti_context_id = "";
32 private int $ref_id = 0;
33 private ?ilLTITool $provider = null;
34 private ?array $messageParameters = null;
35
41 public static function getAuthModeByKey(string $a_auth_key): string
42 {
43 $auth_arr = explode('_', $a_auth_key);
44 if (count($auth_arr) > 1) {
45 return 'lti_' . $auth_arr[1];
46 }
47 return 'lti';
48 }
49
55 public static function getKeyByAuthMode(string $a_auth_mode)
56 {
57 $auth_arr = explode('_', $a_auth_mode);
58 if (count($auth_arr) > 1) {
59 return ilAuthUtils::AUTH_PROVIDER_LTI . '_' . $auth_arr[1];
60 }
62 }
63
68 public static function getActiveAuthModes(): array
69 {
70 global $ilDB;
71
72 // move to connector
73 $query = 'SELECT consumer_pk from lti2_consumer where enabled = ' . $ilDB->quote(1, 'integer');
74 $res = $ilDB->query($query);
75
76 $sids = array();
77 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
78 $sids[] = $row->consumer_pk;
79 }
80 return $sids;
81 }
82
86 public static function getAuthModes(): array
87 {
88 global $ilDB;
89
90 // move to connector
91 $query = 'SELECT distinct(consumer_pk) consumer_pk from lti2_consumer';
92 $res = $ilDB->query($query);
93
94 $sids = array();
95 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
96 $sids[] = $row->consumer_pk;
97 }
98 return $sids;
99 }
100
106 public static function lookupConsumer(int $a_sid): string
107 {
108 $connector = new ilLTIDataConnector();
109 $consumer = ilLTIPlatform::fromRecordId($a_sid, $connector);
110 return $consumer->getTitle();
111 }
112
118 public static function getServerIdByAuthMode(string $a_auth_mode): ?int
119 {
120 if (self::isAuthModeLTI($a_auth_mode)) {
121 $auth_arr = explode('_', $a_auth_mode);
122 return (int) $auth_arr[1];
123 }
124 return null;
125 }
126
132 public static function isAuthModeLTI(string $a_auth_mode): bool
133 {
134 if (!$a_auth_mode) {
135 ilLoggerFactory::getLogger('ltis')->warning('No auth mode given.');
136 return false;
137 }
138 $auth_arr = explode('_', $a_auth_mode);
139 return ($auth_arr[0] == ilAuthUtils::AUTH_PROVIDER_LTI) and $auth_arr[1];
140 }
141
147 protected function findAuthKeyId(string $a_oauth_consumer_key): int
148 {
149 global $ilDB;
150
151 $query = 'SELECT consumer_pk from lti2_consumer where consumer_key = ' . $ilDB->quote(
152 $a_oauth_consumer_key,
153 'text'
154 );
155 // $query = 'SELECT id from lti_ext_consumer where consumer_key = '.$ilDB->quote($a_oauth_consumer_key,'text');
156 $this->getLogger()->debug($query);
157 $res = $ilDB->query($query);
158
159 $lti_id = 0;
160 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
161 $lti_id = $row->consumer_pk;
162 // $lti_id = $row->id;
163 }
164 $this->getLogger()->debug('External consumer key is: ' . (int) $lti_id);
165 return $lti_id;
166 }
167
173 protected function findAuthPrefix(int $a_lti_id): string
174 {
175 global $ilDB;
176
177 $query = 'SELECT prefix from lti_ext_consumer where id = ' . $ilDB->quote($a_lti_id, 'integer');
178 $this->getLogger()->debug($query);
179 $res = $ilDB->query($query);
180
181 // $prefix = 'lti'.$a_lti_id.'_';
182 $prefix = '';
183 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
184 $prefix = $row->prefix;
185 }
186 $this->getLogger()->debug('LTI prefix: ' . $prefix);
187 return $prefix;
188 }
189
195 protected function findGlobalRole(int $a_lti_id): ?int
196 {
197 global $ilDB;
198
199 $query = 'SELECT role from lti_ext_consumer where id = ' . $ilDB->quote($a_lti_id, 'integer');
200 $this->getLogger()->debug($query);
201 $res = $ilDB->query($query);
202
203 $role = null;
204 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
205 $role = (int) $row->role;
206 }
207 $this->getLogger()->debug('LTI role: ' . $role);
208 return $role;
209 }
210
216 public function doAuthentication(\ilAuthStatus $status): bool
217 {
218 global $DIC;
219 //fix for Ilias Consumer
220 // schneider: required?
221 if ($DIC->http()->wrapper()->post()->has('launch_presentation_document_target') &&
222 $DIC->http()->wrapper()->post()->retrieve(
223 'launch_presentation_document_target',
224 $DIC->refinery()->kindlyTo()->string()
225 )) {
226 // TODO move to session-variable
227 // $_POST['launch_presentation_document_target'] = 'window';
228 }
229
230 $this->dataConnector = new ilLTIDataConnector();
231
232 $lti_provider = new ilLTITool($this->dataConnector);
233 // $lti_provider = new Tool\Tool($this->dataConnector);
234 $ok = true;
235 $lti_provider->handleRequest();
236
237 if (!$ok) {
238 $this->getLogger()->info('LTI authentication failed with message: ' . $lti_provider->reason);
239 $status->setReason($lti_provider->reason);
241 return false;
242 } else {
243 $this->getLogger()->debug('LTI authentication success');
244 }
245
249 //LTI 1.1
250 // sm: this does only load the standard lti date connector, not the ilLTIPlatform with extended data, like prefix.
251 // schneider: not required. platform is already initialized by authenticate function in Tool lib
252 /*
253 $consumer = ilLTIPlatform::fromConsumerKey(
254 $DIC->http()->wrapper()->post()->retrieve('oauth_consumer_key', $DIC->refinery()->kindlyTo()->string()),
255 $this->dataConnector
256 );
257 */
258 $this->provider = $lti_provider;
259 $this->messageParameters = $this->provider->getMessageParameters();
260
261 if (empty($this->messageParameters)) {
262 $status->setReason('empty_lti_message_parameters');
264 return false;
265 }
266
267 $this->ref_id = $this->provider->platform->getRefId();
268 // stores ref_ids of all lti consumer within active LTI User Session
269 $lti_context_ids = ilSession::get('lti_context_ids');
270 // if session object exists only add ref_id if not already exists
271 if (isset($lti_context_ids) && is_array($lti_context_ids)) {
272 if (!in_array($this->ref_id, $lti_context_ids)) {
273 $this->getLogger()->debug("push new lti ref_id: " . $this->ref_id);
274 $lti_context_ids[] = $this->ref_id;
275 ilSession::set('lti_context_ids', $lti_context_ids);
276 $this->getLogger()->debug((string) var_export(ilSession::get('lti_context_ids'), true));
277 }
278 } else {
279 $this->getLogger()->debug("lti_context_ids is not set. Create new array...");
280 ilSession::set('lti_context_ids', [$this->ref_id]);
281 $this->getLogger()->debug((string) var_export(ilSession::get('lti_context_ids'), true));
282 }
283
284 // store POST into Consumer Session
285 // $post = (array) $DIC->http()->wrapper()->post();
286 $post = [];
287 if ($DIC->http()->wrapper()->post()->has('launch_presentation_return_url')) {
288 $post['launch_presentation_return_url'] = $DIC->http()->wrapper()->post()->retrieve('launch_presentation_return_url', $DIC->refinery()->kindlyTo()->string());
289 }
290 if ($DIC->http()->wrapper()->post()->has('launch_presentation_css_url')) {
291 $post['launch_presentation_css_url'] = $DIC->http()->wrapper()->post()->retrieve('launch_presentation_css_url', $DIC->refinery()->kindlyTo()->string());
292 }
293 if ($DIC->http()->wrapper()->post()->has('resource_link_title')) {
294 $post['resource_link_title'] = $DIC->http()->wrapper()->post()->retrieve('resource_link_title', $DIC->refinery()->kindlyTo()->string());
295 }
296
297 ilSession::set('lti_' . $this->ref_id . '_post_data', $post);
298 ilSession::set('lti_init_target', ilObject::_lookupType($this->ref_id, true) . '_' . $this->ref_id);
299
300 // lti service activation
301 if (!$this->provider->platform->enabled) {
302 $this->getLogger()->warning('Consumer is not enabled');
303 $status->setReason('lti_consumer_inactive');
305 return false;
306 }
307 // global activation status
308 if (!$this->provider->platform->getActive()) {
309 $this->getLogger()->warning('Consumer is not active');
310 $status->setReason('lti_consumer_inactive');
312 return false;
313 }
314 $lti_id = $this->provider->platform->getExtConsumerId();
315 if (!$lti_id) {
316 $status->setReason('lti_auth_failed_invalid_key');
318 return false;
319 }
320
321 $this->getLogger()->debug('Using prefix:' . $this->provider->platform->getPrefix());
322
323 $internal_account = $this->findUserId(
324 $this->getCredentials()->getUsername(),
325 (string) $lti_id,
326 $this->provider->platform->getPrefix()
327 );
328
329 if ($internal_account) {
330 $this->updateUser($internal_account, $this->provider->platform);
331 } else {
332 $internal_account = $this->createUser($this->provider->platform);
333 }
334
335 $this->handleLocalRoleAssignments($internal_account, $this->provider->platform);
336
338 $status->setAuthenticatedUserId($internal_account);
339
340 return true;
341 }
342
350 protected function findUserId(string $a_oauth_user, string $a_oauth_id, string $a_user_prefix): int
351 {
353 self::AUTH_MODE_PREFIX . '_' . $a_oauth_id,
354 $a_oauth_user
355 );
356 $user_id = 0;
357 if ($user_name) {
358 $user_id = ilObjUser::_lookupId($user_name);
359 }
360 $this->getLogger()->debug('Found user with auth mode lti_' . $a_oauth_id . ' with user_id: ' . $user_id);
361 return $user_id;
362 }
363
371 protected function updateUser(int $a_local_user_id, ilLTIPlatform $consumer): int
372 {
373 global $ilClientIniFile, $DIC;
374 // if (empty($this->messageParameters)) {
375 // $status->setReason('empty_lti_message_parameters');
376 // $status->setStatus(ilAuthStatus::STATUS_AUTHENTICATION_FAILED);
377 // return false;
378 // }
379 $user_obj = new ilObjUser($a_local_user_id);
380 if (isset($this->messageParameters['lis_person_name_given'])) {
381 $user_obj->setFirstname($this->messageParameters['lis_person_name_given']);
382 } else {
383 $user_obj->setFirstname('-');
384 }
385 if (isset($this->messageParameters['lis_person_name_family'])) {
386 $user_obj->setLastname($this->messageParameters['lis_person_name_family']);
387 } else {
388 $user_obj->setLastname('-');
389 }
390 $user_obj->setEmail($this->messageParameters['lis_person_contact_email_primary']);
391
392 $user_obj->setActive(true);
393
394 $until = $user_obj->getTimeLimitUntil();
395
396 if ($until < (time() + (int) $ilClientIniFile->readVariable('session', 'expire'))) {
397 $user_obj->setTimeLimitFrom(time() - 60);
398 $user_obj->setTimeLimitUntil(time() + (int) $ilClientIniFile->readVariable("session", "expire"));
399 }
400 $user_obj->update();
401 $user_obj->refreshLogin();
402
403 $GLOBALS['DIC']->rbac()->admin()->assignUser($consumer->getRole(), $user_obj->getId());
404 $this->getLogger()->debug('Assigned user to: ' . $consumer->getRole());
405
406 $this->getLogger()->info('Update of lti user with uid: ' . $user_obj->getId() . ' and login: ' . $user_obj->getLogin());
407 return $user_obj->getId();
408 }
409
418 protected function createUser(ilLTIPlatform $consumer): int
419 {
420 global $ilClientIniFile, $DIC;
421 // if (empty($this->messageParameters)) {
422 // $status->setReason('empty_lti_message_parameters');
423 // $status->setStatus(ilAuthStatus::STATUS_AUTHENTICATION_FAILED);
424 // return false;
425 // }
426 $userObj = new ilObjUser();
427 $local_user = ilAuthUtils::_generateLogin($consumer->getPrefix() . '_' . $this->getCredentials()->getUsername());
428
429 $newUser["login"] = $local_user;
430 if(isset($this->messageParameters['lis_person_name_given'])) {
431 $newUser["firstname"] = $this->messageParameters['lis_person_name_given'];
432 } else {
433 $newUser["firstname"] = '-';
434 }
435 if(isset($this->messageParameters['lis_person_name_family'])) {
436 $newUser["lastname"] = $this->messageParameters['lis_person_name_family'];
437 } else {
438 $newUser["lastname"] = '-';
439 }
440 $newUser['email'] = $this->messageParameters['lis_person_contact_email_primary'];
441
442 // set "plain md5" password (= no valid password)
443 // $newUser["passwd"] = "";
444 $newUser["passwd_type"] = ilObjUser::PASSWD_CRYPTED;
445
446 $newUser["auth_mode"] = 'lti_' . $consumer->getExtConsumerId();
447 $newUser['ext_account'] = $this->getCredentials()->getUsername();
448 $newUser["profile_incomplete"] = 0;
449
450 // ILIAS 8
451 //check
452 $newUser["gender"] = 'n';
453 $newUser["title"] = null;
454 $newUser["birthday"] = null;
455 $newUser["institution"] = null;
456 $newUser["department"] = null;
457 $newUser["street"] = null;
458 $newUser["city"] = null;
459 $newUser["zipcode"] = null;
460 $newUser["country"] = null;
461 $newUser["sel_country"] = null;
462 $newUser["phone_office"] = null;
463 $newUser["phone_home"] = null;
464 $newUser["phone_mobile"] = null;
465 $newUser["fax"] = null;
466 $newUser["matriculation"] = null;
467 $newUser["second_email"] = null;
468 $newUser["hobby"] = null;
469 $newUser["client_ip"] = null;
470 $newUser["passwd_salt"] = null;//$newUser->getPasswordSalt();
471 $newUser["latitude"] = null;
472 $newUser["longitude"] = null;
473 $newUser["loc_zoom"] = null;
474 $newUser["last_login"] = null;
475 $newUser["first_login"] = null;
476 $newUser["last_profile_prompt"] = null;
477 $newUser["last_update"] = ilUtil::now();
478 $newUser["create_date"] = ilUtil::now();
479 $newUser["referral_comment"] = null;
480 $newUser["approve_date"] = null;
481 $newUser["agree_date"] = null;
482 $newUser["inactivation_date"] = null;
483 $newUser["time_limit_from"] = null;
484 $newUser["time_limit_until"] = null;
485 $newUser["is_self_registered"] = null;
486 //end to check
487
488 $newUser["passwd_enc_type"] = "";
489 $newUser["active"] = true;
490 $newUser["time_limit_owner"] = 7;
491 $newUser["time_limit_unlimited"] = 0;
492 $newUser["time_limit_message"] = 0;
493 $newUser["passwd"] = " ";
494 // $newUser["last_update"]
495
496 // system data
497 $userObj->assignData($newUser);
498 $userObj->setTitle($userObj->getFullname());
499 $userObj->setDescription($userObj->getEmail());
500
501 // set user language
502 $userObj->setLanguage($consumer->getLanguage());
503
504 // Time limit
505 $userObj->setTimeLimitOwner(7);
506 $userObj->setTimeLimitUnlimited(false);
507 $userObj->setTimeLimitFrom(time() - 5);
508 // todo ?
509 $userObj->setTimeLimitUntil(time() + (int) $ilClientIniFile->readVariable("session", "expire"));
510
511 // Create user in DB
512 $userObj->setOwner(6);
513 $userObj->create();
514 $userObj->setActive(true);
515 // $userObj->updateOwner();
516 $userObj->setLastPasswordChangeTS(time());
517 $userObj->saveAsNew();
518 $userObj->writePrefs();
519
520 $GLOBALS['DIC']->rbac()->admin()->assignUser($consumer->getRole(), $userObj->getId());
521
522 $this->getLogger()->info('Created new lti user with uid: ' . $userObj->getId() . ' and login: ' . $userObj->getLogin());
523 return $userObj->getId();
524 }
525
526 protected function handleLocalRoleAssignments(int $user_id, ilLTIPlatform $consumer): bool
527 {
528 global $DIC;
529 // if (empty($this->messageParameters)) {
530 // $status->setReason('empty_lti_message_parameters');
531 // $status->setStatus(ilAuthStatus::STATUS_AUTHENTICATION_FAILED);
532 // return false;
533 // }
534 //$target_ref_id = $_SESSION['lti_current_context_id'];
535 $target_ref_id = $this->ref_id;
536 $this->getLogger()->info('$target_ref_id: ' . $target_ref_id);
537 if (!$target_ref_id) {
538 $this->getLogger()->warning('No target id given');
539 return false;
540 }
541
542 $obj_settings = new ilLTIProviderObjectSetting($target_ref_id, $consumer->getExtConsumerId());
543
544 // @todo read from lti data
545 //$roles = $DIC->http()->wrapper()->post()->retrieve('roles', $DIC->refinery()->kindlyTo()->string());
546 $roles = $this->messageParameters['roles'];
547
548 if (!strlen($roles)) {
549 $this->getLogger()->warning('No role information given');
550 return false;
551 }
552 $role_arr = explode(',', $roles);
553 foreach ($role_arr as $role_name) {
554 $role_name = trim($role_name);
555 $role_name = str_replace('http://purl.imsglobal.org/vocab/lis/v2/membership#', '', $role_name);
556 switch ($role_name) {
557 case 'Administrator':
558 $this->getLogger()->info('Administrator role handling');
559 if ($obj_settings->getAdminRole()) {
560 $GLOBALS['DIC']->rbac()->admin()->assignUser(
561 $obj_settings->getAdminRole(),
563 );
564 }
565 break;
566
567 case 'Instructor':
568 case 'Mentor':
569 case 'TeachingAssistant':
570 $this->getLogger()->info('Instructor role handling');
571 $this->getLogger()->info('Tutor role for request: ' . $obj_settings->getTutorRole());
572 if ($obj_settings->getTutorRole()) {
573 $GLOBALS['DIC']->rbac()->admin()->assignUser(
574 $obj_settings->getTutorRole(),
576 );
577 }
578 break;
579
580 case 'Member':
581 case 'Learner':
582 $this->getLogger()->info('Member role handling');
583 if ($obj_settings->getMemberRole()) {
584 $GLOBALS['DIC']->rbac()->admin()->assignUser(
585 $obj_settings->getMemberRole(),
587 );
588 }
589 break;
590 default: // ToDo: correct parsing of lti1.3 roles
591 $this->getLogger()->info('default role handling');
592 if ($obj_settings->getMemberRole()) {
593 $GLOBALS['DIC']->rbac()->admin()->assignUser(
594 $obj_settings->getMemberRole(),
596 );
597 }
598 }
599 }
600 return true;
601 }
602}
if(!defined('PATH_SEPARATOR')) $GLOBALS['_PEAR_default_error_mode']
Definition: PEAR.php:64
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getServerIdByAuthMode(string $a_auth_mode)
Get auth id by auth mode.
static getActiveAuthModes()
get all active authmode server ids
findAuthKeyId(string $a_oauth_consumer_key)
find consumer key id
static getKeyByAuthMode(string $a_auth_mode)
Get auth id by auth mode.
static lookupConsumer(int $a_sid)
Lookup consumer title.
handleLocalRoleAssignments(int $user_id, ilLTIPlatform $consumer)
static getAuthModeByKey(string $a_auth_key)
Get auth mode by key.
createUser(ilLTIPlatform $consumer)
create new user @access protected
findAuthPrefix(int $a_lti_id)
find lti id
ilLTIDataConnector $dataConnector
findUserId(string $a_oauth_user, string $a_oauth_id, string $a_user_prefix)
Find user by auth mode and lti id.
updateUser(int $a_local_user_id, ilLTIPlatform $consumer)
update existing user @access protected
static isAuthModeLTI(string $a_auth_mode)
Check if user auth mode is LTI.
findGlobalRole(int $a_lti_id)
find global role of consumer
Base class for authentication providers (ldap, apache, ...)
getLogger()
Get logger.
Auth status implementation.
setReason(string $a_reason)
Set reason.
const STATUS_AUTHENTICATION_FAILED
static _generateLogin(string $a_login)
generate free login by starting with a default string and adding postfix numbers
LTI provider for LTI launch.
static fromRecordId(int $id, ilLTIDataConnector $dataConnector)
Load the platform from the database by its record ID.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
LTI provider for LTI launch.
static getLogger(string $a_component_id)
Get component logger.
User class.
const PASSWD_CRYPTED
static _lookupId($a_user_str)
static _checkExternalAuthAccount(string $a_auth, string $a_account, bool $tryFallback=true)
check whether external account and authentication method matches with a user
static _lookupType(int $id, bool $reference=false)
static get(string $a_var)
static set(string $a_var, $a_val)
Set a value.
static now()
Return current timestamp in Y-m-d H:i:s format.
global $DIC
Definition: feed.php:28
Standard interface for auth provider implementations.
doAuthentication(\ilAuthStatus $status)
Do authentication.
$res
Definition: ltiservices.php:69
$post
Definition: ltitoken.php:49
$query