? force_password_change_5.patch Index: password_policy.admin.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/password_policy/password_policy.admin.inc,v retrieving revision 1.12 diff -u -p -r1.12 password_policy.admin.inc --- password_policy.admin.inc 15 Mar 2010 17:24:08 -0000 1.12 +++ password_policy.admin.inc 13 Apr 2010 17:39:20 -0000 @@ -222,7 +222,7 @@ function password_policy_admin_form($for $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 @@ function password_policy_admin_form_subm $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,76 @@ function password_policy_admin_delete_su 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) { + // skip over null values returned by unselected roles + if ($role == 0) { + continue; + } + $uids = array(); + // special handling for authenticated users role + // since this role makes no entries in the users_roles table + if ($role == 2) { + // no point in flagging Anonymous since they can't log in anyway + $db_uids = db_query('SELECT uid FROM {users} WHERE uid <> 0'); + } + else { + $db_uids = db_query('SELECT uid FROM {users_roles} WHERE rid = %d', $role); + } + while ($uid = db_result($db_uids)) { + if (($uid == 1 && PASSWORD_POLICY_ADMIN) || $uid > 1) { + $uids[] = $uid; + } + } + $sql = 'UPDATE {password_policy_force_change} SET force_change = 1 WHERE uid IN (' . db_placeholders($uids, 'int') . ')'; + db_query($sql, $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 =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/password_policy/password_policy.install,v retrieving revision 1.8 diff -u -p -r1.8 password_policy.install --- password_policy.install 15 Mar 2010 17:24:08 -0000 1.8 +++ password_policy.install 13 Apr 2010 17:39:20 -0000 @@ -1,5 +1,6 @@ 0'); } /** @@ -33,6 +35,7 @@ function password_policy_uninstall() { 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 @@ function password_policy_update_6000() { 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 @@ function password_policy_schema() { '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 =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/password_policy/password_policy.module,v retrieving revision 1.26 diff -u -p -r1.26 password_policy.module --- password_policy.module 15 Mar 2010 17:24:08 -0000 1.26 +++ password_policy.module 13 Apr 2010 17:39:21 -0000 @@ -43,6 +43,21 @@ function password_policy_init() { $_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 @@ function password_policy_theme() { * 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 @@ function password_policy_menu() { '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_format_title($p */ 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 @@ function password_policy_user($op, &$edi } } 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 @@ function password_policy_user($op, &$edi } $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 @@ function password_policy_user($op, &$edi } } 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 @@ function password_policy_form_alter(&$fo //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,10 +358,26 @@ function password_policy_form_alter(&$fo $form['account']['pass']['#prefix'] = '
'; } + // 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'; break; + case 'password_policy_password_tab': + $form['submit']['#weight'] = 10; + break; } } @@ -474,12 +526,39 @@ function password_policy_password_submit // 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 +803,30 @@ function _password_policy_store_password } /** + * 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 =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/password_policy/tests/password_policy.test,v retrieving revision 1.4 diff -u -p -r1.4 password_policy.test --- tests/password_policy.test 4 Jun 2009 21:49:08 -0000 1.4 +++ tests/password_policy.test 13 Apr 2010 17:39:22 -0000 @@ -14,6 +14,10 @@ class PasswordPolicyTest extends DrupalW ); } + public function setUp() { + parent::setUp('password_policy'); + } + function testUppercaseConstraint() { $constraint = 2; $uid = 0; @@ -143,62 +147,55 @@ class PasswordPolicyTest extends DrupalW $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,176 @@ class PasswordPolicyTest extends DrupalW } } +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('Your password has expired. You must change your password to proceed on the site.'), t('User presented with error instructing them to change their password.')); + + // 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->assertTrue($admin->uid != 1, t("Admin uid not 1: $admin->uid")); + $this->assertRaw(t('Your password has expired. You must change your password to proceed on the site.'), t('Admin (not uid 1) correctly forced to change password.')); + // test db flags for individual users + $entry_1 = db_result(db_query('SELECT uid FROM {password_policy_force_change} WHERE uid=%d', $user1->uid)); + $this->assertTrue($entry_1 == $user1->uid, t("Entry created in password_policy_force_change for user $user1->uid.")); + $flag_1 = db_result(db_query('SELECT force_change FROM {password_policy_force_change} WHERE uid=%d', $user1->uid)); + $this->assertTrue($flag_1 == 1, t("User $user1->uid flagged: $flag_1.")); + $this->drupalLogout(); + //test individual users + $this->drupalLogin($user1); + $this->drupalGet('node'); + $this->assertRaw(t('Your password has expired. 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('Your password has expired. 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('Your password has expired. 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('Your password has expired. 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('Your password has expired. You must change your password to proceed on the site.'), t('Admin not initially prompted to change password.')); + $this->drupalGet('node'); + $this->assertRaw(t('Your password has expired. 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.')); + } +}