]> git.immae.eu Git - github/shaarli/Shaarli.git/blob - application/security/LoginManager.php
Apply PHP Code Beautifier on source code for linter automatic fixes
[github/shaarli/Shaarli.git] / application / security / LoginManager.php
1 <?php
2
3 namespace Shaarli\Security;
4
5 use Exception;
6 use Psr\Log\LoggerInterface;
7 use Shaarli\Config\ConfigManager;
8
9 /**
10 * User login management
11 */
12 class LoginManager
13 {
14 /** @var array A reference to the $_GLOBALS array */
15 protected $globals = [];
16
17 /** @var ConfigManager Configuration Manager instance **/
18 protected $configManager = null;
19
20 /** @var SessionManager Session Manager instance **/
21 protected $sessionManager = null;
22
23 /** @var BanManager Ban Manager instance **/
24 protected $banManager;
25
26 /** @var bool Whether the user is logged in **/
27 protected $isLoggedIn = false;
28
29 /** @var bool Whether the Shaarli instance is open to public edition **/
30 protected $openShaarli = false;
31
32 /** @var string User sign-in token depending on remote IP and credentials */
33 protected $staySignedInToken = '';
34 /** @var CookieManager */
35 protected $cookieManager;
36 /** @var LoggerInterface */
37 protected $logger;
38
39 /**
40 * Constructor
41 *
42 * @param ConfigManager $configManager Configuration Manager instance
43 * @param SessionManager $sessionManager SessionManager instance
44 * @param CookieManager $cookieManager CookieManager instance
45 * @param BanManager $banManager
46 * @param LoggerInterface $logger Used to log login attempts
47 */
48 public function __construct(
49 ConfigManager $configManager,
50 SessionManager $sessionManager,
51 CookieManager $cookieManager,
52 BanManager $banManager,
53 LoggerInterface $logger
54 ) {
55 $this->configManager = $configManager;
56 $this->sessionManager = $sessionManager;
57 $this->cookieManager = $cookieManager;
58 $this->banManager = $banManager;
59 $this->logger = $logger;
60
61 if ($this->configManager->get('security.open_shaarli') === true) {
62 $this->openShaarli = true;
63 }
64 }
65
66 /**
67 * Generate a token depending on deployment salt, user password and client IP
68 *
69 * @param string $clientIpAddress The remote client IP address
70 */
71 public function generateStaySignedInToken($clientIpAddress)
72 {
73 if ($this->configManager->get('security.session_protection_disabled') === true) {
74 $clientIpAddress = '';
75 }
76 $this->staySignedInToken = sha1(
77 $this->configManager->get('credentials.hash')
78 . $clientIpAddress
79 . $this->configManager->get('credentials.salt')
80 );
81 }
82
83 /**
84 * Return the user's client stay-signed-in token
85 *
86 * @return string User's client stay-signed-in token
87 */
88 public function getStaySignedInToken()
89 {
90 return $this->staySignedInToken;
91 }
92
93 /**
94 * Check user session state and validity (expiration)
95 *
96 * @param string $clientIpId Client IP address identifier
97 */
98 public function checkLoginState($clientIpId)
99 {
100 if (! $this->configManager->exists('credentials.login')) {
101 // Shaarli is not configured yet
102 $this->isLoggedIn = false;
103 return;
104 }
105
106 if ($this->staySignedInToken === $this->cookieManager->getCookieParameter(CookieManager::STAY_SIGNED_IN)) {
107 // The user client has a valid stay-signed-in cookie
108 // Session information is updated with the current client information
109 $this->sessionManager->storeLoginInfo($clientIpId);
110 } elseif (
111 $this->sessionManager->hasSessionExpired()
112 || $this->sessionManager->hasClientIpChanged($clientIpId)
113 ) {
114 $this->sessionManager->logout();
115 $this->isLoggedIn = false;
116 return;
117 }
118
119 $this->isLoggedIn = true;
120 $this->sessionManager->extendSession();
121 }
122
123 /**
124 * Return whether the user is currently logged in
125 *
126 * @return true when the user is logged in, false otherwise
127 */
128 public function isLoggedIn(): bool
129 {
130 if ($this->openShaarli) {
131 return true;
132 }
133 return $this->isLoggedIn;
134 }
135
136 /**
137 * Check user credentials are valid
138 *
139 * @param string $clientIpId Client IP address identifier
140 * @param string $login Username
141 * @param string $password Password
142 *
143 * @return bool true if the provided credentials are valid, false otherwise
144 */
145 public function checkCredentials($clientIpId, $login, $password)
146 {
147 // Check credentials
148 try {
149 $useLdapLogin = !empty($this->configManager->get('ldap.host'));
150 if (
151 $login === $this->configManager->get('credentials.login')
152 && (
153 (false === $useLdapLogin && $this->checkCredentialsFromLocalConfig($login, $password))
154 || (true === $useLdapLogin && $this->checkCredentialsFromLdap($login, $password))
155 )
156 ) {
157 $this->sessionManager->storeLoginInfo($clientIpId);
158 $this->logger->info(format_log('Login successful', $clientIpId));
159
160 return true;
161 }
162 } catch (Exception $exception) {
163 $this->logger->info(format_log('Exception while checking credentials: ' . $exception, $clientIpId));
164 }
165
166 $this->logger->info(format_log('Login failed for user ' . $login, $clientIpId));
167
168 return false;
169 }
170
171
172 /**
173 * Check user credentials from local config
174 *
175 * @param string $login Username
176 * @param string $password Password
177 *
178 * @return bool true if the provided credentials are valid, false otherwise
179 */
180 public function checkCredentialsFromLocalConfig($login, $password)
181 {
182 $hash = sha1($password . $login . $this->configManager->get('credentials.salt'));
183
184 return $login == $this->configManager->get('credentials.login')
185 && $hash == $this->configManager->get('credentials.hash');
186 }
187
188 /**
189 * Check user credentials are valid through LDAP bind
190 *
191 * @param string $remoteIp Remote client IP address
192 * @param string $clientIpId Client IP address identifier
193 * @param string $login Username
194 * @param string $password Password
195 *
196 * @return bool true if the provided credentials are valid, false otherwise
197 */
198 public function checkCredentialsFromLdap($login, $password, $connect = null, $bind = null)
199 {
200 $connect = $connect ?? function ($host) {
201 $resource = ldap_connect($host);
202
203 ldap_set_option($resource, LDAP_OPT_PROTOCOL_VERSION, 3);
204
205 return $resource;
206 };
207 $bind = $bind ?? function ($handle, $dn, $password) {
208 return ldap_bind($handle, $dn, $password);
209 };
210
211 return $bind(
212 $connect($this->configManager->get('ldap.host')),
213 sprintf($this->configManager->get('ldap.dn'), $login),
214 $password
215 );
216 }
217
218 /**
219 * Handle a failed login and ban the IP after too many failed attempts
220 *
221 * @param array $server The $_SERVER array
222 */
223 public function handleFailedLogin($server)
224 {
225 $this->banManager->handleFailedAttempt($server);
226 }
227
228 /**
229 * Handle a successful login
230 *
231 * @param array $server The $_SERVER array
232 */
233 public function handleSuccessfulLogin($server)
234 {
235 $this->banManager->clearFailures($server);
236 }
237
238 /**
239 * Check if the user can login from this IP
240 *
241 * @param array $server The $_SERVER array
242 *
243 * @return bool true if the user is allowed to login
244 */
245 public function canLogin($server)
246 {
247 return ! $this->banManager->isBanned($server);
248 }
249 }