aboutsummaryrefslogtreecommitdiffhomepage
path: root/tests/security
diff options
context:
space:
mode:
Diffstat (limited to 'tests/security')
-rw-r--r--tests/security/LoginManagerTest.php199
-rw-r--r--tests/security/SessionManagerTest.php149
2 files changed, 348 insertions, 0 deletions
diff --git a/tests/security/LoginManagerTest.php b/tests/security/LoginManagerTest.php
new file mode 100644
index 00000000..b957abe3
--- /dev/null
+++ b/tests/security/LoginManagerTest.php
@@ -0,0 +1,199 @@
1<?php
2namespace Shaarli\Security;
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, null);
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, null);
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/security/SessionManagerTest.php b/tests/security/SessionManagerTest.php
new file mode 100644
index 00000000..e4e1cfbc
--- /dev/null
+++ b/tests/security/SessionManagerTest.php
@@ -0,0 +1,149 @@
1<?php
2require_once 'tests/utils/FakeConfigManager.php';
3
4// Initialize reference data _before_ PHPUnit starts a session
5require_once 'tests/utils/ReferenceSessionIdHashes.php';
6ReferenceSessionIdHashes::genAllHashes();
7
8use \Shaarli\Security\SessionManager;
9use \PHPUnit\Framework\TestCase;
10
11
12/**
13 * Test coverage for SessionManager
14 */
15class SessionManagerTest extends TestCase
16{
17 // Session ID hashes
18 protected static $sidHashes = null;
19
20 // Fake ConfigManager
21 protected static $conf = null;
22
23 /**
24 * Assign reference data
25 */
26 public static function setUpBeforeClass()
27 {
28 self::$sidHashes = ReferenceSessionIdHashes::getHashes();
29 self::$conf = new FakeConfigManager();
30 }
31
32 /**
33 * Generate a session token
34 */
35 public function testGenerateToken()
36 {
37 $session = [];
38 $sessionManager = new SessionManager($session, self::$conf);
39
40 $token = $sessionManager->generateToken();
41
42 $this->assertEquals(1, $session['tokens'][$token]);
43 $this->assertEquals(40, strlen($token));
44 }
45
46 /**
47 * Check a session token
48 */
49 public function testCheckToken()
50 {
51 $token = '4dccc3a45ad9d03e5542b90c37d8db6d10f2b38b';
52 $session = [
53 'tokens' => [
54 $token => 1,
55 ],
56 ];
57 $sessionManager = new SessionManager($session, self::$conf);
58
59 // check and destroy the token
60 $this->assertTrue($sessionManager->checkToken($token));
61 $this->assertFalse(isset($session['tokens'][$token]));
62
63 // ensure the token has been destroyed
64 $this->assertFalse($sessionManager->checkToken($token));
65 }
66
67 /**
68 * Generate and check a session token
69 */
70 public function testGenerateAndCheckToken()
71 {
72 $session = [];
73 $sessionManager = new SessionManager($session, self::$conf);
74
75 $token = $sessionManager->generateToken();
76
77 // ensure a token has been generated
78 $this->assertEquals(1, $session['tokens'][$token]);
79 $this->assertEquals(40, strlen($token));
80
81 // check and destroy the token
82 $this->assertTrue($sessionManager->checkToken($token));
83 $this->assertFalse(isset($session['tokens'][$token]));
84
85 // ensure the token has been destroyed
86 $this->assertFalse($sessionManager->checkToken($token));
87 }
88
89 /**
90 * Check an invalid session token
91 */
92 public function testCheckInvalidToken()
93 {
94 $session = [];
95 $sessionManager = new SessionManager($session, self::$conf);
96
97 $this->assertFalse($sessionManager->checkToken('4dccc3a45ad9d03e5542b90c37d8db6d10f2b38b'));
98 }
99
100 /**
101 * Test SessionManager::checkId with a valid ID - TEST ALL THE HASHES!
102 *
103 * This tests extensively covers all hash algorithms / bit representations
104 */
105 public function testIsAnyHashSessionIdValid()
106 {
107 foreach (self::$sidHashes as $algo => $bpcs) {
108 foreach ($bpcs as $bpc => $hash) {
109 $this->assertTrue(SessionManager::checkId($hash));
110 }
111 }
112 }
113
114 /**
115 * Test checkId with a valid ID - SHA-1 hashes
116 */
117 public function testIsSha1SessionIdValid()
118 {
119 $this->assertTrue(SessionManager::checkId(sha1('shaarli')));
120 }
121
122 /**
123 * Test checkId with a valid ID - SHA-256 hashes
124 */
125 public function testIsSha256SessionIdValid()
126 {
127 $this->assertTrue(SessionManager::checkId(hash('sha256', 'shaarli')));
128 }
129
130 /**
131 * Test checkId with a valid ID - SHA-512 hashes
132 */
133 public function testIsSha512SessionIdValid()
134 {
135 $this->assertTrue(SessionManager::checkId(hash('sha512', 'shaarli')));
136 }
137
138 /**
139 * Test checkId with invalid IDs.
140 */
141 public function testIsSessionIdInvalid()
142 {
143 $this->assertFalse(SessionManager::checkId(''));
144 $this->assertFalse(SessionManager::checkId([]));
145 $this->assertFalse(
146 SessionManager::checkId('c0ZqcWF3VFE2NmJBdm1HMVQ0ZHJ3UmZPbTFsNGhkNHI=')
147 );
148 }
149}