View source  
  <?php
define('PASSWORD_STRENGTH_SCORE_VERYWEAK', 0);
define('PASSWORD_STRENGTH_SCORE_WEAK', 1);
define('PASSWORD_STRENGTH_SCORE_GOOD', 2);
define('PASSWORD_STRENGTH_SCORE_STRONG', 3);
define('PASSWORD_STRENGTH_SCORE_VERYSTRONG', 4);
function password_strength_xautoload($api) {
  $api
    ->namespaceRoot('ZxcvbnPhp', 'lib/zxcvbn-php/src');
}
function password_strength_menu() {
  $items = array();
  $items['system/password-strength-check'] = array(
    'title' => 'Check password',
    'page callback' => 'password_strength_ajax_check',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  $items['admin/config/system/password-strength'] = array(
    'title' => 'Password Strength settings',
    'description' => 'Manage password strength settings.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'password_strength_settings',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'file' => 'password_strength.admin.inc',
    'weight' => 20,
  );
  return $items;
}
function password_strength_check_strength($account = NULL) {
  
  if (!class_exists('ZxcvbnPhp\\Zxcvbn')) {
    return FALSE;
  }
  
  return TRUE;
}
function password_strength_form_alter(&$form, &$form_state, $form_id) {
  
  switch ($form_id) {
    case 'user_register_form':
      if (array_key_exists('pass', $form['account']) && password_strength_check_strength()) {
        password_strength_form_password_add($form['account']['pass'], $form);
      }
      break;
    case 'user_profile_form':
      if (password_strength_check_strength()) {
        password_strength_form_password_add($form['account']['pass'], $form);
      }
      break;
  }
}
function password_strength_element_info_alter(&$types) {
  
  if (isset($types['password_confirm']['#process'])) {
    $process = 'user_form_process_password_confirm';
    if (($position = array_search($process, $types['password_confirm']['#process'])) !== FALSE && password_strength_check_strength()) {
      unset($types['password_confirm']['#process'][$position]);
    }
  }
}
function password_strength_form_password_add(&$element, &$form) {
  
  password_strength_form_password_js_attach($element, $form['#user']);
  $form['#validate'][] = 'password_strength_form_password_validate';
  
  $form['#submit'] = array_merge(array(
    'password_strength_form_password_submit',
  ), $form['#submit']);
}
function password_strength_form_password_js_attach(&$element, $account) {
  $js_settings = array();
  $key = 'password_strength';
  $score = password_strength_required_score($account);
  $js_settings['policy_score'] = $score;
  $key .= ':' . $account->uid;
  $js_settings['uid'] = $account->uid;
  $js_settings['token'] = drupal_get_token($key);
  $js_settings['secure_base_url'] = url(NULL, array(
    'absolute' => TRUE,
    'https' => TRUE,
    'purl' => array(
      'disabled' => TRUE,
    ),
  ));
  
  $element['#attached']['css'][] = drupal_get_path('module', 'password_strength') . '/css/password.css';
  $element['#attached']['js'][] = array(
    'data' => array(
      'passwordStrength' => $js_settings,
    ),
    'type' => 'setting',
  );
  $element['#attached']['js'][] = array(
    'data' => drupal_get_path('module', 'password_strength') . '/js/password.js',
    'weight' => 10,
  );
}
function password_strength_form_password_validate($form, &$form_state) {
  
  if (empty($form_state['values']['pass'])) {
    return;
  }
  list($account, $strength) = _password_strength_calculate_strength($form, $form_state);
  $required_score = password_strength_required_score($account);
  
  if ($required_score && $strength['score'] < $required_score) {
    form_set_error('pass', t("Password does not meet required strength."));
  }
  elseif (!empty($strength['matches'])) {
    
    foreach ($strength['matches'] as $match) {
      if ($match['pattern'] == 'name') {
        form_set_error('pass', t("Password cannot match your account name"));
        break;
      }
      elseif ($match['pattern'] == 'mail') {
        form_set_error('pass', t("Password cannot match your account email address"));
        break;
      }
    }
  }
}
function password_strength_form_password_submit($form, &$form_state) {
  
  if (empty($form_state['values']['pass'])) {
    return;
  }
  list($account, $strength) = _password_strength_calculate_strength($form, $form_state);
  
  module_invoke_all('password_strength_change', $account, $strength);
}
function _password_strength_calculate_strength($form, &$form_state) {
  global $user;
  if (isset($form['#user'])) {
    $account = $form["#user"];
  }
  else {
    $account = $user;
  }
  $pass = $form_state['values']['pass'];
  
  if (!isset($account->name) && array_key_exists('name', $form_state['values'])) {
    $account->name = $form_state['values']['name'];
  }
  if (!isset($account->mail) && array_key_exists('mail', $form_state['values'])) {
    $account->mail = $form_state['values']['mail'];
  }
  $strength = password_strength_strength($pass, $account);
  return array(
    $account,
    $strength,
  );
}
function password_strength_ajax_check() {
  
  drupal_page_is_cacheable(FALSE);
  
  if (!isset($_POST['token']) || !isset($_POST['uid']) || !isset($_POST['password']) || !is_numeric($_POST['uid'])) {
    drupal_json_output(FALSE);
    return;
  }
  $password = urldecode($_POST['password']);
  
  if (strlen($password) > 256) {
    drupal_json_output(FALSE);
    return;
  }
  
  $account = user_load($_POST['uid']);
  $key = 'password_strength';
  $key .= ':' . $account->uid;
  
  if ($account->uid && !drupal_valid_token($_POST['token'], $key)) {
    drupal_json_output(FALSE);
    return;
  }
  
  $strength = password_strength_strength($password, $account);
  
  $message_strength = password_strength_get_message_strength($strength);
  $message_requirements = password_strength_get_message_requirements($strength);
  $message_flaws = password_strength_get_message_flaws($strength);
  
  $data = array(
    'entropy' => $strength['entropy'],
    'matches' => $strength['matches'],
    'score' => $strength['score'],
    'score_required' => $strength['score_required'],
    'percent' => $strength['percent'],
    'message_strength' => drupal_render($message_strength),
    'message_requirements' => drupal_render($message_requirements),
    'message_flaws' => drupal_render($message_flaws),
  );
  drupal_json_output($data);
}
function password_strength_strength($password, $account = NULL) {
  global $user;
  if (empty($account)) {
    $account = $user;
  }
  
  $score_required = password_strength_required_score($account);
  
  $strength = array(
    'entropy' => 0,
    'score' => 0,
    'score_required' => $score_required,
    'percent' => 0,
    'match_sequence' => array(),
    'matches' => array(),
  );
  
  if (strlen($password) < (int) variable_get('password_strength_default_password_length', 7)) {
    $strength['matches'][] = array(
      'pattern' => 'length',
      'matched' => $password,
    );
    return $strength;
  }
  if (strtolower(trim(urldecode($password))) == $account->mail) {
    $strength['matches'][] = array(
      'pattern' => 'mail',
      'matched' => $password,
    );
    return $strength;
  }
  if (strtolower(trim(urldecode($password))) == $account->name) {
    $strength['matches'][] = array(
      'pattern' => 'name',
      'matched' => $password,
    );
    return $strength;
  }
  
  $zxcvbn = new ZxcvbnPhp\Zxcvbn();
  $strength = $zxcvbn
    ->passwordStrength($password);
  $strength['score_required'] = $score_required;
  $strength['matches'] = array();
  
  if ($strength['score'] < $score_required) {
    $strength['matches'][] = array(
      'pattern' => 'score',
      'matched' => $password,
    );
  }
  
  foreach ($strength['match_sequence'] as $match) {
    
    if (strlen($match->token) < 3) {
      continue;
    }
    $strength['matches'][] = array(
      'pattern' => password_strength_strength_pattern($match),
      'matched' => $match->token,
    );
  }
  
  $strength['percent'] = round($strength['score'] / 4 * 100);
  $strength['percent'] = $strength['percent'] >= 0 ? $strength['percent'] : 0;
  $strength['percent'] = $strength['percent'] <= 100 ? $strength['percent'] : 100;
  
  return $strength;
}
function password_strength_strength_pattern($match) {
  if (isset($match->l33t) && $match->l33t) {
    $pattern = 'leetspeak';
  }
  else {
    $pattern = $match->pattern;
  }
  return $pattern;
}
function password_strength_score_list() {
  return array(
    PASSWORD_STRENGTH_SCORE_VERYWEAK => t('very weak'),
    PASSWORD_STRENGTH_SCORE_WEAK => t('weak'),
    PASSWORD_STRENGTH_SCORE_GOOD => t('good'),
    PASSWORD_STRENGTH_SCORE_STRONG => t('strong'),
    PASSWORD_STRENGTH_SCORE_VERYSTRONG => t('very strong'),
  );
}
function password_strength_get_score($score) {
  $scores = password_strength_score_list();
  return isset($scores[$score]) ? $scores[$score] : t('unknown');
}
function password_strength_get_message_strength($strength) {
  $score = password_strength_get_score($strength['score']);
  
  $requirement_set = $strength['score_required'] > 0;
  $requirement_met = $strength['score'] >= $strength['score_required'];
  if ($requirement_set && $requirement_met) {
    $result_class = ' check-mark';
  }
  else {
    $result_class = '';
  }
  $build = array();
  $build['content'] = array(
    '#markup' => '<div class="name">' . t('Password strength:') . '</div> ' . '<div class="value text-score-' . $strength['score'] . $result_class . '">' . $score . '</div>',
  );
  return $build;
}
function password_strength_get_message_requirements($strength) {
  $score_required = password_strength_get_score($strength['score_required']);
  $build = array();
  if ($strength['score_required'] > 0) {
    $build['content'] = array(
      '#markup' => '<div>' . t('The required minimum strength is ') . '<span class="value text-score-' . $strength['score_required'] . '">' . $score_required . '.</span></div>',
    );
  }
  return $build;
}
function password_strength_get_message_flaws($strength) {
  $flaws = password_strength_get_flaws($strength);
  $build = array();
  if (!empty($flaws)) {
    $build['intro'] = array(
      '#markup' => '<p>' . t('The following issues were detected with your password:') . '</p>',
    );
    $build['flaws'] = array(
      '#theme' => 'item_list',
      '#items' => $flaws,
    );
  }
  return $build;
}
function password_strength_get_flaws($strength) {
  $use_eg = TRUE;
  $use_eg_live = FALSE;
  $use_eg_live_attr = FALSE;
  $flaws = array(
    'length' => array(
      'text' => t('Is shorter than @count characters', array(
        '@count' => (int) variable_get('password_strength_default_password_length', 7),
      )),
      'examples' => NULL,
    ),
    'mail' => array(
      'text' => t('Matches your email address'),
      'examples' => NULL,
    ),
    'name' => array(
      'text' => t('Matches your account name'),
      'examples' => NULL,
    ),
    'score' => array(
      'text' => t('Is not strong enough'),
      'examples' => NULL,
    ),
    'dictionary' => array(
      'text' => t('Contains dictionary words'),
      'examples' => array(
        'password',
      ),
    ),
    'sequence' => array(
      'text' => t('Has a common character sequence'),
      'examples' => array(
        '12345',
        'abc',
      ),
    ),
    'repeat' => array(
      'text' => t('Includes repeated characters'),
      'examples' => array(
        'aaa',
        '55555',
      ),
    ),
    'leetspeak' => array(
      'text' => t('Has leet (or “1337”), also known as eleet or leetspeak'),
      'examples' => array(
        'p4ssw0rd',
      ),
    ),
    'spatial' => array(
      'text' => t('Has a keyboard sequence'),
      'examples' => array(
        'qwerty',
        'asdf',
      ),
    ),
    'digit' => array(
      'text' => t('Has a series of just digits'),
      'examples' => array(
        '929',
      ),
    ),
    'date' => array(
      'text' => t('Includes a date'),
      'examples' => array(
        '19-11-1978',
      ),
    ),
    'year' => array(
      'text' => t('Includes a year'),
      'examples' => array(
        '2013',
      ),
    ),
  );
  
  $matches = array();
  foreach ($strength['matches'] as $match) {
    $matches[$match['pattern']][] = $match['matched'];
  }
  
  $items = array();
  foreach ($matches as $pattern => $match) {
    if (!isset($flaws[$pattern])) {
      continue;
    }
    $flaw = $flaws[$pattern];
    $item = array();
    $item['data'] = $flaw['text'];
    
    if ($use_eg && !empty($flaw['text']) && !empty($flaw['examples'])) {
      $item['data'] .= t(' (e.g. "@match")', array(
        '@match' => implode('", "', $flaw['examples']),
      ));
    }
    
    if ($use_eg_live) {
      $item['data'] .= t(' (e.g. "@match")', array(
        '@match' => implode('", "', $match),
      ));
    }
    
    if ($use_eg_live_attr) {
      $hint = t('e.g. @match', array(
        '@match' => implode(', ', $match),
      ));
      $item['data'] .= ' (<span class="hint" title="' . $hint . '">?</span>)';
    }
    $items[] = $item;
  }
  return $items;
}
function password_strength_required_score($account) {
  
  $score = variable_get('password_strength_default_required_score', 0);
  
  drupal_alter('password_strength_minimum_score', $score, $account);
  return $score;
}