ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
class.ilUserPasswordManager.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 {
23  private const MIN_SALT_SIZE = 16;
24 
25  private static ?self $instance = null;
26 
28  private ?ilSetting $settings = null;
29  private ?ilDBInterface $db = null;
30  private ?string $encoderName = null;
31 
38  public function __construct(array $config = [])
39  {
40  if (!empty($config)) {
41  foreach ($config as $key => $value) {
42  switch (strtolower($key)) {
43  case 'settings':
44  $this->setSettings($value);
45  break;
46  case 'db':
47  $this->setDb($value);
48  break;
49  case 'password_encoder':
50  $this->setEncoderName($value);
51  break;
52  case 'encoder_factory':
53  $this->setEncoderFactory($value);
54  break;
55  }
56  }
57  }
58 
59  if (!$this->getEncoderName()) {
60  throw new ilUserException(sprintf(
61  '"password_encoder" must be set in %s.',
62  print_r($config, true)
63  ));
64  }
65 
66  if (!($this->getEncoderFactory() instanceof ilUserPasswordEncoderFactory)) {
67  throw new ilUserException(sprintf(
68  '"encoder_factory" must be instance of ilUserPasswordEncoderFactory and set in %s.',
69  print_r($config, true)
70  ));
71  }
72  }
73 
79  public static function getInstance(): self
80  {
81  global $DIC;
82 
83  if (self::$instance instanceof self) {
84  return self::$instance;
85  }
86 
87  $password_manager = new ilUserPasswordManager(
88  [
89  'encoder_factory' => new ilUserPasswordEncoderFactory(
90  [
91  'default_password_encoder' => 'bcryptphp', // bcrypt (native PHP impl.) is still the default for the factory
92  'memory_cost' => 19_456, // Recommended: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id
93  'ignore_security_flaw' => true,
94  'data_directory' => ilFileUtils::getDataDir()
95  ]
96  ),
97  'password_encoder' => 'argon2id', // https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id
98  'settings' => $DIC->isDependencyAvailable('settings') ? $DIC->settings() : null,
99  'db' => $DIC->database(),
100  ]
101  );
102 
103  self::$instance = $password_manager;
104  return self::$instance;
105  }
106 
107  public function setSettings(?ilSetting $settings): void
108  {
109  $this->settings = $settings;
110  }
111 
112  public function setDb(ilDBInterface $db): void
113  {
114  $this->db = $db;
115  }
116 
117  public function getEncoderName(): ?string
118  {
119  return $this->encoderName;
120  }
121 
122  public function setEncoderName(string $encoderName): void
123  {
124  $this->encoderName = $encoderName;
125  }
126 
128  {
129  return $this->encoderFactory;
130  }
131 
132  public function setEncoderFactory(ilUserPasswordEncoderFactory $encoderFactory): void
133  {
134  $this->encoderFactory = $encoderFactory;
135  }
136 
137  public function encodePassword(ilObjUser $user, string $raw): void
138  {
139  $encoder = $this->getEncoderFactory()->getEncoderByName($this->getEncoderName());
140  $user->setPasswordEncodingType($encoder->getName());
141  if ($encoder->requiresSalt()) {
142  $user->setPasswordSalt(
143  substr(str_replace('+', '.', base64_encode(ilPasswordUtils::getBytes(self::MIN_SALT_SIZE))), 0, 22)
144  );
145  } else {
146  $user->setPasswordSalt(null);
147  }
148  $user->setPasswd($encoder->encodePassword($raw, (string) $user->getPasswordSalt()), ilObjUser::PASSWD_CRYPTED);
149  }
150 
151  public function isEncodingTypeSupported(string $name): bool
152  {
153  return in_array($name, $this->getEncoderFactory()->getSupportedEncoderNames());
154  }
155 
156  public function verifyPassword(ilObjUser $user, string $raw): bool
157  {
158  $encoder = $this->getEncoderFactory()->getEncoderByName($user->getPasswordEncodingType());
159  if ($this->getEncoderName() !== $encoder->getName()) {
160  if ($encoder->isPasswordValid($user->getPasswd(), $raw, (string) $user->getPasswordSalt())) {
161  $user->resetPassword($raw, $raw);
162  return true;
163  }
164  } elseif ($encoder->isPasswordValid($user->getPasswd(), $raw, (string) $user->getPasswordSalt())) {
165  if ($encoder->requiresReencoding($user->getPasswd())) {
166  $user->resetPassword($raw, $raw);
167  }
168 
169  return true;
170  }
171 
172  return false;
173  }
174 
175  public function resetLastPasswordChangeForLocalUsers(): void
176  {
177  $defaultAuthMode = $this->settings->get('auth_mode');
178  $defaultAuthModeCondition = '';
179  if ((int) $defaultAuthMode === ilAuthUtils::AUTH_LOCAL) {
180  $defaultAuthModeCondition = ' OR auth_mode = ' . $this->db->quote('default', 'text');
181  }
182 
183  $this->db->manipulateF(
184  "UPDATE usr_data SET passwd_policy_reset = %s WHERE (auth_mode = %s $defaultAuthModeCondition)",
185  ['integer', 'text'],
186  [1, 'local']
187  );
188  }
189 }
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
resetPassword(string $raw, string $raw_retype)
Resets the user password.
encodePassword(ilObjUser $user, string $raw)
__construct(array $config=[])
Please use the singleton method for instance creation The constructor is still public because of the ...
setPasswordSalt(?string $password_salt)
ilUserPasswordEncoderFactory $encoderFactory
setPasswd(string $a_str, string $a_type=ilObjUser::PASSWD_PLAIN)
global $DIC
Definition: feed.php:28
string $key
Consumer key/client ID value.
Definition: System.php:193
static getBytes(int $length)
Generate random bytes using OpenSSL or Mcrypt and mt_rand() as fallback.
setEncoderFactory(ilUserPasswordEncoderFactory $encoderFactory)
static getDataDir()
get data directory (outside webspace)
const PASSWD_CRYPTED
setPasswordEncodingType(?string $password_encryption_type)
verifyPassword(ilObjUser $user, string $raw)
static getInstance()
Singleton method to reduce footprint (included files, created instances)