Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
The current strategy is tightly coupled into Form API validation callback. Client applications, using REST or JSONAPI, should be able to apply the validation logic inside HOOK_user_presave().
Expected Results:
When a client application creates a user that does not meet the password policy constraints, a descriptive exception is thrown, and processed by the client.
Actual Result:
When a client application creates a user that does not meet the password policy constraints, the user is saved.
In Progress Resolution
public function presaveUserValidate(User $entity) {
$request = \Drupal\Component\Serialization\Json::decode(\Drupal::request()->getContent());
$pass = $request['data']['attributes']['pass'];
//@todo get role
$roles = $entity->getRoles();
if (empty($roles)) {
$roles = ['authenticated' => 'authenticated'];
}
// Run validation.
$applicable_policies = [];
$ids = [];
foreach ($roles as $role_key => $role_enabled) {
if ($role_enabled) {
$role_map = ['roles.' . $role_enabled => $role_enabled];
$role_policies = \Drupal::entityTypeManager()->getStorage('password_policy')->loadByProperties($role_map);
/** @var \Drupal\password_policy\Entity\PasswordPolicy $policy */
foreach ($role_policies as $policy) {
if (!in_array($policy->id(), $ids)) {
$applicable_policies[] = $policy;
$ids[] = $policy->id();
}
}
}
}
// Run validation.
$failed = FALSE;
$force_failure = FALSE;
// Process user context
// TODO - Turn this into configuration.
$user_context_fields = ['mail', 'name', 'uid'];
$user_context_values = [];
foreach ($user_context_fields as $user_context_field) {
if($entity->hasField($user_context_field)) {
$user_context_values[$user_context_field] = $entity->get($user_context_field)->value;
}
}
/** @var \Drupal\password_policy\Entity\PasswordPolicy $policy */
foreach ($applicable_policies as $policy_id => $policy) {
$policy_constraints = $policy->getConstraints();
foreach ($policy_constraints as $constraint_id => $constraint) {
$plugin_inst = \Drupal::service('plugin.manager.password_policy.password_constraint');
$plugin_object = $plugin_inst->createInstance($constraint['id'], $constraint);
// Execute validation.
$validation = $plugin_object->validate($pass, $user_context_values);
if ($validation->isValid() && !$force_failure) {
$status = t('Pass');
}
else {
$message = $validation->getErrorMessage();
$failed = TRUE;
}
}
}
if($failed) {
throw new AccessDeniedHttpException('There is an issue with the password');
}
return;
}
Comment | File | Size | Author |
---|---|---|---|
#10 | password_policy-move-password-validation-to-user-entity-2941526-10.patch | 5.5 KB | Nadim Hossain |
Comments
Comment #2
kurtfoster CreditAttribution: kurtfoster at Department of Premier and Cabinet - Victoria, Australia commentedI hoping to get time to look at this soon as well. I think the validation should actually be removed from the form validate function so that it is callable and usable by other code using this module. The validate function can then be used in the form validation and in an entity presave.
Comment #3
kurtfoster CreditAttribution: kurtfoster as a volunteer and at Department of Premier and Cabinet - Victoria, Australia commentedHere's my first roll of a patch for this issue, it needs work. It's against 8.x-3.0-alpha4+11-dev
Comment #4
kurtfoster CreditAttribution: kurtfoster as a volunteer and at Department of Premier and Cabinet - Victoria, Australia commentedThere was a small error in that patch, updated.
Comment #5
malks CreditAttribution: malks commentedHey @kurtfoster, I've rerolled this patch with the check on the existing user removed. The logic being that for a non form update of a user, e.g. via an API the user updating will never be the same as the user being updated so the validation will never run. Let me know what you think.
Comment #6
malks CreditAttribution: malks commentedAdded a small fix to the patch where the wrong value was being passed to the validators.
Comment #7
yovince1. removed PasswordPolicyConstraintValidator.php line86-101
not sure why do we need $user_context_values.
2. line 131
the validate() accepts a string and an `UserInterface` entity. so passing an array into the function which seems not right.
Comment #8
yovinceComment #9
Anas_maw CreditAttribution: Anas_maw at Coders Enterprise Web & Mobile Solutions commentedI think this is duplicate of #3263810: Apply policy globally through validation constraint on user entity
Comment #10
Nadim Hossain CreditAttribution: Nadim Hossain as a volunteer commentedRerolled the patch to be compatible with php 8.1 upgrade.
just one minor change. From
if ($role_enabled['target_id'])
toif (isset($role_enabled['target_id']))
Thanks Anas for pointing out the similar patch and looks like the other patch has more possibility to get merged. I will apply the other patch you mentioned in our project and do some testing, after that will be closing this issue.
Comment #11
Kristen PolThanks to everyone for the work on this issue.
I'm going through all the 8.x issues.
As the 8.x is no longer supported, I'm postponing this issue for now and need feedback as to whether or not this issue is relevant to 4.0.x.
If it is, please reopen and change the version, make sure the issue summary is clear and complete, including steps to reproduce, and reroll the patch. If it's not, please close.
If there is no response to this in a month addressing the above, it can be closed.