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