ILIAS  trunk Revision v11.0_alpha-1731-gff9cd7e2bd3
All Data Structures Namespaces Files Functions Variables Enumerations Enumerator Modules Pages
class.ilOpenIdConnectSettings.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 
24 {
25  private const STORAGE_ID = 'oidc';
26 
27  public const FILE_STORAGE = 'openidconnect/login_form_image';
28  public const DEFAULT_SCOPE = 'openid';
29  public const LOGIN_ELEMENT_TYPE_TXT = 0;
30  public const LOGIN_ELEMENT_TYPE_IMG = 1;
31  public const LOGIN_ENFORCE = 0;
32  public const LOGIN_STANDARD = 1;
33  public const LOGOUT_SCOPE_GLOBAL = 0;
34  public const LOGOUT_SCOPE_LOCAL = 1;
35 
36  public const URL_VALIDATION_PROVIDER = 0;
37  public const URL_VALIDATION_CUSTOM = 1;
38  public const URL_VALIDATION_NONE = 2;
39 
42 
44  private const IGNORED_USER_FIELDS = [
45  'mail_incoming_mail',
46  'preferences',
47  'hide_own_online_status',
48  'show_users_online',
49  'roles',
50  'upload',
51  'password',
52  'username',
53  'language',
54  'skin_style',
55  'interests_general',
56  'interests_help_offered',
57  'interests_help_looking',
58  'bs_allow_to_contact_me',
59  'chat_osc_accept_msg',
60  'chat_broadcast_typing',
61  ];
62 
63  private static ?self $instance = null;
64 
65  private readonly ilSetting $storage;
66  private readonly Filesystem $filesystem;
67  private bool $active = false;
68  private string $provider = '';
69  private string $client_id = '';
70  private string $secret = '';
71  private int $login_element_type = self::LOGIN_ELEMENT_TYPE_TXT;
72  private ?string $login_element_img_name = null;
73  private ?string $login_element_text = null;
74  private int $login_prompt_type = self::LOGIN_ENFORCE;
75  private ?int $logout_scope = null;
76  private bool $custom_session = false;
77  private int $session_duration = 60;
78  private ?bool $allow_sync = null;
79  private ?int $role = null;
80  private string $uid = '';
82  private array $profile_map = [];
84  private array $profile_update_map = [];
86  private array $role_mappings = [];
88  private array $additional_scopes = [];
89  private int $validate_scopes = self::URL_VALIDATION_PROVIDER;
90  private ?string $custom_discovery_url = null;
91  private ilLanguage $lng;
93 
94  private function __construct()
95  {
96  global $DIC;
97 
98  $this->storage = new ilSetting(self::STORAGE_ID);
99  $this->filesystem = $DIC->filesystem()->web();
100  $this->lng = $DIC->language();
101  $this->udf = ilUserDefinedFields::_getInstance();
102  $this->load();
103  }
104 
105  public static function getInstance(): self
106  {
107  if (self::$instance === null) {
108  self::$instance = new self();
109  }
110 
111  return self::$instance;
112  }
113 
114  public function setActive(bool $active): void
115  {
116  $this->active = $active;
117  }
118 
119  public function getActive(): bool
120  {
121  return $this->active;
122  }
123 
124  public function setProvider(string $url): void
125  {
126  $this->provider = $url;
127  }
128 
129  public function getProvider(): string
130  {
131  return $this->provider;
132  }
133 
134  public function setClientId(string $client_id): void
135  {
136  $this->client_id = $client_id;
137  }
138 
139  public function getClientId(): string
140  {
141  return $this->client_id;
142  }
143 
144  public function setSecret(string $secret): void
145  {
146  $this->secret = $secret;
147  }
148 
149  public function getSecret(): string
150  {
151  return $this->secret;
152  }
153 
154  public function setLoginElementType(int $type): void
155  {
156  $this->login_element_type = $type;
157  }
158 
159  public function getLoginElementType(): int
160  {
162  }
163 
164  public function setLoginElementImage(string $a_img_name): void
165  {
166  $this->login_element_img_name = $a_img_name;
167  }
168 
169  public function getLoginElementImage(): string
170  {
172  }
173 
174  public function setLoginElementText(string $text): void
175  {
176  $this->login_element_text = $text;
177  }
178 
179  public function getLoginElemenText(): string
180  {
182  }
183 
184  public function setLoginPromptType(int $a_type): void
185  {
186  $this->login_prompt_type = $a_type;
187  }
188 
189  public function getLoginPromptType(): int
190  {
192  }
193 
194  public function setLogoutScope(int $a_scope): void
195  {
196  $this->logout_scope = $a_scope;
197  }
198 
199  public function getLogoutScope(): int
200  {
201  return $this->logout_scope;
202  }
203 
204  public function useCustomSession(bool $a_stat): void
205  {
206  $this->custom_session = $a_stat;
207  }
208 
209  public function isCustomSession(): bool
210  {
211  return $this->custom_session;
212  }
213 
214  public function setSessionDuration(int $a_duration): void
215  {
216  $this->session_duration = $a_duration;
217  }
218 
219  public function getSessionDuration(): int
220  {
222  }
223 
224  public function isSyncAllowed(): bool
225  {
226  return $this->allow_sync;
227  }
228 
229  public function allowSync(bool $a_stat): void
230  {
231  $this->allow_sync = $a_stat;
232  }
233 
234  public function setRole(int $role): void
235  {
236  $this->role = $role;
237  }
238 
239  public function getRole(): int
240  {
241  return $this->role;
242  }
243 
244  public function setUidField(string $field): void
245  {
246  $this->uid = $field;
247  }
248 
249  public function getUidField(): string
250  {
251  return $this->uid;
252  }
253 
257  public function getAdditionalScopes(): array
258  {
260  }
261 
265  public function setAdditionalScopes(array $additional_scopes): void
266  {
267  $this->additional_scopes = $additional_scopes;
268  }
269 
273  public function getAllScopes(): array
274  {
276  array_unshift($scopes, self::DEFAULT_SCOPE);
277 
278  return $scopes;
279  }
280 
281  public function deleteImageFile(): void
282  {
283  if ($this->filesystem->has(self::FILE_STORAGE . '/' . $this->getLoginElementImage())) {
284  $this->filesystem->delete(self::FILE_STORAGE . '/' . $this->getLoginElementImage());
285  }
286  }
287 
288  public function hasImageFile(): bool
289  {
290  return
291  $this->getLoginElementImage() !== '' &&
292  $this->filesystem->has(self::FILE_STORAGE . '/' . $this->getLoginElementImage());
293  }
294 
295  public function getImageFilePath(): string
296  {
297  return implode(
298  '/',
299  [
301  self::FILE_STORAGE . '/' . $this->getLoginElementImage()
302  ]
303  );
304  }
305 
309  public function setRoleMappings(array $a_role_mappings): void
310  {
311  $this->role_mappings = $a_role_mappings;
312  }
313 
317  public function getRoleMappings(): array
318  {
319  return $this->role_mappings;
320  }
321 
322  public function getRoleMappingValueForId(int $a_role_id): string
323  {
324  if (isset($this->role_mappings[$a_role_id]['value'])) {
325  return (string) $this->role_mappings[$a_role_id]['value'];
326  }
327 
328  return '';
329  }
330 
331  public function getRoleMappingUpdateForId(int $a_role_id): bool
332  {
333  if (isset($this->role_mappings[$a_role_id]['update'])) {
334  return (bool) $this->role_mappings[$a_role_id]['update'];
335  }
336 
337  return false;
338  }
339 
344  public function validateScopes(string $discoveryURL, array $custom_scopes): array
345  {
346  $result = [];
347  try {
348  $curl = new ilCurlConnection($discoveryURL);
349  $curl->init();
350 
351  $curl->setOpt(CURLOPT_HEADER, 0);
352  $curl->setOpt(CURLOPT_RETURNTRANSFER, true);
353  $curl->setOpt(CURLOPT_TIMEOUT, 4);
354 
355  $response = $curl->exec();
356 
357  if ($curl->getInfo(CURLINFO_RESPONSE_CODE) === 200) {
358  $decoded_response = json_decode($response, false, 512, JSON_THROW_ON_ERROR);
359  $available_scopes = $decoded_response->scopes_supported;
360  array_unshift($custom_scopes, self::DEFAULT_SCOPE);
361  if (!empty(array_diff($custom_scopes, $available_scopes))) {
362  $result = [
363  self::VALIDATION_ISSUE_INVALID_SCOPE,
364  array_diff($custom_scopes, $available_scopes)
365  ];
366  }
367  } else {
368  $result = [
369  self::VALIDATION_ISSUE_DISCOVERY_ERROR,
370  is_string($response) ? $response : var_export($response, true)
371  ];
372  }
373  } finally {
374  if (isset($curl)) {
375  $curl->close();
376  }
377  }
378 
379  return $result;
380  }
381 
382  public function getSupportedScopesFromUrl(string $discoveryURL): bool
383  {
384  try {
385  $curl = new ilCurlConnection($discoveryURL);
386  $curl->init();
387 
388  $curl->setOpt(CURLOPT_HEADER, 0);
389  $curl->setOpt(CURLOPT_RETURNTRANSFER, true);
390  $curl->setOpt(CURLOPT_TIMEOUT, 4);
391 
392  $response = $curl->exec();
393 
394  if ($curl->getInfo(CURLINFO_RESPONSE_CODE) === 200) {
395  $decoded_response = json_decode($response, false, 512, JSON_THROW_ON_ERROR);
396 
397  if (isset($decoded_response->scopes_supported) &&
398  is_array($decoded_response->scopes_supported) &&
399  $decoded_response->scopes_supported !== []) {
400  $available_scopes = $decoded_response->scopes_supported;
401  $this->setAdditionalScopes($available_scopes);
402 
403  return true;
404  }
405  }
406  } finally {
407  if (isset($curl)) {
408  $curl->close();
409  }
410  }
411 
412  return false;
413  }
414 
415  public function save(): void
416  {
417  $this->storage->set('active', (string) ((int) $this->getActive()));
418  $this->storage->set('provider', $this->getProvider());
419  $this->storage->set('client_id', $this->getClientId());
420  $this->storage->set('secret', $this->getSecret());
421  $this->storage->set('scopes', serialize($this->getAdditionalScopes()));
422  $this->storage->set('le_img', $this->getLoginElementImage());
423  $this->storage->set('le_text', $this->getLoginElemenText());
424  $this->storage->set('le_type', (string) $this->getLoginElementType());
425  $this->storage->set('prompt_type', (string) $this->getLoginPromptType());
426  $this->storage->set('logout_scope', (string) $this->getLogoutScope());
427  $this->storage->set('custom_session', (string) ((int) $this->isCustomSession()));
428  $this->storage->set('session_duration', (string) $this->getSessionDuration());
429  $this->storage->set('allow_sync', (string) ((int) $this->isSyncAllowed()));
430  $this->storage->set('role', (string) $this->getRole());
431  $this->storage->set('uid', $this->getUidField());
432 
433  foreach ($this->getProfileMappingFields() as $field => $lng_key) {
434  $this->storage->set('pmap_' . $field, $this->getProfileMappingFieldValue($field));
435  $this->storage->set('pumap_' . $field, (string) ((int) $this->getProfileMappingFieldUpdate($field)));
436  }
437 
438  foreach ($this->udf->getDefinitions() as $definition) {
439  $field = 'udf_' . $definition['field_id'];
440  $this->storage->set('pmap_' . $field, $this->getProfileMappingFieldValue($field));
441  $this->storage->set('pumap_' . $field, (string) ((int) $this->getProfileMappingFieldUpdate($field)));
442  }
443  $this->storage->set('role_mappings', serialize($this->getRoleMappings()));
444  $this->storage->set('validate_scopes', (string) $this->getValidateScopes());
445  if (self::URL_VALIDATION_CUSTOM === $this->getValidateScopes()) {
446  $this->storage->set('custom_discovery_url', $this->getCustomDiscoveryUrl());
447  } else {
448  $this->storage->delete('custom_discovery_url');
449  }
450  }
451 
452  protected function load(): void
453  {
454  foreach ($this->getProfileMappingFields() as $field => $lang_key) {
455  $this->profile_map[$field] = (string) $this->storage->get('pmap_' . $field, '');
456  $this->profile_update_map[$field] = (bool) $this->storage->get('pumap_' . $field, '0');
457  }
458  foreach ($this->udf->getDefinitions() as $definition) {
459  $field = 'udf_' . $definition['field_id'];
460  $this->profile_map[$field] = (string) $this->storage->get('pmap_' . $field, '');
461  $this->profile_update_map[$field] = (bool) $this->storage->get('pumap_' . $field, '0');
462  }
463 
464  $this->setActive((bool) $this->storage->get('active', '0'));
465  $this->setProvider($this->storage->get('provider', ''));
466  $this->setClientId($this->storage->get('client_id', ''));
467  $this->setSecret($this->storage->get('secret', ''));
468  $this->setAdditionalScopes(
469  (array) unserialize(
470  $this->storage->get('scopes', serialize([])),
471  ['allowed_classes' => false]
472  )
473  );
474  $this->setLoginElementImage($this->storage->get('le_img', ''));
475  $this->setLoginElementText((string) $this->storage->get('le_text'));
476  $this->setLoginElementType((int) $this->storage->get('le_type'));
477  $this->setLoginPromptType((int) $this->storage->get('prompt_type', (string) self::LOGIN_ENFORCE));
478  $this->setLogoutScope((int) $this->storage->get('logout_scope', (string) self::LOGOUT_SCOPE_GLOBAL));
479  $this->useCustomSession((bool) $this->storage->get('custom_session', '0'));
480  $this->setSessionDuration((int) $this->storage->get('session_duration', '60'));
481  $this->allowSync((bool) $this->storage->get('allow_sync', '0'));
482  $this->setRole((int) $this->storage->get('role', '0'));
483  $this->setUidField((string) $this->storage->get('uid', ''));
484  $this->setRoleMappings(
485  (array) unserialize(
486  $this->storage->get('role_mappings', serialize([])),
487  ['allowed_classes' => false]
488  )
489  );
490  $this->setValidateScopes((int) $this->storage->get('validate_scopes', (string) self::URL_VALIDATION_PROVIDER));
491  if (self::URL_VALIDATION_CUSTOM === $this->getValidateScopes()) {
492  $this->setCustomDiscoveryUrl($this->storage->get('custom_discovery_url'));
493  }
494  }
495 
496  public function getProfileMappingFieldValue(string $field): string
497  {
498  return (string) ($this->profile_map[$field] ?? '');
499  }
500 
501  public function clearProfileMaps(): void
502  {
503  $this->profile_map = [];
504  $this->profile_update_map = [];
505  }
506 
507  public function setProfileMappingFieldValue(string $field, string $value): void
508  {
509  $this->profile_map[$field] = $value;
510  }
511 
512  public function getProfileMappingFieldUpdate(string $field): bool
513  {
514  return (bool) ($this->profile_update_map[$field] ?? false);
515  }
516 
517  public function setProfileMappingFieldUpdate(string $field, bool $value): void
518  {
519  $this->profile_update_map[$field] = $value;
520  }
521 
522  public function setValidateScopes(int $validation_mode): void
523  {
524  $this->validate_scopes = $validation_mode;
525  }
526 
527  public function getValidateScopes(): int
528  {
529  return $this->validate_scopes;
530  }
531 
532  public function setCustomDiscoveryUrl(?string $discoveryUrl): void
533  {
534  $this->custom_discovery_url = $discoveryUrl;
535  }
536 
537  public function getCustomDiscoveryUrl(): ?string
538  {
540  }
541 
545  public function getProfileMappingFields(): array
546  {
547  $mapping_fields = [];
548  $usr_profile = new ilUserProfile();
549 
550  foreach ($usr_profile->getStandardFields() as $id => $definition) {
551  if (in_array($id, self::IGNORED_USER_FIELDS, true)) {
552  continue;
553  }
554  $mapping_fields[$id] = $this->lng->txt($id);
555  }
556 
557  return $mapping_fields;
558  }
559 }
static getWebspaceDir(string $mode="filesystem")
get webspace directory
getSupportedScopesFromUrl(string $discoveryURL)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Additional user data fields definition.
$scopes
Definition: ltitoken.php:96
$response
Definition: xapitoken.php:93
Class ilUserProfile.
setProfileMappingFieldValue(string $field, string $value)
$url
Definition: shib_logout.php:66
setCustomDiscoveryUrl(?string $discoveryUrl)
setRoleMappings(array $a_role_mappings)
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
global $DIC
Definition: shib_login.php:22
setProfileMappingFieldUpdate(string $field, bool $value)
validateScopes(string $discoveryURL, array $custom_scopes)
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
setAdditionalScopes(array $additional_scopes)