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