Index: password_policy.admin.inc
===================================================================
--- password_policy.admin.inc
+++ password_policy.admin.inc	(working copy)
@@ -222,7 +222,7 @@
   $form['roles']['roles'] = array(
     '#type' => 'checkboxes',
     '#title' => t('Roles'),
-    '#options' => user_roles(),
+    '#options' => user_roles(TRUE),
     '#default_value' => isset($policy['roles']) ? $policy['roles'] : array(),
     '#description' => t('Select the roles that this policy will apply to.'),
   );
@@ -309,9 +309,11 @@
   $policy = array();
   foreach ($form_state['values'] as $key => $value) {
     // If we have no form value, then we have no constraint to set.
-    $value = trim($value);
-    if ($value != "" && preg_match("/^constraint_/", $key)) {
-      $policy[substr($key, 11)] = $value;
+    if (!is_array($value)) { //dodge issues with roles array
+      $value = trim($value);
+      if ($value != "" && preg_match("/^constraint_/", $key)) {
+        $policy[substr($key, 11)] = $value;
+      }
     }
   }
 
@@ -367,3 +369,63 @@
   drupal_goto('admin/settings/password_policy/list');
 }
 
+/**
+ * Forced password change form.
+ */
+function password_policy_password_change_settings() {
+  $form['password_policy_new_login_change'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Force password change on first-time login'),
+    '#default_value' => variable_get('password_policy_new_login_change', 0),
+  );
+  $roles = user_roles(TRUE);
+  $form['password_policy_force_change_roles'] = array(
+    '#type' => 'checkboxes',
+    '#options' => $roles,
+    '#title' => t('Force users in the following roles to change their password'),
+    '#description' => t('Users who are not signed in will be required to change their password immediately upon sign in. Users who are currently signed in will be required to change their password upon their next page click, but after changing their password will be redirected back to the page they were attempting to access.'),
+  );
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Submit'),
+  );
+  return $form;
+}
+
+/**
+ * Submit hook for forced password change form.
+ */
+function password_policy_password_change_settings_submit($form, &$form_state) {
+  global $user;
+  $selected_roles = array();
+  variable_set('password_policy_new_login_change', $form_state['values']['password_policy_new_login_change']);
+  if ($form_state['values']['password_policy_new_login_change'] == 1) {
+    watchdog('password policy', t('New user accounts must change password on new login enabled by !admin', array('!admin' => $user->name)), array(), WATCHDOG_NOTICE);
+  }
+  ($form_state['values']['password_policy_new_login_change']) ? drupal_set_message(t('New users will be required to change their password on first-time login.')) : drupal_set_message(t('New users will not be required to change their pasword on first-time login.'));
+  foreach ($form_state['values']['password_policy_force_change_roles'] as $role) {
+    $uids = array();
+    $db_uids = db_query('SELECT uid FROM {users_roles} WHERE rid = %d', $role);
+    while ($uid = db_fetch_array($db_uids)) {
+      if (($uid['uid'] == 1 && PASSWORD_POLICY_ADMIN) || $uid['uid'] > 1) {
+        $uids[] = $uid['uid'];
+      }
+    }
+    db_query("UPDATE {password_policy_force_change} SET force_change = 1 WHERE uid IN ('%s')", implode(', ', $uids));
+    $selected_roles[] = $role;
+  }
+  if (count($selected_roles)) {
+    $roles = user_roles(TRUE);
+    $list = array();
+    foreach ($selected_roles as $sr) {
+      $list[] = $roles[$sr];
+    }
+    $list = implode(', ', $list);
+    drupal_set_message(t('Users in the following roles will be required to immediately change their password: %list', array('%list' => $list)), 'status');
+    watchdog('password policy', t('!admin has flagged users in the following roles to immediately change their password: %list', array('%list' => $list, '!admin' => $user->name)), array(), WATCHDOG_NOTICE);
+  }
+  else {
+    drupal_set_message(t('No roles were selected.'));
+  }
+}
+
Index: password_policy.install
===================================================================
--- password_policy.install
+++ password_policy.install	(working copy)
@@ -1,5 +1,6 @@
 <?php
 // $Id: password_policy.install,v 1.8 2010/03/15 17:24:08 deekayen Exp $
+
 /**
  * @file
  * File module installation and upgrade code.
@@ -20,6 +21,7 @@
  */
 function password_policy_install() {
   drupal_install_schema('password_policy');
+  db_query('INSERT INTO {password_policy_force_change} (uid) SELECT DISTINCT uid FROM {users} WHERE uid > 0');
 }
 
 /**
@@ -33,6 +35,7 @@
   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');
 }
 
 function password_policy_update_6000() {
@@ -75,6 +78,21 @@
   return $ret;
 }
 
+function password_policy_update_6001() {
+  $ret = array();
+
+  db_create_table($ret, 'password_policy_force_change', array(
+    'description' => t('Forced password reset status.'),
+    'fields' => array(
+      'uid' => array('type' => 'int', 'not null' => TRUE),
+      'force_change' => array('type' => 'int', 'default' => 0),
+    ),
+    'indexes' => array('uid' => array('uid')),
+  ));
+  $ret[] = update_sql('INSERT INTO {password_policy_force_change} (uid) SELECT DISTINCT uid FROM {users} WHERE uid > 0');
+  return $ret;
+}
+
 //////////////////////////////////////////////////////////////////////////////
 // Schema API hooks
 
@@ -200,6 +218,14 @@
       'primary key' => array('pid'),
       'indexes' => array('uid' => array('uid')),
     ),
+    'password_policy_force_change' => array(
+      'description' => t('Forced password reset status.'),
+      'fields' => array(
+        'uid' => array('type' => 'int', 'not null' => TRUE),
+        'force_change' => array('type' => 'int', 'default' => 0),
+      ),
+      'indexes' => array('uid' => array('uid')),
+    ),
     'password_policy_role' => array(
       'description' => t("Links policies with roles."),
       'fields' => array(
Index: password_policy.module
===================================================================
--- password_policy.module
+++ password_policy.module	(working copy)
@@ -43,6 +43,21 @@
       $_password_policy[] = drupal_substr($file->name, 11);
     }
   }
+
+  global $user;
+  // Timing issues require reloading the user object
+  // to get the password_change property set.
+  $user = user_load($user->uid);
+
+  // Check password reset status and force a reset if needed.
+  $change_password_url = 'user/'. $user->uid .'/'. (module_exists('password_policy_password_tab') ? 'password' : 'edit');
+  if ($user->force_password_change && $_GET['q'] != $change_password_url) {
+    // let users log out
+    if (arg(0) != 'logout') {
+      drupal_set_message(t('Your password has expired. You must change your password to proceed on the site.'), 'error', FALSE);
+      drupal_goto($change_password_url, 'destination=' . $_GET['q']);
+    }
+  }
 }
 
 /**
@@ -62,7 +77,7 @@
  * Implementation of hook_perm().
  */
 function password_policy_perm() {
-  return array('unblock expired accounts');
+  return array('unblock expired accounts', 'force password change');
 }
 
 /**
@@ -129,6 +144,16 @@
     'access arguments' => array('administer site configuration'),
     'file' => 'password_policy.admin.inc',
   );
+  $items['admin/settings/password_policy/password_change'] = array(
+    'title' => 'Force password change',
+    'description' => 'Force users to change their password',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('password_policy_password_change_settings'),
+    'access arguments' => array('force password change'),
+    'file' => 'password_policy.admin.inc',
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 10,
+  );
   $items['admin/user/expired'] = array(
     'title' => 'Expired accounts',
     'description' => 'Lists all expired accounts.',
@@ -210,7 +235,23 @@
  */
 function password_policy_user($op, &$edit, &$account, $category = NULL) {
   switch ($op) {
+    case "load":
+      $account->force_password_change = db_result(db_query('SELECT force_change FROM {password_policy_force_change} WHERE uid=%d', $account->uid));
+      break;
+
+    case "validate":
+      global $user;
+      if (isset($account->uid) && $account->force_password_change == 1) {
+        // Admins can edit accounts without having to reset passwords.
+        if ($edit['pass'] == '' && $user->name == $account->name) {
+          form_set_error('pass', t('Your password has expired. You must change your password to proceed on the site.'));
+        }
+      }
+      break;
+
     case "insert":
+      $force = variable_get('password_policy_new_login_change', 0);
+      db_query('INSERT INTO {password_policy_force_change} VALUES(%d, %d)', $account->uid, $force);
       if (!empty($edit['pass'])) {
         // New users do not yet have an uid during the validation step, but they do have at this insert step.
         // Store their first password in the system for use with the history constraint (if used).
@@ -219,6 +260,13 @@
         }
       }
       break;
+
+    case "update":
+      if ($account->force_password_change) {
+        db_query('UPDATE {password_policy_force_change} SET force_change = 0 WHERE uid = %d', $account->uid);
+      }
+      break;
+
     case "login":
       $roles = is_array($account->roles) ? array_keys($account->roles) : array();
       $policy = _password_policy_load_active_policy($roles);
@@ -247,28 +295,14 @@
           }
           $time = time();
           if ($time > max($policy_start, $last_change) + $expiration_seconds) {
-            // The account has expired.
-            db_query("UPDATE {users} SET status = '0' WHERE uid = %d", $account->uid);
-            $result = db_query("SELECT * FROM {password_policy_expiration} WHERE uid = %d", $account->uid);
-            if ($row = db_fetch_array($result)) {
-              db_query("UPDATE {password_policy_expiration} SET blocked = %d WHERE uid = %d", $time, $account->uid);
-            }
-            else {
-              db_query("INSERT INTO {password_policy_expiration} (uid, blocked) VALUES (%d, %d)", $account->uid, $time);
-            }
-            watchdog('password_policy', 'Password for user %name has expired.', array('%name' => $account->name), WATCHDOG_NOTICE, l(t('edit'), 'user/'. $account->uid .'/edit'));
             if (PASSWORD_POLICY_BLOCK == 0) {
               // User is blocked imediately and cannot change his password after expiration.
-              include_once(drupal_get_path('module', 'user') .'/user.pages.inc');
-              user_logout();
+              _password_policy_block_account($account);
             }
             else {
-              // User is transfered to a password change page. User is unblocked on password change.
-              // If the user don't change password now, he will not be able to login again, since he is still blocked.
-              drupal_set_message(t('Your password has expired. You have to change it now or you won\'t be able to login again.'), 'error');
-              $destination = drupal_get_destination();
-              unset($_REQUEST['destination']);
-              drupal_goto('user/'. $account->uid .'/'. (module_exists('password_policy_password_tab') ? 'password' : 'edit'), $destination);
+              // Redirect user and let password force change handle.
+              db_query('UPDATE {password_policy_force_change} SET force_change=%d WHERE uid=%d', 1, $account->uid);
+              drupal_goto();
             }
           }
           elseif ($time > max($policy_start, $last_change) + $expiration_seconds - $warning_seconds) {
@@ -282,10 +316,13 @@
         }
       }
       break;
+
     case "delete":
       db_query("DELETE FROM {password_policy_history} WHERE uid = %d", $account->uid);
       db_query("DELETE FROM {password_policy_expiration} WHERE uid = %d", $account->uid);
+      db_query("DELETE FROM {password_policy_force_change} WHERE uid = %d", $account->uid);
       break;
+
     case "submit":
       if ($account->status == 0 && isset($edit['status']) && $edit['status'] == 1 && user_access('unblock expired accounts')) {
         // Account is beeing unblocked.
@@ -307,7 +344,6 @@
       //if ($uid == 1 && !PASSWORD_POLICY_ADMIN) { break; }
       $roles = isset($form['_account']['#value']) ? array_keys($form['_account']['#value']->roles) : array();
       $policy = _password_policy_load_active_policy($roles);
-      
       $translate = array();
       if (!empty($policy['policy'])) {
         // Some policy constraints are active.
@@ -322,6 +358,19 @@
         $form['account']['pass']['#prefix'] = '<div id="account-pass-restrictions"><ul><li>'. implode('</li><li>', $translate) .'</li></ul></div>';
       }
 
+      // Force Password Change on user account.
+      if (isset($form['account']['pass']) && user_access('force password change')) {
+        $form['password_policy'] = array(
+          '#type' => 'fieldset',
+          '#title' => t('Password settings'),
+        );
+        $form['password_policy']['force_password_change'] = array(
+          '#type' => 'checkbox',
+          '#title' => t('Force password change on next login'),
+        );
+        $form['#submit'][] = 'password_policy_password_change_user_submit';
+      }
+
       // Set a custom form validate and submit handlers.
       $form['#validate'][] = 'password_policy_password_validate';
       $form['#submit'][] = 'password_policy_password_submit';
@@ -474,13 +523,40 @@
   // Track the hashed password values which can then be used in the history constraint.
   if ($account->uid && !empty($values['pass'])) {
     _password_policy_store_password($account->uid, $values['pass']);
+  }
+}
 
-    // If user successfully changed his password we will unblock the account.
-    if ($user->uid == $account->uid) {
-      db_query("UPDATE {users} SET status = 1 WHERE uid = %d", $account->uid);
-      db_query("DELETE FROM {password_policy_expiration} WHERE uid = %d", $account->uid);
+/**
+ * Submit handler for force password change field
+ */
+function password_policy_password_change_user_submit($form, $form_state) {
+  global $user;
+  
+  $force_change = $form_state['values']['force_password_change'];
+  $name = $form_state['values']['name'];
+  $uid = $form['#uid'];
+
+  if (is_numeric($uid)) {
+    // existing account
+    if ($force_change == 1) {
+      db_query('UPDATE {password_policy_force_change} SET force_change=%d WHERE uid=%d', $force_change, $form['#uid']);
     }
   }
+  
+  else {
+    // new account
+    if (variable_get('password_policy_new_login_change', 0)) {
+      $force_change = 1;
+    }
+    db_query("UPDATE {password_policy_force_change} AS f INNER JOIN {users} u ON u.uid = f.uid SET force_change=%d WHERE name='%s'", $force_change, $name);
+  }
+
+  if ($force_change == 1) {
+    if ($user->name != $name) {
+      drupal_set_message(t('!user will be required to change their password the next time they log in.', array('!user' => $name)));
+    }
+    watchdog('password policy', t('!user flagged to change password on next login by !admin', array('!user' => $name, '!admin' => $user->name)), array(), WATCHDOG_NOTICE);
+  }
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -724,6 +800,30 @@
 }
 
 /**
+ * Block the expired account.
+ *
+ * @param $account
+ *   User object.
+ */
+function _password_policy_block_account($account) {
+  if ($account->uid > 1) { // We never block the superuser account.
+    db_query("UPDATE {users} SET status = '0' WHERE uid = %d", $account->uid);
+    
+    if (db_result(db_query("SELECT pid FROM {password_policy_expiration} WHERE uid = %d", $account->uid))) {
+      db_query("UPDATE {password_policy_expiration} SET blocked = %d WHERE uid = %d", time(), $account->uid);
+    }
+    else {
+      db_query("INSERT INTO {password_policy_expiration} (uid, blocked) VALUES (%d, %d)", $account->uid, time());
+    }
+    
+    watchdog('password_policy', 'Password for user %name has expired.', array('%name' => $account->name), WATCHDOG_NOTICE, l(t('edit'), 'user/'. $account->uid .'/edit'));
+    
+    include_once(drupal_get_path('module', 'user') .'/user.pages.inc');
+    user_logout();
+  }
+} 
+
+/**
  * Unblocks the expired account.
  *
  * @param $uccount
Index: tests/password_policy.test
===================================================================
--- tests/password_policy.test
+++ tests/password_policy.test	(working copy)
@@ -14,6 +14,10 @@
     );
   }
 
+  public function setUp() {
+    parent::setUp('password_policy');
+  }
+
   function testUppercaseConstraint() {
     $constraint = 2;
     $uid = 0;
@@ -143,62 +147,55 @@
     $policy_name = $this->randomName();
     $edit = array(
       'name' => $policy_name,
-      'constraint_history' => '2',
+      'constraint_history' => t('2'),
+      'roles[2]' => '2',
     );
     $this->drupalPost('admin/settings/password_policy/add', $edit, t('Create'));
     $this->assertText('Policy '. $policy_name .' has been created.', 'Policy '. $policy_name .' has been created');
-    // Saving default policy
-    $source = $this->drupalGet('admin/settings/password_policy/list');
-    $source = str_replace("\n", '', $source);
-    $default = preg_replace('/(.*)value="(\d+)"  checked="checked"(.*)/', '$2', $source);
-    $default = ctype_digit($default) ? $default : '';
-    // Finding the policy's pid
-    $pid = preg_replace('/(.*)'. $policy_name .'[^\d]+(\d+)"(.*)/', '$2', $source);
-    // Setting default policy
+    // Enable newly created policy
+    $pid = db_result(db_query('SELECT pid FROM {password_policy} WHERE name="%s"', $policy_name));
+    $constraints = unserialize(db_result(db_query('SELECT policy FROM {password_policy} WHERE pid=%d', $pid)));
+    $this->assertTrue($constraints['history'] == 2, t('Verified history constraint set.'));
     $edit = array(
-      'default' => $pid,
+      "enabled[$pid]" => $pid,
     );
-    $this->drupalPost('admin/settings/password_policy/list', $edit, t('Set default policy'));
-    $this->assertText($policy_name .' has been set as the default password policy.', $policy_name .' has been set as the default password policy');
+    $this->drupalPost('admin/settings/password_policy/list', $edit, t('Save changes'));
+    $this->assertText(t('The changes have been saved.'), t('Form submitted successfully.'));
+    $enabled = db_result(db_query('SELECT enabled FROM {password_policy} WHERE pid=%d', $pid));
+    $this->assertTrue($enabled == 1, t('Policy enabled.'));
+
     // Change password
     $pass1 = "aaaaaa";
     $edit = array(
       'pass[pass1]' => $pass1,
       'pass[pass2]' => $pass1,
     );
-    $this->drupalPost('user/'. $user->uid .'/edit',  $edit, t('Save'));
-    $this->assertText('The changes have been saved.', 'The password has been changed');
+    $this->drupalPost("user/$user->uid/edit",  $edit, t('Save'));
+    $this->assertText(t('The changes have been saved.'), t("1st password change: $pass1"));
     // Change password second time
     $pass2 = "bbbbbb";
     $edit = array(
-      'pass[pass1]' => $pass2,
-      'pass[pass2]' => $pass2,
+      'pass[pass1]' => $pass1,
+      'pass[pass2]' => $pass1,
     );
-    $this->drupalPost('user/'. $user->uid .'/edit',  $edit, t('Save'));
-    $this->assertText('The changes have been saved.', 'The password has been changed');
+    $this->drupalPost("user/$user->uid/edit",  $edit, t('Save'));
+    $this->assertText(t('Your password has not met the following requirement(s):'), t("2nd password change should fail: $pass1"));
     // Try changing password with the first one
     $edit = array(
-      'pass[pass1]' => $pass1,
-      'pass[pass2]' => $pass1,
+      'pass[pass1]' => $pass2,
+      'pass[pass2]' => $pass2,
     );
-    $this->drupalPost('user/'. $user->uid .'/edit',  $edit, t('Save'));
-    $this->assertText('Password must not match 2 last passwords.', 'The password has not been changed');
+    $this->drupalPost("user/$user->uid/edit",  $edit, t('Save'));
+    $this->assertText(t('The changes have been saved.'), t("3rd password change should pass: $pass2"));
     // Change password again
     $pass3 = "cccccc";
     $edit = array(
       'pass[pass1]' => $pass3,
       'pass[pass2]' => $pass3,
     );
-    $this->drupalPost('user/'. $user->uid .'/edit',  $edit, t('Save'));
-    $this->assertText('The changes have been saved.', 'The password has been changed');
-    // Setting back default policy
-    if ($default) {
-      $edit = array(
-        'default' => $default,
-      );
-      $this->drupalPost('admin/settings/password_policy/list', $edit, t('Set default policy'));
-      $this->assertText(' has been set as the default password policy.', 'Default password policy was restored');
-    }
+    $this->drupalPost("user/$user->uid/edit",  $edit, t('Save'));
+    $this->assertText(t('The changes have been saved.'), t("4th password change should pass: $pass3"));
+
     // Delete test policy
     $this->drupalPost('admin/settings/password_policy/delete/'. $pid, array(), t('Delete'));
     $this->assertText('Password policy '. $policy_name .' was deleted.', 'Default password policy '. $policy_name .'was deleted');
@@ -239,3 +236,185 @@
   }
 }
 
+class PasswordPolicyForcePasswordChangeTestCase extends DrupalWebTestCase {
+  protected $admin_user;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Force password change tests',
+      'description' => 'Tests forced password reset for single user, role and all new users.',
+      'group' => 'Password Policy',
+    );
+  }
+
+  public function setUp() {
+    parent::setUp('password_policy');
+  }
+
+  // test permissions
+  public function testPerms() {
+    // no perms
+    $user = $this->drupalCreateUser();
+    $this->drupalLogin($user);
+    $this->drupalGet('admin/settings/password_policy/password_change');
+    $this->assertResponse('403', t('Acces should be denied.'));
+    $this->drupalLogout();
+
+    // with perms
+    $user = $this->drupalCreateUser(array('force password change'));
+    $this->drupalLogin($user);
+    $this->drupalGet('admin/settings/password_policy/password_change');
+    $this->assertResponse('200', t('Access should be granted.'));
+    $this->drupalLogout();
+  }
+
+  // test form elements
+  public function testForms() {
+    // test admin form
+    $user = $this->drupalCreateUser(array('force password change', 'administer users'));
+    $this->drupalLogin($user);
+    $this->drupalGet('admin/settings/password_policy/password_change');
+    $this->assertFieldByName('password_policy_new_login_change', '', t('Found first time login change checkbox.'));
+    $this->assertFieldByName('password_policy_force_change_roles[2]', '', t('Found roles checkboxes.'));
+    $this->assertFieldById('edit-submit', '', t('Found submit button.'));
+
+    // test user edit form with perms
+    $this->drupalGet("user/$user->uid/edit");
+    $this->assertFieldByName('force_password_change', '', 'Force password change checkbox is visible to admin.');
+    $this->drupalLogout();
+
+    // test user edit form without perms
+    $user = $this->drupalCreateUser();
+    $this->drupalLogin($user);
+    $this->drupalGet("user/$user->uid/edit");
+    $this->assertNoFieldByName('force_password_change', '', 'Force password change checkbox is hidden for normal users.');
+    $this->drupalLogout();
+  }
+
+  // test single user password change
+  public function testSingleUser() {
+    $admin = $this->drupalCreateUser(array('force password change', 'administer users'));
+    $user = $this->drupalCreateUser();
+    $this->drupalLogin($admin);
+    $edit = array(
+      'force_password_change' => 1,
+    );
+    $this->drupalPost("user/$user->uid/edit", $edit, t('Save'));
+    $this->assertRaw(t('!user will be required to change their password the next time they log in.', array('!user' => $user->name)), t('User flagged for password change.'));
+    $force_change = db_result(db_query('SELECT force_change FROM {password_policy_force_change} WHERE uid=%d', $user->uid));
+    $this->assertTRUE($force_change == 1, t('Force change flag set to %d for %s.', array('%d' => $force_change, '%s' => $user->name)));
+
+    //confirm admin can edit user account without changing password
+    $edit = array(
+      'name' => $user->name,
+      'mail' => $user->mail,
+      'force_password_change' => 1,
+    );
+    $this->drupalPost("user/$user->uid/edit", $edit, t('Save'));
+    $force_change = db_result(db_query('SELECT force_change FROM {password_policy_force_change} WHERE uid=%d', $user->uid));
+    $this->assertTrue($force_change == 1, t('User force change flag set in database:%s.', array('%s' => $force_change)));
+    $this->assertNoRaw(t('An administrator has required that you change your password. You must change your password to proceed on the site.'), t('Admin can edit user account without changing password.'));
+    $this->drupalLogout();
+
+    // verify user is forced to change password
+    $this->drupalLogin($user);
+    $this->assertFieldByName('mail', '', t('User redirected correctly.')); // marginal
+    $this->assertRaw(t('An administrator has required that you change your password. You must change your password to proceed on the site.'), t('User presented with error instructing them to change their password.'));
+
+    // attempt to submit form with null password fields
+    $edit = array(
+      'mail' => $user->mail,
+    );
+    $this->drupalPost('user/$user->uid/edit', $edit, t('Save'));
+    $this->assertRaw(t('You must choose a new password.'), t('Null password not permitted.'));
+
+    // attempt to submit form with current password
+    $edit = array(
+      'mail' => $user->mail,
+      'pass[pass1]' => $user->pass_raw,
+      'pass[pass2]' => $user->pass_raw,
+    );
+    $this->drupalPost('user/$user->uid/edit', $edit, t('Save'));
+    $this->assertRaw(t('You cannot use your current password. Please choose something different.'), t('Old password not permitted.'));
+
+    // attempt to change password
+    $edit = array(
+      'mail' => $user->mail,
+      'pass[pass1]' => 'random_string',
+      'pass[pass2]' => 'random_string',
+    );
+    $this->drupalPost('user/$user->uid/edit', $edit, t('Save'));
+    $this->assertRaw(t('The changes have been saved.'), t('Password change successful.'));
+
+    // verify user not prompted to change password a 2nd time
+    $this->drupalGet('node');
+    $this->assertNoFieldByName('mail', '', t('User not forced to change password a 2nd time.'));
+    $this->drupalLogout();
+  }
+
+  // test role-based password change
+  public function testRoleChange() {
+    $admin = $this->drupalCreateUser(array('administer users', 'force password change'));
+    $user1 = $this->drupalCreateUser();
+    $user2 = $this->drupalCreateUser();
+    $this->drupalLogin($admin);
+    $edit = array(
+      'password_policy_force_change_roles[2]' => 2,
+    );
+    $this->drupalPost('admin/settings/password_policy/password_change', $edit, t('Submit'));
+    $this->assertText(t('Users in the following roles will be required to immediately change their password: authenticated user'), t('Authenticated users role selected.'));
+    $this->assertRaw(t('An administrator has required that you change your password. You must change your password to proceed on the site.'), t('Admin forced to change password due to role selection.'));
+    $this->drupalLogout();
+
+    //test individual users
+    $this->drupalLogin($user1);
+    $this->assertRaw(t('An administrator has required that you change your password. You must change your password to proceed on the site.'), t('First test user forced to change password.'));
+    $this->drupalLogout();
+
+    //test 2nd user
+    $this->drupalLogin($user2);
+    $this->assertRaw(t('An administrator has required that you change your password. You must change your password to proceed on the site.'), t('Second test user forced to change password.'));
+    $this->drupalLogout();
+
+  }
+
+  //test new user change
+  public function testNewUserChange() {
+    $admin = $this->drupalCreateUser(array('administer users', 'force password change'));
+    $this->drupalLogin($admin);
+    $edit = array(
+      'password_policy_new_login_change' => 1,
+    );
+    $this->drupalPost('admin/settings/password_policy/password_change', $edit, t('Submit'));
+    $this->assertRaw(t('New users will be required to change their password on first-time login.'), t('New users required to change password on 1st login.'));
+    $this->drupalLogout();
+    $user = $this->drupalCreateUser();
+    $this->drupalLogin($user);
+    $this->drupalGet('node');
+    $this->assertRaw(t('An administrator has required that you change your password. You must change your password to proceed on the site.'), t('New user forced to change password.'));
+  }
+
+  //test admin forcing their own account to reset
+  public function testSelfChange() {
+    $admin = $this->drupalCreateUser(array('administer users', 'force password change'));
+    $this->drupalLogin($admin);
+    $this->assertNoRaw(t('An administrator has required that you change your password. You must change your password to proceed on the site.'), t('Admin should not be prompted to change password yet.'));
+    $edit = array(
+      'force_password_change' => 1,
+    );
+    $this->drupalPost("user/$admin->uid/edit", $edit, t('Save'));
+    $this->assertRaw(t('The changes have been saved.'), t('Admin has queued account for password change.'));
+    $this->assertNoRaw(t('An administrator has required that you change your pasword. You must change your password to proceed on the site.'), t('Admin not initially prompted to change password.'));
+    $this->drupalGet('node');
+    $this->assertRaw(t('An administrator has required that you change your password. You must change your password to proceed on the site.'), t('Admin forced to change password once they try to leave account page.'));
+    $edit = array(
+      'mail' => $admin->mail,
+      'pass[pass1]' => 'fpcR@nd0m!',
+      'pass[pass2]' => 'fpcR@nd0m!',
+    );
+    $this->drupalPost("user/$admin->uid/edit", $edit, t('Save'));
+    $this->assertRaw(t('The changes have been saved.'), t('Admin changed password.'));
+    $this->drupalGet('node');
+    $this->assertNoRaw(t('An administrator has required that you change your password. You must change your password to proceed on the site.'), t('Not prompted to change password a 2nd time.'));
+  }
+}
