]> git.immae.eu Git - github/shaarli/Shaarli.git/blob - tests/security/LoginManagerTest.php
Merge pull request #1273 from ArthurHoaro/feature/ban-manager
[github/shaarli/Shaarli.git] / tests / security / LoginManagerTest.php
1 <?php
2 namespace Shaarli\Security;
3
4 require_once 'tests/utils/FakeConfigManager.php';
5
6 use PHPUnit\Framework\TestCase;
7
8 /**
9 * Test coverage for LoginManager
10 */
11 class LoginManagerTest extends TestCase
12 {
13 /** @var \FakeConfigManager Configuration Manager instance */
14 protected $configManager = null;
15
16 /** @var LoginManager Login Manager instance */
17 protected $loginManager = null;
18
19 /** @var SessionManager Session Manager instance */
20 protected $sessionManager = null;
21
22 /** @var string Banned IP filename */
23 protected $banFile = 'sandbox/ipbans.php';
24
25 /** @var string Log filename */
26 protected $logFile = 'sandbox/shaarli.log';
27
28 /** @var array Simulates the $_COOKIE array */
29 protected $cookie = [];
30
31 /** @var array Simulates the $GLOBALS array */
32 protected $globals = [];
33
34 /** @var array Simulates the $_SERVER array */
35 protected $server = [];
36
37 /** @var array Simulates the $_SESSION array */
38 protected $session = [];
39
40 /** @var string Advertised client IP address */
41 protected $clientIpAddress = '10.1.47.179';
42
43 /** @var string Local client IP address */
44 protected $ipAddr = '127.0.0.1';
45
46 /** @var string Trusted proxy IP address */
47 protected $trustedProxy = '10.1.1.100';
48
49 /** @var string User login */
50 protected $login = 'johndoe';
51
52 /** @var string User password */
53 protected $password = 'IC4nHazL0g1n?';
54
55 /** @var string Hash of the salted user password */
56 protected $passwordHash = '';
57
58 /** @var string Salt used by hash functions */
59 protected $salt = '669e24fa9c5a59a613f98e8e38327384504a4af2';
60
61 /**
62 * Prepare or reset test resources
63 */
64 public function setUp()
65 {
66 if (file_exists($this->banFile)) {
67 unlink($this->banFile);
68 }
69
70 $this->passwordHash = sha1($this->password . $this->login . $this->salt);
71
72 $this->configManager = new \FakeConfigManager([
73 'credentials.login' => $this->login,
74 'credentials.hash' => $this->passwordHash,
75 'credentials.salt' => $this->salt,
76 'resource.ban_file' => $this->banFile,
77 'resource.log' => $this->logFile,
78 'security.ban_after' => 2,
79 'security.ban_duration' => 3600,
80 'security.trusted_proxies' => [$this->trustedProxy],
81 ]);
82
83 $this->cookie = [];
84 $this->session = [];
85
86 $this->sessionManager = new SessionManager($this->session, $this->configManager);
87 $this->loginManager = new LoginManager($this->configManager, $this->sessionManager);
88 $this->server['REMOTE_ADDR'] = $this->ipAddr;
89 }
90
91 /**
92 * Record a failed login attempt
93 */
94 public function testHandleFailedLogin()
95 {
96 $this->loginManager->handleFailedLogin($this->server);
97 $this->loginManager->handleFailedLogin($this->server);
98 $this->assertFalse($this->loginManager->canLogin($this->server));
99 }
100
101 /**
102 * Record a failed login attempt - IP behind a trusted proxy
103 */
104 public function testHandleFailedLoginBehindTrustedProxy()
105 {
106 $server = [
107 'REMOTE_ADDR' => $this->trustedProxy,
108 'HTTP_X_FORWARDED_FOR' => $this->ipAddr,
109 ];
110 $this->loginManager->handleFailedLogin($server);
111 $this->loginManager->handleFailedLogin($server);
112 $this->assertFalse($this->loginManager->canLogin($server));
113 }
114
115 /**
116 * Record a failed login attempt - IP behind a trusted proxy but not forwarded
117 */
118 public function testHandleFailedLoginBehindTrustedProxyNoIp()
119 {
120 $server = [
121 'REMOTE_ADDR' => $this->trustedProxy,
122 ];
123 $this->loginManager->handleFailedLogin($server);
124 $this->loginManager->handleFailedLogin($server);
125 $this->assertTrue($this->loginManager->canLogin($server));
126 }
127
128 /**
129 * Nothing to do
130 */
131 public function testHandleSuccessfulLogin()
132 {
133 $this->assertTrue($this->loginManager->canLogin($this->server));
134
135 $this->loginManager->handleSuccessfulLogin($this->server);
136 $this->assertTrue($this->loginManager->canLogin($this->server));
137 }
138
139 /**
140 * Erase failure records after successfully logging in from this IP
141 */
142 public function testHandleSuccessfulLoginAfterFailure()
143 {
144 $this->loginManager->handleFailedLogin($this->server);
145 $this->assertTrue($this->loginManager->canLogin($this->server));
146
147 $this->loginManager->handleSuccessfulLogin($this->server);
148 $this->loginManager->handleFailedLogin($this->server);
149 $this->assertTrue($this->loginManager->canLogin($this->server));
150 }
151
152 /**
153 * The IP is not banned
154 */
155 public function testCanLoginIpNotBanned()
156 {
157 $this->assertTrue($this->loginManager->canLogin($this->server));
158 }
159
160 /**
161 * Generate a token depending on the user credentials and client IP
162 */
163 public function testGenerateStaySignedInToken()
164 {
165 $this->loginManager->generateStaySignedInToken($this->clientIpAddress);
166
167 $this->assertEquals(
168 sha1($this->passwordHash . $this->clientIpAddress . $this->salt),
169 $this->loginManager->getStaySignedInToken()
170 );
171 }
172
173 /**
174 * Generate a token depending on the user credentials with session protected disabled
175 */
176 public function testGenerateStaySignedInTokenSessionProtectionDisabled()
177 {
178 $this->configManager->set('security.session_protection_disabled', true);
179 $this->loginManager->generateStaySignedInToken($this->clientIpAddress);
180
181 $this->assertEquals(
182 sha1($this->passwordHash . $this->salt),
183 $this->loginManager->getStaySignedInToken()
184 );
185 }
186
187 /**
188 * Check user login - Shaarli has not yet been configured
189 */
190 public function testCheckLoginStateNotConfigured()
191 {
192 $configManager = new \FakeConfigManager([
193 'resource.ban_file' => $this->banFile,
194 ]);
195 $loginManager = new LoginManager($configManager, null);
196 $loginManager->checkLoginState([], '');
197
198 $this->assertFalse($loginManager->isLoggedIn());
199 }
200
201 /**
202 * Check user login - the client cookie does not match the server token
203 */
204 public function testCheckLoginStateStaySignedInWithInvalidToken()
205 {
206 // simulate a previous login
207 $this->session = [
208 'ip' => $this->clientIpAddress,
209 'expires_on' => time() + 100,
210 ];
211 $this->loginManager->generateStaySignedInToken($this->clientIpAddress);
212 $this->cookie[LoginManager::$STAY_SIGNED_IN_COOKIE] = 'nope';
213
214 $this->loginManager->checkLoginState($this->cookie, $this->clientIpAddress);
215
216 $this->assertTrue($this->loginManager->isLoggedIn());
217 $this->assertTrue(empty($this->session['username']));
218 }
219
220 /**
221 * Check user login - the client cookie matches the server token
222 */
223 public function testCheckLoginStateStaySignedInWithValidToken()
224 {
225 $this->loginManager->generateStaySignedInToken($this->clientIpAddress);
226 $this->cookie[LoginManager::$STAY_SIGNED_IN_COOKIE] = $this->loginManager->getStaySignedInToken();
227
228 $this->loginManager->checkLoginState($this->cookie, $this->clientIpAddress);
229
230 $this->assertTrue($this->loginManager->isLoggedIn());
231 $this->assertEquals($this->login, $this->session['username']);
232 $this->assertEquals($this->clientIpAddress, $this->session['ip']);
233 }
234
235 /**
236 * Check user login - the session has expired
237 */
238 public function testCheckLoginStateSessionExpired()
239 {
240 $this->loginManager->generateStaySignedInToken($this->clientIpAddress);
241 $this->session['expires_on'] = time() - 100;
242
243 $this->loginManager->checkLoginState($this->cookie, $this->clientIpAddress);
244
245 $this->assertFalse($this->loginManager->isLoggedIn());
246 }
247
248 /**
249 * Check user login - the remote client IP has changed
250 */
251 public function testCheckLoginStateClientIpChanged()
252 {
253 $this->loginManager->generateStaySignedInToken($this->clientIpAddress);
254
255 $this->loginManager->checkLoginState($this->cookie, '10.7.157.98');
256
257 $this->assertFalse($this->loginManager->isLoggedIn());
258 }
259
260 /**
261 * Check user credentials - wrong login supplied
262 */
263 public function testCheckCredentialsWrongLogin()
264 {
265 $this->assertFalse(
266 $this->loginManager->checkCredentials('', '', 'b4dl0g1n', $this->password)
267 );
268 }
269
270 /**
271 * Check user credentials - wrong password supplied
272 */
273 public function testCheckCredentialsWrongPassword()
274 {
275 $this->assertFalse(
276 $this->loginManager->checkCredentials('', '', $this->login, 'b4dp455wd')
277 );
278 }
279
280 /**
281 * Check user credentials - wrong login and password supplied
282 */
283 public function testCheckCredentialsWrongLoginAndPassword()
284 {
285 $this->assertFalse(
286 $this->loginManager->checkCredentials('', '', 'b4dl0g1n', 'b4dp455wd')
287 );
288 }
289
290 /**
291 * Check user credentials - correct login and password supplied
292 */
293 public function testCheckCredentialsGoodLoginAndPassword()
294 {
295 $this->assertTrue(
296 $this->loginManager->checkCredentials('', '', $this->login, $this->password)
297 );
298 }
299 }