You are here

better_passwords.module in Better Passwords 8

Same filename and directory in other branches
  1. 2.x better_passwords.module

The better passwords module file.

File

better_passwords.module
View source
<?php

/**
 * @file
 * The better passwords module file.
 */
use ZxcvbnPhp\Zxcvbn;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Form\FormStateInterface;

/**
 * Implements hook_element_info_alter().
 */
function better_passwords_element_info_alter(array &$info) {
  if (isset($info['password_confirm'])) {
    $info['password_confirm']['#after_build'][] = 'better_passwords_after_build';
  }
}

/**
 * After build function for password_confirm elements.
 *
 * @param array $element
 *   The element being altered after build.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   The form_state object returned with the element.
 */
function better_passwords_after_build(array $element, FormStateInterface $form_state) {
  $config = \Drupal::config('better_passwords.settings');

  // Hide the password fields if Better Passwords is generating passwords.
  if (($generate = $config
    ->get('auto_generate')) && \Drupal::currentUser()
    ->isAuthenticated() && $form_state
    ->getFormObject()
    ->getFormId() == 'user_register_form') {
    $element['#required'] = $element['pass1']['#required'] = $element['pass2']['#required'] = FALSE;
    if ($generate == 1) {
      $element['pass1']['#states']['visible']['#auto-generate-password'] = $element['pass2']['#states']['visible']['#auto-generate-password'] = [
        'checked' => FALSE,
      ];
      $element['auto_generate_password'] = [
        '#type' => 'checkbox',
        '#title' => t('Auto-generate password'),
        '#checked' => TRUE,
        '#attributes' => [
          'id' => 'auto-generate-password',
        ],
        '#parents' => $element['#parents'],
        '#array_parents' => $element['#array_parents'],
      ];
    }
    else {
      $element['pass1']['#access'] = $element['pass2']['#access'] = FALSE;
    }
  }

  // Better Password validate should come before other validate functions.
  array_unshift($element['#element_validate'], 'better_passwords_validate');
  return $element;
}

/**
 * Element validate function for password_confirm elements.
 *
 * @param array $element
 *   The element being validated.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   The form_state object returned with the element.
 */
function better_passwords_validate(array $element, FormStateInterface $form_state) {
  $config = \Drupal::config('better_passwords.settings');
  $minLength = $config
    ->get('length');
  $minStrength = $config
    ->get('strength');
  $autoGenerate = $config
    ->get('auto_generate');
  $value = $element['pass1']['#value'];
  if ($value === "") {
    if ($autoGenerate && \Drupal::currentUser()
      ->isAuthenticated() && $form_state
      ->getFormObject()
      ->getFormId() == 'user_register_form') {
      $genpass = user_password(64);
      $form_state
        ->setValueForElement($element, [
        'pass1' => $genpass,
        'pass2' => $genpass,
      ]);
    }
    return TRUE;
  }
  if (strlen($value) < $minLength) {
    $form_state
      ->setError($element, t('Passwords must be at least @num characters.', [
      '@num' => $minLength,
    ]));
    return;
  }
  elseif (!empty($minStrength)) {
    $userdata = [
      $form_state
        ->get('name'),
      $form_state
        ->get('mail'),
    ];
    $z = new Zxcvbn();
    $strength = $z
      ->passwordStrength($value, $userdata);
    if ($strength['score'] < $minStrength) {
      $matches = [];
      foreach ($strength['sequence'] as $obj) {
        if (!empty($obj->pattern)) {
          $str = substr($value, $obj->begin, $obj->end + 1 - $obj->begin);
          switch ($obj->pattern) {
            case 'date':
              $matches[] = t('%str is a date', [
                '%str' => $str,
              ]);
              break;
            case 'dictionary':
              $matches[] = t('%str matches @dict dictionary (#@num)', [
                '%str' => $str,
                '@dict' => $obj->dictionaryName,
                '@num' => $obj->rank,
              ]);
              break;
            case 'digit':
              $matches[] = t('%str is numeric', [
                '%str' => $str,
              ]);
              break;
            case 'repeat':
              $matches[] = t('%str is repetitive', [
                '%str' => $str,
              ]);
              break;
            case 'sequence':
              $matches[] = t('%str is sequential', [
                '%str' => $str,
              ]);
              break;
            case 'spatial':
              $matches[] = t('%str is adjacent on the keyboard', [
                '%str' => $str,
              ]);
              break;
            case 'year':
              $matches[] = t('%str is a year', [
                '%str' => $str,
              ]);
              break;
          }
        }
      }
      $error = new FormattableMarkup(t('Please choose a stronger password. Current strength: @score. Minimum: @min.', [
        '@score' => $strength['score'],
        '@min' => $minStrength,
      ]) . '<ul><li>' . implode('</li><li>', $matches) . '</li></ul>', []);
      $form_state
        ->setErrorByName('pass1', $error);
    }
    else {
      \Drupal::messenger()
        ->addMessage(t('Password strength: @score. Minimum: @min.', [
        '@score' => $strength['score'],
        '@min' => $minStrength,
      ]));
    }
  }
}

Functions

Namesort descending Description
better_passwords_after_build After build function for password_confirm elements.
better_passwords_element_info_alter Implements hook_element_info_alter().
better_passwords_validate Element validate function for password_confirm elements.