Elgg  Version 5.1
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 
27  protected $config;
28 
32  protected $translator;
33 
37  protected $events;
38 
47  $this->config = $config;
48  $this->translator = $translator;
49  $this->events = $events;
50  }
51 
59  public function generatePassword(int $length = 12): string {
60 
61  $length = $this->getValidLength($length);
62 
63  $generator = $this->getGenerator();
64  $generator->setLength($length);
65 
66  return $this->events->triggerResults('generate', 'password', [], $generator->generatePassword());
67  }
68 
78  public function assertValidPassword(string $password) {
79 
80  if (!$this->validatePasswordLength($password)) {
82  }
83 
84  if ($this->isValidPassword($password)) {
85  return;
86  }
87 
89  }
90 
98  public function isValidPassword(string $password) {
99 
100  // generator can't handle min length only length
101  if (!$this->validatePasswordLength($password)) {
102  return false;
103  }
104 
105  $generator = $this->getGenerator();
106  // no make sure length isn't a failure condition
107  $generator->setLength(strlen($password));
108 
109  return $generator->validatePassword($password);
110  }
111 
119  public function getInputRegEx() {
120  $regex = '';
121 
122  $lower = $this->config->min_password_lower;
123  $upper = $this->config->min_password_upper;
124  $number = $this->config->min_password_number;
125  $special = $this->config->min_password_special;
126 
127  if (!Values::isEmpty($lower)) {
128  $lower = (int) $lower;
129  if ($lower < 1) {
130  $regex .= '(?!.*[a-z])';
131  } else {
132  $regex .= '(?=' . str_repeat('.*[a-z]', $lower) . ')';
133  }
134  }
135 
136  if (!Values::isEmpty($upper)) {
137  $upper = (int) $upper;
138  if ($upper < 1) {
139  $regex .= '(?!.*[A-Z])';
140  } else {
141  $regex .= '(?=' . str_repeat('.*[A-Z]', $upper) . ')';
142  }
143  }
144 
145  if (!Values::isEmpty($number)) {
146  $number = (int) $number;
147  if ($number < 1) {
148  $regex .= '(?!.*[0-9])';
149  } else {
150  $regex .= '(?=' . str_repeat('.*[0-9]', $number) . ')';
151  }
152  }
153 
154  if (!Values::isEmpty($special)) {
155  $generator = $this->getGenerator();
156  $special_chars = $generator->getParameter(RequirementPasswordGenerator::PARAMETER_SYMBOLS);
157  $special_chars = str_replace(']', '\\]', $special_chars);
158  $special_chars = str_replace('-', '\\-', $special_chars);
159 
160  $special = (int) $special;
161  if ($special < 1) {
162  $regex .= '(?!.*[' . $special_chars . '])';
163  } else {
164  $regex .= '(?=' . str_repeat('.*[' . $special_chars . ']', $special) . ')';
165  }
166  }
167 
168  $length = (int) $this->config->min_password_length;
169  if ($length < 1) {
170  $length = 6;
171  }
172 
173  $regex .= '.{' . $length . ',}';
174 
175  return $regex;
176  }
177 
184 
185  $result = [];
186  $result[] = $this->translator->translate('password:requirements:min_length', [$this->config->min_password_length]);
187 
188  $lower = $this->config->min_password_lower;
189  if (!Values::isEmpty($lower)) {
190  $lower = (int) $lower;
191  if ($lower > 0) {
192  $result[] = $this->translator->translate('password:requirements:lower', [$lower]);
193  } else {
194  $result[] = $this->translator->translate('password:requirements:no_lower');
195  }
196  }
197 
198  $upper = $this->config->min_password_upper;
199  if (!Values::isEmpty($upper)) {
200  $upper = (int) $upper;
201  if ($upper > 0) {
202  $result[] = $this->translator->translate('password:requirements:upper', [$upper]);
203  } else {
204  $result[] = $this->translator->translate('password:requirements:no_upper');
205  }
206  }
207 
208  $number = $this->config->min_password_number;
209  if (!Values::isEmpty($number)) {
210  $number = (int) $number;
211  if ($number > 0) {
212  $result[] = $this->translator->translate('password:requirements:number', [$number]);
213  } else {
214  $result[] = $this->translator->translate('password:requirements:no_number');
215  }
216  }
217 
218  $special = $this->config->min_password_special;
219  if (!Values::isEmpty($special)) {
220  $special = (int) $special;
221  if ($special > 0) {
222  $result[] = $this->translator->translate('password:requirements:special', [$special]);
223  } else {
224  $result[] = $this->translator->translate('password:requirements:no_special');
225  }
226  }
227 
228  return implode(' ', $result);
229  }
230 
238  public function addInputRequirements(\Elgg\Event $event) {
239 
240  $vars = $event->getValue();
241  if (!(bool) elgg_extract('add_security_requirements', $vars, false)) {
242  return;
243  }
244 
245  // add requirements pattern
246  $vars['pattern'] = $this->getInputRegEx();
247 
248  // add requirements to title so on pattern failure the user knows what to do
249  $title = elgg_extract('title', $vars, '');
250  $title .= ' ' . $this->getPasswordRequirementsDescription();
251  $vars['title'] = trim($title);
252 
253  return $vars;
254  }
255 
264  public function registerUserPasswordValidation(\Elgg\Event $event) {
265 
266  $password = $event->getParam('password');
267  if (empty($password)) {
268  return;
269  }
270 
271  try {
273  } catch (\Exception $e) {
274  throw new RegistrationException($e->getMessage(), $e->getCode(), $e);
275  }
276  }
277 
283  protected function getGenerator() {
284  $generator = new RequirementPasswordGenerator();
285  $generator->setLength($this->config->min_password_length);
286 
287  // set lower case requirements
288  $lower = $this->config->min_password_lower;
289  if (!Values::isEmpty($lower)) {
290  $lower = (int) $lower;
291  if (empty($lower)) {
292  $generator->setOptionValue(RequirementPasswordGenerator::OPTION_LOWER_CASE, false);
293  } else {
294  $generator->setOptionValue(RequirementPasswordGenerator::OPTION_LOWER_CASE, true);
295  $generator->setMinimumCount(RequirementPasswordGenerator::OPTION_LOWER_CASE, $lower);
296  }
297  }
298 
299  // set upper case requirements
300  $upper = $this->config->min_password_upper;
301  if (!Values::isEmpty($upper)) {
302  $upper = (int) $upper;
303  if (empty($upper)) {
304  $generator->setOptionValue(RequirementPasswordGenerator::OPTION_UPPER_CASE, false);
305  } else {
306  $generator->setOptionValue(RequirementPasswordGenerator::OPTION_UPPER_CASE, true);
307  $generator->setMinimumCount(RequirementPasswordGenerator::OPTION_UPPER_CASE, $upper);
308  }
309  }
310 
311  // set number requirements
312  $number = $this->config->min_password_number;
313  if (!Values::isEmpty($number)) {
314  $number = (int) $number;
315  if (empty($number)) {
316  $generator->setOptionValue(RequirementPasswordGenerator::OPTION_NUMBERS, false);
317  } else {
318  $generator->setOptionValue(RequirementPasswordGenerator::OPTION_NUMBERS, true);
319  $generator->setMinimumCount(RequirementPasswordGenerator::OPTION_NUMBERS, $number);
320  }
321  }
322 
323  // set special character requirements
324  $special = $this->config->min_password_special;
325  if (!Values::isEmpty($special)) {
326  $special = (int) $special;
327  if (empty($special)) {
328  $generator->setOptionValue(RequirementPasswordGenerator::OPTION_SYMBOLS, false);
329  } else {
330  $generator->setOptionValue(RequirementPasswordGenerator::OPTION_SYMBOLS, true);
331  $generator->setMinimumCount(RequirementPasswordGenerator::OPTION_SYMBOLS, $special);
332  }
333  }
334 
335  return $generator;
336  }
337 
345  protected function getValidLength(int $length) {
346  $min_length = (int) $this->config->min_password_length;
347 
348  $requirements_length = (int) $this->config->min_password_lower;
349  $requirements_length += (int) $this->config->min_password_upper;
350  $requirements_length += (int) $this->config->min_password_number;
351  $requirements_length += (int) $this->config->min_password_special;
352 
353  return max($min_length, $requirements_length, $length);
354  }
355 
363  protected function validatePasswordLength(string $password) {
364  if (elgg_strlen($password) < $this->config->min_password_length) {
365  return false;
366  }
367 
368  return true;
369  }
370 }
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.
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
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.
__construct(Config $config, Translator $translator, EventsService $events)
Constructor.
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