aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorVirtualTam <virtualtam@flibidi.net>2018-05-06 17:06:36 +0200
committerVirtualTam <virtualtam@flibidi.net>2018-06-02 16:46:06 +0200
commitc689e108639a4f6aa9e15928422e14db7cbe30ca (patch)
tree4c118404cc33f2542c01787b638581ba02bbb8bb
parent51f0128cdba52099c40693379e72f094b42a6f80 (diff)
downloadShaarli-c689e108639a4f6aa9e15928422e14db7cbe30ca.tar.gz
Shaarli-c689e108639a4f6aa9e15928422e14db7cbe30ca.tar.zst
Shaarli-c689e108639a4f6aa9e15928422e14db7cbe30ca.zip
Refactor LoginManager stay-signed-in token management
Signed-off-by: VirtualTam <virtualtam@flibidi.net>
-rw-r--r--application/security/LoginManager.php37
-rw-r--r--application/security/SessionManager.php3
-rw-r--r--index.php12
-rw-r--r--tests/security/LoginManagerTest.php31
4 files changed, 69 insertions, 14 deletions
diff --git a/application/security/LoginManager.php b/application/security/LoginManager.php
index 27247f3f..41fa9a20 100644
--- a/application/security/LoginManager.php
+++ b/application/security/LoginManager.php
@@ -8,6 +8,9 @@ use Shaarli\Config\ConfigManager;
8 */ 8 */
9class LoginManager 9class LoginManager
10{ 10{
11 /** @var string Name of the cookie set after logging in **/
12 public static $STAY_SIGNED_IN_COOKIE = 'shaarli_staySignedIn';
13
11 /** @var array A reference to the $_GLOBALS array */ 14 /** @var array A reference to the $_GLOBALS array */
12 protected $globals = []; 15 protected $globals = [];
13 16
@@ -26,6 +29,9 @@ class LoginManager
26 /** @var bool Whether the Shaarli instance is open to public edition **/ 29 /** @var bool Whether the Shaarli instance is open to public edition **/
27 protected $openShaarli = false; 30 protected $openShaarli = false;
28 31
32 /** @var string User sign-in token depending on remote IP and credentials */
33 protected $staySignedInToken = '';
34
29 /** 35 /**
30 * Constructor 36 * Constructor
31 * 37 *
@@ -46,15 +52,38 @@ class LoginManager
46 } 52 }
47 53
48 /** 54 /**
55 * Generate a token depending on deployment salt, user password and client IP
56 *
57 * @param string $clientIpAddress The remote client IP address
58 */
59 public function generateStaySignedInToken($clientIpAddress)
60 {
61 $this->staySignedInToken = sha1(
62 $this->configManager->get('credentials.hash')
63 . $clientIpAddress
64 . $this->configManager->get('credentials.salt')
65 );
66 }
67
68 /**
69 * Return the user's client stay-signed-in token
70 *
71 * @return string User's client stay-signed-in token
72 */
73 public function getStaySignedInToken()
74 {
75 return $this->staySignedInToken;
76 }
77
78 /**
49 * Check user session state and validity (expiration) 79 * Check user session state and validity (expiration)
50 * 80 *
51 * @param array $cookie The $_COOKIE array 81 * @param array $cookie The $_COOKIE array
52 * @param string $clientIpId Client IP address identifier 82 * @param string $clientIpId Client IP address identifier
53 * @param string $token Session token
54 * 83 *
55 * @return bool true if the user session is valid, false otherwise 84 * @return bool true if the user session is valid, false otherwise
56 */ 85 */
57 public function checkLoginState($cookie, $clientIpId, $token) 86 public function checkLoginState($cookie, $clientIpId)
58 { 87 {
59 if (! $this->configManager->exists('credentials.login')) { 88 if (! $this->configManager->exists('credentials.login')) {
60 // Shaarli is not configured yet 89 // Shaarli is not configured yet
@@ -62,8 +91,8 @@ class LoginManager
62 return; 91 return;
63 } 92 }
64 93
65 if (isset($cookie[SessionManager::$LOGGED_IN_COOKIE]) 94 if (isset($cookie[self::$STAY_SIGNED_IN_COOKIE])
66 && $cookie[SessionManager::$LOGGED_IN_COOKIE] === $token 95 && $cookie[self::$STAY_SIGNED_IN_COOKIE] === $this->staySignedInToken
67 ) { 96 ) {
68 $this->sessionManager->storeLoginInfo($clientIpId); 97 $this->sessionManager->storeLoginInfo($clientIpId);
69 $this->isLoggedIn = true; 98 $this->isLoggedIn = true;
diff --git a/application/security/SessionManager.php b/application/security/SessionManager.php
index 0dcd7f90..58973130 100644
--- a/application/security/SessionManager.php
+++ b/application/security/SessionManager.php
@@ -14,9 +14,6 @@ class SessionManager
14 /** @var int Session expiration timeout, in seconds */ 14 /** @var int Session expiration timeout, in seconds */
15 public static $LONG_TIMEOUT = 31536000; // 1 year 15 public static $LONG_TIMEOUT = 31536000; // 1 year
16 16
17 /** @var string Name of the cookie set after logging in **/
18 public static $LOGGED_IN_COOKIE = 'shaarli_staySignedIn';
19
20 /** @var array Local reference to the global $_SESSION array */ 17 /** @var array Local reference to the global $_SESSION array */
21 protected $session = []; 18 protected $session = [];
22 19
diff --git a/index.php b/index.php
index 8e3bade0..c34434dd 100644
--- a/index.php
+++ b/index.php
@@ -123,6 +123,7 @@ if (isset($_COOKIE['shaarli']) && !SessionManager::checkId($_COOKIE['shaarli']))
123$conf = new ConfigManager(); 123$conf = new ConfigManager();
124$sessionManager = new SessionManager($_SESSION, $conf); 124$sessionManager = new SessionManager($_SESSION, $conf);
125$loginManager = new LoginManager($GLOBALS, $conf, $sessionManager); 125$loginManager = new LoginManager($GLOBALS, $conf, $sessionManager);
126$loginManager->generateStaySignedInToken($_SERVER['REMOTE_ADDR']);
126$clientIpId = client_ip_id($_SERVER); 127$clientIpId = client_ip_id($_SERVER);
127 128
128// LC_MESSAGES isn't defined without php-intl, in this case use LC_COLLATE locale instead. 129// LC_MESSAGES isn't defined without php-intl, in this case use LC_COLLATE locale instead.
@@ -176,10 +177,7 @@ if (! is_file($conf->getConfigFileExt())) {
176 install($conf, $sessionManager); 177 install($conf, $sessionManager);
177} 178}
178 179
179// a token depending of deployment salt, user password, and the current ip 180$loginManager->checkLoginState($_COOKIE, $clientIpId);
180define('STAY_SIGNED_IN_TOKEN', sha1($conf->get('credentials.hash') . $_SERVER['REMOTE_ADDR'] . $conf->get('credentials.salt')));
181
182$loginManager->checkLoginState($_COOKIE, $clientIpId, STAY_SIGNED_IN_TOKEN);
183 181
184/** 182/**
185 * Adapter function to ensure compatibility with third-party templates 183 * Adapter function to ensure compatibility with third-party templates
@@ -219,8 +217,8 @@ if (isset($_POST['login'])) {
219 $expirationTime = $sessionManager->extendSession(); 217 $expirationTime = $sessionManager->extendSession();
220 218
221 setcookie( 219 setcookie(
222 $sessionManager::$LOGGED_IN_COOKIE, 220 $loginManager::$STAY_SIGNED_IN_COOKIE,
223 STAY_SIGNED_IN_TOKEN, 221 $loginManager->getStaySignedInToken(),
224 $expirationTime, 222 $expirationTime,
225 WEB_PATH 223 WEB_PATH
226 ); 224 );
@@ -595,7 +593,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
595 { 593 {
596 invalidateCaches($conf->get('resource.page_cache')); 594 invalidateCaches($conf->get('resource.page_cache'));
597 $sessionManager->logout(); 595 $sessionManager->logout();
598 setcookie(SessionManager::$LOGGED_IN_COOKIE, 'false', 0, WEB_PATH); 596 setcookie(LoginManager::$STAY_SIGNED_IN_COOKIE, 'false', 0, WEB_PATH);
599 header('Location: ?'); 597 header('Location: ?');
600 exit; 598 exit;
601 } 599 }
diff --git a/tests/security/LoginManagerTest.php b/tests/security/LoginManagerTest.php
index b957abe3..633f1bb9 100644
--- a/tests/security/LoginManagerTest.php
+++ b/tests/security/LoginManagerTest.php
@@ -18,6 +18,18 @@ class LoginManagerTest extends TestCase
18 protected $server = []; 18 protected $server = [];
19 protected $trustedProxy = '10.1.1.100'; 19 protected $trustedProxy = '10.1.1.100';
20 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
21 /** 33 /**
22 * Prepare or reset test resources 34 * Prepare or reset test resources
23 */ 35 */
@@ -27,7 +39,12 @@ class LoginManagerTest extends TestCase
27 unlink($this->banFile); 39 unlink($this->banFile);
28 } 40 }
29 41
42 $this->passwordHash = sha1($this->password . $this->login . $this->salt);
43
30 $this->configManager = new \FakeConfigManager([ 44 $this->configManager = new \FakeConfigManager([
45 'credentials.login' => $this->login,
46 'credentials.hash' => $this->passwordHash,
47 'credentials.salt' => $this->salt,
31 'resource.ban_file' => $this->banFile, 48 'resource.ban_file' => $this->banFile,
32 'resource.log' => $this->logFile, 49 'resource.log' => $this->logFile,
33 'security.ban_after' => 4, 50 'security.ban_after' => 4,
@@ -196,4 +213,18 @@ class LoginManagerTest extends TestCase
196 $this->globals['IPBANS']['BANS'][$this->ipAddr] = time() - 3600; 213 $this->globals['IPBANS']['BANS'][$this->ipAddr] = time() - 3600;
197 $this->assertTrue($this->loginManager->canLogin($this->server)); 214 $this->assertTrue($this->loginManager->canLogin($this->server));
198 } 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 }
199} 230}