]> git.immae.eu Git - github/shaarli/Shaarli.git/blob - tests/security/LoginManagerTest.php
633f1bb9ea7e853c2e961f2c11ccc2f2ae81bb24
[github/shaarli/Shaarli.git] / tests / security / LoginManagerTest.php
1 <?php
2 namespace Shaarli\Security;
3
4 require_once 'tests/utils/FakeConfigManager.php';
5 use \PHPUnit\Framework\TestCase;
6
7 /**
8 * Test coverage for LoginManager
9 */
10 class LoginManagerTest extends TestCase
11 {
12 protected $configManager = null;
13 protected $loginManager = null;
14 protected $banFile = 'sandbox/ipbans.php';
15 protected $logFile = 'sandbox/shaarli.log';
16 protected $globals = [];
17 protected $ipAddr = '127.0.0.1';
18 protected $server = [];
19 protected $trustedProxy = '10.1.1.100';
20
21 /** @var string User login */
22 protected $login = 'johndoe';
23
24 /** @var string User password */
25 protected $password = 'IC4nHazL0g1n?';
26
27 /** @var string Hash of the salted user password */
28 protected $passwordHash = '';
29
30 /** @var string Salt used by hash functions */
31 protected $salt = '669e24fa9c5a59a613f98e8e38327384504a4af2';
32
33 /**
34 * Prepare or reset test resources
35 */
36 public function setUp()
37 {
38 if (file_exists($this->banFile)) {
39 unlink($this->banFile);
40 }
41
42 $this->passwordHash = sha1($this->password . $this->login . $this->salt);
43
44 $this->configManager = new \FakeConfigManager([
45 'credentials.login' => $this->login,
46 'credentials.hash' => $this->passwordHash,
47 'credentials.salt' => $this->salt,
48 'resource.ban_file' => $this->banFile,
49 'resource.log' => $this->logFile,
50 'security.ban_after' => 4,
51 'security.ban_duration' => 3600,
52 'security.trusted_proxies' => [$this->trustedProxy],
53 ]);
54
55 $this->globals = &$GLOBALS;
56 unset($this->globals['IPBANS']);
57
58 $this->loginManager = new LoginManager($this->globals, $this->configManager, null);
59 $this->server['REMOTE_ADDR'] = $this->ipAddr;
60 }
61
62 /**
63 * Wipe test resources
64 */
65 public function tearDown()
66 {
67 unset($this->globals['IPBANS']);
68 }
69
70 /**
71 * Instantiate a LoginManager and load ban records
72 */
73 public function testReadBanFile()
74 {
75 file_put_contents(
76 $this->banFile,
77 "<?php\n\$GLOBALS['IPBANS']=array('FAILURES' => array('127.0.0.1' => 99));\n?>"
78 );
79 new LoginManager($this->globals, $this->configManager, null);
80 $this->assertEquals(99, $this->globals['IPBANS']['FAILURES']['127.0.0.1']);
81 }
82
83 /**
84 * Record a failed login attempt
85 */
86 public function testHandleFailedLogin()
87 {
88 $this->loginManager->handleFailedLogin($this->server);
89 $this->assertEquals(1, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]);
90
91 $this->loginManager->handleFailedLogin($this->server);
92 $this->assertEquals(2, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]);
93 }
94
95 /**
96 * Record a failed login attempt - IP behind a trusted proxy
97 */
98 public function testHandleFailedLoginBehindTrustedProxy()
99 {
100 $server = [
101 'REMOTE_ADDR' => $this->trustedProxy,
102 'HTTP_X_FORWARDED_FOR' => $this->ipAddr,
103 ];
104 $this->loginManager->handleFailedLogin($server);
105 $this->assertEquals(1, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]);
106
107 $this->loginManager->handleFailedLogin($server);
108 $this->assertEquals(2, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]);
109 }
110
111 /**
112 * Record a failed login attempt - IP behind a trusted proxy but not forwarded
113 */
114 public function testHandleFailedLoginBehindTrustedProxyNoIp()
115 {
116 $server = [
117 'REMOTE_ADDR' => $this->trustedProxy,
118 ];
119 $this->loginManager->handleFailedLogin($server);
120 $this->assertFalse(isset($this->globals['IPBANS']['FAILURES'][$this->ipAddr]));
121
122 $this->loginManager->handleFailedLogin($server);
123 $this->assertFalse(isset($this->globals['IPBANS']['FAILURES'][$this->ipAddr]));
124 }
125
126 /**
127 * Record a failed login attempt and ban the IP after too many failures
128 */
129 public function testHandleFailedLoginBanIp()
130 {
131 $this->loginManager->handleFailedLogin($this->server);
132 $this->assertEquals(1, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]);
133 $this->assertTrue($this->loginManager->canLogin($this->server));
134
135 $this->loginManager->handleFailedLogin($this->server);
136 $this->assertEquals(2, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]);
137 $this->assertTrue($this->loginManager->canLogin($this->server));
138
139 $this->loginManager->handleFailedLogin($this->server);
140 $this->assertEquals(3, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]);
141 $this->assertTrue($this->loginManager->canLogin($this->server));
142
143 $this->loginManager->handleFailedLogin($this->server);
144 $this->assertEquals(4, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]);
145 $this->assertFalse($this->loginManager->canLogin($this->server));
146
147 // handleFailedLogin is not supposed to be called at this point:
148 // - no login form should be displayed once an IP has been banned
149 // - yet this could happen when using custom templates / scripts
150 $this->loginManager->handleFailedLogin($this->server);
151 $this->assertEquals(5, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]);
152 $this->assertFalse($this->loginManager->canLogin($this->server));
153 }
154
155 /**
156 * Nothing to do
157 */
158 public function testHandleSuccessfulLogin()
159 {
160 $this->assertTrue($this->loginManager->canLogin($this->server));
161
162 $this->loginManager->handleSuccessfulLogin($this->server);
163 $this->assertTrue($this->loginManager->canLogin($this->server));
164 }
165
166 /**
167 * Erase failure records after successfully logging in from this IP
168 */
169 public function testHandleSuccessfulLoginAfterFailure()
170 {
171 $this->loginManager->handleFailedLogin($this->server);
172 $this->loginManager->handleFailedLogin($this->server);
173 $this->assertEquals(2, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]);
174 $this->assertTrue($this->loginManager->canLogin($this->server));
175
176 $this->loginManager->handleSuccessfulLogin($this->server);
177 $this->assertTrue($this->loginManager->canLogin($this->server));
178 $this->assertFalse(isset($this->globals['IPBANS']['FAILURES'][$this->ipAddr]));
179 $this->assertFalse(isset($this->globals['IPBANS']['BANS'][$this->ipAddr]));
180 }
181
182 /**
183 * The IP is not banned
184 */
185 public function testCanLoginIpNotBanned()
186 {
187 $this->assertTrue($this->loginManager->canLogin($this->server));
188 }
189
190 /**
191 * The IP is banned
192 */
193 public function testCanLoginIpBanned()
194 {
195 // ban the IP for an hour
196 $this->globals['IPBANS']['FAILURES'][$this->ipAddr] = 10;
197 $this->globals['IPBANS']['BANS'][$this->ipAddr] = time() + 3600;
198
199 $this->assertFalse($this->loginManager->canLogin($this->server));
200 }
201
202 /**
203 * The IP is banned, and the ban duration is over
204 */
205 public function testCanLoginIpBanExpired()
206 {
207 // ban the IP for an hour
208 $this->globals['IPBANS']['FAILURES'][$this->ipAddr] = 10;
209 $this->globals['IPBANS']['BANS'][$this->ipAddr] = time() + 3600;
210 $this->assertFalse($this->loginManager->canLogin($this->server));
211
212 // lift the ban
213 $this->globals['IPBANS']['BANS'][$this->ipAddr] = time() - 3600;
214 $this->assertTrue($this->loginManager->canLogin($this->server));
215 }
216
217 /**
218 * Generate a token depending on the user credentials and client IP
219 */
220 public function testGenerateStaySignedInToken()
221 {
222 $ipAddress = '10.1.47.179';
223 $this->loginManager->generateStaySignedInToken($ipAddress);
224
225 $this->assertEquals(
226 sha1($this->passwordHash . $ipAddress . $this->salt),
227 $this->loginManager->getStaySignedInToken()
228 );
229 }
230 }