ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.SettingsGUI.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
23use ILIAS\User\RedirectOnMissingWrite;
24use ILIAS\User\Profile\Fields\ConfigurationRepository as ProfileConfigurationRepository;
26use ILIAS\UI\Factory as UIFactory;
27use ILIAS\UI\Renderer as UIRenderer;
34use Psr\Http\Message\ServerRequestInterface;
35
37{
38 use RedirectOnMissingWrite;
39
40 private $modal = null;
41
42 public function __construct(
43 private readonly \ILIAS\Language\Language $lng,
44 private readonly \ilCtrl $ctrl,
45 private readonly \ilAccess $access,
46 private readonly \ilSetting $settings,
47 private readonly \ilGlobalTemplateInterface $tpl,
48 private readonly UIFactory $ui_factory,
49 private readonly UIRenderer $ui_renderer,
50 private readonly Refinery $refinery,
51 private readonly ServerRequestInterface $request,
52 private readonly ProfileConfigurationRepository $profile_configuration_repository
53 ) {
54
55 }
56
57 public function executeCommand(): void
58 {
59 $this->redirectOnMissingWrite($this->access, $this->ctrl, $this->tpl, $this->lng);
60 $cmd = $this->ctrl->getCmd() . 'Cmd';
61 $this->$cmd();
62 }
63
64 private function showCmd(?StandardForm $form = null): void
65 {
66 if ($form !== null) {
67 $this->tpl->setContent(
68 $this->ui_renderer->render($form)
69 );
70 return;
71 }
72
73 $content = [
74 $this->buildForm()
75 ];
76
77 if ($this->modal !== null) {
78 $content[] = $this->modal;
79 }
80
81 $this->tpl->setContent(
82 $this->ui_renderer->render($content)
83 );
84 }
85
86 private function saveCmd(): void
87 {
88 $form = $this->buildForm()->withRequest($this->request);
89 $data = $form->getData();
90 if ($data === null) {
91 $this->tpl->setOnScreenMessage('failure', $this->lng->txt('form_input_not_valid'));
92 $this->showCmd($form);
93 return;
94 }
95
97
98 // account security settings
99 $security->setPasswordCharsAndNumbersEnabled(
100 $data['password']['password_chars_and_numbers_enabled']
101 );
102 $security->setPasswordSpecialCharsEnabled(
103 $data['password']['password_special_chars_enabled']
104 );
105 $security->setPasswordMinLength(
106 $data['password']['password_min_length']
107 );
108 $security->setPasswordMaxLength(
109 $data['password']['password_max_length']
110 );
111 $security->setPasswordNumberOfUppercaseChars(
112 $data['password']['password_min_uppercase_chars']
113 );
114 $security->setPasswordNumberOfLowercaseChars(
115 $data['password']['password_min_lowercase_chars']
116 );
117 $security->setPasswordMaxAge(
118 $data['password']['password_max_age']
119 );
120 $security->setLoginMaxAttempts(
121 $data['security']['login_max_attempts']
122 );
123 $security->setPreventionOfSimultaneousLogins(
124 $data['security']['prevent_simultaneous_logins']
125 );
126 $security->setPasswordChangeOnFirstLoginEnabled(
127 $data['password']['password_change_on_first_login_enabled']
128 );
129 $security->setPasswordMustNotContainLoginnameStatus(
130 $data['password']['password_must_not_contain_loginame']
131 );
132
133 $security->save();
134
135 \ilUserAccountSettings::getInstance()->enableLocalUserAdministration(
136 $data['general']['local_user_administration']
137 );
138 \ilUserAccountSettings::getInstance()->restrictUserAccess(
139 $data['general']['restrict_search_in_user_accounts']
140 );
142
143 if ($this->profile_configuration_repository->getByClass(Alias::class)->isChangeableByUser()) {
144
145 $this->settings->set(
146 'create_history_loginname',
147 $data['login_name']['create_history_loginname'] ? '1' : '0'
148 );
149 $this->settings->set(
150 'reuse_of_loginnames',
151 $data['login_name']['allow_reuse_of_loginnames'] ? '1' : '0'
152 );
153 $this->settings->set(
154 'loginname_change_blocking_time',
155 $this->refinery->kindlyTo()->string()->transform(
156 $data['login_name']['loginname_change_blocking_time']
157 )
158 );
159 }
160
161 $this->settings->set(
162 'user_reactivate_code',
163 $data['general']['reactivate_by_code'] ? '1' : '0'
164 );
165
166 $this->settings->set(
167 'user_delete_own_account',
168 $data['general']['allow_account_deletion']['allow_account_deletion'] ? '1' : '0'
169 );
170
171 if ($data['general']['allow_account_deletion']['allow_account_deletion']) {
172 $this->settings->set(
173 'user_delete_own_account_email',
174 $data['general']['allow_account_deletion']['notification_email']
175 );
176 }
177
178 $this->settings->set(
179 'tos_withdrawal_usr_deletion',
180 $data['general']['tos_withdrawal_usr_deletion'] ? '1' : '0'
181 );
182
183 $this->settings->set(
184 'dpro_withdrawal_usr_deletion',
185 $data['general']['dpro_withdrawal_usr_deletion'] ? '1' : '0'
186 );
187
188 $this->settings->set(
189 'session_reminder_lead_time',
190 $data['general']['session_reminder_lead_time']
191 );
192
193 $this->settings->set(
194 'password_assistance',
195 $data['password']['password_assistance'] ? '1' : '0'
196 );
197
198 $this->settings->set(
199 'letter_avatars',
200 $data['login_name']['letter_avatars'] ? '1' : '0'
201 );
202
203 if ($this->needsPasswortResetPrompt($data['password']['password_policy_hash'], $security)) {
204 $this->askForPasswordReset();
205 return;
206 }
207
208 $this->tpl->setOnScreenMessage('success', $this->lng->txt('saved_successfully'));
209 $this->showCmd();
210 }
211
212 private function forcePasswordResetCmd(): void
213 {
214 LocalUserPasswordManager::getInstance()->resetLastPasswordChangeForLocalUsers();
215
216 $this->tpl->setOnScreenMessage('success', $this->lng->txt('ps_passwd_policy_change_force_user_reset_succ'), true);
217 $this->showCmd();
218 }
219
220 private function needsPasswortResetPrompt(
221 string $password_settings_hash_from_form,
222 \ilSecuritySettings $security
223 ): bool {
224 if ($password_settings_hash_from_form === '') {
225 return false;
226 }
227
228 return $this->getPasswordPolicySettingsHash($security) !== $password_settings_hash_from_form;
229 }
230
231 private function askForPasswordReset(): void
232 {
233 $this->modal = $this->ui_factory->modal()->interruptive(
234 $this->lng->txt('ps_password_force_user_reset'),
235 $this->lng->txt('ps_passwd_policy_changed_force_user_reset'),
236 $this->ctrl->getFormActionByClass(self::class, 'forcePasswordReset')
237 )->withActionButtonLabel($this->lng->txt('yes'))
238 ->withCancelButtonLabel($this->lng->txt('no'));
239
240 $this->modal = $this->modal->withOnLoad($this->modal->getShowSignal());
241 $this->showCmd();
242 }
243
244 private function buildForm(): StandardForm
245 {
246 $security_settings = \ilSecuritySettings::_getInstance();
247
248 return $this->ui_factory->input()->container()->form()->standard(
249 $this->ctrl->getFormActionByClass(
250 [\ilAdministrationGUI::class, \ilObjUserFolderGUI::class, self::class],
251 'save'
252 ),
253 [
254 'general' => $this->buildGeneralSettings(\ilUserAccountSettings::getInstance()),
255 'password' => $this->buildPasswordSettings($security_settings),
256 'security' => $this->buildSecuritySettings($security_settings),
257 'login_name' => $this->buildLoginNameSettings()
258 ]
259 );
260 }
261
262 private function buildGeneralSettings(
263 \ilUserAccountSettings $account_settings
264 ): Section {
265 $ff = $this->ui_factory->input()->field();
266
267 return $ff->section(
268 [
269 'local_user_administration' => $ff->checkbox(
270 $this->lng->txt('enable_local_user_administration'),
271 $this->lng->txt('enable_local_user_administration_info')
272 )->withValue($account_settings->isLocalUserAdministrationEnabled()),
273 'restrict_search_in_user_accounts' => $ff->checkbox(
274 $this->lng->txt('restrict_user_access'),
275 $this->lng->txt('restrict_user_access_info')
276 )->withValue($account_settings->isUserAccessRestricted()),
277 'reactivate_by_code' => $ff->checkbox(
278 $this->lng->txt('user_account_code_setting'),
279 $this->lng->txt('user_account_code_setting_info')
280 )->withValue($this->settings->get('user_reactivate_code') === '1'),
281 'allow_account_deletion' => $ff->optionalGroup(
282 [
283 'notification_email' => $ff->text(
284 $this->lng->txt('user_delete_own_account_notification_email')
285 )->withValue($this->settings->get('user_delete_own_account_email', ''))
286 ],
287 $this->lng->txt('user_allow_delete_own_account')
288 )->withAdditionalTransformation(
289 $this->buildAllowAccountDeletionTrafo()
290 )->withValue(
291 $this->settings->get('user_delete_own_account') === '1'
292 ? [
293 'notification_email' => $this->settings->get('user_delete_own_account_email', '')
294 ] : null
295 ),
296 'tos_withdrawal_usr_deletion' => $ff->checkbox(
297 $this->lng->txt('tos_withdrawal_usr_deletion'),
298 $this->lng->txt('tos_withdrawal_usr_deletion_desc')
299 )->withValue($this->settings->get('tos_withdrawal_usr_deletion') === '1'),
300 'dpro_withdrawal_usr_deletion' => $ff->checkbox(
301 $this->lng->txt('dpro_withdrawal_usr_deletion'),
302 $this->lng->txt('dpro_withdrawal_usr_deletion_desc')
303 )->withValue($this->settings->get('dpro_withdrawal_usr_deletion') === '1'),
304 'session_reminder_lead_time' => $ff->numeric(
305 $this->lng->txt('session_reminder_input'),
306 sprintf(
307 $this->lng->txt('session_reminder_default_lead_time_info'),
311 )
312 )->withRequired(true)
313 ->withAdditionalTransformation($this->refinery->kindlyTo()->string())
314 ->withValue(
315 \ilSessionReminder::byLoggedInUser()->getGlobalSessionReminderLeadTime()
316 )
317 ],
318 $this->lng->txt('general_settings')
319 );
320 }
321
322 private function buildPasswordSettings(
323 \ilSecuritySettings $security_settings
324 ): Section {
325 $ff = $this->ui_factory->input()->field();
326
327 return $ff->section(
328 [
329 'password_change_on_first_login_enabled' => $ff->checkbox(
330 $this->lng->txt('ps_password_change_on_first_login_enabled'),
331 $this->lng->txt('ps_password_change_on_first_login_enabled_info')
332 )->withValue($security_settings->isPasswordChangeOnFirstLoginEnabled()),
333 'password_must_not_contain_loginame' => $ff->checkbox(
334 $this->lng->txt('ps_password_must_not_contain_loginame'),
335 $this->lng->txt('ps_password_must_not_contain_loginame_info')
336 )->withValue($security_settings->getPasswordMustNotContainLoginnameStatus()),
337 'password_chars_and_numbers_enabled' => $ff->checkbox(
338 $this->lng->txt('ps_password_chars_and_numbers_enabled'),
339 $this->lng->txt('ps_password_chars_and_numbers_enabled_info')
340 )->withValue($security_settings->isPasswordCharsAndNumbersEnabled()),
341 'password_special_chars_enabled' => $ff->checkbox(
342 $this->lng->txt('ps_password_special_chars_enabled'),
343 $this->lng->txt('ps_password_special_chars_enabled_info')
344 )->withValue($security_settings->isPasswordSpecialCharsEnabled()),
345 'password_min_length' => $ff->numeric(
346 $this->lng->txt('ps_password_min_length'),
347 $this->lng->txt('ps_password_min_length_info')
348 )->withRequired(true)
349 ->withAdditionalTransformation($this->refinery->int()->isGreaterThan(0))
350 ->withValue($security_settings->getPasswordMinLength()),
351 'password_max_length' => $ff->numeric(
352 $this->lng->txt('ps_password_max_length'),
353 $this->lng->txt('ps_password_max_length_info')
354 )->withRequired(true)
355 ->withValue($security_settings->getPasswordMaxLength()),
356 'password_min_uppercase_chars' => $ff->numeric(
357 $this->lng->txt('ps_password_uppercase_chars_num'),
358 $this->lng->txt('ps_password_uppercase_chars_num_info')
359 )->withRequired(true)
360 ->withValue($security_settings->getPasswordNumberOfUppercaseChars()),
361 'password_min_lowercase_chars' => $ff->numeric(
362 $this->lng->txt('ps_password_lowercase_chars_num'),
363 $this->lng->txt('ps_password_lowercase_chars_num_info')
364 )->withRequired(true)
365 ->withValue($security_settings->getPasswordNumberOfLowercaseChars()),
366 'password_max_age' => $ff->numeric(
367 $this->lng->txt('ps_password_max_age'),
368 $this->lng->txt('ps_password_max_age_info')
369 )->withRequired(true)
370 ->withValue($security_settings->getPasswordMaxAge()),
371 'password_assistance' => $ff->checkbox(
372 $this->lng->txt('enable_password_assistance'),
373 $this->lng->txt('password_assistance_info')
374 )->withValue($this->settings->get('password_assistance') === '1'),
375 'password_policy_hash' => $ff->hidden()->withValue(
376 $this->getPasswordPolicySettingsHash($security_settings)
377 )
378 ],
379 $this->lng->txt('ps_password_settings')
380 )->withAdditionalTransformation($this->buildCheckPasswordMinLengthConstraint())
381 ->withAdditionalTransformation($this->buildCheckPasswordMaxLengthConstraint());
382 }
383
384 private function buildSecuritySettings(
385 \ilSecuritySettings $security_settings
386 ): Section {
387 $ff = $this->ui_factory->input()->field();
388
389 return $ff->section(
390 [
391 'login_max_attempts' => $ff->numeric(
392 $this->lng->txt('ps_login_max_attempts'),
393 $this->lng->txt('ps_login_max_attempts_info')
394 )->withRequired(true)
395 ->withAdditionalTransformation(
396 $this->refinery->int()->isLessThan(\ilSecuritySettings::MAX_LOGIN_ATTEMPTS)
397 )->withValue($security_settings->getLoginMaxAttempts()),
398 'prevent_simultaneous_logins' => $ff->checkbox(
399 $this->lng->txt('ps_prevent_simultaneous_logins'),
400 $this->lng->txt('ps_prevent_simultaneous_logins_info')
401 )->withValue($security_settings->isPreventionOfSimultaneousLoginsEnabled())
402 ],
403 $this->lng->txt('ps_security_protection')
404 );
405 }
406
407 private function buildLoginNameSettings(): Section
408 {
409 $ff = $this->ui_factory->input()->field();
410 $alias_changeable_by_user = $this->profile_configuration_repository->getByClass(Alias::class)->isChangeableByUser();
411 return $ff->section(
412 [
413 'create_history_loginname' => $ff->checkbox(
414 $this->lng->txt('history_loginname'),
415 $alias_changeable_by_user
416 ? $this->lng->txt('loginname_history_info')
417 : $this->lng->txt('activate_in_profile_fields')
418 )->withValue($this->settings->get('create_history_loginname') === '1')
419 ->withDisabled(!$alias_changeable_by_user),
420 'allow_reuse_of_loginnames' => $ff->checkbox(
421 $this->lng->txt('reuse_of_loginnames_contained_in_history'),
422 $alias_changeable_by_user
423 ? $this->lng->txt('reuse_of_loginnames_contained_in_history_info')
424 : $this->lng->txt('activate_in_profile_fields')
425 )->withValue($this->settings->get('reuse_of_loginnames') === '1')
426 ->withDisabled(!$alias_changeable_by_user),
427 'loginname_change_blocking_time' => $ff->numeric(
428 $this->lng->txt('loginname_change_blocking_time'),
429 $alias_changeable_by_user
430 ? $this->lng->txt('loginname_change_blocking_time_info')
431 : $this->lng->txt('activate_in_profile_fields')
432 )->withStepSize(
433 0.00001
434 )->withAdditionalTransformation(
435 $this->refinery->custom()->transformation(
436 fn(?float $v): ?float => $v === null ? null : $v * 86400
437 )
438 )->withValue(
439 (float) $this->settings->get('loginname_change_blocking_time') / 86400
440 )->withDisabled(!$alias_changeable_by_user),
441 'letter_avatars' => $ff->checkbox(
442 $this->lng->txt('usr_letter_avatars'),
443 $this->lng->txt('usr_letter_avatars_info')
444 )->withValue($this->settings->get('letter_avatars') === '1'),
445 ],
446 $this->lng->txt('loginname_settings')
447 );
448 }
449
451 {
452 return $this->refinery->custom()->transformation(
453 function (?array $vs): array {
454 if ($vs === null) {
455 return [
456 'allow_account_deletion' => false
457 ];
458 }
459
460 $vs['allow_account_deletion'] = true;
461 return $vs;
462 }
463 );
464 }
465
467 {
468 return $this->refinery->custom()->constraint(
469 static fn(array $vs): bool => $vs['password_min_length'] > 1 + $vs['password_min_uppercase_chars']
470 + $vs['password_min_lowercase_chars'] + (int) $vs['password_special_chars_enabled']
471 + (int) $vs['password_chars_and_numbers_enabled'],
472 $this->lng->txt('ps_min_value_too_small_for_requirements')
473 );
474 }
475
477 {
478 return $this->refinery->custom()->constraint(
479 static fn(array $vs): bool => $vs['password_min_length'] > $vs['password_max_length'],
480 $this->lng->txt('ps_error_message_password_max_less_min')
481 );
482 }
483
484 private function getPasswordPolicySettingsHash(\ilSecuritySettings $security): string
485 {
486 return md5(
487 implode(
488 '',
489 [
490 'password_must_not_contain_loginame' => $security->getPasswordMustNotContainLoginnameStatus() ? 1 : 0,
491 'password_chars_and_numbers_enabled' => $security->isPasswordCharsAndNumbersEnabled() ? 1 : 0,
492 'password_special_chars_enabled' => $security->isPasswordSpecialCharsEnabled() ? 1 : 0,
493 'password_min_length' => $security->getPasswordMinLength(),
494 'password_max_length' => $security->getPasswordMaxLength(),
495 'password_ucase_chars_num' => $security->getPasswordNumberOfUppercaseChars(),
496 'password_lowercase_chars_num' => $security->getPasswordNumberOfLowercaseChars(),
497 ]
498 )
499 );
500 }
501}
static getInstance()
Singleton method to reduce footprint (included files, created instances)
Builds a Color from either hex- or rgb values.
Definition: Factory.php:31
Builds data types.
Definition: Factory.php:36
buildGeneralSettings(\ilUserAccountSettings $account_settings)
buildPasswordSettings(\ilSecuritySettings $security_settings)
getPasswordPolicySettingsHash(\ilSecuritySettings $security)
buildSecuritySettings(\ilSecuritySettings $security_settings)
needsPasswortResetPrompt(string $password_settings_hash_from_form, \ilSecuritySettings $security)
__construct(private readonly \ILIAS\Language\Language $lng, private readonly \ilCtrl $ctrl, private readonly \ilAccess $access, private readonly \ilSetting $settings, private readonly \ilGlobalTemplateInterface $tpl, private readonly UIFactory $ui_factory, private readonly UIRenderer $ui_renderer, private readonly Refinery $refinery, private readonly ServerRequestInterface $request, private readonly ProfileConfigurationRepository $profile_configuration_repository)
Class ilAccessHandler Checks access for ILIAS objects.
Class ilCtrl provides processing control methods.
static secondsToString(int $seconds, bool $force_with_seconds=false, ?ilLanguage $a_lng=null)
converts seconds to string: Long: 7 days 4 hour(s) ...
Singleton class that stores all security settings.
isPasswordSpecialCharsEnabled()
get boolean if the passwords have to contain special characters
getLoginMaxAttempts()
get the maximum count of login attempts
getPasswordMustNotContainLoginnameStatus()
Return whether the password must not contain the loginname or not.
getPasswordMinLength()
get the minimum length for passwords
getPasswordMaxLength()
get the maximum length for passwords
isPreventionOfSimultaneousLoginsEnabled()
Prevention of simultaneous logins with the same account.
getPasswordNumberOfUppercaseChars()
Returns number of uppercase characters required.
isPasswordCharsAndNumbersEnabled()
get boolean if the passwords have to contain characters and numbers
isPasswordChangeOnFirstLoginEnabled()
get boolean if the passwords have to be changed by users on first login
getPasswordMaxAge()
get the maximum password age
static _getInstance()
Get instance of ilSecuritySettings.
getPasswordNumberOfLowercaseChars()
Returns number of lowercase characters required.
static getSessionExpireValue()
Returns the session expiration value.
ILIAS Setting Class.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
isLocalUserAdministrationEnabled()
Check if local user administration is enabled.
isUserAccessRestricted()
Check if user access is restricted.
A transformation is a function from one datatype to another.
This describes a standard form.
Definition: Standard.php:29
This describes section inputs.
Definition: Section.php:29
withAdditionalTransformation(Transformation $trafo)
Apply a transformation to the content of the input.
An entity that renders components to a string output.
Definition: Renderer.php:31
modal(string $title="", string $cancel_label="")
withValue($value)
Get an input like this with another value displayed on the client side.
Definition: Group.php:61
Interface Observer \BackgroundTasks Contains several chained tasks and infos about them.
global $lng
Definition: privfeed.php:31
if(!file_exists('../ilias.ini.php'))