]> git.immae.eu Git - github/shaarli/Shaarli.git/blob - tests/security/BanManagerTest.php
Move utils classes to Shaarli\Helper namespace and folder
[github/shaarli/Shaarli.git] / tests / security / BanManagerTest.php
1 <?php
2
3
4 namespace Shaarli\Security;
5
6 use Psr\Log\LoggerInterface;
7 use Shaarli\Helper\FileUtils;
8 use Shaarli\TestCase;
9
10 /**
11 * Test coverage for BanManager
12 */
13 class BanManagerTest extends TestCase
14 {
15 /** @var BanManager Ban Manager instance */
16 protected $banManager;
17
18 /** @var string Banned IP filename */
19 protected $banFile = 'sandbox/ipbans.php';
20
21 /** @var string Log filename */
22 protected $logFile = 'sandbox/shaarli.log';
23
24 /** @var string Local client IP address */
25 protected $ipAddr = '127.0.0.1';
26
27 /** @var string Trusted proxy IP address */
28 protected $trustedProxy = '10.1.1.100';
29
30 /** @var array Simulates the $_SERVER array */
31 protected $server = [];
32
33 /**
34 * Prepare or reset test resources
35 */
36 protected function setUp(): void
37 {
38 if (file_exists($this->banFile)) {
39 unlink($this->banFile);
40 }
41
42 $this->banManager = $this->getNewBanManagerInstance();
43 $this->server['REMOTE_ADDR'] = $this->ipAddr;
44 }
45
46 /**
47 * Test constructor with initial file.
48 */
49 public function testInstantiateFromFile()
50 {
51 $time = time() + 10;
52 FileUtils::writeFlatDB(
53 $this->banFile,
54 [
55 'failures' => [
56 $this->ipAddr => 2,
57 $ip = '1.2.3.4' => 1,
58 ],
59 'bans' => [
60 $ip2 = '8.8.8.8' => $time,
61 $ip3 = '1.1.1.1' => $time + 1,
62 ],
63 ]
64 );
65 $this->banManager = $this->getNewBanManagerInstance();
66
67 $this->assertCount(2, $this->banManager->getFailures());
68 $this->assertEquals(2, $this->banManager->getFailures()[$this->ipAddr]);
69 $this->assertEquals(1, $this->banManager->getFailures()[$ip]);
70 $this->assertCount(2, $this->banManager->getBans());
71 $this->assertEquals($time, $this->banManager->getBans()[$ip2]);
72 $this->assertEquals($time + 1, $this->banManager->getBans()[$ip3]);
73 }
74
75 /**
76 * Test constructor with initial file with invalid values
77 */
78 public function testInstantiateFromCrappyFile()
79 {
80 FileUtils::writeFlatDB($this->banFile, 'plop');
81 $this->banManager = $this->getNewBanManagerInstance();
82
83 $this->assertEquals([], $this->banManager->getFailures());
84 $this->assertEquals([], $this->banManager->getBans());
85 }
86
87 /**
88 * Test failed attempt with a direct IP.
89 */
90 public function testHandleFailedAttempt()
91 {
92 $this->assertCount(0, $this->banManager->getFailures());
93
94 $this->banManager->handleFailedAttempt($this->server);
95 $this->assertCount(1, $this->banManager->getFailures());
96 $this->assertEquals(1, $this->banManager->getFailures()[$this->ipAddr]);
97
98 $this->banManager->handleFailedAttempt($this->server);
99 $this->assertCount(1, $this->banManager->getFailures());
100 $this->assertEquals(2, $this->banManager->getFailures()[$this->ipAddr]);
101 }
102
103 /**
104 * Test failed attempt behind a trusted proxy IP (with proper IP forwarding).
105 */
106 public function testHandleFailedAttemptBehingProxy()
107 {
108 $server = [
109 'REMOTE_ADDR' => $this->trustedProxy,
110 'HTTP_X_FORWARDED_FOR' => $this->ipAddr,
111 ];
112 $this->assertCount(0, $this->banManager->getFailures());
113
114 $this->banManager->handleFailedAttempt($server);
115 $this->assertCount(1, $this->banManager->getFailures());
116 $this->assertEquals(1, $this->banManager->getFailures()[$this->ipAddr]);
117
118 $this->banManager->handleFailedAttempt($server);
119 $this->assertCount(1, $this->banManager->getFailures());
120 $this->assertEquals(2, $this->banManager->getFailures()[$this->ipAddr]);
121 }
122
123 /**
124 * Test failed attempt behind a trusted proxy IP but without IP forwarding.
125 */
126 public function testHandleFailedAttemptBehindNotConfiguredProxy()
127 {
128 $server = [
129 'REMOTE_ADDR' => $this->trustedProxy,
130 ];
131 $this->assertCount(0, $this->banManager->getFailures());
132
133 $this->banManager->handleFailedAttempt($server);
134 $this->assertCount(0, $this->banManager->getFailures());
135
136 $this->banManager->handleFailedAttempt($server);
137 $this->assertCount(0, $this->banManager->getFailures());
138 }
139
140 /**
141 * Test failed attempts with multiple direct IP.
142 */
143 public function testHandleFailedAttemptMultipleIp()
144 {
145 $this->assertCount(0, $this->banManager->getFailures());
146 $this->banManager->handleFailedAttempt($this->server);
147 $this->server['REMOTE_ADDR'] = '1.2.3.4';
148 $this->banManager->handleFailedAttempt($this->server);
149 $this->banManager->handleFailedAttempt($this->server);
150 $this->assertCount(2, $this->banManager->getFailures());
151 $this->assertEquals(1, $this->banManager->getFailures()[$this->ipAddr]);
152 $this->assertEquals(2, $this->banManager->getFailures()[$this->server['REMOTE_ADDR']]);
153 }
154
155 /**
156 * Test clear failure for provided IP without any additional data.
157 */
158 public function testClearFailuresEmpty()
159 {
160 $this->assertCount(0, $this->banManager->getFailures());
161 $this->banManager->clearFailures($this->server);
162 $this->assertCount(0, $this->banManager->getFailures());
163 }
164
165 /**
166 * Test clear failure for provided IP with failed attempts.
167 */
168 public function testClearFailuresFromFile()
169 {
170 FileUtils::writeFlatDB(
171 $this->banFile,
172 [
173 'failures' => [
174 $this->ipAddr => 2,
175 $ip = '1.2.3.4' => 1,
176 ]
177 ]
178 );
179 $this->banManager = $this->getNewBanManagerInstance();
180
181 $this->assertCount(2, $this->banManager->getFailures());
182 $this->banManager->clearFailures($this->server);
183 $this->assertCount(1, $this->banManager->getFailures());
184 $this->assertEquals(1, $this->banManager->getFailures()[$ip]);
185 }
186
187 /**
188 * Test clear failure for provided IP with failed attempts, behind a reverse proxy.
189 */
190 public function testClearFailuresFromFileBehindProxy()
191 {
192 $server = [
193 'REMOTE_ADDR' => $this->trustedProxy,
194 'HTTP_X_FORWARDED_FOR' => $this->ipAddr,
195 ];
196
197 FileUtils::writeFlatDB(
198 $this->banFile,
199 [
200 'failures' => [
201 $this->ipAddr => 2,
202 $ip = '1.2.3.4' => 1,
203 ]
204 ]
205 );
206 $this->banManager = $this->getNewBanManagerInstance();
207
208 $this->assertCount(2, $this->banManager->getFailures());
209 $this->banManager->clearFailures($server);
210 $this->assertCount(1, $this->banManager->getFailures());
211 $this->assertEquals(1, $this->banManager->getFailures()[$ip]);
212 }
213
214 /**
215 * Test clear failure for provided IP with failed attempts,
216 * behind a reverse proxy without forwarding.
217 */
218 public function testClearFailuresFromFileBehindNotConfiguredProxy()
219 {
220 $server = [
221 'REMOTE_ADDR' => $this->trustedProxy,
222 ];
223
224 FileUtils::writeFlatDB(
225 $this->banFile,
226 [
227 'failures' => [
228 $this->ipAddr => 2,
229 $ip = '1.2.3.4' => 1,
230 ]
231 ]
232 );
233 $this->banManager = $this->getNewBanManagerInstance();
234
235 $this->assertCount(2, $this->banManager->getFailures());
236 $this->banManager->clearFailures($server);
237 $this->assertCount(2, $this->banManager->getFailures());
238 }
239
240 /**
241 * Test isBanned without any data
242 */
243 public function testIsBannedEmpty()
244 {
245 $this->assertFalse($this->banManager->isBanned($this->server));
246 }
247
248 /**
249 * Test isBanned with banned IP from file data
250 */
251 public function testBannedFromFile()
252 {
253 FileUtils::writeFlatDB(
254 $this->banFile,
255 [
256 'bans' => [
257 $this->ipAddr => time() + 10,
258 ]
259 ]
260 );
261 $this->banManager = $this->getNewBanManagerInstance();
262
263 $this->assertCount(1, $this->banManager->getBans());
264 $this->assertTrue($this->banManager->isBanned($this->server));
265 }
266
267 /**
268 * Test isBanned with banned IP from file data behind a reverse proxy
269 */
270 public function testBannedFromFileBehindProxy()
271 {
272 $server = [
273 'REMOTE_ADDR' => $this->trustedProxy,
274 'HTTP_X_FORWARDED_FOR' => $this->ipAddr,
275 ];
276 FileUtils::writeFlatDB(
277 $this->banFile,
278 [
279 'bans' => [
280 $this->ipAddr => time() + 10,
281 ]
282 ]
283 );
284 $this->banManager = $this->getNewBanManagerInstance();
285
286 $this->assertCount(1, $this->banManager->getBans());
287 $this->assertTrue($this->banManager->isBanned($server));
288 }
289
290 /**
291 * Test isBanned with banned IP from file data behind a reverse proxy,
292 * without IP forwarding
293 */
294 public function testBannedFromFileBehindNotConfiguredProxy()
295 {
296 $server = [
297 'REMOTE_ADDR' => $this->trustedProxy,
298 ];
299 FileUtils::writeFlatDB(
300 $this->banFile,
301 [
302 'bans' => [
303 $this->ipAddr => time() + 10,
304 ]
305 ]
306 );
307 $this->banManager = $this->getNewBanManagerInstance();
308
309 $this->assertCount(1, $this->banManager->getBans());
310 $this->assertFalse($this->banManager->isBanned($server));
311 }
312
313 /**
314 * Test isBanned with an expired ban
315 */
316 public function testLiftBan()
317 {
318 FileUtils::writeFlatDB(
319 $this->banFile,
320 [
321 'bans' => [
322 $this->ipAddr => time() - 10,
323 ]
324 ]
325 );
326 $this->banManager = $this->getNewBanManagerInstance();
327
328 $this->assertCount(1, $this->banManager->getBans());
329 $this->assertFalse($this->banManager->isBanned($this->server));
330 }
331
332 /**
333 * Test isBanned with an expired ban behind a reverse proxy
334 */
335 public function testLiftBanBehindProxy()
336 {
337 $server = [
338 'REMOTE_ADDR' => $this->trustedProxy,
339 'HTTP_X_FORWARDED_FOR' => $this->ipAddr,
340 ];
341
342 FileUtils::writeFlatDB(
343 $this->banFile,
344 [
345 'bans' => [
346 $this->ipAddr => time() - 10,
347 ]
348 ]
349 );
350 $this->banManager = $this->getNewBanManagerInstance();
351
352 $this->assertCount(1, $this->banManager->getBans());
353 $this->assertFalse($this->banManager->isBanned($server));
354 }
355
356 /**
357 * Test isBanned with an expired ban behind a reverse proxy
358 */
359 public function testLiftBanBehindNotConfiguredProxy()
360 {
361 $server = [
362 'REMOTE_ADDR' => $this->trustedProxy,
363 ];
364
365 FileUtils::writeFlatDB(
366 $this->banFile,
367 [
368 'bans' => [
369 $this->ipAddr => time() - 10,
370 ]
371 ]
372 );
373 $this->banManager = $this->getNewBanManagerInstance();
374
375 $this->assertCount(1, $this->banManager->getBans());
376 $this->assertFalse($this->banManager->isBanned($server));
377 }
378
379 /**
380 * Build a new instance of BanManager, which will reread the ban file.
381 *
382 * @return BanManager instance
383 */
384 protected function getNewBanManagerInstance()
385 {
386 return new BanManager(
387 [$this->trustedProxy],
388 3,
389 1800,
390 $this->banFile,
391 $this->createMock(LoggerInterface::class)
392 );
393 }
394 }