globals = &$globals; $this->configManager = $configManager; $this->sessionManager = $sessionManager; $this->banFile = $this->configManager->get('resource.ban_file', 'data/ipbans.php'); $this->readBanFile(); if ($this->configManager->get('security.open_shaarli') === true) { $this->openShaarli = true; } } /** * Generate a token depending on deployment salt, user password and client IP * * @param string $clientIpAddress The remote client IP address */ public function generateStaySignedInToken($clientIpAddress) { $this->staySignedInToken = sha1( $this->configManager->get('credentials.hash') . $clientIpAddress . $this->configManager->get('credentials.salt') ); } /** * Return the user's client stay-signed-in token * * @return string User's client stay-signed-in token */ public function getStaySignedInToken() { return $this->staySignedInToken; } /** * Check user session state and validity (expiration) * * @param array $cookie The $_COOKIE array * @param string $clientIpId Client IP address identifier */ public function checkLoginState($cookie, $clientIpId) { if (! $this->configManager->exists('credentials.login') || (isset($_SESSION['username']) && $_SESSION['username'] && $this->configManager->get('credentials.login') !== $_SESSION['username'])) { // Shaarli is not configured yet $this->isLoggedIn = false; return; } if (isset($cookie[self::$STAY_SIGNED_IN_COOKIE]) && $cookie[self::$STAY_SIGNED_IN_COOKIE] === $this->staySignedInToken ) { // The user client has a valid stay-signed-in cookie // Session information is updated with the current client information $this->sessionManager->storeLoginInfo($clientIpId); } elseif ($this->sessionManager->hasSessionExpired() || $this->sessionManager->hasClientIpChanged($clientIpId) ) { $this->sessionManager->logout(); $this->isLoggedIn = false; return; } $this->isLoggedIn = true; $this->sessionManager->extendSession(); } /** * Return whether the user is currently logged in * * @return true when the user is logged in, false otherwise */ public function isLoggedIn() { if ($this->openShaarli) { return true; } return $this->isLoggedIn; } /** * Check user credentials are valid * * @param string $remoteIp Remote client IP address * @param string $clientIpId Client IP address identifier * @param string $login Username * @param string $password Password * * @return bool true if the provided credentials are valid, false otherwise */ public function checkCredentials($remoteIp, $clientIpId, $login, $password) { $this->lastErrorIsBanishable = false; if ($this->configManager->getUserSpace() !== null && $this->configManager->getUserSpace() !== $login) { logm($this->configManager->get('resource.log'), $remoteIp, 'Trying to login to wrong user space'); $this->lastErrorReason = 'You’re trying to access the wrong account.'; return false; } logm($this->configManager->get('resource.log'), $remoteIp, 'Trying LDAP connection'); $result = $this->configManager->findLDAPUser($login, $password); if ($result === false) { logm( $this->configManager->get('resource.log'), $remoteIp, 'Impossible to connect to LDAP' ); $this->lastErrorReason = 'Server error.'; return false; } else if (is_null($result)) { logm( $this->configManager->get('resource.log'), $remoteIp, 'Login failed for user ' . $login ); $this->lastErrorIsBanishable = true; $this->lastErrorReason = 'Wrong login/password.'; return false; } $this->sessionManager->storeLoginInfo($clientIpId, $login); logm( $this->configManager->get('resource.log'), $remoteIp, 'Login successful' ); return true; } /** * Read a file containing banned IPs */ protected function readBanFile() { if (! file_exists($this->banFile)) { return; } include $this->banFile; } /** * Write the banned IPs to a file */ protected function writeBanFile() { if (! array_key_exists('IPBANS', $this->globals)) { return; } file_put_contents( $this->banFile, "globals['IPBANS'], true) . ";\n?>" ); } /** * Handle a failed login and ban the IP after too many failed attempts * * @param array $server The $_SERVER array */ public function handleFailedLogin($server) { if (!$this->lastErrorIsBanishable) { return $this->lastErrorReason ?: 'Error during login.'; }; $ip = $server['REMOTE_ADDR']; $trusted = $this->configManager->get('security.trusted_proxies', []); if (in_array($ip, $trusted)) { $ip = getIpAddressFromProxy($server, $trusted); if (! $ip) { // the IP is behind a trusted forward proxy, but is not forwarded // in the HTTP headers, so we do nothing return; } } // increment the fail count for this IP if (isset($this->globals['IPBANS']['FAILURES'][$ip])) { $this->globals['IPBANS']['FAILURES'][$ip]++; } else { $this->globals['IPBANS']['FAILURES'][$ip] = 1; } if ($this->globals['IPBANS']['FAILURES'][$ip] >= $this->configManager->get('security.ban_after')) { $this->globals['IPBANS']['BANS'][$ip] = time() + $this->configManager->get('security.ban_duration', 1800); logm( $this->configManager->get('resource.log'), $server['REMOTE_ADDR'], 'IP address banned from login' ); } $this->writeBanFile(); return $this->lastErrorReason ?: 'Error during login.'; } /** * Handle a successful login * * @param array $server The $_SERVER array */ public function handleSuccessfulLogin($server) { $ip = $server['REMOTE_ADDR']; // FIXME unban when behind a trusted proxy? unset($this->globals['IPBANS']['FAILURES'][$ip]); unset($this->globals['IPBANS']['BANS'][$ip]); $this->writeBanFile(); } /** * Check if the user can login from this IP * * @param array $server The $_SERVER array * * @return bool true if the user is allowed to login */ public function canLogin($server) { $ip = $server['REMOTE_ADDR']; if (! isset($this->globals['IPBANS']['BANS'][$ip])) { // the user is not banned return true; } if ($this->globals['IPBANS']['BANS'][$ip] > time()) { // the user is still banned return false; } // the ban has expired, the user can attempt to log in again logm($this->configManager->get('resource.log'), $server['REMOTE_ADDR'], 'Ban lifted.'); unset($this->globals['IPBANS']['FAILURES'][$ip]); unset($this->globals['IPBANS']['BANS'][$ip]); $this->writeBanFile(); return true; } }