2 namespace Shaarli\Security
;
5 use Psr\Log\LoggerInterface
;
6 use Shaarli\Config\ConfigManager
;
9 * User login management
13 /** @var array A reference to the $_GLOBALS array */
14 protected $globals = [];
16 /** @var ConfigManager Configuration Manager instance **/
17 protected $configManager = null;
19 /** @var SessionManager Session Manager instance **/
20 protected $sessionManager = null;
22 /** @var BanManager Ban Manager instance **/
23 protected $banManager;
25 /** @var bool Whether the user is logged in **/
26 protected $isLoggedIn = false;
28 /** @var bool Whether the Shaarli instance is open to public edition **/
29 protected $openShaarli = false;
31 /** @var string User sign-in token depending on remote IP and credentials */
32 protected $staySignedInToken = '';
33 /** @var CookieManager */
34 protected $cookieManager;
35 /** @var LoggerInterface */
41 * @param ConfigManager $configManager Configuration Manager instance
42 * @param SessionManager $sessionManager SessionManager instance
43 * @param CookieManager $cookieManager CookieManager instance
44 * @param BanManager $banManager
45 * @param LoggerInterface $logger Used to log login attempts
47 public function __construct(
48 ConfigManager
$configManager,
49 SessionManager
$sessionManager,
50 CookieManager
$cookieManager,
51 BanManager
$banManager,
52 LoggerInterface
$logger
54 $this->configManager
= $configManager;
55 $this->sessionManager
= $sessionManager;
56 $this->cookieManager
= $cookieManager;
57 $this->banManager
= $banManager;
58 $this->logger
= $logger;
60 if ($this->configManager
->get('security.open_shaarli') === true) {
61 $this->openShaarli
= true;
66 * Generate a token depending on deployment salt, user password and client IP
68 * @param string $clientIpAddress The remote client IP address
70 public function generateStaySignedInToken($clientIpAddress)
72 if ($this->configManager
->get('security.session_protection_disabled') === true) {
73 $clientIpAddress = '';
75 $this->staySignedInToken
= sha1(
76 $this->configManager
->get('credentials.hash')
78 . $this->configManager
->get('credentials.salt')
83 * Return the user's client stay-signed-in token
85 * @return string User's client stay-signed-in token
87 public function getStaySignedInToken()
89 return $this->staySignedInToken
;
93 * Check user session state and validity (expiration)
95 * @param string $clientIpId Client IP address identifier
97 public function checkLoginState($clientIpId)
99 if (! $this->configManager
->exists('credentials.login')) {
100 // Shaarli is not configured yet
101 $this->isLoggedIn
= false;
105 if ($this->staySignedInToken
=== $this->cookieManager
->getCookieParameter(CookieManager
::STAY_SIGNED_IN
)) {
106 // The user client has a valid stay-signed-in cookie
107 // Session information is updated with the current client information
108 $this->sessionManager
->storeLoginInfo($clientIpId);
109 } elseif ($this->sessionManager
->hasSessionExpired()
110 || $this->sessionManager
->hasClientIpChanged($clientIpId)
112 $this->sessionManager
->logout();
113 $this->isLoggedIn
= false;
117 $this->isLoggedIn
= true;
118 $this->sessionManager
->extendSession();
122 * Return whether the user is currently logged in
124 * @return true when the user is logged in, false otherwise
126 public function isLoggedIn(): bool
128 if ($this->openShaarli
) {
131 return $this->isLoggedIn
;
135 * Check user credentials are valid
137 * @param string $clientIpId Client IP address identifier
138 * @param string $login Username
139 * @param string $password Password
141 * @return bool true if the provided credentials are valid, false otherwise
143 public function checkCredentials($clientIpId, $login, $password)
147 $useLdapLogin = !empty($this->configManager
->get('ldap.host'));
148 if ($login === $this->configManager
->get('credentials.login')
150 (false === $useLdapLogin && $this->checkCredentialsFromLocalConfig($login, $password))
151 || (true === $useLdapLogin && $this->checkCredentialsFromLdap($login, $password))
154 $this->sessionManager
->storeLoginInfo($clientIpId);
155 $this->logger
->info(format_log('Login successful', $clientIpId));
159 } catch(Exception
$exception) {
160 $this->logger
->info(format_log('Exception while checking credentials: ' . $exception, $clientIpId));
163 $this->logger
->info(format_log('Login failed for user ' . $login, $clientIpId));
170 * Check user credentials from local config
172 * @param string $login Username
173 * @param string $password Password
175 * @return bool true if the provided credentials are valid, false otherwise
177 public function checkCredentialsFromLocalConfig($login, $password) {
178 $hash = sha1($password . $login . $this->configManager
->get('credentials.salt'));
180 return $login == $this->configManager
->get('credentials.login')
181 && $hash == $this->configManager
->get('credentials.hash');
185 * Check user credentials are valid through LDAP bind
187 * @param string $remoteIp Remote client IP address
188 * @param string $clientIpId Client IP address identifier
189 * @param string $login Username
190 * @param string $password Password
192 * @return bool true if the provided credentials are valid, false otherwise
194 public function checkCredentialsFromLdap($login, $password, $connect = null, $bind = null)
196 $connect = $connect ?? function($host) {
197 $resource = ldap_connect($host);
199 ldap_set_option($resource, LDAP_OPT_PROTOCOL_VERSION
, 3);
203 $bind = $bind ?? function($handle, $dn, $password) {
204 return ldap_bind($handle, $dn, $password);
208 $connect($this->configManager
->get('ldap.host')),
209 sprintf($this->configManager
->get('ldap.dn'), $login),
215 * Handle a failed login and ban the IP after too many failed attempts
217 * @param array $server The $_SERVER array
219 public function handleFailedLogin($server)
221 $this->banManager
->handleFailedAttempt($server);
225 * Handle a successful login
227 * @param array $server The $_SERVER array
229 public function handleSuccessfulLogin($server)
231 $this->banManager
->clearFailures($server);
235 * Check if the user can login from this IP
237 * @param array $server The $_SERVER array
239 * @return bool true if the user is allowed to login
241 public function canLogin($server)
243 return ! $this->banManager
->isBanned($server);