2008-04-26 08:31:52 +02:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* Test the security class, including log-in form, change password form, etc
|
2008-06-15 15:33:53 +02:00
|
|
|
*
|
|
|
|
* @package sapphire
|
|
|
|
* @subpackage tests
|
2008-04-26 08:31:52 +02:00
|
|
|
*/
|
2008-08-11 07:27:18 +02:00
|
|
|
class SecurityTest extends FunctionalTest {
|
2008-04-26 08:31:52 +02:00
|
|
|
static $fixture_file = 'sapphire/tests/security/MemberTest.yml';
|
|
|
|
|
2008-08-12 01:04:25 +02:00
|
|
|
protected $autoFollowRedirection = false;
|
2008-04-26 08:31:52 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Test that the login form redirects to the change password form after logging in with an expired password
|
|
|
|
*/
|
|
|
|
function testExpiredPassword() {
|
2008-04-26 08:32:52 +02:00
|
|
|
/* BAD PASSWORDS ARE LOCKED OUT */
|
2008-08-12 01:04:25 +02:00
|
|
|
$badResponse = $this->doTestLoginForm('sam@silverstripe.com' , 'badpassword');
|
2008-04-26 08:31:52 +02:00
|
|
|
$this->assertEquals(302, $badResponse->getStatusCode());
|
|
|
|
$this->assertRegExp('/Security\/login/', $badResponse->getHeader('Location'));
|
2008-08-12 01:04:25 +02:00
|
|
|
$this->assertNull($this->session()->inst_get('loggedInAs'));
|
2008-04-26 08:31:52 +02:00
|
|
|
|
2008-04-26 08:32:52 +02:00
|
|
|
/* UNEXPIRED PASSWORD GO THROUGH WITHOUT A HITCH */
|
2008-08-12 01:04:25 +02:00
|
|
|
$goodResponse = $this->doTestLoginForm('sam@silverstripe.com' , '1nitialPassword');
|
2008-04-26 08:31:52 +02:00
|
|
|
$this->assertEquals(302, $goodResponse->getStatusCode());
|
|
|
|
$this->assertEquals(Director::baseURL() . 'test/link', $goodResponse->getHeader('Location'));
|
2008-08-12 01:04:25 +02:00
|
|
|
$this->assertEquals($this->idFromFixture('Member', 'test'), $this->session()->inst_get('loggedInAs'));
|
2008-04-26 08:31:52 +02:00
|
|
|
|
2008-04-26 08:32:52 +02:00
|
|
|
/* EXPIRED PASSWORDS ARE SENT TO THE CHANGE PASSWORD FORM */
|
2008-08-12 01:04:25 +02:00
|
|
|
$expiredResponse = $this->doTestLoginForm('expired@silverstripe.com' , '1nitialPassword');
|
2008-04-26 08:32:05 +02:00
|
|
|
$this->assertEquals(302, $expiredResponse->getStatusCode());
|
|
|
|
$this->assertEquals(Director::baseURL() . 'Security/changepassword', $expiredResponse->getHeader('Location'));
|
2008-08-12 01:04:25 +02:00
|
|
|
$this->assertEquals($this->idFromFixture('Member', 'expiredpassword'), $this->session()->inst_get('loggedInAs'));
|
2008-04-26 08:32:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function testRepeatedLoginAttemptsLockingPeopleOut() {
|
|
|
|
Member::lock_out_after_incorrect_logins(5);
|
|
|
|
|
2008-04-26 08:32:52 +02:00
|
|
|
/* LOG IN WITH A BAD PASSWORD 7 TIMES */
|
2008-04-26 08:32:05 +02:00
|
|
|
|
|
|
|
for($i=1;$i<=7;$i++) {
|
2008-08-12 01:04:25 +02:00
|
|
|
$this->doTestLoginForm('sam@silverstripe.com' , 'incorrectpassword');
|
2008-04-26 08:32:05 +02:00
|
|
|
$member = DataObject::get_by_id("Member", $this->idFromFixture('Member', 'test'));
|
|
|
|
|
2008-04-26 08:32:52 +02:00
|
|
|
/* THE FIRST 4 TIMES, THE MEMBER SHOULDN'T BE LOCKED OUT */
|
2008-04-26 08:32:05 +02:00
|
|
|
if($i < 5) {
|
|
|
|
$this->assertNull($member->LockedOutUntil);
|
2008-08-12 01:04:25 +02:00
|
|
|
$this->assertTrue(false !== stripos($this->loginErrorMessage(), "That doesn't seem to be the right e-mail address or password"));
|
2008-04-26 08:32:05 +02:00
|
|
|
}
|
|
|
|
|
2008-04-26 08:32:52 +02:00
|
|
|
/* AFTER THAT THE USER IS LOCKED OUT FOR 15 MINUTES */
|
2008-04-26 08:32:05 +02:00
|
|
|
|
|
|
|
//(we check for at least 14 minutes because we don't want a slow running test to report a failure.)
|
|
|
|
else {
|
|
|
|
$this->assertGreaterThan(time() + 14*60, strtotime($member->LockedOutUntil));
|
|
|
|
}
|
|
|
|
|
|
|
|
if($i > 5) {
|
2008-08-12 01:04:25 +02:00
|
|
|
$this->assertTrue(false !== stripos($this->loginErrorMessage(), "Your account has been temporarily disabled"));
|
2008-04-26 08:32:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-04-26 08:32:52 +02:00
|
|
|
/* THE USER CAN'T LOG IN NOW, EVEN IF THEY GET THE RIGHT PASSWORD */
|
2008-04-26 08:32:05 +02:00
|
|
|
|
2008-08-12 01:04:25 +02:00
|
|
|
$this->doTestLoginForm('sam@silverstripe.com' , '1nitialPassword');
|
|
|
|
$this->assertNull($this->session()->inst_get('loggedInAs'));
|
2008-04-26 08:32:05 +02:00
|
|
|
|
2008-04-26 08:32:52 +02:00
|
|
|
/* BUT, IF TIME PASSES, THEY CAN LOG IN */
|
2008-04-26 08:32:05 +02:00
|
|
|
|
|
|
|
// (We fake this by re-setting LockedOutUntil)
|
|
|
|
$member = DataObject::get_by_id("Member", $this->idFromFixture('Member', 'test'));
|
2008-07-21 14:21:53 +02:00
|
|
|
$member->LockedOutUntil = date('Y-m-d H:i:s', time() - 30);
|
2008-04-26 08:32:05 +02:00
|
|
|
$member->write();
|
|
|
|
|
2008-08-12 01:04:25 +02:00
|
|
|
$this->doTestLoginForm('sam@silverstripe.com' , '1nitialPassword');
|
|
|
|
$this->assertEquals($this->session()->inst_get('loggedInAs'), $member->ID);
|
2008-04-26 08:32:05 +02:00
|
|
|
|
|
|
|
// Log the user out
|
2008-08-12 01:04:25 +02:00
|
|
|
$this->session()->inst_set('loggedInAs', null);
|
2008-04-26 08:32:05 +02:00
|
|
|
|
2008-04-26 08:32:52 +02:00
|
|
|
/* NOW THAT THE LOCK-OUT HAS EXPIRED, CHECK THAT WE ARE ALLOWED 4 FAILED ATTEMPTS BEFORE LOGGING IN */
|
2008-04-26 08:32:05 +02:00
|
|
|
|
2008-08-12 01:04:25 +02:00
|
|
|
$this->doTestLoginForm('sam@silverstripe.com' , 'incorrectpassword');
|
|
|
|
$this->doTestLoginForm('sam@silverstripe.com' , 'incorrectpassword');
|
|
|
|
$this->doTestLoginForm('sam@silverstripe.com' , 'incorrectpassword');
|
|
|
|
$this->doTestLoginForm('sam@silverstripe.com' , 'incorrectpassword');
|
|
|
|
$this->assertNull($this->session()->inst_get('loggedInAs'));
|
|
|
|
$this->assertTrue(false !== stripos($this->loginErrorMessage(), "That doesn't seem to be the right e-mail address or password"));
|
|
|
|
|
|
|
|
$this->doTestLoginForm('sam@silverstripe.com' , '1nitialPassword');
|
|
|
|
$this->assertEquals($this->session()->inst_get('loggedInAs'), $member->ID);
|
2008-04-26 08:32:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function testAlternatingRepeatedLoginAttempts() {
|
|
|
|
Member::lock_out_after_incorrect_logins(3);
|
|
|
|
|
|
|
|
// ATTEMPTING LOG-IN TWICE WITH ONE ACCOUNT AND TWICE WITH ANOTHER SHOULDN'T LOCK ANYBODY OUT
|
|
|
|
|
2008-08-12 01:04:25 +02:00
|
|
|
$this->doTestLoginForm('sam@silverstripe.com' , 'incorrectpassword');
|
|
|
|
$this->doTestLoginForm('sam@silverstripe.com' , 'incorrectpassword');
|
2008-04-26 08:32:05 +02:00
|
|
|
|
2008-08-12 01:04:25 +02:00
|
|
|
$this->doTestLoginForm('noexpiry@silverstripe.com' , 'incorrectpassword');
|
|
|
|
$this->doTestLoginForm('noexpiry@silverstripe.com' , 'incorrectpassword');
|
2008-04-26 08:32:05 +02:00
|
|
|
|
|
|
|
$member1 = DataObject::get_by_id("Member", $this->idFromFixture('Member', 'test'));
|
|
|
|
$member2 = DataObject::get_by_id("Member", $this->idFromFixture('Member', 'noexpiry'));
|
|
|
|
|
|
|
|
$this->assertNull($member1->LockedOutUntil);
|
|
|
|
$this->assertNull($member2->LockedOutUntil);
|
|
|
|
|
|
|
|
// BUT, DOING AN ADDITIONAL LOG-IN WITH EITHER OF THEM WILL LOCK OUT, SINCE THAT IS THE 3RD FAILURE IN THIS SESSION
|
|
|
|
|
2008-08-12 01:04:25 +02:00
|
|
|
$this->doTestLoginForm('sam@silverstripe.com' , 'incorrectpassword');
|
2008-04-26 08:32:05 +02:00
|
|
|
$member1 = DataObject::get_by_id("Member", $this->idFromFixture('Member', 'test'));
|
|
|
|
$this->assertNotNull($member1->LockedOutUntil);
|
|
|
|
|
2008-08-12 01:04:25 +02:00
|
|
|
$this->doTestLoginForm('noexpiry@silverstripe.com' , 'incorrectpassword');
|
2008-04-26 08:32:05 +02:00
|
|
|
$member2 = DataObject::get_by_id("Member", $this->idFromFixture('Member', 'noexpiry'));
|
|
|
|
$this->assertNotNull($member2->LockedOutUntil);
|
|
|
|
}
|
2008-08-11 02:14:48 +02:00
|
|
|
|
|
|
|
function testUnsuccessfulLoginAttempts() {
|
|
|
|
Security::set_login_recording(true);
|
|
|
|
|
|
|
|
/* UNSUCCESSFUL ATTEMPTS WITH WRONG PASSWORD FOR EXISTING USER ARE LOGGED */
|
2008-08-12 01:04:25 +02:00
|
|
|
$this->doTestLoginForm('sam@silverstripe.com', 'wrongpassword');
|
2008-08-11 02:14:48 +02:00
|
|
|
$attempt = DataObject::get_one('LoginAttempt', 'Email = "sam@silverstripe.com"');
|
|
|
|
$this->assertTrue(is_object($attempt));
|
|
|
|
$member = DataObject::get_one('Member', 'Email = "sam@silverstripe.com"');
|
|
|
|
$this->assertEquals($attempt->Status, 'Failure');
|
|
|
|
$this->assertEquals($attempt->Email, 'sam@silverstripe.com');
|
|
|
|
$this->assertEquals($attempt->Member(), $member);
|
|
|
|
|
|
|
|
/* UNSUCCESSFUL ATTEMPTS WITH NONEXISTING USER ARE LOGGED */
|
2008-08-12 01:04:25 +02:00
|
|
|
$this->doTestLoginForm('wronguser@silverstripe.com', 'wrongpassword');
|
2008-08-11 02:14:48 +02:00
|
|
|
$attempt = DataObject::get_one('LoginAttempt', 'Email = "wronguser@silverstripe.com"');
|
|
|
|
$this->assertTrue(is_object($attempt));
|
|
|
|
$this->assertEquals($attempt->Status, 'Failure');
|
|
|
|
$this->assertEquals($attempt->Email, 'wronguser@silverstripe.com');
|
|
|
|
}
|
|
|
|
|
|
|
|
function testSuccessfulLoginAttempts() {
|
|
|
|
Security::set_login_recording(true);
|
|
|
|
|
|
|
|
/* SUCCESSFUL ATTEMPTS ARE LOGGED */
|
2008-08-12 01:04:25 +02:00
|
|
|
$this->doTestLoginForm('sam@silverstripe.com', '1nitialPassword');
|
2008-08-11 02:14:48 +02:00
|
|
|
$attempt = DataObject::get_one('LoginAttempt', 'Email = "sam@silverstripe.com"');
|
|
|
|
$member = DataObject::get_one('Member', 'Email = "sam@silverstripe.com"');
|
|
|
|
$this->assertTrue(is_object($attempt));
|
|
|
|
$this->assertEquals($attempt->Status, 'Success');
|
|
|
|
$this->assertEquals($attempt->Email, 'sam@silverstripe.com');
|
|
|
|
$this->assertEquals($attempt->Member(), $member);
|
|
|
|
}
|
2008-04-26 08:32:05 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Execute a log-in form using Director::test().
|
|
|
|
* Helper method for the tests above
|
|
|
|
*/
|
2008-08-12 01:04:25 +02:00
|
|
|
function doTestLoginForm($email, $password) {
|
|
|
|
$this->session()->inst_set('BackURL', 'test/link');
|
|
|
|
$this->get('Security/login');
|
|
|
|
|
|
|
|
return $this->submitForm(
|
2008-08-11 02:14:48 +02:00
|
|
|
"MemberLoginForm_LoginForm",
|
|
|
|
null,
|
|
|
|
array(
|
|
|
|
'Email' => $email,
|
|
|
|
'Password' => $password,
|
|
|
|
'AuthenticationMethod' => 'MemberAuthenticator',
|
|
|
|
'action_dologin' => 1,
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2008-04-26 08:32:05 +02:00
|
|
|
/**
|
|
|
|
* Get the error message on the login form
|
|
|
|
*/
|
2008-08-12 01:04:25 +02:00
|
|
|
function loginErrorMessage() {
|
|
|
|
return $this->session()->inst_get('FormInfo.MemberLoginForm_LoginForm.formError.message');
|
2008-08-11 02:14:48 +02:00
|
|
|
}
|
2008-04-26 08:31:52 +02:00
|
|
|
|
2008-08-11 02:14:48 +02:00
|
|
|
}
|
|
|
|
?>
|