aboutsummaryrefslogtreecommitdiffhomepage
path: root/tests
diff options
context:
space:
mode:
authorVirtualTam <virtualtam@flibidi.net>2017-10-25 23:03:31 +0200
committerVirtualTam <virtualtam@flibidi.net>2018-02-05 18:12:09 +0100
commit44acf706812bc77812e6648c2cc28af36e172a14 (patch)
tree2c211d422b9d6c27ab341644531913361b6f1024 /tests
parenta381c373b30ed04001ea31ff5c38e077edacaf18 (diff)
downloadShaarli-44acf706812bc77812e6648c2cc28af36e172a14.tar.gz
Shaarli-44acf706812bc77812e6648c2cc28af36e172a14.tar.zst
Shaarli-44acf706812bc77812e6648c2cc28af36e172a14.zip
Refactor login / ban authentication steps
Relates to https://github.com/shaarli/Shaarli/issues/324 Added: - Add the `LoginManager` class to manage logins and bans Changed: - Refactor IP ban management - Simplify logic - Avoid using globals, inject dependencies Fixed: - Use `ban_duration` instead of `ban_after` when setting a new ban Signed-off-by: VirtualTam <virtualtam@flibidi.net>
Diffstat (limited to 'tests')
-rw-r--r--tests/LoginManagerTest.php199
-rw-r--r--tests/utils/FakeConfigManager.php35
2 files changed, 233 insertions, 1 deletions
diff --git a/tests/LoginManagerTest.php b/tests/LoginManagerTest.php
new file mode 100644
index 00000000..4159038e
--- /dev/null
+++ b/tests/LoginManagerTest.php
@@ -0,0 +1,199 @@
1<?php
2namespace Shaarli;
3
4require_once 'tests/utils/FakeConfigManager.php';
5use \PHPUnit\Framework\TestCase;
6
7/**
8 * Test coverage for LoginManager
9 */
10class 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 /**
22 * Prepare or reset test resources
23 */
24 public function setUp()
25 {
26 if (file_exists($this->banFile)) {
27 unlink($this->banFile);
28 }
29
30 $this->configManager = new \FakeConfigManager([
31 'resource.ban_file' => $this->banFile,
32 'resource.log' => $this->logFile,
33 'security.ban_after' => 4,
34 'security.ban_duration' => 3600,
35 'security.trusted_proxies' => [$this->trustedProxy],
36 ]);
37
38 $this->globals = &$GLOBALS;
39 unset($this->globals['IPBANS']);
40
41 $this->loginManager = new LoginManager($this->globals, $this->configManager);
42 $this->server['REMOTE_ADDR'] = $this->ipAddr;
43 }
44
45 /**
46 * Wipe test resources
47 */
48 public function tearDown()
49 {
50 unset($this->globals['IPBANS']);
51 }
52
53 /**
54 * Instantiate a LoginManager and load ban records
55 */
56 public function testReadBanFile()
57 {
58 file_put_contents(
59 $this->banFile,
60 "<?php\n\$GLOBALS['IPBANS']=array('FAILURES' => array('127.0.0.1' => 99));\n?>"
61 );
62 new LoginManager($this->globals, $this->configManager);
63 $this->assertEquals(99, $this->globals['IPBANS']['FAILURES']['127.0.0.1']);
64 }
65
66 /**
67 * Record a failed login attempt
68 */
69 public function testHandleFailedLogin()
70 {
71 $this->loginManager->handleFailedLogin($this->server);
72 $this->assertEquals(1, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]);
73
74 $this->loginManager->handleFailedLogin($this->server);
75 $this->assertEquals(2, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]);
76 }
77
78 /**
79 * Record a failed login attempt - IP behind a trusted proxy
80 */
81 public function testHandleFailedLoginBehindTrustedProxy()
82 {
83 $server = [
84 'REMOTE_ADDR' => $this->trustedProxy,
85 'HTTP_X_FORWARDED_FOR' => $this->ipAddr,
86 ];
87 $this->loginManager->handleFailedLogin($server);
88 $this->assertEquals(1, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]);
89
90 $this->loginManager->handleFailedLogin($server);
91 $this->assertEquals(2, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]);
92 }
93
94 /**
95 * Record a failed login attempt - IP behind a trusted proxy but not forwarded
96 */
97 public function testHandleFailedLoginBehindTrustedProxyNoIp()
98 {
99 $server = [
100 'REMOTE_ADDR' => $this->trustedProxy,
101 ];
102 $this->loginManager->handleFailedLogin($server);
103 $this->assertFalse(isset($this->globals['IPBANS']['FAILURES'][$this->ipAddr]));
104
105 $this->loginManager->handleFailedLogin($server);
106 $this->assertFalse(isset($this->globals['IPBANS']['FAILURES'][$this->ipAddr]));
107 }
108
109 /**
110 * Record a failed login attempt and ban the IP after too many failures
111 */
112 public function testHandleFailedLoginBanIp()
113 {
114 $this->loginManager->handleFailedLogin($this->server);
115 $this->assertEquals(1, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]);
116 $this->assertTrue($this->loginManager->canLogin($this->server));
117
118 $this->loginManager->handleFailedLogin($this->server);
119 $this->assertEquals(2, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]);
120 $this->assertTrue($this->loginManager->canLogin($this->server));
121
122 $this->loginManager->handleFailedLogin($this->server);
123 $this->assertEquals(3, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]);
124 $this->assertTrue($this->loginManager->canLogin($this->server));
125
126 $this->loginManager->handleFailedLogin($this->server);
127 $this->assertEquals(4, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]);
128 $this->assertFalse($this->loginManager->canLogin($this->server));
129
130 // handleFailedLogin is not supposed to be called at this point:
131 // - no login form should be displayed once an IP has been banned
132 // - yet this could happen when using custom templates / scripts
133 $this->loginManager->handleFailedLogin($this->server);
134 $this->assertEquals(5, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]);
135 $this->assertFalse($this->loginManager->canLogin($this->server));
136 }
137
138 /**
139 * Nothing to do
140 */
141 public function testHandleSuccessfulLogin()
142 {
143 $this->assertTrue($this->loginManager->canLogin($this->server));
144
145 $this->loginManager->handleSuccessfulLogin($this->server);
146 $this->assertTrue($this->loginManager->canLogin($this->server));
147 }
148
149 /**
150 * Erase failure records after successfully logging in from this IP
151 */
152 public function testHandleSuccessfulLoginAfterFailure()
153 {
154 $this->loginManager->handleFailedLogin($this->server);
155 $this->loginManager->handleFailedLogin($this->server);
156 $this->assertEquals(2, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]);
157 $this->assertTrue($this->loginManager->canLogin($this->server));
158
159 $this->loginManager->handleSuccessfulLogin($this->server);
160 $this->assertTrue($this->loginManager->canLogin($this->server));
161 $this->assertFalse(isset($this->globals['IPBANS']['FAILURES'][$this->ipAddr]));
162 $this->assertFalse(isset($this->globals['IPBANS']['BANS'][$this->ipAddr]));
163 }
164
165 /**
166 * The IP is not banned
167 */
168 public function testCanLoginIpNotBanned()
169 {
170 $this->assertTrue($this->loginManager->canLogin($this->server));
171 }
172
173 /**
174 * The IP is banned
175 */
176 public function testCanLoginIpBanned()
177 {
178 // ban the IP for an hour
179 $this->globals['IPBANS']['FAILURES'][$this->ipAddr] = 10;
180 $this->globals['IPBANS']['BANS'][$this->ipAddr] = time() + 3600;
181
182 $this->assertFalse($this->loginManager->canLogin($this->server));
183 }
184
185 /**
186 * The IP is banned, and the ban duration is over
187 */
188 public function testCanLoginIpBanExpired()
189 {
190 // ban the IP for an hour
191 $this->globals['IPBANS']['FAILURES'][$this->ipAddr] = 10;
192 $this->globals['IPBANS']['BANS'][$this->ipAddr] = time() + 3600;
193 $this->assertFalse($this->loginManager->canLogin($this->server));
194
195 // lift the ban
196 $this->globals['IPBANS']['BANS'][$this->ipAddr] = time() - 3600;
197 $this->assertTrue($this->loginManager->canLogin($this->server));
198 }
199}
diff --git a/tests/utils/FakeConfigManager.php b/tests/utils/FakeConfigManager.php
index f29760cb..85434de7 100644
--- a/tests/utils/FakeConfigManager.php
+++ b/tests/utils/FakeConfigManager.php
@@ -5,8 +5,41 @@
5 */ 5 */
6class FakeConfigManager 6class FakeConfigManager
7{ 7{
8 public static function get($key) 8 protected $values = [];
9
10 /**
11 * Initialize with test values
12 *
13 * @param array $values Initial values
14 */
15 public function __construct($values = [])
16 {
17 $this->values = $values;
18 }
19
20 /**
21 * Set a given value
22 *
23 * @param string $key Key of the value to set
24 * @param mixed $value Value to set
25 */
26 public function set($key, $value)
27 {
28 $this->values[$key] = $value;
29 }
30
31 /**
32 * Get a given configuration value
33 *
34 * @param string $key Index of the value to retrieve
35 *
36 * @return mixed The value if set, else the name of the key
37 */
38 public function get($key)
9 { 39 {
40 if (isset($this->values[$key])) {
41 return $this->values[$key];
42 }
10 return $key; 43 return $key;
11 } 44 }
12} 45}