From d91691573f108422cc2080462af35ebd62dc93fb Mon Sep 17 00:00:00 2001 From: Jeremy Date: Sun, 8 Feb 2015 21:47:36 +0100 Subject: Add custom auth encoder & provider These custom classes allow Wallabag v2 to be compatible with Wallabag v1 salted password --- src/Wallabag/CoreBundle/Entity/User.php | 6 +- .../Encoder/WallabagPasswordEncoder.php | 88 +++++++++++++++++++++ .../Provider/WallabagAuthenticationProvider.php | 89 ++++++++++++++++++++++ 3 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 src/Wallabag/CoreBundle/Security/Authentication/Encoder/WallabagPasswordEncoder.php create mode 100644 src/Wallabag/CoreBundle/Security/Authentication/Provider/WallabagAuthenticationProvider.php (limited to 'src/Wallabag') diff --git a/src/Wallabag/CoreBundle/Entity/User.php b/src/Wallabag/CoreBundle/Entity/User.php index cfbd57f8..c83250c3 100644 --- a/src/Wallabag/CoreBundle/Entity/User.php +++ b/src/Wallabag/CoreBundle/Entity/User.php @@ -161,7 +161,11 @@ class User implements AdvancedUserInterface, \Serializable */ public function setPassword($password) { - $this->password = $password; + if (!$password && 0 === strlen($password)) { + return; + } + + $this->password = sha1($password.$this->getUsername().$this->getSalt()); return $this; } diff --git a/src/Wallabag/CoreBundle/Security/Authentication/Encoder/WallabagPasswordEncoder.php b/src/Wallabag/CoreBundle/Security/Authentication/Encoder/WallabagPasswordEncoder.php new file mode 100644 index 00000000..56f1affe --- /dev/null +++ b/src/Wallabag/CoreBundle/Security/Authentication/Encoder/WallabagPasswordEncoder.php @@ -0,0 +1,88 @@ +algorithm = $algorithm; + $this->encodeHashAsBase64 = $encodeHashAsBase64; + $this->iterations = $iterations; + } + + public function setUsername($username) + { + $this->username = $username; + } + + /** + * {@inheritdoc} + */ + public function encodePassword($raw, $salt) + { + if (null === $this->username) { + throw new \LogicException('We can not check the password without a username.'); + } + + if ($this->isPasswordTooLong($raw)) { + throw new BadCredentialsException('Invalid password.'); + } + + if (!in_array($this->algorithm, hash_algos(), true)) { + throw new \LogicException(sprintf('The algorithm "%s" is not supported.', $this->algorithm)); + } + + $salted = $this->mergePasswordAndSalt($raw, $salt); + $digest = hash($this->algorithm, $salted, true); + + // "stretch" hash + for ($i = 1; $i < $this->iterations; $i++) { + $digest = hash($this->algorithm, $digest.$salted, true); + } + + return $this->encodeHashAsBase64 ? base64_encode($digest) : bin2hex($digest); + } + + /** + * {@inheritdoc} + * + * We inject the username inside the salted password + */ + protected function mergePasswordAndSalt($password, $salt) + { + if (empty($salt)) { + return $password; + } + + return $password.$this->username.$salt; + } + + /** + * {@inheritdoc} + */ + public function isPasswordValid($encoded, $raw, $salt) + { + return !$this->isPasswordTooLong($raw) && $this->comparePasswords($encoded, $this->encodePassword($raw, $salt)); + } +} diff --git a/src/Wallabag/CoreBundle/Security/Authentication/Provider/WallabagAuthenticationProvider.php b/src/Wallabag/CoreBundle/Security/Authentication/Provider/WallabagAuthenticationProvider.php new file mode 100644 index 00000000..1c7c5fae --- /dev/null +++ b/src/Wallabag/CoreBundle/Security/Authentication/Provider/WallabagAuthenticationProvider.php @@ -0,0 +1,89 @@ +encoderFactory = $encoderFactory; + $this->userProvider = $userProvider; + } + + /** + * {@inheritdoc} + */ + protected function checkAuthentication(UserInterface $user, UsernamePasswordToken $token) + { + $currentUser = $token->getUser(); + if ($currentUser instanceof UserInterface) { + if ($currentUser->getPassword() !== $user->getPassword()) { + throw new BadCredentialsException('The credentials were changed from another session.'); + } + } else { + if ("" === ($presentedPassword = $token->getCredentials())) { + throw new BadCredentialsException('The presented password cannot be empty.'); + } + + // give username, it's used to hash the password + $encoder = $this->encoderFactory->getEncoder($user); + $encoder->setUsername($user->getUsername()); + + if (!$encoder->isPasswordValid($user->getPassword(), $presentedPassword, $user->getSalt())) { + throw new BadCredentialsException('The presented password is invalid.'); + } + } + } + + /** + * {@inheritdoc} + */ + protected function retrieveUser($username, UsernamePasswordToken $token) + { + $user = $token->getUser(); + if ($user instanceof UserInterface) { + return $user; + } + + try { + $user = $this->userProvider->loadUserByUsername($username); + + if (!$user instanceof UserInterface) { + throw new AuthenticationServiceException('The user provider must return a UserInterface object.'); + } + + return $user; + } catch (UsernameNotFoundException $notFound) { + $notFound->setUsername($username); + throw $notFound; + } catch (\Exception $repositoryProblem) { + $ex = new AuthenticationServiceException($repositoryProblem->getMessage(), 0, $repositoryProblem); + $ex->setToken($token); + throw $ex; + } + } +} -- cgit v1.2.3