Completed
Push — 3.4 ( 9c2b9d...cf8f78 )
by Daniel
13:20
created

testNonExistantMemberGetsLockedOut()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 13
nc 1
nop 0
dl 0
loc 20
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * @package framework
4
 * @subpackage tests
5
 */
6
class MemberAuthenticatorTest extends SapphireTest {
7
8
	protected $usesDatabase = true;
9
10
	protected $defaultUsername = null;
11
	protected $defaultPassword = null;
12
13
	public function setUp() {
14
		parent::setUp();
15
16
		$this->defaultUsername = Security::default_admin_username();
17
		$this->defaultPassword = Security::default_admin_password();
18
		Security::clear_default_admin();
19
		Security::setDefaultAdmin('admin', 'password');
20
	}
21
22
	public function tearDown() {
23
		Security::setDefaultAdmin($this->defaultUsername, $this->defaultPassword);
24
		parent::tearDown();
25
	}
26
27
	public function testLegacyPasswordHashMigrationUponLogin() {
28
		$member = new Member();
29
30
		$field=Member::config()->unique_identifier_field;
0 ignored issues
show
Documentation introduced by
The property unique_identifier_field does not exist on object<Config_ForClass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
31
32
		$member->$field = '[email protected]';
33
		$member->PasswordEncryption = "sha1";
34
		$member->Password = "mypassword";
35
		$member->write();
36
37
		$data = array(
38
			'Email' => $member->$field,
39
			'Password' => 'mypassword'
40
		);
41
		MemberAuthenticator::authenticate($data);
42
43
		$member = DataObject::get_by_id('Member', $member->ID);
44
		$this->assertEquals($member->PasswordEncryption, "sha1_v2.4");
45
		$result = $member->checkPassword('mypassword');
46
		$this->assertTrue($result->valid());
47
	}
48
49
	public function testNoLegacyPasswordHashMigrationOnIncompatibleAlgorithm() {
50
		Config::inst()->update('PasswordEncryptor', 'encryptors',
51
			array('crc32'=>array('PasswordEncryptor_PHPHash'=>'crc32')));
52
		$field=Member::config()->unique_identifier_field;
0 ignored issues
show
Documentation introduced by
The property unique_identifier_field does not exist on object<Config_ForClass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
53
54
		$member = new Member();
55
		$member->$field = '[email protected]';
56
		$member->PasswordEncryption = "crc32";
57
		$member->Password = "mypassword";
58
		$member->write();
59
60
		$data = array(
61
			'Email' => $member->$field,
62
			'Password' => 'mypassword'
63
		);
64
		MemberAuthenticator::authenticate($data);
65
66
		$member = DataObject::get_by_id('Member', $member->ID);
67
		$this->assertEquals($member->PasswordEncryption, "crc32");
68
		$result = $member->checkPassword('mypassword');
69
		$this->assertTrue($result->valid());
70
	}
71
72
	public function testCustomIdentifierField(){
73
74
		$origField = Member::config()->unique_identifier_field;
0 ignored issues
show
Documentation introduced by
The property unique_identifier_field does not exist on object<Config_ForClass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
75
		Member::config()->unique_identifier_field = 'Username';
0 ignored issues
show
Documentation introduced by
The property unique_identifier_field does not exist on object<Config_ForClass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
76
77
		$label=singleton('Member')->fieldLabel(Member::config()->unique_identifier_field);
78
79
		$this->assertEquals($label, 'Username');
80
81
		Member::config()->unique_identifier_field = $origField;
0 ignored issues
show
Documentation introduced by
The property unique_identifier_field does not exist on object<Config_ForClass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
82
	}
83
84
	public function testGenerateLoginForm() {
85
		$controller = new Security();
86
87
		// Create basic login form
88
		$frontendForm = MemberAuthenticator::get_login_form($controller);
89
		$this->assertTrue($frontendForm instanceof MemberLoginForm);
90
91
		// Supports cms login form
92
		$this->assertTrue(MemberAuthenticator::supports_cms());
93
		$cmsForm = MemberAuthenticator::get_cms_login_form($controller);
94
		$this->assertTrue($cmsForm instanceof CMSMemberLoginForm);
95
	}
96
97
	/**
98
	 * Test that a member can be authenticated via their temp id
99
	 */
100
	public function testAuthenticateByTempID() {
101
		$member = new Member();
102
		$member->Email = '[email protected]';
103
		$member->PasswordEncryption = "sha1";
104
		$member->Password = "mypassword";
105
		$member->write();
106
107
		// Make form
108
		$controller = new Security();
109
		$form = new Form($controller, 'Form', new FieldList(), new FieldList());
110
111
		// If the user has never logged in, then the tempid should be empty
112
		$tempID = $member->TempIDHash;
113
		$this->assertEmpty($tempID);
114
115
		// If the user logs in then they have a temp id
116
		$member->logIn(true);
117
		$tempID = $member->TempIDHash;
118
		$this->assertNotEmpty($tempID);
119
120
		// Test correct login
121
		$result = MemberAuthenticator::authenticate(array(
122
			'tempid' => $tempID,
123
			'Password' => 'mypassword'
124
		), $form);
125
		$this->assertNotEmpty($result);
126
		$this->assertEquals($result->ID, $member->ID);
127
		$this->assertEmpty($form->Message());
128
129
		// Test incorrect login
130
		$form->clearMessage();
131
		$result = MemberAuthenticator::authenticate(array(
132
			'tempid' => $tempID,
133
			'Password' => 'notmypassword'
134
		), $form);
135
		$this->assertEmpty($result);
136
		$this->assertEquals('The provided details don&#039;t seem to be correct. Please try again.', $form->Message());
137
		$this->assertEquals('bad', $form->MessageType());
138
	}
139
140
	/**
141
	 * Test that the default admin can be authenticated
142
	 */
143
	public function testDefaultAdmin() {
144
		// Make form
145
		$controller = new Security();
146
		$form = new Form($controller, 'Form', new FieldList(), new FieldList());
147
148
		// Test correct login
149
		$result = MemberAuthenticator::authenticate(array(
150
			'Email' => 'admin',
151
			'Password' => 'password'
152
		), $form);
153
		$this->assertNotEmpty($result);
154
		$this->assertEquals($result->Email, Security::default_admin_username());
155
		$this->assertEmpty($form->Message());
156
157
		// Test incorrect login
158
		$form->clearMessage();
159
		$result = MemberAuthenticator::authenticate(array(
160
			'Email' => 'admin',
161
			'Password' => 'notmypassword'
162
		), $form);
163
		$this->assertEmpty($result);
164
		$this->assertEquals('The provided details don&#039;t seem to be correct. Please try again.', $form->Message());
165
		$this->assertEquals('bad', $form->MessageType());
166
	}
167
168
	public function testDefaultAdminLockOut()
169
	{
170
		Config::inst()->update('Member', 'lock_out_after_incorrect_logins', 1);
171
		Config::inst()->update('Member', 'lock_out_delay_mins', 10);
172
		SS_Datetime::set_mock_now('2016-04-18 00:00:00');
173
		$controller = new Security();
174
		$form = new Form($controller, 'Form', new FieldList(), new FieldList());
175
176
		// Test correct login
177
		MemberAuthenticator::authenticate(array(
178
			'Email' => 'admin',
179
			'Password' => 'wrongpassword'
180
		), $form);
181
182
		$this->assertTrue(Member::default_admin()->isLockedOut());
183
		$this->assertEquals('2016-04-18 00:10:00', Member::default_admin()->LockedOutUntil);
184
	}
185
186
	public function testNonExistantMemberGetsLoginAttemptRecorded()
187
	{
188
		Config::inst()->update('Member', 'lock_out_after_incorrect_logins', 1);
189
		$email = '[email protected]';
190
		$this->assertFalse(Member::get()->filter(array('Email' => $email))->exists());
191
		$this->assertCount(0, LoginAttempt::get());
192
		$response = MemberAuthenticator::authenticate(array(
193
			'Email' => $email,
194
			'Password' => 'password',
195
		));
196
		$this->assertNull($response);
197
		$this->assertCount(1, LoginAttempt::get());
198
		$attempt = LoginAttempt::get()->first();
199
		$this->assertEquals($email, $attempt->Email);
200
		$this->assertEquals('Failure', $attempt->Status);
201
202
	}
203
204
	public function testNonExistantMemberGetsLockedOut()
205
	{
206
		Config::inst()->update('Member', 'lock_out_after_incorrect_logins', 1);
207
		Config::inst()->update('Member', 'lock_out_delay_mins', 10);
208
		$email = '[email protected]';
209
210
		$this->assertFalse(Member::get()->filter(array('Email' => $email))->exists());
211
212
		$response = MemberAuthenticator::authenticate(array(
213
			'Email' => $email,
214
			'Password' => 'password'
215
		));
216
217
		$this->assertNull($response);
218
		$member = new Member();
219
		$member->Email = $email;
220
221
		$this->assertTrue($member->isLockedOut());
222
		$this->assertFalse($member->canLogIn()->valid());
223
	}
224
}
225