]> git.immae.eu Git - github/shaarli/Shaarli.git/blame - tests/security/LoginManagerTest.php
Merge branch 'master' into v0.12
[github/shaarli/Shaarli.git] / tests / security / LoginManagerTest.php
CommitLineData
44acf706 1<?php
44acf706 2
c4ad3d4f 3namespace Shaarli\Security;
dea72c71 4
b38a1b02
A
5use Psr\Log\LoggerInterface;
6use Shaarli\FakeConfigManager;
a5a9cf23 7use Shaarli\TestCase;
44acf706
V
8
9/**
10 * Test coverage for LoginManager
11 */
12class LoginManagerTest extends TestCase
13{
b38a1b02 14 /** @var FakeConfigManager Configuration Manager instance */
44acf706 15 protected $configManager = null;
704637bf
V
16
17 /** @var LoginManager Login Manager instance */
44acf706 18 protected $loginManager = null;
704637bf
V
19
20 /** @var SessionManager Session Manager instance */
21 protected $sessionManager = null;
22
23 /** @var string Banned IP filename */
44acf706 24 protected $banFile = 'sandbox/ipbans.php';
704637bf
V
25
26 /** @var string Log filename */
44acf706 27 protected $logFile = 'sandbox/shaarli.log';
704637bf
V
28
29 /** @var array Simulates the $_COOKIE array */
30 protected $cookie = [];
31
32 /** @var array Simulates the $GLOBALS array */
44acf706 33 protected $globals = [];
704637bf
V
34
35 /** @var array Simulates the $_SERVER array */
44acf706 36 protected $server = [];
704637bf
V
37
38 /** @var array Simulates the $_SESSION array */
39 protected $session = [];
40
41 /** @var string Advertised client IP address */
42 protected $clientIpAddress = '10.1.47.179';
43
44 /** @var string Local client IP address */
45 protected $ipAddr = '127.0.0.1';
46
47 /** @var string Trusted proxy IP address */
44acf706
V
48 protected $trustedProxy = '10.1.1.100';
49
c689e108
V
50 /** @var string User login */
51 protected $login = 'johndoe';
52
53 /** @var string User password */
54 protected $password = 'IC4nHazL0g1n?';
55
56 /** @var string Hash of the salted user password */
57 protected $passwordHash = '';
58
59 /** @var string Salt used by hash functions */
60 protected $salt = '669e24fa9c5a59a613f98e8e38327384504a4af2';
61
c4ad3d4f
A
62 /** @var CookieManager */
63 protected $cookieManager;
64
b38a1b02
A
65 /** @var BanManager */
66 protected $banManager;
67
44acf706
V
68 /**
69 * Prepare or reset test resources
70 */
8f60e120 71 protected function setUp(): void
44acf706
V
72 {
73 if (file_exists($this->banFile)) {
74 unlink($this->banFile);
75 }
76
c689e108
V
77 $this->passwordHash = sha1($this->password . $this->login . $this->salt);
78
b38a1b02 79 $this->configManager = new FakeConfigManager([
c689e108
V
80 'credentials.login' => $this->login,
81 'credentials.hash' => $this->passwordHash,
82 'credentials.salt' => $this->salt,
44acf706
V
83 'resource.ban_file' => $this->banFile,
84 'resource.log' => $this->logFile,
b49a04f7 85 'security.ban_after' => 2,
44acf706
V
86 'security.ban_duration' => 3600,
87 'security.trusted_proxies' => [$this->trustedProxy],
cc2ded54 88 'ldap.host' => '',
44acf706
V
89 ]);
90
704637bf 91 $this->cookie = [];
8edd7f15 92 $this->session = [];
704637bf 93
c4ad3d4f
A
94 $this->cookieManager = $this->createMock(CookieManager::class);
95 $this->cookieManager->method('getCookieParameter')->willReturnCallback(function (string $key) {
96 return $this->cookie[$key] ?? null;
97 });
98 $this->sessionManager = new SessionManager($this->session, $this->configManager, 'session_path');
b38a1b02
A
99 $this->banManager = $this->createMock(BanManager::class);
100 $this->loginManager = new LoginManager(
101 $this->configManager,
102 $this->sessionManager,
103 $this->cookieManager,
104 $this->banManager,
105 $this->createMock(LoggerInterface::class)
106 );
44acf706
V
107 $this->server['REMOTE_ADDR'] = $this->ipAddr;
108 }
109
44acf706
V
110 /**
111 * Record a failed login attempt
112 */
b38a1b02 113 public function testHandleFailedLogin(): void
44acf706 114 {
b38a1b02
A
115 $this->banManager->expects(static::exactly(2))->method('handleFailedAttempt');
116 $this->banManager->method('isBanned')->willReturn(true);
117
44acf706 118 $this->loginManager->handleFailedLogin($this->server);
44acf706 119 $this->loginManager->handleFailedLogin($this->server);
b38a1b02
A
120
121 static::assertFalse($this->loginManager->canLogin($this->server));
44acf706
V
122 }
123
124 /**
125 * Record a failed login attempt - IP behind a trusted proxy
126 */
127 public function testHandleFailedLoginBehindTrustedProxy()
128 {
129 $server = [
130 'REMOTE_ADDR' => $this->trustedProxy,
131 'HTTP_X_FORWARDED_FOR' => $this->ipAddr,
132 ];
b38a1b02
A
133
134 $this->banManager->expects(static::exactly(2))->method('handleFailedAttempt');
135 $this->banManager->method('isBanned')->willReturn(true);
136
44acf706 137 $this->loginManager->handleFailedLogin($server);
44acf706 138 $this->loginManager->handleFailedLogin($server);
b38a1b02 139
b49a04f7 140 $this->assertFalse($this->loginManager->canLogin($server));
44acf706
V
141 }
142
143 /**
144 * Record a failed login attempt - IP behind a trusted proxy but not forwarded
145 */
146 public function testHandleFailedLoginBehindTrustedProxyNoIp()
147 {
148 $server = [
149 'REMOTE_ADDR' => $this->trustedProxy,
150 ];
151 $this->loginManager->handleFailedLogin($server);
44acf706 152 $this->loginManager->handleFailedLogin($server);
b49a04f7 153 $this->assertTrue($this->loginManager->canLogin($server));
44acf706
V
154 }
155
156 /**
157 * Nothing to do
158 */
159 public function testHandleSuccessfulLogin()
160 {
161 $this->assertTrue($this->loginManager->canLogin($this->server));
162
163 $this->loginManager->handleSuccessfulLogin($this->server);
164 $this->assertTrue($this->loginManager->canLogin($this->server));
165 }
166
167 /**
168 * Erase failure records after successfully logging in from this IP
169 */
170 public function testHandleSuccessfulLoginAfterFailure()
171 {
172 $this->loginManager->handleFailedLogin($this->server);
44acf706
V
173 $this->assertTrue($this->loginManager->canLogin($this->server));
174
175 $this->loginManager->handleSuccessfulLogin($this->server);
b49a04f7 176 $this->loginManager->handleFailedLogin($this->server);
44acf706 177 $this->assertTrue($this->loginManager->canLogin($this->server));
44acf706
V
178 }
179
180 /**
181 * The IP is not banned
182 */
183 public function testCanLoginIpNotBanned()
184 {
185 $this->assertTrue($this->loginManager->canLogin($this->server));
186 }
187
c689e108
V
188 /**
189 * Generate a token depending on the user credentials and client IP
190 */
191 public function testGenerateStaySignedInToken()
192 {
704637bf 193 $this->loginManager->generateStaySignedInToken($this->clientIpAddress);
c689e108
V
194
195 $this->assertEquals(
704637bf 196 sha1($this->passwordHash . $this->clientIpAddress . $this->salt),
c689e108
V
197 $this->loginManager->getStaySignedInToken()
198 );
199 }
704637bf 200
d9ba1cdd
A
201 /**
202 * Generate a token depending on the user credentials with session protected disabled
203 */
204 public function testGenerateStaySignedInTokenSessionProtectionDisabled()
205 {
206 $this->configManager->set('security.session_protection_disabled', true);
207 $this->loginManager->generateStaySignedInToken($this->clientIpAddress);
208
209 $this->assertEquals(
210 sha1($this->passwordHash . $this->salt),
211 $this->loginManager->getStaySignedInToken()
212 );
213 }
214
704637bf
V
215 /**
216 * Check user login - Shaarli has not yet been configured
217 */
218 public function testCheckLoginStateNotConfigured()
219 {
b38a1b02 220 $configManager = new FakeConfigManager([
704637bf
V
221 'resource.ban_file' => $this->banFile,
222 ]);
b38a1b02
A
223 $loginManager = new LoginManager(
224 $configManager,
225 $this->sessionManager,
226 $this->cookieManager,
227 $this->banManager,
228 $this->createMock(LoggerInterface::class)
229 );
c4ad3d4f 230 $loginManager->checkLoginState('');
704637bf
V
231
232 $this->assertFalse($loginManager->isLoggedIn());
233 }
234
235 /**
236 * Check user login - the client cookie does not match the server token
237 */
238 public function testCheckLoginStateStaySignedInWithInvalidToken()
239 {
8edd7f15
V
240 // simulate a previous login
241 $this->session = [
242 'ip' => $this->clientIpAddress,
243 'expires_on' => time() + 100,
244 ];
704637bf 245 $this->loginManager->generateStaySignedInToken($this->clientIpAddress);
c4ad3d4f 246 $this->cookie[CookieManager::STAY_SIGNED_IN] = 'nope';
704637bf 247
c4ad3d4f 248 $this->loginManager->checkLoginState($this->clientIpAddress);
704637bf 249
8edd7f15
V
250 $this->assertTrue($this->loginManager->isLoggedIn());
251 $this->assertTrue(empty($this->session['username']));
704637bf
V
252 }
253
254 /**
255 * Check user login - the client cookie matches the server token
256 */
257 public function testCheckLoginStateStaySignedInWithValidToken()
258 {
259 $this->loginManager->generateStaySignedInToken($this->clientIpAddress);
c4ad3d4f 260 $this->cookie[CookieManager::STAY_SIGNED_IN] = $this->loginManager->getStaySignedInToken();
704637bf 261
c4ad3d4f 262 $this->loginManager->checkLoginState($this->clientIpAddress);
704637bf
V
263
264 $this->assertTrue($this->loginManager->isLoggedIn());
8edd7f15
V
265 $this->assertEquals($this->login, $this->session['username']);
266 $this->assertEquals($this->clientIpAddress, $this->session['ip']);
704637bf
V
267 }
268
269 /**
270 * Check user login - the session has expired
271 */
272 public function testCheckLoginStateSessionExpired()
273 {
274 $this->loginManager->generateStaySignedInToken($this->clientIpAddress);
275 $this->session['expires_on'] = time() - 100;
276
c4ad3d4f 277 $this->loginManager->checkLoginState($this->clientIpAddress);
704637bf
V
278
279 $this->assertFalse($this->loginManager->isLoggedIn());
280 }
281
282 /**
283 * Check user login - the remote client IP has changed
284 */
285 public function testCheckLoginStateClientIpChanged()
286 {
287 $this->loginManager->generateStaySignedInToken($this->clientIpAddress);
288
c4ad3d4f 289 $this->loginManager->checkLoginState('10.7.157.98');
704637bf
V
290
291 $this->assertFalse($this->loginManager->isLoggedIn());
292 }
293
294 /**
295 * Check user credentials - wrong login supplied
296 */
297 public function testCheckCredentialsWrongLogin()
298 {
299 $this->assertFalse(
b38a1b02 300 $this->loginManager->checkCredentials('', 'b4dl0g1n', $this->password)
704637bf
V
301 );
302 }
303
304 /**
305 * Check user credentials - wrong password supplied
306 */
307 public function testCheckCredentialsWrongPassword()
308 {
309 $this->assertFalse(
b38a1b02 310 $this->loginManager->checkCredentials('', $this->login, 'b4dp455wd')
704637bf
V
311 );
312 }
313
314 /**
315 * Check user credentials - wrong login and password supplied
316 */
317 public function testCheckCredentialsWrongLoginAndPassword()
318 {
319 $this->assertFalse(
b38a1b02 320 $this->loginManager->checkCredentials('', 'b4dl0g1n', 'b4dp455wd')
704637bf
V
321 );
322 }
323
324 /**
325 * Check user credentials - correct login and password supplied
326 */
327 public function testCheckCredentialsGoodLoginAndPassword()
328 {
329 $this->assertTrue(
b38a1b02 330 $this->loginManager->checkCredentials('', $this->login, $this->password)
704637bf
V
331 );
332 }
cc2ded54
SN
333
334 /**
335 * Check user credentials through LDAP - server unreachable
336 */
337 public function testCheckCredentialsFromUnreachableLdap()
338 {
339 $this->configManager->set('ldap.host', 'dummy');
340 $this->assertFalse(
b38a1b02 341 $this->loginManager->checkCredentials('', $this->login, $this->password)
cc2ded54
SN
342 );
343 }
344
345 /**
346 * Check user credentials through LDAP - wrong login and password supplied
347 */
348 public function testCheckCredentialsFromLdapWrongLoginAndPassword()
349 {
46846fd4 350 $this->configManager->set('ldap.host', 'dummy');
cc2ded54
SN
351 $this->assertFalse(
352 $this->loginManager->checkCredentialsFromLdap($this->login, $this->password, function() { return null; }, function() { return false; })
353 );
354 }
355
356 /**
357 * Check user credentials through LDAP - correct login and password supplied
358 */
359 public function testCheckCredentialsFromLdapGoodLoginAndPassword()
360 {
361 $this->configManager->set('ldap.host', 'dummy');
362 $this->assertTrue(
363 $this->loginManager->checkCredentialsFromLdap($this->login, $this->password, function() { return null; }, function() { return true; })
364 );
365 }
44acf706 366}