password_policy.test in Password Policy 7
Same filename and directory in other branches
Functional tests for Password policy module.
File
tests/password_policy.testView source
<?php
/**
* @file
* Functional tests for Password policy module.
*/
/**
* Tests of basic Password Policy constraints.
*/
class PasswordPolicyTestCase extends DrupalWebTestCase {
/**
* Get info about the test case.
*/
public static function getInfo() {
return array(
'name' => 'Password constraints',
'description' => 'Test password constraints.',
'group' => 'Password Policy',
);
}
/**
* Set up the test.
*/
public function setUp() {
parent::setUp('password_policy');
}
/**
* Test uppercase constraint.
*/
public function testUppercaseConstraint() {
module_load_include('inc', 'password_policy', 'constraints/constraint_uppercase');
$constraint = 2;
$user = $this
->drupalCreateUser();
$pass = 'Abc';
$result = password_policy_constraint_uppercase_validate($pass, $constraint, $user);
$this
->assertFalse($result, 'One uppercase letter in the uppercase constraint with param 2');
$pass = 'ABc';
$result = password_policy_constraint_uppercase_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'Two uppercase letters in the uppercase constraint with param 2');
$pass = 'ABC';
$result = password_policy_constraint_uppercase_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'Three uppercase letters in the uppercase constraint with param 2');
}
/**
* Test lowercase constraint.
*/
public function testLowercaseConstraint() {
module_load_include('inc', 'password_policy', 'constraints/constraint_lowercase');
$constraint = 2;
$user = $this
->drupalCreateUser();
$pass = 'Abc';
$result = password_policy_constraint_lowercase_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'One uppercase letter in the lowercase constraint with param 2');
$pass = 'ABc';
$result = password_policy_constraint_lowercase_validate($pass, $constraint, $user);
$this
->assertFalse($result, 'Two uppercase letters in the lowercase constraint with param 2');
$pass = 'ABC';
$result = password_policy_constraint_lowercase_validate($pass, $constraint, $user);
$this
->assertFalse($result, 'Three uppercase letters in the lowercase constraint with param 2');
}
/**
* Test letter constraint.
*/
public function testLetterConstraint() {
module_load_include('inc', 'password_policy', 'constraints/constraint_letter');
$constraint = 2;
$user = $this
->drupalCreateUser();
$pass = 'a12';
$result = password_policy_constraint_letter_validate($pass, $constraint, $user);
$this
->assertFalse($result, 'One lowercase letter in the letter constraint with param 2');
$pass = 'aB1';
$result = password_policy_constraint_letter_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'One lowercase and one uppercase letter in the letter constraint with param 2');
$pass = 'abC';
$result = password_policy_constraint_letter_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'Two lowercase and one uppercase letter in the letter constraint with param 2');
}
/**
* Test digit constraint.
*/
public function testDigitConstraint() {
module_load_include('inc', 'password_policy', 'constraints/constraint_digit');
$constraint = 2;
$user = $this
->drupalCreateUser();
$pass = '1ab';
$result = password_policy_constraint_digit_validate($pass, $constraint, $user);
$this
->assertFalse($result, 'One digit in the digit constraint with param 2');
$pass = '12a';
$result = password_policy_constraint_digit_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'Two digits in the digit constraint with param 2');
$pass = '123';
$result = password_policy_constraint_digit_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'Three digits in the digit constraint with param 2');
}
/**
* Test length constraint.
*/
public function testLengthConstraint() {
module_load_include('inc', 'password_policy', 'constraints/constraint_length');
$constraint = 6;
$user = $this
->drupalCreateUser();
$pass = 'abcde';
$result = password_policy_constraint_length_validate($pass, $constraint, $user);
$this
->assertFalse($result, 'Five characters password in the length constraint with param 6');
$pass = 'abcdef';
$result = password_policy_constraint_length_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'Six characters password in the length constraint with param 6');
$pass = 'abcdefg';
$result = password_policy_constraint_length_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'Seven characters password in the length constraint with param 6');
}
/**
* Test alphanumeric constraint.
*/
public function testAlphanumericConstraint() {
module_load_include('inc', 'password_policy', 'constraints/constraint_alphanumeric');
$constraint = 2;
$user = $this
->drupalCreateUser();
$pass = '1$%';
$result = password_policy_constraint_alphanumeric_validate($pass, $constraint, $user);
$this
->assertFalse($result, 'One digit and no letter in the alphanumeric constraint with param 2');
$pass = '1a#';
$result = password_policy_constraint_alphanumeric_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'One digit and one letter in the alphanumeric constraint with param 2');
$pass = '1ab';
$result = password_policy_constraint_alphanumeric_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'One digit ant two letters in the alphanumeric constraint with param 2');
}
/**
* Test punctuation constraint.
*/
public function testPunctuationConstraint() {
module_load_include('inc', 'password_policy', 'constraints/constraint_punctuation');
$constraint = 2;
$user = $this
->drupalCreateUser();
$pass = '%1a';
$result = password_policy_constraint_punctuation_validate($pass, $constraint, $user);
$this
->assertFalse($result, 'One punctuation character in the punctuation constraint with param 2');
$pass = '%^a';
$result = password_policy_constraint_punctuation_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'Two punctuation character in the punctuation constraint with param 2');
$pass = '%^&';
$result = password_policy_constraint_punctuation_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'Three punctuation character in the punctuation constraint with param 2');
}
/**
* Test character types constraint.
*/
public function testCharacterTypesConstraint() {
module_load_include('inc', 'password_policy', 'constraints/constraint_character_types');
$constraint = 2;
$user = $this
->drupalCreateUser();
$pass = 'abc';
$result = password_policy_constraint_character_types_validate($pass, $constraint, $user);
$this
->assertFalse($result, 'Three letters in the type constraint with param 2');
$pass = 'a1c';
$result = password_policy_constraint_character_types_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'Two letters and one digit in the type constraint with param 2');
$pass = 'a1&';
$result = password_policy_constraint_character_types_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'One letter, one digit and one punctuation in the type constraint with param 2');
}
/**
* Test username constraint.
*/
public function testUsernameConstraint() {
module_load_include('inc', 'password_policy', 'constraints/constraint_username');
$user = $this
->drupalCreateUser();
$name = $this
->randomName();
$result = password_policy_constraint_username_validate($name, '', $user);
$this
->assertTrue($result, 'Random string in the username constraint');
$result = password_policy_constraint_username_validate($user->name, '', $user);
$this
->assertFalse($result, 'Username in the username constraint');
$result = password_policy_constraint_username_validate('foo' . $user->name . 'bar', '', $user);
$this
->assertFalse($result, 'String containing username in the username constraint');
}
/**
* Test delay constraint.
*/
public function testDelayConstraint() {
module_load_include('inc', 'password_policy', 'constraints/constraint_delay');
// Log in.
$user = $this
->drupalCreateUser(array(
'administer password policies',
));
$this
->drupalLogin($user);
// Create a policy.
$policy_name = $this
->randomName();
$edit = array(
'name' => $policy_name,
'constraint_delay' => t('1'),
'roles[2]' => '2',
);
$this
->drupalPost('admin/config/people/password_policy/add', $edit, t('Create'));
$this
->assertText('Policy ' . $policy_name . ' has been created.', 'Policy ' . $policy_name . ' has been created');
// Enable newly created policy.
$pid = db_query('SELECT pid FROM {password_policy} WHERE name = :name', array(
':name' => $policy_name,
))
->fetchField();
$constraints = unserialize(db_query('SELECT constraints FROM {password_policy} WHERE pid = :pid', array(
':pid' => $pid,
))
->fetchField());
$this
->assertTrue($constraints['delay'] == 1, 'Verified delay constraint set.');
$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.');
$enabled = db_query('SELECT enabled FROM {password_policy} WHERE pid = :pid', array(
':pid' => $pid,
))
->fetchField();
$this
->assertTrue($enabled == 1, 'Policy enabled.');
$two_hours = 60 * 60 * 2;
_password_policy_advance_test_clock($two_hours);
// Change password.
$pass1 = 'aaaaaa';
$edit = array(
'current_pass' => $user->pass_raw,
'pass[pass1]' => $pass1,
'pass[pass2]' => $pass1,
);
$this
->drupalPost("user/{$user->uid}/edit", $edit, t('Save'));
$this
->assertText(t('The changes have been saved.'), format_string('1st password change: !pass1', array(
'!pass1' => $pass1,
)));
// Change password second time.
$pass2 = 'bbbbbb';
$edit = array(
'current_pass' => $pass1,
'pass[pass1]' => $pass2,
'pass[pass2]' => $pass2,
);
$this
->drupalPost("user/{$user->uid}/edit", $edit, t('Save'));
$this
->assertText(t('Your password has not met the following requirement(s):'), format_string('2nd password change should fail: !pass1', array(
'!pass1' => $pass1,
)));
// Delete test policy.
$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');
}
/**
* Test history constraint.
*/
public function testHistoryConstraint() {
module_load_include('inc', 'password_policy', 'constraints/constraint_history');
// Log in.
$user = $this
->drupalCreateUser(array(
'administer password policies',
));
$this
->drupalLogin($user);
// Create a policy.
$policy_name = $this
->randomName();
$edit = array(
'name' => $policy_name,
'constraint_history' => t('2'),
'roles[2]' => '2',
);
$this
->drupalPost('admin/config/people/password_policy/add', $edit, t('Create'));
$this
->assertText('Policy ' . $policy_name . ' has been created.', 'Policy ' . $policy_name . ' has been created');
// Enable newly created policy.
$pid = db_query('SELECT pid FROM {password_policy} WHERE name = :name', array(
':name' => $policy_name,
))
->fetchField();
$constraints = unserialize(db_query('SELECT constraints FROM {password_policy} WHERE pid = :pid', array(
':pid' => $pid,
))
->fetchField());
$this
->assertTrue($constraints['history'] == 2, 'Verified history constraint set.');
$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.');
$enabled = db_query('SELECT enabled FROM {password_policy} WHERE pid = :pid', array(
':pid' => $pid,
))
->fetchField();
$this
->assertTrue($enabled == 1, 'Policy enabled.');
// Change password.
$pass1 = 'aaaaaa';
$edit = array(
'current_pass' => $user->pass_raw,
'pass[pass1]' => $pass1,
'pass[pass2]' => $pass1,
);
$this
->drupalPost("user/{$user->uid}/edit", $edit, t('Save'));
$this
->assertText(t('The changes have been saved.'), format_string('1st password change: !pass1', array(
'!pass1' => $pass1,
)));
// Change password second time.
$pass2 = 'bbbbbb';
$edit = array(
'current_pass' => $pass1,
'pass[pass1]' => $pass1,
'pass[pass2]' => $pass1,
);
$this
->drupalPost("user/{$user->uid}/edit", $edit, t('Save'));
$this
->assertText(t('Your password has not met the following requirement(s):'), format_string('2nd password change should fail: !pass1', array(
'!pass1' => $pass1,
)));
// Try changing password with the first one.
$edit = array(
'current_pass' => $pass1,
'pass[pass1]' => $pass2,
'pass[pass2]' => $pass2,
);
$this
->drupalPost("user/{$user->uid}/edit", $edit, t('Save'));
$this
->assertText(t('The changes have been saved.'), format_string('3rd password change should pass: !pass2', array(
'!pass2' => $pass2,
)));
// Change password again.
$pass3 = 'cccccc';
$edit = array(
'current_pass' => $pass2,
'pass[pass1]' => $pass3,
'pass[pass2]' => $pass3,
);
$this
->drupalPost("user/{$user->uid}/edit", $edit, t('Save'));
$this
->assertText(t('The changes have been saved.'), format_string('4th password change should pass: !pass3', array(
'!pass3' => $pass3,
)));
// Delete test policy.
$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');
}
/**
* Test digit placement constraint.
*/
public function testDigitPlacementConstraint() {
module_load_include('inc', 'password_policy', 'constraints/constraint_digit_placement');
$constraint = 0;
$user = $this
->drupalCreateUser();
$pass = 'ILove2Password';
$result = password_policy_constraint_digit_placement_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'One numeric character in the digit placement constraint with param 0');
$pass = 'ILovePassword2';
$result = password_policy_constraint_digit_placement_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'One numeric character in the digit placement constraint with param 0');
$constraint = 1;
$pass = 'ILove2Password';
$result = password_policy_constraint_digit_placement_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'One numeric character in the digit placement constraint with param 1');
$pass = 'ILovePassword2';
$result = password_policy_constraint_digit_placement_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'One numeric character in the digit placement constraint with param 1');
$constraint = 2;
$pass = 'ILove2Password';
$result = password_policy_constraint_digit_placement_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'One numeric character in the digit placement constraint with param 2');
$pass = 'ILovePassword2';
$result = password_policy_constraint_digit_placement_validate($pass, $constraint, $user);
$this
->assertFalse($result, 'One numeric character in the digit placement constraint with param 2');
$pass = '1LovePassword';
$result = password_policy_constraint_digit_placement_validate($pass, $constraint, $user);
$this
->assertFalse($result, 'One numeric character in the digit placement constraint with param 2');
$pass = '1LovePassword2';
$result = password_policy_constraint_digit_placement_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'One numeric character in the digit placement constraint with param 2');
$pass = 'ILove2Password3';
$result = password_policy_constraint_digit_placement_validate($pass, $constraint, $user);
$this
->assertTrue($result, 'One numeric character in the digit placement constraint with param 2');
}
}
/**
* Tests of forcing password changes.
*/
class PasswordPolicyForcePasswordChangeTestCase extends DrupalWebTestCase {
/**
* Gets info about the test case.
*/
public static function getInfo() {
return array(
'name' => 'Forced password changes',
'description' => 'Test forced password changes for single user, role, and all new users.',
'group' => 'Password Policy',
);
}
/**
* Sets up the test.
*/
public function setUp() {
parent::setUp('password_policy', 'password_policy_test');
}
/**
* Tests permissions.
*/
public function testPerms() {
// No perms.
$user = $this
->drupalCreateUser();
$this
->drupalLogin($user);
$this
->drupalGet('admin/config/people/password_policy/password_change');
$this
->assertResponse('403', 'Access should be denied.');
$this
->drupalLogout();
// With perms.
$user = $this
->drupalCreateUser(array(
'force password change',
));
$this
->drupalLogin($user);
$this
->drupalGet('admin/config/people/password_policy/password_change');
$this
->assertResponse('200', 'Access should be granted.');
$this
->drupalLogout();
}
/**
* Tests form elements.
*/
public function testForms() {
// Test admin form.
$user = $this
->drupalCreateUser(array(
'force password change',
'administer users',
));
$this
->drupalLogin($user);
$this
->drupalGet('admin/config/people/password_policy/password_change');
$this
->assertFieldByName('password_policy_new_login_change', NULL, 'Found first time login change checkbox.');
$this
->assertFieldByName('password_policy_force_change_roles[2]', NULL, 'Found roles checkboxes.');
$this
->assertFieldById('edit-submit', NULL, 'Found submit button.');
// Test user edit form with perms.
$this
->drupalGet("user/{$user->uid}/edit");
$this
->assertFieldByName('force_password_change', NULL, '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', NULL, 'Force password change checkbox is hidden for normal users.');
$this
->drupalLogout();
}
/**
* Tests 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' => TRUE,
);
$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,
)), 'User flagged for password change.');
$force_change = db_query('SELECT force_change FROM {password_policy_force_change} WHERE uid = :uid', array(
':uid' => $user->uid,
))
->fetchField();
$this
->assertTrue($force_change == 1, format_string('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,
'force_password_change' => TRUE,
);
$this
->drupalPost("user/{$user->uid}/edit", $edit, t('Save'));
$force_change = db_query('SELECT force_change FROM {password_policy_force_change} WHERE uid = :uid', array(
':uid' => $user->uid,
))
->fetchField();
$this
->assertTrue($force_change == 1, format_string('User force change flag set in database:%s.', array(
'%s' => $force_change,
)));
$this
->assertNoRaw(t('Your password has expired. You must change your password to proceed on the site.'), 'Admin can edit user account without changing password.');
$this
->drupalLogout();
// Verify user is forced to change password.
$this
->drupalLogin($user);
$this
->assertFieldByName('current_pass', NULL, 'User redirected correctly.');
$this
->assertRaw(t('Your password has expired. You must change your password to proceed on the site.'), 'User presented with error instructing them to change their password.');
// Attempt to change password.
$edit = array(
'current_pass' => $user->pass_raw,
'pass[pass1]' => 'random_string',
'pass[pass2]' => 'random_string',
);
$this
->drupalPost(NULL, $edit, t('Save'));
$this
->assertRaw(t('The changes have been saved.'), 'Password change successful.');
// Verify user not prompted to change password a 2nd time.
$this
->drupalGet('node');
$this
->assertNoFieldByName('current_pass', NULL, 'User not forced to change password a 2nd time.');
$this
->drupalLogout();
}
/**
* Tests 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/config/people/password_policy/password_change', $edit, t('Save changes'));
$this
->assertText(t('Users in the following roles will be required to immediately change their password: authenticated user'), 'Authenticated users role selected.');
$this
->assertTrue($admin->uid != 1, format_string('Admin uid not 1: !admin_uid', array(
'admin_uid' => $admin->uid,
)));
$this
->assertRaw(t('Your password has expired. You must change your password to proceed on the site.'), 'Admin (not uid 1) correctly forced to change password.');
// Test db flags for individual users.
$entry_1 = db_query('SELECT uid FROM {password_policy_force_change} WHERE uid = :uid', array(
':uid' => $user1->uid,
))
->fetchField();
$this
->assertTrue($entry_1 == $user1->uid, format_string('Entry created in password_policy_force_change for user !uid.', array(
'!uid' => $user1->uid,
)));
$flag_1 = db_query('SELECT force_change FROM {password_policy_force_change} WHERE uid = :uid', array(
':uid' => $user1->uid,
))
->fetchField();
$this
->assertTrue($flag_1 == 1, format_string("User !uid flagged: !flag.", array(
'!uid' => $user1->uid,
'!flag' => $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.'), '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.'), 'Second test user forced to change password.');
$this
->drupalLogout();
}
/**
* Tests 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' => TRUE,
);
$this
->drupalPost('admin/config/people/password_policy/password_change', $edit, t('Save changes'));
$this
->assertRaw(t('New users will be required to change their password on first-time login.'), '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.'), 'New user forced to change password.');
}
/**
* Tests 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.'), 'Admin should not be prompted to change password yet.');
$edit = array(
'force_password_change' => TRUE,
);
$this
->drupalPost("user/{$admin->uid}/edit/account", $edit, t('Save'));
$this
->assertRaw(t('The changes have been saved.'), 'Admin has queued account for password change.');
$this
->assertNoRaw(t('Your password has expired. You must change your password to proceed on the site.'), '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.'), 'Admin forced to change password once they try to leave account page.');
$edit = array(
'current_pass' => $admin->pass_raw,
'pass[pass1]' => 'fpcR@nd0m!',
'pass[pass2]' => 'fpcR@nd0m!',
);
$this
->drupalPost("user/{$admin->uid}/edit/account", $edit, t('Save'));
$this
->assertRaw(t('The changes have been saved.'), 'Admin changed password.');
$this
->drupalGet('node');
$this
->assertNoRaw(t('Your password has expired. You must change your password to proceed on the site.'), 'Not prompted to change password a 2nd time.');
}
/**
* Tests unforcing a password change.
*/
public function testUnforceChange() {
$admin = $this
->drupalCreateUser(array(
'force password change',
'administer users',
));
$user = $this
->drupalCreateUser();
$this
->drupalLogin($admin);
// Force a password change.
$edit = array(
'force_password_change' => TRUE,
);
$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,
)), 'User flagged for password change.');
// Unforce the password change.
$edit = array(
'force_password_change' => FALSE,
);
$this
->drupalPost("user/{$user->uid}/edit", $edit, t('Save'));
$force_change = db_query('SELECT force_change FROM {password_policy_force_change} WHERE uid = :uid', array(
':uid' => $user->uid,
))
->fetchField();
$this
->assertTrue($force_change == 0, format_string('Force change flag set to %d for %s.', array(
'%d' => $force_change,
'%s' => $user->name,
)));
}
/**
* Tests "Force password change on reset" setting.
*
* Some code copied from UserPasswordResetTestCase::testUserPasswordReset().
*/
public function testForceChangeOnReset() {
// Create a user.
$user = $this
->drupalCreateUser();
$this
->drupalLogin($user);
$this
->drupalLogout();
// Check that user is not forced to change password on reset by default.
// Attempt to reset password.
$edit = array(
'name' => $user->name,
);
$this
->drupalPost('user/password', $edit, t('E-mail new password'));
// Visit reset URL.
$reset_url = $this
->getPasswordResetUrlFromMail();
$this
->drupalGet($reset_url);
$this
->drupalPost(NULL, array(), t('Log in'));
// Try to visit another page without changing password.
$this
->drupalGet('node');
// Verify user not redirected to change password.
$this
->assertNoFieldByName('mail', NULL, 'User not redirected back to user-edit page.');
$this
->assertNoRaw(t('Your password has expired. You must change your password to proceed on the site.'), 'User not forced to change password.');
$this
->drupalLogout();
// Enable force change on reset.
$admin = $this
->drupalCreateUser(array(
'administer password policies',
));
$this
->drupalLogin($admin);
$edit = array(
'password_policy_force_change_reset' => TRUE,
);
$this
->drupalPost('admin/config/people/password_policy', $edit, t('Save configuration'));
$this
->assertRaw(t('The configuration options have been saved.'), 'Enabled "Force password change on reset".');
$this
->drupalLogout();
// Check user is forced to change password if they try to skip doing so.
// Attempt to reset password.
$edit = array(
'name' => $user->name,
);
$this
->drupalPost('user/password', $edit, t('E-mail new password'));
// Visit reset URL.
$reset_url = $this
->getPasswordResetUrlFromMail();
$this
->drupalGet($reset_url);
$this
->drupalPost(NULL, array(), t('Log in'));
// Try to visit another page without changing password.
$this
->drupalGet('node');
// Verify user redirected to change password.
$this
->assertFieldByName('mail', NULL, 'User redirected back to user-edit page.');
$this
->assertRaw(t('Your password has expired. You must change your password to proceed on the site.'), 'User forced to change password.');
// Change password.
$edit = array(
'pass[pass1]' => 'fpcR@nd0m!',
'pass[pass2]' => 'fpcR@nd0m!',
);
$this
->drupalPost(NULL, $edit, t('Save'));
$this
->assertRaw(t('The changes have been saved.'), 'User changed password.');
// Try to visit another page without changing password.
$this
->drupalGet('node');
// Verify user not again redirected to change password.
$this
->assertNoFieldByName('mail', NULL, 'User not redirected back to user-edit page.');
$this
->assertNoRaw(t('Your password has expired. You must change your password to proceed on the site.'), 'User not forced to change password.');
$this
->drupalLogout();
// Check that user is not forced to change password twice if they
// immediately change their password.
// Attempt to reset password.
$edit = array(
'name' => $user->name,
);
$this
->drupalPost('user/password', $edit, t('E-mail new password'));
// Visit reset URL.
$reset_url = $this
->getPasswordResetUrlFromMail();
$this
->drupalGet($reset_url);
$this
->drupalPost(NULL, array(), t('Log in'));
// Change password.
$edit = array(
'pass[pass1]' => 'fpcR@nd0m!',
'pass[pass2]' => 'fpcR@nd0m!',
);
$this
->drupalPost(NULL, $edit, t('Save'));
$this
->assertRaw(t('The changes have been saved.'), 'User changed password.');
// Try to visit another page.
$this
->drupalGet('node');
// Verify user not redirected to change password.
$this
->assertNoFieldByName('mail', NULL, 'User not redirected back to user-edit page.');
$this
->assertNoRaw(t('Your password has expired. You must change your password to proceed on the site.'), 'User not forced to change password.');
}
/**
* Tests "Extra allowed paths" setting.
*/
public function testExtraAllowedPaths() {
$admin = $this
->drupalCreateUser(array(
'force password change',
'administer password policies',
'administer users',
));
$user = $this
->drupalCreateUser();
// Force user to change their password.
$this
->drupalLogin($admin);
$edit = array(
'force_password_change' => TRUE,
);
$this
->drupalPost("user/{$user->uid}/edit", $edit, t('Save'));
$this
->drupalLogout();
// Verify user is forced to change password.
$this
->drupalLogin($user);
$this
->assertFieldByName('current_pass', NULL, 'User redirected correctly.');
$this
->assertRaw(t('Your password has expired. You must change your password to proceed on the site.'), 'User presented with error instructing them to change their password.');
// Try to visit disallowed paths.
$this
->drupalGet('node');
$this
->assertFieldByName('mail', NULL, 'User redirected back to user-edit page.');
$this
->assertRaw(t('Your password has expired. You must change your password to proceed on the site.'), 'User forced to change password.');
$this
->drupalGet('node/add/page');
$this
->assertFieldByName('mail', NULL, 'User redirected back to user-edit page.');
$this
->assertRaw(t('Your password has expired. You must change your password to proceed on the site.'), 'User forced to change password.');
$this
->drupalLogout();
// Add extra allowed paths.
// One path tests using a wildcard and the other does not.
$this
->drupalLogin($admin);
$edit = array(
'password_policy_force_change_extra_allowed_paths' => "node\nnode/add/*",
);
$this
->drupalPost('admin/config/people/password_policy', $edit, t('Save configuration'));
$this
->assertRaw(t('The configuration options have been saved.'), 'Enabled "Force password change by e-mail".');
$this
->drupalLogout();
// Verify user is still forced to change password.
$this
->drupalLogin($user);
$this
->assertFieldByName('current_pass', NULL, 'User redirected correctly.');
$this
->assertRaw(t('Your password has expired. You must change your password to proceed on the site.'), 'User presented with error instructing them to change their password.');
// Try to visit paths that are now allowed.
// Note that "Access denied" is expected for node/add/page.
$this
->drupalGet('node');
$this
->assertNoFieldByName('mail', NULL, 'User not redirected back to user-edit page.');
$this
->drupalGet('node/add/page');
$this
->assertNoFieldByName('mail', NULL, 'User not redirected back to user-edit page.');
// Try to visit a path that is still disallowed.
$this
->drupalGet("user/{$user->uid}/view");
$this
->assertFieldByName('mail', NULL, 'User redirected back to user-edit page.');
$this
->assertRaw(t('Your password has expired. You must change your password to proceed on the site.'), 'User forced to change password.');
$this
->drupalLogout();
}
/**
* Tests altering allowed paths from another module.
*/
public function testAllowedPathsAlter() {
$admin = $this
->drupalCreateUser(array(
'force password change',
'administer password policies',
'administer users',
));
$user = $this
->drupalCreateUser();
// Force user to change their password.
$this
->drupalLogin($admin);
$edit = array(
'force_password_change' => TRUE,
);
$this
->drupalPost("user/{$user->uid}/edit", $edit, t('Save'));
$this
->drupalLogout();
// Verify user is forced to change password.
$this
->drupalLogin($user);
$this
->assertFieldByName('current_pass', NULL, 'User redirected correctly.');
$this
->assertRaw(t('Your password has expired. You must change your password to proceed on the site.'), 'User presented with error instructing them to change their password.');
// Try to visit disallowed paths.
$this
->drupalGet('node');
$this
->assertFieldByName('mail', NULL, 'User redirected back to user-edit page.');
$this
->assertRaw(t('Your password has expired. You must change your password to proceed on the site.'), 'User forced to change password.');
$this
->drupalGet('node/add/page');
$this
->assertFieldByName('mail', NULL, 'User redirected back to user-edit page.');
$this
->assertRaw(t('Your password has expired. You must change your password to proceed on the site.'), 'User forced to change password.');
$this
->drupalLogout();
// Add allowed path using
// hook_password_policy_force_change_allowed_path_alter().
variable_set('password_policy_test_force_change_allowed_paths_alter', TRUE);
// Try to visit paths that are now allowed.
// Note that "Access denied" is expected for node/add/page.
$this
->drupalLogin($user);
$this
->drupalGet('node');
$this
->assertNoFieldByName('mail', NULL, 'User not redirected back to user-edit page.');
$this
->drupalGet('node/add/page');
$this
->assertNoFieldByName('mail', NULL, 'User not redirected back to user-edit page.');
// Try to visit a path that is still disallowed.
$this
->drupalGet("user/{$user->uid}/view");
$this
->assertFieldByName('mail', NULL, 'User redirected back to user-edit page.');
$this
->assertRaw(t('Your password has expired. You must change your password to proceed on the site.'), 'User forced to change password.');
$this
->drupalLogout();
}
/**
* Parses the last sent e-mail and returns the one-time login link URL.
*
* Copy of OpenIDWebTestCase::getPasswordResetURLFromMail().
*/
protected function getPasswordResetUrlFromMail() {
$mails = $this
->drupalGetMails();
$mail = end($mails);
preg_match('@.+user/reset/.+@', $mail['body'], $matches);
return $matches[0];
}
}
/**
* Tests administrator changing password of another user.
*/
class PasswordPolicyAdministratorPasswordChangeTestCase extends DrupalWebTestCase {
protected $admin;
protected $nonadmin;
protected $policyRid;
/**
* Get info about the test case.
*/
public static function getInfo() {
return array(
'name' => 'Administrator password changes',
'description' => 'Test administrator changing password of another user.',
'group' => 'Password Policy',
);
}
/**
* Set up the test.
*/
public function setUp() {
parent::setUp('password_policy');
$this
->createAdmin();
$this
->createNonAdmin();
}
/**
* 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 non-administrator user for testing.
*
* The non-administrator is given a dummy role so password policies can be
* applied exclusively to the non-administrator.
*/
protected function createNonAdmin() {
$this->nonadmin = $this
->drupalCreateUser();
}
/**
* Tests administrator changing password of another user.
*/
public function testAdministratorPasswordChange() {
$admin = $this->admin;
$this
->drupalLogin($admin);
$this
->setPolicyThatAppliesToNewRole();
$this
->attemptNonAdminPasswordChanges();
$this
->attemptAdminPasswordChanges();
}
/**
* Set a password policy that applies just to the non-administrator.
*
* A password policy is created that applies to the role only the
* non-administrator has. Then, it is enabled.
*/
protected function setPolicyThatAppliesToNewRole() {
$rid = $this
->drupalCreateRole(array());
$policy_name = $this
->randomName();
$edit = array(
'name' => $policy_name,
"roles[{$rid}]" => $rid,
'constraint_length' => 4,
);
$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);
$this
->enablePolicy($policy_name);
$this->policyRid = $rid;
}
/**
* Attempts to change password for non-administrator.
*
* This is intended to check that the password policy of the
* non-administrator is applied and not the password policy of the
* administrator.
*/
protected function attemptNonAdminPasswordChanges() {
$nonadmin_uid = $this->nonadmin->uid;
$nonadmin_user_edit_path = 'user/' . $nonadmin_uid . '/edit';
$policy_rid = $this->policyRid;
$pass = 'foo';
$edit = array(
'pass[pass1]' => $pass,
'pass[pass2]' => $pass,
);
$this
->drupalPost($nonadmin_user_edit_path, $edit, t('Save'));
$this
->assertText(t('The changes have been saved.'), 'Change password without policy applied.');
$pass = 'foo';
$edit = array(
"roles[{$policy_rid}]" => $policy_rid,
'pass[pass1]' => $pass,
'pass[pass2]' => $pass,
);
$this
->drupalPost($nonadmin_user_edit_path, $edit, t('Save'));
$this
->assertText(t('Password must be at least 4 characters in length.'), 'Password not accepted that is invalid per policy that applies to administrator-selected role.');
$pass = 'foobar';
$edit = array(
"roles[{$policy_rid}]" => $policy_rid,
'pass[pass1]' => $pass,
'pass[pass2]' => $pass,
);
$this
->drupalPost($nonadmin_user_edit_path, $edit, t('Save'));
$this
->assertText(t('The changes have been saved.'), 'Password accepted that is valid per policy that applies to administrator-selected role.');
$pass = 'foo';
$edit = array(
'pass[pass1]' => $pass,
'pass[pass2]' => $pass,
);
$this
->drupalPost($nonadmin_user_edit_path, $edit, t('Save'));
$this
->assertText(t('Password must be at least 4 characters in length.'), 'Password not accepted that is invalid per policy that applies to role of user.');
$pass = 'foobar';
$edit = array(
'pass[pass1]' => $pass,
'pass[pass2]' => $pass,
);
$this
->drupalPost($nonadmin_user_edit_path, $edit, t('Save'));
$this
->assertText(t('The changes have been saved.'), 'Password accepted that is valid per policy that applies to role of user.');
}
/**
* Attempts to change password for administrator.
*
* This checks that the password policy that is supposed to apply only to
* non-administrators does not also apply to administrators.
*/
protected function attemptAdminPasswordChanges() {
$admin_uid = $this->admin->uid;
$current_pass = $this->admin->pass_raw;
$new_pass = 'foo';
$edit = array(
// Set a new e-mail address because the default will not pass validation.
'mail' => 'foo@example.com',
'current_pass' => $current_pass,
'pass[pass1]' => $new_pass,
'pass[pass2]' => $new_pass,
);
$this
->drupalPost('user/' . $admin_uid . '/edit', $edit, t('Save'));
$this
->assertText(t('The changes have been saved.'), 'Password accepted for administrator, who has no policy that applies to their roles.');
}
/**
* 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.');
}
}
/**
* Tests of restriction on password length.
*/
class PasswordPolicyPasswordLengthRestrictionTestCase extends DrupalWebTestCase {
protected $admin;
/**
* Get info about the test case.
*/
public static function getInfo() {
return array(
'name' => 'Password length restriction',
'description' => 'Test that overlong passwords are disallowed.',
'group' => 'Password Policy',
);
}
/**
* Set up the test.
*/
public function setUp() {
parent::setUp('password_policy');
$this
->createAdmin();
}
/**
* 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. We also give it a valid email
* address so its user edit form can be submitted.
*/
protected function createAdmin() {
global $user;
$pass = user_password();
$edit = array(
'pass' => $pass,
'mail' => 'foo@example.com',
);
user_save($user, $edit);
$user->pass_raw = $pass;
$this->admin = $user;
}
/**
* Tests module response to submission of an overlong password.
*/
public function testOverlongPasswordSubmission() {
$admin = $this->admin;
$this
->drupalLogin($admin);
$this
->setPolicyThatAppliesToAuthenticatedUser();
$this
->submitOverlongPassword();
$this
->submitNotOverlongPassword();
}
/**
* Sets a password policy that applies to the authenticated user role.
*
* This is just a minimal policy to apply to the admin (UID=1) user, which is
* being used for this test.
*/
protected function setPolicyThatAppliesToAuthenticatedUser() {
$rid = DRUPAL_AUTHENTICATED_RID;
$policy_name = $this
->randomName();
$edit = array(
'name' => $policy_name,
"roles[{$rid}]" => $rid,
'constraint_alphanumeric' => 1,
);
$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);
$this
->enablePolicy($policy_name);
$this->policyRid = $rid;
}
/**
* Submits a password that is overlong.
*/
protected function submitOverlongPassword() {
$pass = str_repeat('a', 513);
$edit = array(
'current_pass' => $this->admin->pass_raw,
'pass[pass1]' => $pass,
'pass[pass2]' => $pass,
);
$this
->drupalPost('user/1/edit', $edit, t('Save'));
$this
->assertText(t('Password exceeds maximum length.'), 'Overlong password causes form error.');
$this
->assertNoText(t('The changes have been saved.'), 'Overlong password is not saved.');
}
/**
* Submits a password that is not overlong.
*/
protected function submitNotOverlongPassword() {
$pass = str_repeat('a', 512);
$edit = array(
'current_pass' => $this->admin->pass_raw,
'pass[pass1]' => $pass,
'pass[pass2]' => $pass,
);
$this
->drupalPost('user/1/edit', $edit, t('Save'));
$this
->assertNoText(t('Password exceeds maximum length.'), 'Not-overlong password does not cause form error.');
$this
->assertText(t('The changes have been saved.'), 'Not-overlong password is saved.');
}
/**
* 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.');
}
}
Classes
Name | Description |
---|---|
PasswordPolicyAdministratorPasswordChangeTestCase | Tests administrator changing password of another user. |
PasswordPolicyForcePasswordChangeTestCase | Tests of forcing password changes. |
PasswordPolicyPasswordLengthRestrictionTestCase | Tests of restriction on password length. |
PasswordPolicyTestCase | Tests of basic Password Policy constraints. |