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;
}