password_policy_expiration.test in Password Policy 7
Tests for Password policy module expiration functionality.
File
tests/password_policy_expiration.testView source
<?php
/**
* @file
* Tests for Password policy module expiration functionality.
*/
/**
* Tests of password expiration.
*/
class PasswordPolicyExpirationTestCase extends DrupalWebTestCase {
protected $admin;
protected $policyMaker;
/**
* Gets info about the test case.
*/
public static function getInfo() {
return array(
'name' => 'Password expiration',
'description' => 'Test password expiration and related settings.',
'group' => 'Password Policy',
);
}
/**
* Sets up the test.
*/
public function setUp() {
parent::setUp('password_policy');
$this
->createAdmin();
$this
->createPolicyMaker();
}
/**
* Creates a usable admin (UID=1) user.
*
* SimpleTest creates an admin user, but it cannot log in since it has no
* password set. So, we give it a password.
*/
protected function createAdmin() {
global $user;
$pass = user_password();
$edit = array(
'pass' => $pass,
);
user_save($user, $edit);
$user->pass_raw = $pass;
$this->admin = $user;
}
/**
* Creates policy maker.
*/
protected function createPolicyMaker() {
$this->policyMaker = $this
->drupalCreateUser(array(
'administer users',
'administer permissions',
'administer password policies',
'unblock expired accounts',
));
}
/**
* Tests expiration constraint.
*/
public function testExpirationConstraint() {
// Create role to which the expiration policy will apply.
// It is identical to the 'authenticated user' role in permissions. We
// create this separate role because we would like the policy maker user to
// be exempt from the expiration policy.
$expiration_rid = $this
->drupalCreateRole(array());
// Log in policy maker.
$policy_maker_user = $this->policyMaker;
$this
->drupalLogin($policy_maker_user);
// Create a policy.
$policy_name = $this
->createExpirationPolicy(1, $expiration_rid);
// Verify that an expiration rule has been set in the policy.
$pid = db_query('SELECT pid FROM {password_policy} WHERE name = :name', array(
':name' => $policy_name,
))
->fetchField();
$expiration = db_query('SELECT expiration FROM {password_policy} WHERE pid = :pid', array(
':pid' => $pid,
))
->fetchField();
$this
->verbose('Expiration: ' . var_export($expiration, TRUE));
$this
->assertTrue($expiration == 1, 'Verified expiration set.');
// Enable the policy.
$this
->enablePolicy($policy_name);
_password_policy_advance_test_clock(60 * 60 * 24 + 1);
// Create an account to test with.
$name1 = $this
->randomName();
$pass1 = 'aaaaaa';
$edit = array(
'name' => $name1,
'mail' => $name1 . '@example.com',
'pass[pass1]' => $pass1,
'pass[pass2]' => $pass1,
);
$this
->drupalPost('admin/people/create', $edit, 'Create new account');
$uid = db_query('SELECT uid FROM {users} WHERE name = :name', array(
':name' => $name1,
))
->fetchField();
$this
->drupalGet('user/' . $uid . '/edit');
$this
->assertFieldChecked('edit-status-1', 'Account status is set to active.');
// Add user to role covered by expiration policy.
user_multiple_role_edit(array(
$uid,
), 'add_role', $expiration_rid);
// Log out and attempt to log in with the newly created test account.
$this
->drupalLogout();
$edit = array(
'name' => $name1,
'pass' => $pass1,
);
$this
->drupalPost('user/login', $edit, t('Log in'));
$this
->assertNoText(t('The username !name has not been activated or is blocked.', array(
'!name' => $name1,
)), 'Account not blocked from logging in.');
_password_policy_advance_test_clock(60 * 60 * 24 + 1);
// Check that password should be expired, once cron runs.
$created = db_query('SELECT created FROM {password_policy_history} WHERE uid = :uid', array(
':uid' => '3',
))
->fetchField();
$this
->verbose('Created: ' . var_export($created, TRUE));
$created = db_query('SELECT created FROM {password_policy} WHERE pid = :pid', array(
':pid' => $pid,
))
->fetchField();
$this
->verbose('$pid ' . $pid . ' created: ' . var_export($created, TRUE));
// Run cron to trigger password expirations.
$this
->cronRun();
// Check that test account has been blocked.
$this
->drupalLogin($policy_maker_user);
$this
->drupalGet('user/' . $uid . '/edit');
$this
->assertFieldChecked('edit-status-0', 'Account status is set to blocked.');
$this
->drupalGet('admin/people/expired');
$this
->assertText('unblock', 'Account marked as blocked on Expired Accounts tab.');
// Log out and attempt to log in to the expired account again, to verify
// block.
$this
->drupalLogout();
$edit = array(
'name' => $name1,
'pass' => $pass1,
);
$this
->drupalPost('user/login', $edit, t('Log in'));
$this
->assertText(t('The username !name has not been activated or is blocked.', array(
'!name' => $name1,
)), 'Account blocked from logging in.');
// Log in as policy making user to unblock the test user.
$this
->drupalLogin($policy_maker_user);
$this
->drupalPost('admin/people/expired/unblock/' . $uid, array(), t('Unblock user'));
$this
->assertText(t('The user !name has been unblocked.', array(
'!name' => $name1,
)), 'Account account has been unblocked.');
$this
->drupalGet('admin/people/expired');
$this
->assertNoText('unblock', 'Account not marked as blocked on Expired Accounts tab.');
// Log out and attempt to log in the expired account again.
$this
->drupalLogout();
$edit = array(
'name' => $name1,
'pass' => $pass1,
);
$this
->drupalPost('user/login', $edit, t('Log in'));
$this
->assertNoText(t('The username !name has not been activated or is blocked.', array(
'!name' => $name1,
)), 'Account not blocked from logging in.');
$this
->assertNoText(t('User login'), 'Check that login block is not shown, to verify user successfully logged in.');
$this
->assertRaw(t('Your password has expired. You must change your password to proceed on the site.'), 'User forced to change password.');
// Change test user account's password.
$pass2 = 'bbbbbb';
$edit = array(
'current_pass' => $pass1,
'pass[pass1]' => $pass2,
'pass[pass2]' => $pass2,
);
$this
->drupalPost('user/' . $uid . '/edit', $edit, t('Save'));
$this
->assertText(t('The changes have been saved.'), format_string('1st password change: !pass', array(
'!pass' => $pass2,
)));
$this
->drupalGet('node');
$this
->drupalLogout();
// Run cron to trigger password expirations.
$this
->cronRun();
// Log in to confirm password not seen as expired now that it has changed.
$edit = array(
'name' => $name1,
'pass' => $pass1,
);
$this
->drupalPost('user/login', $edit, t('Log in'));
$this
->assertNoText(t('The username !name has not been activated or is blocked.', array(
'!name' => $name1,
)), 'Account not blocked from logging in.');
$this
->assertNoText(t('User login'), 'Check that login block is not shown, to verify user successfully logged in.');
$this
->drupalLogout();
// Delete test policy.
$this
->drupalLogin($policy_maker_user);
$this
->drupalPost('admin/config/people/password_policy/' . $pid . '/delete', array(), t('Delete'));
$this
->assertText('Password policy ' . $policy_name . ' was deleted.', 'Default password policy ' . $policy_name . 'was deleted');
}
/**
* Tests unblocking an expired account via user edit form.
*/
public function testUnblockingExpiredAccountViaUserEditForm() {
// Create an account to test with.
$account = $this
->drupalCreateUser();
$expiration_rid = $this
->assignToNewRole($account);
// Set 180-day expiration policy.
$expiration_days = 180;
$this
->setExpirationPolicy($expiration_days, $expiration_rid);
// Advance to at least one second past expiration.
$one_day = 24 * 60 * 60;
_password_policy_advance_test_clock($expiration_days * $one_day + 1);
// Run cron to trigger password expirations.
$this
->cronRun();
// Confirm account blocked.
$edit = array(
'name' => $account->name,
'pass' => $account->pass_raw,
);
$this
->drupalPost('user/login', $edit, t('Log in'));
$this
->assertText(t('The username !name has not been activated or is blocked.', array(
'!name' => $account->name,
)), 'Account blocked from logging in.');
// Unblock account using user edit form.
$admin = $this->admin;
$this
->drupalLogin($admin);
$edit = array(
'status' => 1,
);
$this
->drupalPost("user/{$account->uid}/edit", $edit, t('Save'));
$this
->drupalLogout();
// Run cron since it could errantly reblock account.
$this
->cronRun();
// Confirm account unblocked.
$edit = array(
'name' => $account->name,
'pass' => $account->pass_raw,
);
$this
->drupalPost('user/login', $edit, t('Log in'));
$this
->assertNoText(t('The username !name has not been activated or is blocked.', array(
'!name' => $account->name,
)), 'Account not blocked from logging in.');
}
/**
* Tests unblocking a "reblocked" account.
*
* By "reblocked" account we mean one that was blocked due to password
* expiration, unblocked, then blocked again due to the password not being
* changed within 24 hours.
*/
public function testUnblockingReblockedAccount() {
// Create role to which the expiration policy will apply.
$expiration_rid = $this
->drupalCreateRole(array());
// Add user covered by expiration policy.
$account = $this
->drupalCreateUser();
user_multiple_role_edit(array(
$account->uid,
), 'add_role', $expiration_rid);
// Set an expiration policy.
$expiration_days = 7;
$this
->setExpirationPolicy($expiration_days, $expiration_rid);
// Allow the password to expire.
$seven_days_one_second = 60 * 60 * 24 * 7 + 1;
_password_policy_advance_test_clock($seven_days_one_second);
$this
->cronRun();
// Log in as policy making user to unblock the test user.
$this
->drupalLogin($this->policyMaker);
$this
->drupalPost('admin/people/expired/unblock/' . $account->uid, array(), t('Unblock user'));
$this
->assertText(t('The user !name has been unblocked.', array(
'!name' => $account->name,
)), 'Account account has been unblocked.');
$this
->drupalGet('admin/people/expired');
$this
->assertNoText('unblock', 'Account not marked as blocked on Expired Accounts tab.');
$this
->drupalLogout();
// Wait over one day so account is reblocked.
$one_day_one_second = 60 * 60 * 24 + 1;
_password_policy_advance_test_clock($one_day_one_second);
$this
->cronRun();
// Attempt to log in. User should be blocked.
$edit = array(
'name' => $account->name,
'pass' => $account->pass_raw,
);
$this
->drupalPost('user/login', $edit, t('Log in'));
$this
->assertText(t('The username !name has not been activated or is blocked.', array(
'!name' => $account->name,
)), 'Account blocked from logging in.');
// Log in as policy making user to unblock the test user.
$this
->drupalLogin($this->policyMaker);
$this
->drupalPost('admin/people/expired/unblock/' . $account->uid, array(), t('Unblock user'));
$this
->assertText(t('The user !name has been unblocked.', array(
'!name' => $account->name,
)), 'Account account has been unblocked.');
$this
->drupalGet('admin/people/expired');
$this
->assertNoText('unblock', 'Account not marked as blocked on Expired Accounts tab.');
$this
->drupalLogout();
// Attempt to log in. User should not be blocked.
$edit = array(
'name' => $account->name,
'pass' => $account->pass_raw,
);
$this
->drupalPost('user/login', $edit, t('Log in'));
$this
->assertNoText(t('The username !name has not been activated or is blocked.', array(
'!name' => $account->name,
)), 'Account not blocked from logging in.');
$this
->drupalLogout();
}
/**
* Tests "Admin (UID=1) password expires" being disabled (i.e., unchecked).
*/
public function testAdminExpirationDisabled() {
$this
->disableAdminExpiration();
// Set an expiration policy.
$expiration_days = 30;
$this
->setExpirationPolicy($expiration_days);
$this
->cronRun();
// Advance to at least one second past expiration.
$one_day = 24 * 60 * 60;
_password_policy_advance_test_clock($expiration_days * $one_day + 1);
$this
->cronRun();
// Attempt login as admin and confirm password not expired.
$admin = $this->admin;
$edit = array(
'name' => $admin->name,
'pass' => $admin->pass_raw,
);
$this
->drupalPost('user/login', $edit, t('Log in'));
$this
->assertNoText(t('The username !name has not been activated or is blocked.', array(
'!name' => $admin->name,
)), 'Admin not blocked from logging in.');
}
/**
* Tests "Admin (UID=1) password expires" being enabled (i.e., checked).
*
* This is the default setting.
*/
public function testAdminExpirationEnabled() {
// Set an expiration policy.
$expiration_days = 30;
$this
->setExpirationPolicy($expiration_days);
$this
->cronRun();
// Advance to at least one second past expiration.
$one_day = 24 * 60 * 60;
_password_policy_advance_test_clock($expiration_days * $one_day + 1);
$this
->cronRun();
// Attempt login as admin and confirm password expired.
$admin = $this->admin;
$edit = array(
'name' => $admin->name,
'pass' => $admin->pass_raw,
);
$this
->drupalPost('user/login', $edit, t('Log in'));
$this
->assertText(t('The username !name has not been activated or is blocked.', array(
'!name' => $admin->name,
)), 'Admin blocked from logging in.');
}
/**
* Tests expiration warning e-mails.
*/
public function testWarningEmails() {
$expiration_days = 180;
$rid = DRUPAL_AUTHENTICATED_RID;
$warning = '14,7';
$this
->setExpirationPolicy($expiration_days, $rid, $warning);
// Disable admin (UID=1) expiration to prevent multiple expiration emails.
$this
->disableAdminExpiration();
// Start at 180 days before expiration.
// By "day" in the rest of this method, a 24-hour period of time is meant,
// not a calendar day.
$this
->cronRun();
$this
->assertNoMail('No e-mails sent initially.');
// Advance to one second past 90 days before expiration.
$one_day = 24 * 60 * 60;
_password_policy_advance_test_clock(90 * $one_day + 1);
$this
->cronRun();
$this
->assertNoMail('No e-mails sent before first expiration warning day.');
// Advance to one second past 14 days before expiration.
_password_policy_advance_test_clock(76 * $one_day);
$this
->cronRun();
$warning_subject = t('Password expiration warning for !username at !site', array(
'!username' => $this->policyMaker->name,
'!site' => variable_get('site_name', 'Drupal'),
));
$this
->assertMail('subject', $warning_subject, 'First expiration warning e-mail sent.');
$this
->clearMails();
// Advance to one second past 10 days before expiration.
_password_policy_advance_test_clock(4 * $one_day);
$this
->cronRun();
$this
->assertNoMail('No e-mails sent between first and second expiration warning days.');
// Advance to one second past 7 days before expiration.
_password_policy_advance_test_clock(3 * $one_day);
$this
->cronRun();
$warning_subject = t('Password expiration warning for !username at !site', array(
'!username' => $this->policyMaker->name,
'!site' => variable_get('site_name', 'Drupal'),
));
$this
->assertMail('subject', $warning_subject, 'Second expiration warning e-mail sent.');
$this
->clearMails();
// Advance to one second past 6 days before expiration.
_password_policy_advance_test_clock(1 * $one_day);
$this
->cronRun();
$this
->assertNoMail('No e-mails sent after expiration warnings and before expiration.');
// Advance to one second past day before expiration.
_password_policy_advance_test_clock(6 * $one_day);
$this
->cronRun();
$this
->assertNoMail('No e-mails sent after expiration warnings and less than one day before expiration.');
// Advance to one second past 3 days after expiration.
_password_policy_advance_test_clock(3 * $one_day);
$this
->cronRun();
$this
->assertNoMail('No e-mails sent after expiration.');
}
/**
* Tests warning e-mails not sent when they are disabled.
*
* Limitation: This method does not test that e-mails are not sent on the
* exact second of expiration.
*/
public function testWarningEmailsDisabled() {
$expiration_days = 2;
$rid = DRUPAL_AUTHENTICATED_RID;
$warning = '';
$this
->setExpirationPolicy($expiration_days, $rid, $warning);
// Run cron at least one second past two days before expiration.
// By "day" in the rest of this method, a 24-hour period of time is meant,
// not a calendar day.
$one_day = 24 * 60 * 60;
_password_policy_advance_test_clock($one_day + 1);
$this
->cronRun();
// Run cron at least one second past day before expiration.
_password_policy_advance_test_clock($one_day);
$this
->cronRun();
// Run cron at least one second past expiration.
_password_policy_advance_test_clock($one_day);
$this
->cronRun();
$this
->assertNoMail('No e-mails sent.');
}
/**
* Tests tokens replacement in warning e-mails.
*
* Importantly, this tests the Password Policy tokens.
*/
public function testWarningEmailsTokens() {
$expiration_days = 30;
$rid = DRUPAL_AUTHENTICATED_RID;
$warning = '14';
$this
->setExpirationPolicy($expiration_days, $rid, $warning);
// Disable admin (UID=1) expiration to prevent multiple expiration emails.
$this
->disableAdminExpiration();
// Trigger expiration warning e-mail.
// (Advance to one second past 14 days before expiration and run cron.)
$one_day = 24 * 60 * 60;
_password_policy_advance_test_clock(16 * $one_day + 1);
$this
->cronRun();
// Assert correct subject.
$warning_subject = t('Password expiration warning for !username at !site', array(
'!username' => $this->policyMaker->name,
'!site' => variable_get('site_name', 'Drupal'),
));
$this
->assertMail('subject', $warning_subject, "Tokens replaced in subject correctly.");
// Assert correct body.
$edit_url = _password_policy_get_preferred_password_edit_url_for_user($this->policyMaker);
global $language;
$langcode = isset($language) ? $language->language : NULL;
$warning_body = t("!username,\n\nYour password at !site will expire in less than !days_left day(s).\n\nPlease go to !edit_url to change your password.", array(
'!username' => $this->policyMaker->name,
'!site' => variable_get('site_name', 'Drupal'),
'!days_left' => '14',
'!edit_url' => $edit_url,
), array(
'langcode' => $langcode,
));
// assertMail() does not seem to work for the body because the mail
// functions insert some whitespace into the body. assertMailString() seems
// to work, though.
$this
->assertMailString('body', $warning_body, 1);
}
/**
* Sets expiration policy.
*
* Has Policy Maker user create and enable an expiration policy.
*
* @param int $expiration
* Number of days after which password expires.
* @param int $rid
* (optional) Role ID to which this policy should apply.
* @param string $warning
* (optional) Comma-delimited list of numbers of days before expiration on
* which expiration warnings are to be sent.
*/
protected function setExpirationPolicy($expiration, $rid = DRUPAL_AUTHENTICATED_RID, $warning = '') {
$policy_maker = $this->policyMaker;
$this
->drupalLogin($policy_maker);
$name = $this
->createExpirationPolicy($expiration, $rid, $warning);
$this
->enablePolicy($name);
$this
->drupalLogout();
}
/**
* Disables "Admin (UID=1) password expires" setting.
*/
protected function disableAdminExpiration() {
$admin = $this->admin;
$this
->drupalLogin($admin);
$edit = array(
'password_policy_admin' => FALSE,
);
$this
->drupalPost('admin/config/people/password_policy', $edit, t('Save configuration'));
$this
->drupalLogout();
}
/**
* Creates expiration policy.
*
* @param int $expiration
* Number of days after which password expires.
* @param int $rid
* (optional) Role ID to which this policy should apply.
* @param string $warning
* (optional) Comma-delimited list of numbers of days before expiration on
* which expiration warnings are to be sent.
*
* @return string
* Name of created policy.
*/
protected function createExpirationPolicy($expiration, $rid = DRUPAL_AUTHENTICATED_RID, $warning = '') {
$policy_name = $this
->randomName();
$edit = array(
'name' => $policy_name,
'expiration' => $expiration,
'warning' => $warning,
"roles[{$rid}]" => $rid,
);
$this
->drupalPost('admin/config/people/password_policy/add', $edit, t('Create'));
$created_text = "Policy {$policy_name} has been created.";
$this
->assertText($created_text, $created_text);
return $policy_name;
}
/**
* Enables policy.
*/
protected function enablePolicy($policy_name) {
$pid = db_query('SELECT pid FROM {password_policy} WHERE name = :name', array(
':name' => $policy_name,
))
->fetchField();
$edit = array(
"policies[{$pid}][enabled]" => $pid,
);
$this
->drupalPost('admin/config/people/password_policy/list', $edit, t('Save changes'));
$this
->assertText(t('The changes have been saved.'), 'Form submitted successfully.');
$this
->drupalGet('admin/config/people/password_policy');
$enabled = db_query('SELECT enabled FROM {password_policy} WHERE pid = :pid', array(
':pid' => $pid,
))
->fetchField();
$this
->assertTrue($enabled == 1, 'Policy enabled.');
}
/**
* Assigns user to a newly created role.
*
* @param object $account
* User object.
*
* @return int
* Role ID of newly created role.
*/
protected function assignToNewRole($account) {
$expiration_rid = $this
->drupalCreateRole(array());
user_multiple_role_edit(array(
$account->uid,
), 'add_role', $expiration_rid);
return $expiration_rid;
}
/**
* Asserts that no e-mail has been sent.
*
* Specifically, asserts that no e-mail has been sent since the e-mails
* collected during the test were last cleared.
*/
protected function assertNoMail($message) {
$captured_emails = variable_get('drupal_test_email_collector', array());
$this
->assertTrue(empty($captured_emails), $message);
}
/**
* Clears all e-mails collected during test.
*/
protected function clearMails() {
variable_set('drupal_test_email_collector', array());
}
}
Classes
Name | Description |
---|---|
PasswordPolicyExpirationTestCase | Tests of password expiration. |