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