From 92b7dd3335a6572debeacfb5faa82c63a5e67888 Mon Sep 17 00:00:00 2001 From: Marvin Borner Date: Fri, 8 Jun 2018 20:03:25 +0200 Subject: Some minor fixes --- .../account/src/Authenticate/Authenticator.php | 814 ++++++++++----------- 1 file changed, 407 insertions(+), 407 deletions(-) (limited to 'main/app/sprinkles/account/src/Authenticate/Authenticator.php') diff --git a/main/app/sprinkles/account/src/Authenticate/Authenticator.php b/main/app/sprinkles/account/src/Authenticate/Authenticator.php index 735a688..a4586e4 100644 --- a/main/app/sprinkles/account/src/Authenticate/Authenticator.php +++ b/main/app/sprinkles/account/src/Authenticate/Authenticator.php @@ -1,407 +1,407 @@ -classMapper = $classMapper; - $this->session = $session; - $this->config = $config; - $this->cache = $cache; - - // Initialize RememberMe storage - $this->rememberMeStorage = new RememberMePDO($this->config['remember_me.table']); - - // Get the actual PDO instance from Eloquent - $pdo = Capsule::connection()->getPdo(); - - $this->rememberMeStorage->setConnection($pdo); - - // Set up RememberMe - $this->rememberMe = new RememberMe($this->rememberMeStorage); - // Set cookie name - $cookieName = $this->config['session.name'] . '-' . $this->config['remember_me.cookie.name']; - $this->rememberMe->getCookie()->setName($cookieName); - - // Change cookie path - $this->rememberMe->getCookie()->setPath($this->config['remember_me.session.path']); - - // Set expire time, if specified - if ($this->config->has('remember_me.expire_time') && ($this->config->has('remember_me.expire_time') != NULL)) { - $this->rememberMe->getCookie()->setExpireTime($this->config['remember_me.expire_time']); - } - - $this->user = NULL; - - $this->viaRemember = FALSE; - } - - /** - * Attempts to authenticate a user based on a supplied identity and password. - * - * If successful, the user's id is stored in session. - */ - public function attempt($identityColumn, $identityValue, $password, $rememberMe = FALSE) { - // Try to load the user, using the specified conditions - $user = $this->classMapper->staticMethod('user', 'where', $identityColumn, $identityValue)->first(); - - if (!$user) { - throw new InvalidCredentialsException(); - } - - // Check that the user has a password set (so, rule out newly created accounts without a password) - if (!$user->password) { - throw new InvalidCredentialsException(); - } - - // Check that the user's account is enabled - if ($user->flag_enabled == 0) { - throw new AccountDisabledException(); - } - - // Check that the user's account is verified (if verification is required) - if ($this->config['site.registration.require_email_verification'] && $user->flag_verified == 0) { - throw new AccountNotVerifiedException(); - } - - // Here is my password. May I please assume the identify of this user now? - if (Password::verify($password, $user->password)) { - $this->login($user, $rememberMe); - return $user; - } else { - // We know the password is at fault here (as opposed to the identity), but lets not give away the combination in case of someone bruteforcing - throw new InvalidCredentialsException(); - } - } - - /** - * Determine if the current user is authenticated. - * - * @return bool - */ - public function check() { - return !is_null($this->user()); - } - - /** - * Determine if the current user is a guest (unauthenticated). - * - * @return bool - */ - public function guest() { - return !$this->check(); - } - - /** - * Process an account login request. - * - * This method logs in the specified user, allowing the client to assume the user's identity for the duration of the session. - * @param User $user The user to log in. - * @param bool $rememberMe Set to true to make this a "persistent session", i.e. one that will re-login even after the session expires. - * @odo Figure out a way to update the currentUser service to reflect the logged-in user *immediately* in the service provider. - * As it stands, the currentUser service will still reflect a "guest user" for the remainder of the request. - */ - public function login($user, $rememberMe = FALSE) { - $oldId = session_id(); - $this->session->regenerateId(TRUE); - - // Since regenerateId deletes the old session, we'll do the same in cache - $this->flushSessionCache($oldId); - - // If the user wants to be remembered, create Rememberme cookie - if ($rememberMe) { - $this->rememberMe->createCookie($user->id); - } else { - $this->rememberMe->clearCookie(); - } - - // Assume identity - $key = $this->config['session.keys.current_user_id']; - $this->session[$key] = $user->id; - - // Set auth mode - $this->viaRemember = FALSE; - - // User login actions - $user->onLogin(); - } - - /** - * Processes an account logout request. - * - * Logs the currently authenticated user out, destroying the PHP session and clearing the persistent session. - * This can optionally remove persistent sessions across all browsers/devices, since there can be a "RememberMe" cookie - * and corresponding database entries in multiple browsers/devices. See http://jaspan.com/improved_persistent_login_cookie_best_practice. - * - * @param bool $complete If set to true, will ensure that the user is logged out from *all* browsers on all devices. - */ - public function logout($complete = FALSE) { - $currentUserId = $this->session->get($this->config['session.keys.current_user_id']); - - // This removes all of the user's persistent logins from the database - if ($complete) { - $this->storage->cleanAllTriplets($currentUserId); - } - - // Clear the rememberMe cookie - $this->rememberMe->clearCookie(); - - // User logout actions - if ($currentUserId) { - $currentUser = $this->classMapper->staticMethod('user', 'find', $currentUserId); - if ($currentUser) { - $currentUser->onLogout(); - } - } - - $this->user = NULL; - $this->loggedOut = TRUE; - - $oldId = session_id(); - - // Completely destroy the session - $this->session->destroy(); - - // Since regenerateId deletes the old session, we'll do the same in cache - $this->flushSessionCache($oldId); - - // Restart the session service - $this->session->start(); - } - - /** - * Try to get the currently authenticated user, returning a guest user if none was found. - * - * Tries to re-establish a session for "remember-me" users who have been logged out due to an expired session. - * @return User|null - * @throws AuthExpiredException - * @throws AuthCompromisedException - * @throws AccountInvalidException - * @throws AccountDisabledException - */ - public function user() { - $user = NULL; - - if (!$this->loggedOut) { - - // Return any cached user - if (!is_null($this->user)) { - return $this->user; - } - - // If this throws a PDOException we catch it and return null than allowing the exception to propagate. - // This is because the error handler relies on Twig, which relies on a Twig Extension, which relies on the global current_user variable. - // So, we really don't want this method to throw any database exceptions. - try { - // Now, check to see if we have a user in session - $user = $this->loginSessionUser(); - - // If no user was found in the session, try to login via RememberMe cookie - if (!$user) { - $user = $this->loginRememberedUser(); - } - } catch (\PDOException $e) { - $user = NULL; - } - } - - return $this->user = $user; - } - - /** - * Determine whether the current user was authenticated using a remember me cookie. - * - * This function is useful when users are performing sensitive operations, and you may want to force them to re-authenticate. - * @return bool - */ - public function viaRemember() { - return $this->viaRemember; - } - - /** - * Attempt to log in the client from their rememberMe token (in their cookie). - * - * @return User|bool If successful, the User object of the remembered user. Otherwise, return false. - * @throws AuthCompromisedException The client attempted to log in with an invalid rememberMe token. - */ - protected function loginRememberedUser() { - /** @var \Birke\Rememberme\LoginResult $loginResult */ - $loginResult = $this->rememberMe->login(); - - if ($loginResult->isSuccess()) { - // Update in session - $this->session[$this->config['session.keys.current_user_id']] = $loginResult->getCredential(); - // There is a chance that an attacker has stolen the login token, - // so we store the fact that the user was logged in via RememberMe (instead of login form) - $this->viaRemember = TRUE; - } else { - // If $rememberMe->login() was not successful, check if the token was invalid as well. This means the cookie was stolen. - if ($loginResult->hasPossibleManipulation()) { - throw new AuthCompromisedException(); - } - } - - return $this->validateUserAccount($loginResult->getCredential()); - } - - /** - * Attempt to log in the client from the session. - * - * @return User|null If successful, the User object of the user in session. Otherwise, return null. - * @throws AuthExpiredException The client attempted to use an expired rememberMe token. - */ - protected function loginSessionUser() { - $userId = $this->session->get($this->config['session.keys.current_user_id']); - - // If a user_id was found in the session, check any rememberMe cookie that was submitted. - // If they submitted an expired rememberMe cookie, then we need to log them out. - if ($userId) { - if (!$this->validateRememberMeCookie()) { - $this->logout(); - throw new AuthExpiredException(); - } - } - - return $this->validateUserAccount($userId); - } - - /** - * Determine if the cookie contains a valid rememberMe token. - * - * @return bool - */ - protected function validateRememberMeCookie() { - $cookieValue = $this->rememberMe->getCookie()->getValue(); - if (!$cookieValue) { - return TRUE; - } - $triplet = RememberMeTriplet::fromString($cookieValue); - if (!$triplet->isValid()) { - return FALSE; - } - - return TRUE; - } - - /** - * Tries to load the specified user by id from the database. - * - * Checks that the account is valid and enabled, throwing an exception if not. - * @param int $userId - * @return User|null - * @throws AccountInvalidException - * @throws AccountDisabledException - */ - protected function validateUserAccount($userId) { - if ($userId) { - $user = $this->classMapper->staticMethod('user', 'find', $userId); - - // If the user doesn't exist any more, throw an exception. - if (!$user) { - throw new AccountInvalidException(); - } - - // If the user has been disabled since their last request, throw an exception. - if (!$user->flag_enabled) { - throw new AccountDisabledException(); - } - - return $user; - } else { - return NULL; - } - } - - /** - * Flush the cache associated with a session id - * - * @param string $id The session id - * @return bool - */ - public function flushSessionCache($id) { - return $this->cache->tags('_s' . $id)->flush(); - } -} +classMapper = $classMapper; + $this->session = $session; + $this->config = $config; + $this->cache = $cache; + + // Initialize RememberMe storage + $this->rememberMeStorage = new RememberMePDO($this->config['remember_me.table']); + + // Get the actual PDO instance from Eloquent + $pdo = Capsule::connection()->getPdo(); + + $this->rememberMeStorage->setConnection($pdo); + + // Set up RememberMe + $this->rememberMe = new RememberMe($this->rememberMeStorage); + // Set cookie name + $cookieName = $this->config['session.name'] . '-' . $this->config['remember_me.cookie.name']; + $this->rememberMe->getCookie()->setName($cookieName); + + // Change cookie path + $this->rememberMe->getCookie()->setPath($this->config['remember_me.session.path']); + + // Set expire time, if specified + if ($this->config->has('remember_me.expire_time') && ($this->config->has('remember_me.expire_time') != NULL)) { + $this->rememberMe->getCookie()->setExpireTime($this->config['remember_me.expire_time']); + } + + $this->user = NULL; + + $this->viaRemember = FALSE; + } + + /** + * Attempts to authenticate a user based on a supplied identity and password. + * + * If successful, the user's id is stored in session. + */ + public function attempt($identityColumn, $identityValue, $password, $rememberMe = FALSE) { + // Try to load the user, using the specified conditions + $user = $this->classMapper->staticMethod('user', 'where', $identityColumn, $identityValue)->first(); + + if (!$user) { + throw new InvalidCredentialsException(); + } + + // Check that the user has a password set (so, rule out newly created accounts without a password) + if (!$user->password) { + throw new InvalidCredentialsException(); + } + + // Check that the user's account is enabled + if ($user->flag_enabled == 0) { + throw new AccountDisabledException(); + } + + // Check that the user's account is verified (if verification is required) + if ($this->config['site.registration.require_email_verification'] && $user->flag_verified == 0) { + throw new AccountNotVerifiedException(); + } + + // Here is my password. May I please assume the identify of this user now? + if (Password::verify($password, $user->password)) { + $this->login($user, $rememberMe); + return $user; + } else { + // We know the password is at fault here (as opposed to the identity), but lets not give away the combination in case of someone bruteforcing + throw new InvalidCredentialsException(); + } + } + + /** + * Determine if the current user is authenticated. + * + * @return bool + */ + public function check() { + return !is_null($this->user()); + } + + /** + * Determine if the current user is a guest (unauthenticated). + * + * @return bool + */ + public function guest() { + return !$this->check(); + } + + /** + * Process an account login request. + * + * This method logs in the specified user, allowing the client to assume the user's identity for the duration of the session. + * @param User $user The user to log in. + * @param bool $rememberMe Set to true to make this a "persistent session", i.e. one that will re-login even after the session expires. + * @odo Figure out a way to update the currentUser service to reflect the logged-in user *immediately* in the service provider. + * As it stands, the currentUser service will still reflect a "guest user" for the remainder of the request. + */ + public function login($user, $rememberMe = FALSE) { + $oldId = session_id(); + $this->session->regenerateId(TRUE); + + // Since regenerateId deletes the old session, we'll do the same in cache + $this->flushSessionCache($oldId); + + // If the user wants to be remembered, create Rememberme cookie + if ($rememberMe) { + $this->rememberMe->createCookie($user->id); + } else { + $this->rememberMe->clearCookie(); + } + + // Assume identity + $key = $this->config['session.keys.current_user_id']; + $this->session[$key] = $user->id; + + // Set auth mode + $this->viaRemember = FALSE; + + // User login actions + $user->onLogin(); + } + + /** + * Processes an account logout request. + * + * Logs the currently authenticated user out, destroying the PHP session and clearing the persistent session. + * This can optionally remove persistent sessions across all browsers/devices, since there can be a "RememberMe" cookie + * and corresponding database entries in multiple browsers/devices. See http://jaspan.com/improved_persistent_login_cookie_best_practice. + * + * @param bool $complete If set to true, will ensure that the user is logged out from *all* browsers on all devices. + */ + public function logout($complete = FALSE) { + $currentUserId = $this->session->get($this->config['session.keys.current_user_id']); + + // This removes all of the user's persistent logins from the database + if ($complete) { + $this->storage->cleanAllTriplets($currentUserId); + } + + // Clear the rememberMe cookie + $this->rememberMe->clearCookie(); + + // User logout actions + if ($currentUserId) { + $currentUser = $this->classMapper->staticMethod('user', 'find', $currentUserId); + if ($currentUser) { + $currentUser->onLogout(); + } + } + + $this->user = NULL; + $this->loggedOut = TRUE; + + $oldId = session_id(); + + // Completely destroy the session + $this->session->destroy(); + + // Since regenerateId deletes the old session, we'll do the same in cache + $this->flushSessionCache($oldId); + + // Restart the session service + $this->session->start(); + } + + /** + * Try to get the currently authenticated user, returning a guest user if none was found. + * + * Tries to re-establish a session for "remember-me" users who have been logged out due to an expired session. + * @return User|null + * @throws AuthExpiredException + * @throws AuthCompromisedException + * @throws AccountInvalidException + * @throws AccountDisabledException + */ + public function user() { + $user = NULL; + + if (!$this->loggedOut) { + + // Return any cached user + if (!is_null($this->user)) { + return $this->user; + } + + // If this throws a PDOException we catch it and return null than allowing the exception to propagate. + // This is because the error handler relies on Twig, which relies on a Twig Extension, which relies on the global current_user variable. + // So, we really don't want this method to throw any database exceptions. + try { + // Now, check to see if we have a user in session + $user = $this->loginSessionUser(); + + // If no user was found in the session, try to login via RememberMe cookie + if (!$user) { + $user = $this->loginRememberedUser(); + } + } catch (\PDOException $e) { + $user = NULL; + } + } + + return $this->user = $user; + } + + /** + * Determine whether the current user was authenticated using a remember me cookie. + * + * This function is useful when users are performing sensitive operations, and you may want to force them to re-authenticate. + * @return bool + */ + public function viaRemember() { + return $this->viaRemember; + } + + /** + * Attempt to log in the client from their rememberMe token (in their cookie). + * + * @return User|bool If successful, the User object of the remembered user. Otherwise, return false. + * @throws AuthCompromisedException The client attempted to log in with an invalid rememberMe token. + */ + protected function loginRememberedUser() { + /** @var \Birke\Rememberme\LoginResult $loginResult */ + $loginResult = $this->rememberMe->login(); + + if ($loginResult->isSuccess()) { + // Update in session + $this->session[$this->config['session.keys.current_user_id']] = $loginResult->getCredential(); + // There is a chance that an attacker has stolen the login token, + // so we store the fact that the user was logged in via RememberMe (instead of login form) + $this->viaRemember = TRUE; + } else { + // If $rememberMe->login() was not successful, check if the token was invalid as well. This means the cookie was stolen. + if ($loginResult->hasPossibleManipulation()) { + throw new AuthCompromisedException(); + } + } + + return $this->validateUserAccount($loginResult->getCredential()); + } + + /** + * Attempt to log in the client from the session. + * + * @return User|null If successful, the User object of the user in session. Otherwise, return null. + * @throws AuthExpiredException The client attempted to use an expired rememberMe token. + */ + protected function loginSessionUser() { + $userId = $this->session->get($this->config['session.keys.current_user_id']); + + // If a user_id was found in the session, check any rememberMe cookie that was submitted. + // If they submitted an expired rememberMe cookie, then we need to log them out. + if ($userId) { + if (!$this->validateRememberMeCookie()) { + $this->logout(); + throw new AuthExpiredException(); + } + } + + return $this->validateUserAccount($userId); + } + + /** + * Determine if the cookie contains a valid rememberMe token. + * + * @return bool + */ + protected function validateRememberMeCookie() { + $cookieValue = $this->rememberMe->getCookie()->getValue(); + if (!$cookieValue) { + return TRUE; + } + $triplet = RememberMeTriplet::fromString($cookieValue); + if (!$triplet->isValid()) { + return FALSE; + } + + return TRUE; + } + + /** + * Tries to load the specified user by id from the database. + * + * Checks that the account is valid and enabled, throwing an exception if not. + * @param int $userId + * @return User|null + * @throws AccountInvalidException + * @throws AccountDisabledException + */ + protected function validateUserAccount($userId) { + if ($userId) { + $user = $this->classMapper->staticMethod('user', 'find', $userId); + + // If the user doesn't exist any more, throw an exception. + if (!$user) { + throw new AccountInvalidException(); + } + + // If the user has been disabled since their last request, throw an exception. + if (!$user->flag_enabled) { + throw new AccountDisabledException(); + } + + return $user; + } else { + return NULL; + } + } + + /** + * Flush the cache associated with a session id + * + * @param string $id The session id + * @return bool + */ + public function flushSessionCache($id) { + return $this->cache->tags('_s' . $id)->flush(); + } +} -- cgit v1.2.3