aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorArthurHoaro <arthur@hoa.ro>2020-06-25 16:53:18 +0200
committerGitHub <noreply@github.com>2020-06-25 16:53:18 +0200
commit78c2f122e067f8bab62deb7ef758708721f4a9ba (patch)
tree521baacd42ca547c13bf22549cd0a9025af0c371
parente1231265bc46b070a4edd573c417aa030fe83426 (diff)
parent8694e8411b19d499ff58d8168fba448c63a5e443 (diff)
downloadShaarli-78c2f122e067f8bab62deb7ef758708721f4a9ba.tar.gz
Shaarli-78c2f122e067f8bab62deb7ef758708721f4a9ba.tar.zst
Shaarli-78c2f122e067f8bab62deb7ef758708721f4a9ba.zip
Merge pull request #1428 from pipoprods/feat/ldap-auth
-rw-r--r--application/security/LoginManager.php79
-rw-r--r--doc/md/Shaarli-configuration.md9
-rw-r--r--tests/security/LoginManagerTest.php34
3 files changed, 113 insertions, 9 deletions
diff --git a/application/security/LoginManager.php b/application/security/LoginManager.php
index 0b0ce0b1..39ec9b2e 100644
--- a/application/security/LoginManager.php
+++ b/application/security/LoginManager.php
@@ -1,6 +1,7 @@
1<?php 1<?php
2namespace Shaarli\Security; 2namespace Shaarli\Security;
3 3
4use Exception;
4use Shaarli\Config\ConfigManager; 5use Shaarli\Config\ConfigManager;
5 6
6/** 7/**
@@ -139,26 +140,86 @@ class LoginManager
139 */ 140 */
140 public function checkCredentials($remoteIp, $clientIpId, $login, $password) 141 public function checkCredentials($remoteIp, $clientIpId, $login, $password)
141 { 142 {
142 $hash = sha1($password . $login . $this->configManager->get('credentials.salt')); 143 // Check login matches config
144 if ($login !== $this->configManager->get('credentials.login')) {
145 return false;
146 }
143 147
144 if ($login != $this->configManager->get('credentials.login') 148 // Check credentials
145 || $hash != $this->configManager->get('credentials.hash') 149 try {
146 ) { 150 $useLdapLogin = !empty($this->configManager->get('ldap.host'));
151 if ((false === $useLdapLogin && $this->checkCredentialsFromLocalConfig($login, $password))
152 || (true === $useLdapLogin && $this->checkCredentialsFromLdap($login, $password))
153 ) {
154 $this->sessionManager->storeLoginInfo($clientIpId);
155 logm(
156 $this->configManager->get('resource.log'),
157 $remoteIp,
158 'Login successful'
159 );
160 return true;
161 }
162 }
163 catch(Exception $exception) {
147 logm( 164 logm(
148 $this->configManager->get('resource.log'), 165 $this->configManager->get('resource.log'),
149 $remoteIp, 166 $remoteIp,
150 'Login failed for user ' . $login 167 'Exception while checking credentials: ' . $exception
151 ); 168 );
152 return false;
153 } 169 }
154 170
155 $this->sessionManager->storeLoginInfo($clientIpId);
156 logm( 171 logm(
157 $this->configManager->get('resource.log'), 172 $this->configManager->get('resource.log'),
158 $remoteIp, 173 $remoteIp,
159 'Login successful' 174 'Login failed for user ' . $login
175 );
176 return false;
177 }
178
179
180 /**
181 * Check user credentials from local config
182 *
183 * @param string $login Username
184 * @param string $password Password
185 *
186 * @return bool true if the provided credentials are valid, false otherwise
187 */
188 public function checkCredentialsFromLocalConfig($login, $password) {
189 $hash = sha1($password . $login . $this->configManager->get('credentials.salt'));
190
191 return $login == $this->configManager->get('credentials.login')
192 && $hash == $this->configManager->get('credentials.hash');
193 }
194
195 /**
196 * Check user credentials are valid through LDAP bind
197 *
198 * @param string $remoteIp Remote client IP address
199 * @param string $clientIpId Client IP address identifier
200 * @param string $login Username
201 * @param string $password Password
202 *
203 * @return bool true if the provided credentials are valid, false otherwise
204 */
205 public function checkCredentialsFromLdap($login, $password, $connect = null, $bind = null)
206 {
207 $connect = $connect ?? function($host) {
208 $resource = ldap_connect($host);
209
210 ldap_set_option($resource, LDAP_OPT_PROTOCOL_VERSION, 3);
211
212 return $resource;
213 };
214 $bind = $bind ?? function($handle, $dn, $password) {
215 return ldap_bind($handle, $dn, $password);
216 };
217
218 return $bind(
219 $connect($this->configManager->get('ldap.host')),
220 sprintf($this->configManager->get('ldap.dn'), $login),
221 $password
160 ); 222 );
161 return true;
162 } 223 }
163 224
164 /** 225 /**
diff --git a/doc/md/Shaarli-configuration.md b/doc/md/Shaarli-configuration.md
index 664e36dd..2462e20e 100644
--- a/doc/md/Shaarli-configuration.md
+++ b/doc/md/Shaarli-configuration.md
@@ -122,6 +122,11 @@ Must be an associative array: `translation domain => translation path`.
122- **enable_thumbnails**: Enable or disable thumbnail display. 122- **enable_thumbnails**: Enable or disable thumbnail display.
123- **enable_localcache**: Enable or disable local cache. 123- **enable_localcache**: Enable or disable local cache.
124 124
125### LDAP
126
127- **host**: LDAP host used for user authentication
128- **dn**: user DN template (`sprintf` format, `%s` being replaced by user login)
129
125## Configuration file example 130## Configuration file example
126 131
127```json 132```json
@@ -223,6 +228,10 @@ Must be an associative array: `translation domain => translation path`.
223 "extensions": { 228 "extensions": {
224 "demo": "plugins/demo_plugin/languages/" 229 "demo": "plugins/demo_plugin/languages/"
225 } 230 }
231 },
232 "ldap": {
233 "host": "ldap://localhost",
234 "dn": "uid=%s,ou=people,dc=example,dc=org"
226 } 235 }
227} ?> 236} ?>
228``` 237```
diff --git a/tests/security/LoginManagerTest.php b/tests/security/LoginManagerTest.php
index eef0f22a..8fd1698c 100644
--- a/tests/security/LoginManagerTest.php
+++ b/tests/security/LoginManagerTest.php
@@ -78,6 +78,7 @@ class LoginManagerTest extends TestCase
78 'security.ban_after' => 2, 78 'security.ban_after' => 2,
79 'security.ban_duration' => 3600, 79 'security.ban_duration' => 3600,
80 'security.trusted_proxies' => [$this->trustedProxy], 80 'security.trusted_proxies' => [$this->trustedProxy],
81 'ldap.host' => '',
81 ]); 82 ]);
82 83
83 $this->cookie = []; 84 $this->cookie = [];
@@ -296,4 +297,37 @@ class LoginManagerTest extends TestCase
296 $this->loginManager->checkCredentials('', '', $this->login, $this->password) 297 $this->loginManager->checkCredentials('', '', $this->login, $this->password)
297 ); 298 );
298 } 299 }
300
301 /**
302 * Check user credentials through LDAP - server unreachable
303 */
304 public function testCheckCredentialsFromUnreachableLdap()
305 {
306 $this->configManager->set('ldap.host', 'dummy');
307 $this->assertFalse(
308 $this->loginManager->checkCredentials('', '', $this->login, $this->password)
309 );
310 }
311
312 /**
313 * Check user credentials through LDAP - wrong login and password supplied
314 */
315 public function testCheckCredentialsFromLdapWrongLoginAndPassword()
316 {
317 $this->configManager->set('ldap.host', 'dummy');
318 $this->assertFalse(
319 $this->loginManager->checkCredentialsFromLdap($this->login, $this->password, function() { return null; }, function() { return false; })
320 );
321 }
322
323 /**
324 * Check user credentials through LDAP - correct login and password supplied
325 */
326 public function testCheckCredentialsFromLdapGoodLoginAndPassword()
327 {
328 $this->configManager->set('ldap.host', 'dummy');
329 $this->assertTrue(
330 $this->loginManager->checkCredentialsFromLdap($this->login, $this->password, function() { return null; }, function() { return true; })
331 );
332 }
299} 333}