diff --git a/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php b/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php index 0354e25..6445590 100644 --- a/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php +++ b/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php @@ -253,6 +253,9 @@ public function validateForm(array &$form, FormStateInterface $form_state) { if ($error = user_validate_name($form_state->getValue(array('account', 'name')))) { $form_state->setErrorByName('account][name', $error); } + if (strlen($form_state->getValue(array('account', 'pass'))) < \Drupal::config('user.settings')->get('minimum_password_length')) { + $form_state->setErrorByName('account][pass', $this->t('Password must be at least @minimum_password_length characters long.', array('@minimum_password_length' => \Drupal::config('user.settings')->get('minimum_password_length')))); + } } /** diff --git a/core/modules/user/config/install/user.settings.yml b/core/modules/user/config/install/user.settings.yml index 788d95a..b02f4c5 100644 --- a/core/modules/user/config/install/user.settings.yml +++ b/core/modules/user/config/install/user.settings.yml @@ -1,5 +1,6 @@ anonymous: Anonymous verify_mail: true +minimum_password_length: 6 notify: cancel_confirm: true password_reset: true diff --git a/core/modules/user/config/schema/user.schema.yml b/core/modules/user/config/schema/user.schema.yml index f38fd4c..6f36160 100644 --- a/core/modules/user/config/schema/user.schema.yml +++ b/core/modules/user/config/schema/user.schema.yml @@ -10,6 +10,9 @@ user.settings: verify_mail: type: boolean label: 'Require email verification when a visitor creates an account' + minimum_password_length: + type: integer + label: 'The minimum password length required for registration.' notify: type: mapping label: 'Notify user' diff --git a/core/modules/user/src/AccountForm.php b/core/modules/user/src/AccountForm.php index 8c14682..850e55d 100644 --- a/core/modules/user/src/AccountForm.php +++ b/core/modules/user/src/AccountForm.php @@ -393,6 +393,7 @@ public function validate(array $form, FormStateInterface $form_state) { $field_names = array( 'name', 'mail', + 'pass', 'signature', 'signature_format', 'timezone', diff --git a/core/modules/user/src/AccountSettingsForm.php b/core/modules/user/src/AccountSettingsForm.php index 38c39e6..6473934 100644 --- a/core/modules/user/src/AccountSettingsForm.php +++ b/core/modules/user/src/AccountSettingsForm.php @@ -159,6 +159,13 @@ public function buildForm(array $form, FormStateInterface $form_state) { USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL => $this->t('Visitors, but administrator approval is required'), ) ); + $form['registration_cancellation']['minimum_password_length'] = array( + '#type' => 'number', + '#title' => $this->t('Minimum password length'), + '#default_value' => $config->get('minimum_password_length'), + '#description' => $this->t("The minimum password length required for registration."), + '#min' => '1', + ); $form['registration_cancellation']['user_email_verification'] = array( '#type' => 'checkbox', '#title' => $this->t('Require email verification when a visitor creates an account'), @@ -448,6 +455,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { $this->config('user.settings') ->set('anonymous', $form_state->getValue('anonymous')) ->set('register', $form_state->getValue('user_register')) + ->set('minimum_password_length', $form_state->getValue('minimum_password_length')) ->set('password_strength', $form_state->getValue('user_password_strength')) ->set('verify_mail', $form_state->getValue('user_email_verification')) ->set('signatures', $form_state->getValue('user_signatures')) diff --git a/core/modules/user/src/Entity/User.php b/core/modules/user/src/Entity/User.php index 7fd9f85..01cedad 100644 --- a/core/modules/user/src/Entity/User.php +++ b/core/modules/user/src/Entity/User.php @@ -515,7 +515,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields['pass'] = BaseFieldDefinition::create('password') ->setLabel(t('Password')) - ->setDescription(t('The password of this user (hashed).')); + ->setDescription(t('The password of this user (hashed).')) + ->addConstraint('UserPasswordLength'); $fields['mail'] = BaseFieldDefinition::create('email') ->setLabel(t('Email')) diff --git a/core/modules/user/src/Plugin/Validation/Constraint/UserPasswordLength.php b/core/modules/user/src/Plugin/Validation/Constraint/UserPasswordLength.php new file mode 100644 index 0000000..1180cfe --- /dev/null +++ b/core/modules/user/src/Plugin/Validation/Constraint/UserPasswordLength.php @@ -0,0 +1,28 @@ +config = $config_factory->get('user.settings'); + } + + /** + * {@inheritdoc} + */ + public function validate($items, Constraint $constraint) { + if (!$items) { + // If no entity is present, we cannot validate. + // @todo Remove once we can access the raw password on user-edits when + // https://www.drupal.org/node/2418119 is resolved. + return; + } + + /* @var \Drupal\Core\Field\FieldItemListInterface $items */ + $password = $items->value; + $entity = $items->getEntity(); + // @todo Revisit user edit password validation when blocker + // https://www.drupal.org/node/2418119 is resolved. + $password_length = Unicode::strlen($password); + $minimum_password_length = $this->config->get('minimum_password_length'); + + if ($password_length < $minimum_password_length) { + $this->context->addViolation($constraint->message, array('@minimum_password_length' => $minimum_password_length)); + } + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static($container->get('config.factory')); + } +} diff --git a/core/modules/user/src/Tests/UserValidationTest.php b/core/modules/user/src/Tests/UserValidationTest.php index acc64b7..814bc5e 100644 --- a/core/modules/user/src/Tests/UserValidationTest.php +++ b/core/modules/user/src/Tests/UserValidationTest.php @@ -188,6 +188,34 @@ function testValidation() { $this->assertEqual(count($violations), 1); $this->assertEqual($violations[0]->getPropertyPath(), 'roles.1'); $this->assertEqual($violations[0]->getMessage(), t('The referenced entity (%entity_type: %name) does not exist.', array('%entity_type' => 'user_role', '%name' => 'unknown_role'))); + + // Test minimium password length validation. + // Default minimum length is 6 characters. + $passwords = [ + // More than 6 characters. + 'valid_pw' => TRUE, + // Less than 6 characters. + 'wrong' => FALSE, + // Check UTF-8 validation: less than 6 characters, but more than 6 bytes. + 'þòøÇß' => FALSE, + ]; + foreach ($passwords as $password => $valid) { + // Test cardinality of user roles. + $user = entity_create('user', array( + 'name' => 'role_test', + 'mail' => 'test@example.com', + 'pass' => $password, + )); + $violations = $user->validate(); + if ($valid) { + $this->assertEqual(count($violations), 0); + } + else { + $this->assertEqual(count($violations), 1); + $this->assertEqual($violations[0]->getPropertyPath(), 'pass'); + $this->assertEqual($violations[0]->getMessage(), t('Password must be at least @minimum_password_length characters long.', ['@minimum_password_length' => 6])); + } + } } /** diff --git a/core/modules/user/user.js b/core/modules/user/user.js index ded4c0a..e1d0faa 100644 --- a/core/modules/user/user.js +++ b/core/modules/user/user.js @@ -101,10 +101,11 @@ var usernameBox = $('input.username'); var username = (usernameBox.length > 0) ? usernameBox.val() : translate.username; - // Lose 5 points for every character less than 6, plus a 30 point penalty. - if (password.length < 6) { + // Lose 5 points for every character less than translate.numCharacters + // plus a 30 point penalty. + if (password.length < translate.minPasswordLength) { msg.push(translate.tooShort); - strength -= ((6 - password.length) * 5) + 30; + strength -= ((translate.minPasswordLength - password.length) * 5) + 30; } // Count weaknesses. diff --git a/core/modules/user/user.module b/core/modules/user/user.module index fbc900c..f3ff577 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -1306,10 +1306,11 @@ function user_form_process_password_confirm($element) { if (\Drupal::config('user.settings')->get('password_strength')) { $password_settings['showStrengthIndicator'] = TRUE; + $password_settings['minPasswordLength'] = \Drupal::config('user.settings')->get('minimum_password_length'); $password_settings += array( 'strengthTitle' => t('Password strength:'), 'hasWeaknesses' => t('To make your password stronger:'), - 'tooShort' => t('Make it at least 6 characters'), + 'tooShort' => t('Make it at least @minimum_password_length characters', array('@minimum_password_length' => \Drupal::config('user.settings')->get('minimum_password_length'))), 'addLowerCase' => t('Add lowercase letters'), 'addUpperCase' => t('Add uppercase letters'), 'addNumbers' => t('Add numbers'),