You are here

password_policy.install in Password Policy 7

Password Policy module installation and upgrade code.

File

password_policy.install
View source
<?php

/**
 * @file
 * Password Policy module installation and upgrade code.
 */

/**
 * Implements hook_schema().
 */
function password_policy_schema() {
  return array(
    'password_policy' => array(
      'description' => 'Stores password policies.',
      'fields' => array(
        'pid' => array(
          'description' => 'Primary Key: Unique password policy ID.',
          'type' => 'serial',
          'not null' => TRUE,
        ),
        'name' => array(
          'description' => 'The name of the policy.',
          'type' => 'varchar',
          'length' => 64,
          'not null' => TRUE,
          'default' => '',
        ),
        'description' => array(
          'description' => 'The description of the policy.',
          'type' => 'varchar',
          'length' => 255,
          'default' => '',
        ),
        'enabled' => array(
          'description' => 'Whether the policy is enabled.',
          'type' => 'int',
          'not null' => TRUE,
          'default' => 0,
          'size' => 'tiny',
        ),
        'constraints' => array(
          'description' => 'The policy\'s serialized constraints.',
          'type' => 'varchar',
          'length' => 1024,
          'not null' => TRUE,
          'default' => '',
          'serialize' => TRUE,
        ),
        'created' => array(
          'description' => 'Timestamp for when the policy was created.',
          'type' => 'int',
          'not null' => TRUE,
          'default' => 0,
        ),
        'expiration' => array(
          'description' => 'The passwords will expire after this number of days.',
          'type' => 'int',
          'default' => 0,
        ),
        'warning' => array(
          'description' => 'Comma separated list of days when warning is sent out.',
          'type' => 'varchar',
          'length' => 64,
        ),
        'weight' => array(
          'description' => 'Weight of the policy, used to order active policies.',
          'type' => 'int',
          'size' => 'tiny',
          'not null' => TRUE,
          'default' => 0,
        ),
      ),
      'primary key' => array(
        'pid',
      ),
      'unique keys' => array(
        'name' => array(
          'name',
        ),
      ),
    ),
    'password_policy_history' => array(
      'description' => 'Stores user\'s old password hashes.',
      'fields' => array(
        'pid' => array(
          'description' => 'Primary Key: Unique password policy users ID.',
          'type' => 'serial',
          'not null' => TRUE,
        ),
        'uid' => array(
          'description' => 'User\'s {users}.uid.',
          'type' => 'int',
          'not null' => TRUE,
        ),
        'pass' => array(
          'type' => 'varchar',
          'length' => 128,
          'not null' => TRUE,
          'default' => '',
          'description' => 'User\'s password (hashed).',
        ),
        'created' => array(
          'description' => 'Timestamp for when the policy was created.',
          'type' => 'int',
          'not null' => TRUE,
        ),
      ),
      'foreign keys' => array(
        'password_policy' => array(
          'table' => 'password_policy',
          'columns' => array(
            'pid' => 'pid',
          ),
        ),
        'user' => array(
          'table' => 'users',
          'columns' => array(
            'uid' => 'uid',
          ),
        ),
      ),
      'indexes' => array(
        'uid' => array(
          'uid',
        ),
      ),
      'primary key' => array(
        'pid',
      ),
    ),
    'password_policy_expiration' => array(
      'description' => 'Stores users password expiration data.',
      'fields' => array(
        'pid' => array(
          'description' => 'Primary Key: Unique password policy expirations ID.',
          'type' => 'serial',
          'not null' => TRUE,
        ),
        'uid' => array(
          'description' => 'User\'s {users}.uid.',
          'type' => 'int',
          'not null' => TRUE,
          'default' => 0,
        ),
        'warning' => array(
          'description' => 'Timestamp for when the warning was shown.',
          'type' => 'int',
        ),
        'blocked' => array(
          'description' => 'Timestamp for when the user was blocked.',
          'type' => 'int',
        ),
        'unblocked' => array(
          'description' => 'Timestamp for when the user was unblocked.',
          'type' => 'int',
        ),
      ),
      'primary key' => array(
        'pid',
      ),
      'foreign keys' => array(
        'password_policy' => array(
          'table' => 'password_policy',
          'columns' => array(
            'pid' => 'pid',
          ),
        ),
        'role' => array(
          'table' => 'role',
          'columns' => array(
            'rid' => 'rid',
          ),
        ),
      ),
      'indexes' => array(
        'uid' => array(
          'uid',
        ),
      ),
    ),
    'password_policy_force_change' => array(
      'description' => 'Forced password reset status.',
      'fields' => array(
        'uid' => array(
          'type' => 'int',
          'not null' => TRUE,
        ),
        'force_change' => array(
          'type' => 'int',
          'default' => 0,
        ),
      ),
      'foreign keys' => array(
        'user' => array(
          'table' => 'users',
          'columns' => array(
            'uid' => 'uid',
          ),
        ),
      ),
      'indexes' => array(
        'uid' => array(
          'uid',
        ),
      ),
      'primary key' => array(
        'uid',
      ),
    ),
    'password_policy_role' => array(
      'description' => 'Links policies with roles.',
      'fields' => array(
        'pid' => array(
          'description' => 'Policy ID.',
          'type' => 'int',
          'not null' => TRUE,
          'default' => 0,
        ),
        'rid' => array(
          'description' => 'Role ID.',
          'type' => 'int',
          'not null' => TRUE,
          'default' => 0,
        ),
      ),
      'foreign keys' => array(
        'password_policy' => array(
          'table' => 'password_policy',
          'columns' => array(
            'pid' => 'pid',
          ),
        ),
        'role' => array(
          'table' => 'role',
          'columns' => array(
            'rid' => 'rid',
          ),
        ),
      ),
      'primary key' => array(
        'rid',
        'pid',
      ),
    ),
    'password_policy_excluded_authentication_modules' => array(
      'description' => 'Authentication modules to be excluded from specified policies.',
      'fields' => array(
        'pid' => array(
          'description' => 'Policy ID.',
          'type' => 'int',
          'not null' => TRUE,
          'default' => 0,
        ),
        'module' => array(
          'description' => 'Module.',
          'type' => 'varchar',
          'length' => 128,
          'not null' => TRUE,
          'default' => '',
        ),
      ),
      'foreign keys' => array(
        'password_policy' => array(
          'table' => 'password_policy',
          'columns' => array(
            'pid' => 'pid',
          ),
        ),
        'module' => array(
          'table' => 'authmap',
          'columns' => array(
            'module' => 'module',
          ),
        ),
      ),
      'primary key' => array(
        'module',
        'pid',
      ),
    ),
  );
}

/**
 * Implements hook_enable().
 */
function password_policy_enable() {
  drupal_set_message(t('Password policy module successfully installed. Please review the available <a href="@settings">configuration settings</a>.', array(
    '@settings' => url('admin/config/people/password_policy'),
  )));
}

/**
 * Implements hook_install().
 */
function password_policy_install() {
  $query = db_select('users', 'u')
    ->fields('u', array(
    'uid',
  ))
    ->distinct()
    ->condition('u.uid', 0, '>');
  db_insert('password_policy_force_change')
    ->fields(array(
    'uid',
  ))
    ->from($query)
    ->execute();
}

/**
 * Implements hook_uninstall().
 */
function password_policy_uninstall() {
  variable_del('password_policy_admin');
  variable_del('password_policy_begin');
  variable_del('password_policy_block');
  variable_del('password_policy_show_restrictions');
  variable_del('password_policy_warning_subject');
  variable_del('password_policy_warning_body');
  variable_del('password_policy_new_login_change');
  variable_del('password_policy_force_change_reset');
  variable_del('password_policy_force_change_by_mail');
  variable_del('password_policy_force_change_path');
  variable_del('password_policy_force_change_extra_allowed_paths');
  variable_del('password_policy_expired_account_entries');
}

/**
 * Remove unused 'name' column from {password_policy_role}.
 */
function password_policy_update_7000() {
  db_drop_field('password_policy_role', 'name');
  db_drop_unique_key('password_policy_role', 'name');
}

/**
 * The implementation of password_policy_update_7001() has been removed.
 *
 * Do not re-implement it. hook_update_N() functions should not be renumbered.
 */

/**
 * The implementation of password_policy_update_7002() has been removed.
 *
 * Do not re-implement it. hook_update_N() functions should not be renumbered.
 */

/**
 * Notify of change to username constraint behavior.
 */
function password_policy_update_7100() {
  return t('The username constraint has changed to disallow passwords containing the username in addition to passwords matching the username.');
}

/**
 * Update schema left unchanged from D6 -> D7 migration.
 */
function password_policy_update_7101() {
  db_change_field('password_policy', 'expiration', 'expiration', array(
    'description' => 'The passwords will expire after this number of days.',
    'type' => 'int',
    'default' => 0,
  ), array());
  db_change_field('password_policy_history', 'pass', 'pass', array(
    'description' => 'User\'s password (hashed).',
    'type' => 'varchar',
    'length' => 128,
    'not null' => TRUE,
    'default' => '',
  ), array());
}

/**
 * Convert any D6 password hashes in history to D7 format.
 *
 * This may be a lengthy process, and is performed batch-wise.
 *
 * Code mostly copied from user_update_7000(), which updates user password
 * hashes upon D6 -> D7 migration.
 *
 * @see user_update_7000()
 */
function password_policy_update_7102(&$sandbox) {

  // Ignore Coder warning to minimize differences versus user_update_7000().
  // @ignore sniffer_commenting_inlinecomment_spacingbefore:function
  // @codingStandardsIgnoreStart
  $sandbox['#finished'] = 0;

  // Multi-part update.
  if (!isset($sandbox['password_from'])) {
    $sandbox['password_from'] = 0;
    $sandbox['password_count'] = db_query('SELECT COUNT(pid) FROM {password_policy_history}')
      ->fetchField();
  }
  else {
    require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');

    //  Hash again all current hashed passwords.
    $has_rows = FALSE;

    // Update this many per page load.
    $count = 1000;
    $result = db_query_range('SELECT pid, pass FROM {password_policy_history} ORDER BY pid', $sandbox['password_from'], $count);
    foreach ($result as $row) {
      $has_rows = TRUE;

      // If the $row->pass value is not a MD5 hash (a 32 character
      // hexadecimal string) then skip it.
      if (!preg_match('/^[0-9a-f]{32}$/', $row->pass)) {
        continue;
      }
      $new_hash = user_hash_password($row->pass);
      if ($new_hash) {

        // Indicate an updated password.
        $new_hash = 'U' . $new_hash;
        db_update('password_policy_history')
          ->fields(array(
          'pass' => $new_hash,
        ))
          ->condition('pid', $row->pid)
          ->execute();
      }
    }
    $sandbox['#finished'] = $sandbox['password_count'] == 0 ? 1 : $sandbox['password_from'] / $sandbox['password_count'];
    $sandbox['password_from'] += $count;
    if (!$has_rows) {
      $sandbox['#finished'] = 1;
      return t('User passwords in Password Policy history rehashed to improve security');
    }
  }

  // @codingStandardsIgnoreEnd
}

/**
 * Convert expiration warning e-mail tokens to Drupal 7 format.
 */
function password_policy_update_7103() {
  $variables = array(
    'password_policy_warning_subject',
    'password_policy_warning_body',
  );
  foreach ($variables as $variable) {
    _password_policy_convert_variable_tokens_d6_to_d7($variable);
  }
}

/**
 * Converts tokens in a Password Policy string variable to Drupal 7 format.
 *
 * @param string $variable
 *   Variable name.
 */
function _password_policy_convert_variable_tokens_d6_to_d7($variable) {
  $value = variable_get($variable, FALSE);
  if ($value) {
    $value = _password_policy_replace_d6_with_d7_tokens($value);
    variable_set($variable, $value);
  }
}

/**
 * Replaces D6 tokens in a Password Policy string with D7 tokens.
 */
function _password_policy_replace_d6_with_d7_tokens($subject) {
  $d6_to_d7_tokens = _password_policy_get_d6_to_d7_tokens_mapping();
  $d6_tokens = array_keys($d6_to_d7_tokens);
  $d7_tokens = array_values($d6_to_d7_tokens);
  return str_replace($d6_tokens, $d7_tokens, $subject);
}

/**
 * Gets D6-to-D7 mapping for tokens used by the Password Policy module.
 *
 * @see user_update_7017()
 */
function _password_policy_get_d6_to_d7_tokens_mapping() {

  // D6-to-D7 mapping for tokens that are used by User module e-mails. Copied
  // from user_update_7017().
  $user_email_tokens = array(
    '!site' => '[site:name]',
    '!username' => '[user:name]',
    '!mailto' => '[user:mail]',
    '!login_uri' => '[site:login-url]',
    '!uri_brief' => '[site:url-brief]',
    '!login_url' => '[user:one-time-login-url]',
    '!uri' => '[site:url]',
    '!date' => '[date:medium]',
    '!password' => '',
  );

  // D6-to-D7 mapping for Password Policy tokens.
  $password_policy_email_tokens = array(
    '!days_left' => '[password-policy:days-left]',
    '!edit_uri' => '[password-policy:password-edit-url]',
  );

  // Combined D6-to-D7 mapping for tokens.
  $tokens = array_merge($user_email_tokens, $password_policy_email_tokens);
  return $tokens;
}

/**
 * Preserve "Admin (UID=1) password expires" setting.
 *
 * For increased security, the admin (UID=1) password is no longer exempt from
 * password expiration by default. That is, "Admin (UID=1) password expires" is
 * now checked by default.
 *
 * This database update will keep the current setting so that the change in the
 * default will have no effect on existing installations.
 */
function password_policy_update_7104() {
  $current_setting = variable_get('password_policy_admin', 0);
  variable_set('password_policy_admin', $current_setting);
}

/**
 * Improve naming of database field.
 *
 * Rename "policy" of {password_policy} to "constraints".
 */
function password_policy_update_7106() {
  $table_name = 'password_policy';
  $old_field_name = 'policy';
  $new_field_name = 'constraints';
  $field_spec = array(
    'description' => 'The policy\'s serialized constraints.',
    'type' => 'varchar',
    'length' => 1024,
    'not null' => TRUE,
    'default' => '',
    'serialize' => TRUE,
  );
  db_change_field($table_name, $old_field_name, $new_field_name, $field_spec);
}

/**
 * Add primary key to {password_policy_force_change}.
 */
function password_policy_update_7107() {
  db_add_primary_key('password_policy_force_change', array(
    'uid',
  ));
}

/**
 * Rename "Complexity" constraint to "Character Types".
 */
function password_policy_update_7108() {
  $result = db_query('SELECT pid, constraints FROM {password_policy}');
  foreach ($result as $row) {
    $pid = $row->pid;
    $constraints = $row->constraints;
    $constraints = unserialize($constraints);
    if (isset($constraints['complexity'])) {
      $constraints['character_types'] = $constraints['complexity'];
      unset($constraints['complexity']);
      $constraints = serialize($constraints);
      db_update('password_policy')
        ->fields(array(
        'constraints' => $constraints,
      ))
        ->condition('pid', $pid)
        ->execute();
    }
  }
}

/**
 * Add {password_policy_excluded_authentication_modules} table.
 */
function password_policy_update_7109() {
  $schema['password_policy_excluded_authentication_modules'] = array(
    'description' => 'Authentication modules to be excluded from specified policies.',
    'fields' => array(
      'pid' => array(
        'description' => 'Policy ID.',
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
      ),
      'module' => array(
        'description' => 'Module.',
        'type' => 'varchar',
        'length' => 128,
        'not null' => TRUE,
        'default' => '',
      ),
    ),
    'foreign keys' => array(
      'password_policy' => array(
        'table' => 'password_policy',
        'columns' => array(
          'pid' => 'pid',
        ),
      ),
      'module' => array(
        'table' => 'authmap',
        'columns' => array(
          'module' => 'module',
        ),
      ),
    ),
    'primary key' => array(
      'module',
      'pid',
    ),
  );
  db_create_table('password_policy_excluded_authentication_modules', $schema['password_policy_excluded_authentication_modules']);
}

/**
 * Clear schema cache to prevent Features errors.
 *
 * When a site is using the Features module and is upgraded from a version of
 * Password Policy before 7.x-1.10, it is possible that Features will attempt
 * to define some functions twice, resulting in PHP errors. Clearing the schema
 * cache prevents that from occurring.
 */
function password_policy_update_7110() {
  cache_clear_all('schema', 'cache', TRUE);
}

Functions

Namesort descending Description
password_policy_enable Implements hook_enable().
password_policy_install Implements hook_install().
password_policy_schema Implements hook_schema().
password_policy_uninstall Implements hook_uninstall().
password_policy_update_7000 Remove unused 'name' column from {password_policy_role}.
password_policy_update_7100 Notify of change to username constraint behavior.
password_policy_update_7101 Update schema left unchanged from D6 -> D7 migration.
password_policy_update_7102 Convert any D6 password hashes in history to D7 format.
password_policy_update_7103 Convert expiration warning e-mail tokens to Drupal 7 format.
password_policy_update_7104 Preserve "Admin (UID=1) password expires" setting.
password_policy_update_7106 Improve naming of database field.
password_policy_update_7107 Add primary key to {password_policy_force_change}.
password_policy_update_7108 Rename "Complexity" constraint to "Character Types".
password_policy_update_7109 Add {password_policy_excluded_authentication_modules} table.
password_policy_update_7110 Clear schema cache to prevent Features errors.
_password_policy_convert_variable_tokens_d6_to_d7 Converts tokens in a Password Policy string variable to Drupal 7 format.
_password_policy_get_d6_to_d7_tokens_mapping Gets D6-to-D7 mapping for tokens used by the Password Policy module.
_password_policy_replace_d6_with_d7_tokens Replaces D6 tokens in a Password Policy string with D7 tokens.