Elgg  Version master
PasswordGeneratorService.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Security;
4 
5 use Elgg\Config;
11 use Elgg\Values;
13 
23 
31  public function __construct(
32  protected Config $config,
33  protected Translator $translator,
34  protected EventsService $events
35  ) {
36  }
37 
45  public function generatePassword(int $length = 12): string {
46 
47  $length = $this->getValidLength($length);
48 
49  $generator = $this->getGenerator();
50  $generator->setLength($length);
51 
52  return $this->events->triggerResults('generate', 'password', [], $generator->generatePassword());
53  }
54 
64  public function assertValidPassword(string $password) {
65 
66  if (!$this->validatePasswordLength($password)) {
68  }
69 
70  if ($this->isValidPassword($password)) {
71  return;
72  }
73 
75  }
76 
84  public function isValidPassword(string $password) {
85 
86  // generator can't handle min length only length
87  if (!$this->validatePasswordLength($password)) {
88  return false;
89  }
90 
91  $generator = $this->getGenerator();
92  // no make sure length isn't a failure condition
93  $generator->setLength(strlen($password));
94 
95  return $generator->validatePassword($password);
96  }
97 
105  public function getInputRegEx() {
106  $regex = '';
107 
108  $lower = $this->config->min_password_lower;
109  $upper = $this->config->min_password_upper;
110  $number = $this->config->min_password_number;
111  $special = $this->config->min_password_special;
112 
113  if (!Values::isEmpty($lower)) {
114  $lower = (int) $lower;
115  if ($lower < 1) {
116  $regex .= '(?!.*[a-z])';
117  } else {
118  $regex .= '(?=' . str_repeat('.*[a-z]', $lower) . ')';
119  }
120  }
121 
122  if (!Values::isEmpty($upper)) {
123  $upper = (int) $upper;
124  if ($upper < 1) {
125  $regex .= '(?!.*[A-Z])';
126  } else {
127  $regex .= '(?=' . str_repeat('.*[A-Z]', $upper) . ')';
128  }
129  }
130 
131  if (!Values::isEmpty($number)) {
132  $number = (int) $number;
133  if ($number < 1) {
134  $regex .= '(?!.*[0-9])';
135  } else {
136  $regex .= '(?=' . str_repeat('.*[0-9]', $number) . ')';
137  }
138  }
139 
140  if (!Values::isEmpty($special)) {
141  $generator = $this->getGenerator();
142  $special_chars = $generator->getParameter(RequirementPasswordGenerator::PARAMETER_SYMBOLS);
143  $special_chars = str_replace(']', '\\]', $special_chars);
144  $special_chars = str_replace('-', '\\-', $special_chars);
145 
146  $special = (int) $special;
147  if ($special < 1) {
148  $regex .= '(?!.*[' . $special_chars . '])';
149  } else {
150  $regex .= '(?=' . str_repeat('.*[' . $special_chars . ']', $special) . ')';
151  }
152  }
153 
154  $length = (int) $this->config->min_password_length;
155  if ($length < 1) {
156  $length = 6;
157  }
158 
159  $regex .= '.{' . $length . ',}';
160 
161  return $regex;
162  }
163 
170 
171  $result = [];
172  $result[] = $this->translator->translate('password:requirements:min_length', [$this->config->min_password_length]);
173 
174  $lower = $this->config->min_password_lower;
175  if (!Values::isEmpty($lower)) {
176  $lower = (int) $lower;
177  if ($lower > 0) {
178  $result[] = $this->translator->translate('password:requirements:lower', [$lower]);
179  } else {
180  $result[] = $this->translator->translate('password:requirements:no_lower');
181  }
182  }
183 
184  $upper = $this->config->min_password_upper;
185  if (!Values::isEmpty($upper)) {
186  $upper = (int) $upper;
187  if ($upper > 0) {
188  $result[] = $this->translator->translate('password:requirements:upper', [$upper]);
189  } else {
190  $result[] = $this->translator->translate('password:requirements:no_upper');
191  }
192  }
193 
194  $number = $this->config->min_password_number;
195  if (!Values::isEmpty($number)) {
196  $number = (int) $number;
197  if ($number > 0) {
198  $result[] = $this->translator->translate('password:requirements:number', [$number]);
199  } else {
200  $result[] = $this->translator->translate('password:requirements:no_number');
201  }
202  }
203 
204  $special = $this->config->min_password_special;
205  if (!Values::isEmpty($special)) {
206  $special = (int) $special;
207  if ($special > 0) {
208  $result[] = $this->translator->translate('password:requirements:special', [$special]);
209  } else {
210  $result[] = $this->translator->translate('password:requirements:no_special');
211  }
212  }
213 
214  return implode(' ', $result);
215  }
216 
224  public function addInputRequirements(\Elgg\Event $event) {
225 
226  $vars = $event->getValue();
227  if (!(bool) elgg_extract('add_security_requirements', $vars, false)) {
228  return;
229  }
230 
231  // add requirements pattern
232  $vars['pattern'] = $this->getInputRegEx();
233 
234  // add requirements to title so on pattern failure the user knows what to do
235  $title = elgg_extract('title', $vars, '');
236  $title .= ' ' . $this->getPasswordRequirementsDescription();
237  $vars['title'] = trim($title);
238 
239  return $vars;
240  }
241 
250  public function registerUserPasswordValidation(\Elgg\Event $event) {
251 
252  $password = $event->getParam('password');
253  if (empty($password)) {
254  return;
255  }
256 
257  try {
259  } catch (\Exception $e) {
260  throw new RegistrationException($e->getMessage(), $e->getCode(), $e);
261  }
262  }
263 
269  protected function getGenerator() {
270  $generator = new RequirementPasswordGenerator();
271  $generator->setLength($this->config->min_password_length);
272 
273  // set lower case requirements
274  $lower = $this->config->min_password_lower;
275  if (!Values::isEmpty($lower)) {
276  $lower = (int) $lower;
277  if (empty($lower)) {
278  $generator->setOptionValue(RequirementPasswordGenerator::OPTION_LOWER_CASE, false);
279  } else {
280  $generator->setOptionValue(RequirementPasswordGenerator::OPTION_LOWER_CASE, true);
281  $generator->setMinimumCount(RequirementPasswordGenerator::OPTION_LOWER_CASE, $lower);
282  }
283  }
284 
285  // set upper case requirements
286  $upper = $this->config->min_password_upper;
287  if (!Values::isEmpty($upper)) {
288  $upper = (int) $upper;
289  if (empty($upper)) {
290  $generator->setOptionValue(RequirementPasswordGenerator::OPTION_UPPER_CASE, false);
291  } else {
292  $generator->setOptionValue(RequirementPasswordGenerator::OPTION_UPPER_CASE, true);
293  $generator->setMinimumCount(RequirementPasswordGenerator::OPTION_UPPER_CASE, $upper);
294  }
295  }
296 
297  // set number requirements
298  $number = $this->config->min_password_number;
299  if (!Values::isEmpty($number)) {
300  $number = (int) $number;
301  if (empty($number)) {
302  $generator->setOptionValue(RequirementPasswordGenerator::OPTION_NUMBERS, false);
303  } else {
304  $generator->setOptionValue(RequirementPasswordGenerator::OPTION_NUMBERS, true);
305  $generator->setMinimumCount(RequirementPasswordGenerator::OPTION_NUMBERS, $number);
306  }
307  }
308 
309  // set special character requirements
310  $special = $this->config->min_password_special;
311  if (!Values::isEmpty($special)) {
312  $special = (int) $special;
313  if (empty($special)) {
314  $generator->setOptionValue(RequirementPasswordGenerator::OPTION_SYMBOLS, false);
315  } else {
316  $generator->setOptionValue(RequirementPasswordGenerator::OPTION_SYMBOLS, true);
317  $generator->setMinimumCount(RequirementPasswordGenerator::OPTION_SYMBOLS, $special);
318  }
319  }
320 
321  return $generator;
322  }
323 
331  protected function getValidLength(int $length) {
332  $min_length = (int) $this->config->min_password_length;
333 
334  $requirements_length = (int) $this->config->min_password_lower;
335  $requirements_length += (int) $this->config->min_password_upper;
336  $requirements_length += (int) $this->config->min_password_number;
337  $requirements_length += (int) $this->config->min_password_special;
338 
339  return max($min_length, $requirements_length, $length);
340  }
341 
349  protected function validatePasswordLength(string $password) {
350  if (elgg_strlen($password) < $this->config->min_password_length) {
351  return false;
352  }
353 
354  return true;
355  }
356 }
if(parse_url(elgg_get_site_url(), PHP_URL_PATH)!== '/') if(file_exists(elgg_get_root_path(). 'robots.txt'))
Set robots.txt.
Definition: robots.php:10
addInputRequirements(\Elgg\Event $event)
Add the security password requirements to an input/password field.
Elgg registration action.
Saves user notification settings.
__construct(protected Config $config, protected Translator $translator, protected EventsService $events)
Constructor.
if(!$item instanceof\ElggEntity) $length
Definition: excerpt.php:16
$title
Definition: generic.php:50
Events service.
Could not register a new user for whatever reason.
static isEmpty($value)
Check if a value isn&#39;t empty, but allow 0 and &#39;0&#39;.
Definition: Values.php:192
elgg_strlen()
Wrapper function for mb_strlen().
Definition: mb_wrapper.php:53
elgg_extract($key, $array, $default=null, bool $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
Definition: elgglib.php:254
$config
Advanced site settings, debugging section.
Definition: debugging.php:6
validatePasswordLength(string $password)
Validate that a password meets the minimal length requirement.
isValidPassword(string $password)
Validate that a given string matches the password requirements.
generatePassword(int $length=12)
Generate a new random password.
if(!$user||!$user->canEdit()) $password
getValidLength(int $length)
Make sure the password length requirement can be met by the password settings.
assertValidPassword(string $password)
Assert that a given string matches the password requirements.
$vars
Definition: theme.php:5
getInputRegEx()
Get the regex to set on an input/password to validate password requirements during input...
registerUserPasswordValidation(\Elgg\Event $event)
Validate password during user registration.
getPasswordRequirementsDescription()
Get a description of how a valid password should be made.
Indicate a password string doesn&#39;t meet the character requirements.
Indicate a password string doesn&#39;t meet the minimal length requirements.
Models an event passed to event handlers.
Definition: Event.php:11