Skip to content

Commit

Permalink
ENH Generate a random password if a blank password is entered
Browse files Browse the repository at this point in the history
  • Loading branch information
emteknetnz committed Oct 12, 2023
1 parent 87958e7 commit b045115
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 8 deletions.
47 changes: 40 additions & 7 deletions src/Forms/ConfirmedPasswordField.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ class ConfirmedPasswordField extends FormField
public $requireStrongPassword = false;

/**
* Allow empty fields in serverside validation
* Allow empty fields in when entering the password for the first time
* If this is set to true then a random password will be created before saving
*
* @var boolean
*/
Expand Down Expand Up @@ -119,6 +120,8 @@ class ConfirmedPasswordField extends FormField
*/
protected $hiddenField = null;

private string $strongPasswordRegex = '/^(([a-zA-Z]+\d+)|(\d+[a-zA-Z]+))[a-zA-Z0-9]*$/';

/**
* @param string $name
* @param string $title
Expand Down Expand Up @@ -255,7 +258,15 @@ public function getChildren()
public function setCanBeEmpty($value)
{
$this->canBeEmpty = (bool)$value;

$text = _t(
__CLASS__ . '.CANBEEMTPYRIGHT',
'If an password is not set then a hidden random password will be automatically generated. The user will need to click the forgotten password link to receive an email with a password reset link.'
);
if ($this->canBeEmpty) {
$this->passwordField->setRightTitle($text);
} elseif (!$this->canBeEmpty && $this->passwordField->getRightTitle() === $text) {
$this->passwordField->setRightTitle('');
}
return $this;
}

Expand Down Expand Up @@ -488,7 +499,7 @@ public function validate($validator)
}

if ($this->getRequireStrongPassword()) {
if (!preg_match('/^(([a-zA-Z]+\d+)|(\d+[a-zA-Z]+))[a-zA-Z0-9]*$/', $value ?? '')) {
if (!preg_match($this->strongPasswordRegex, $value ?? '')) {
$validator->validationError(
$name,
_t(
Expand Down Expand Up @@ -552,8 +563,6 @@ public function validate($validator)
}

/**
* Only save if field was shown on the client, and is not empty.
*
* @param DataObjectInterface $record
*/
public function saveInto(DataObjectInterface $record)
Expand All @@ -562,9 +571,33 @@ public function saveInto(DataObjectInterface $record)
return;
}

if (!($this->canBeEmpty && !$this->value)) {
parent::saveInto($record);
// Create a random password if password is blank
if ($this->canBeEmpty && !$this->value) {
$this->value = $this->createStrongRandomPassword();
}

parent::saveInto($record);
}

private function createStrongRandomPassword(): string
{
$value = '';
do {
// random_int() in OK to use for random password generation
$randomInt = random_int(0, PHP_INT_MAX);
// add in a-f as extra possible characters
$hashed = substr(md5($randomInt), 0, $this->getMaxLength());
// uppercase half of the characters
$hashed = implode('', [
strtoupper(substr($hashed, 0, floor(strlen($hashed) / 2))),
substr($hashed, ceil(strlen($hashed) / 2)),
]);
// shuffle the characters
$arr = str_split($hashed);
shuffle($arr);
$value = implode('', $arr);
} while (!preg_match($this->strongPasswordRegex, $value));
return $value;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Security/Member.php
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,7 @@ public function getMemberPasswordField()
$password->setRequireExistingPassword(true);
}

$password->setCanBeEmpty(false);
$password->setCanBeEmpty(true);
$this->extend('updateMemberPasswordField', $password);

return $password;
Expand Down

0 comments on commit b045115

Please sign in to comment.