From 785827fda1a15c46ae63842e5b1534ea06afae27 Mon Sep 17 00:00:00 2001 From: Ariel Ferrandini Date: Tue, 26 Aug 2014 12:04:34 +0200 Subject: [PATCH] New service to simplify password encoding --- book/security.rst | 35 +++++++++++++++-- .../custom_password_authenticator.rst | 38 ++++++++----------- 2 files changed, 47 insertions(+), 26 deletions(-) diff --git a/book/security.rst b/book/security.rst index 0572bc13330..ab1ce30c862 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1459,23 +1459,36 @@ is available by calling the PHP function :phpfunction:`hash_algos`. Determining the Hashed Password ............................... +.. versionadded:: 2.6 + The ``security.password_encoder`` service was introduced in Symfony 2.6. + If you're storing users in the database and you have some sort of registration form for users, you'll need to be able to determine the hashed password so that you can set it on your user before inserting it. No matter what algorithm you configure for your user object, the hashed password can always be determined in the following way from a controller:: - $factory = $this->get('security.encoder_factory'); $user = new Acme\UserBundle\Entity\User(); + $plainPassword = 'ryanpass'; + $encoded = $this->container->get('security.password_encoder') + ->encodePassword($user, $plainPassword); - $encoder = $factory->getEncoder($user); - $password = $encoder->encodePassword('ryanpass', $user->getSalt()); - $user->setPassword($password); + $user->setPassword($encoded); In order for this to work, just make sure that you have the encoder for your user class (e.g. ``Acme\UserBundle\Entity\User``) configured under the ``encoders`` key in ``app/config/security.yml``. +.. sidebar:: Get the User Encoder + + In some cases, you need a specific encoder for a given user (e.g. ``Acme\UserBundle\Entity\User``). + You can use the ``EncoderFactory`` to get this encoder:: + + $factory = $this->get('security.encoder_factory'); + $user = new Acme\UserBundle\Entity\User(); + + $encoder = $factory->getEncoder($user); + .. caution:: When you allow a user to submit a plaintext password (e.g. registration @@ -1483,6 +1496,20 @@ key in ``app/config/security.yml``. that the password is 4096 characters or less. Read more details in :ref:`How to implement a simple Registration Form `. +Validating a Plaintext Password +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sometimes you want to check if a plain password is valid for a given user:: + + // a user instance of some class which implements Symfony\Component\Security\Core\User\UserInterface + $user = ...; + + // the password that should be checked + $plainPassword = ...; + + $isValidPassword = $this->container->get('security.password_encoder') + ->isPasswordValid($user, $plainPassword); + Retrieving the User Object ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/cookbook/security/custom_password_authenticator.rst b/cookbook/security/custom_password_authenticator.rst index 288d0e65a6f..6894a5043c2 100644 --- a/cookbook/security/custom_password_authenticator.rst +++ b/cookbook/security/custom_password_authenticator.rst @@ -8,6 +8,7 @@ Imagine you want to allow access to your website only between 2pm and 4pm UTC. Before Symfony 2.4, you had to create a custom token, factory, listener and provider. In this entry, you'll learn how to do this for a login form (i.e. where your user submits their username and password). +Before Symfony 2.6, you had to use the password encoder to authenticate the user password. The Password Authenticator -------------------------- @@ -15,6 +16,9 @@ The Password Authenticator .. versionadded:: 2.4 The ``SimpleFormAuthenticatorInterface`` interface was introduced in Symfony 2.4. +.. versionadded:: 2.6 + The ``UserPasswordEncoderInterface`` interface was introduced in Symfony 2.6. + First, create a new class that implements :class:`Symfony\\Component\\Security\\Core\\Authentication\\SimpleFormAuthenticatorInterface`. Eventually, this will allow you to create custom logic for authenticating @@ -27,18 +31,18 @@ the user:: use Symfony\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; - use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; + use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Core\User\UserProviderInterface; class TimeAuthenticator implements SimpleFormAuthenticatorInterface { - private $encoderFactory; + private $encoder; - public function __construct(EncoderFactoryInterface $encoderFactory) + public function __construct(UserPasswordEncoderInterface $encoder) { - $this->encoderFactory = $encoderFactory; + $this->encoder = $encoder; } public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey) @@ -49,12 +53,7 @@ the user:: throw new AuthenticationException('Invalid username or password'); } - $encoder = $this->encoderFactory->getEncoder($user); - $passwordValid = $encoder->isPasswordValid( - $user->getPassword(), - $token->getCredentials(), - $user->getSalt() - ); + $passwordValid = $this->encoder->isPasswordValid($user, $token->getCredentials()); if ($passwordValid) { $currentHour = date('G'); @@ -127,17 +126,12 @@ Ultimately, your job is to return a *new* token object that is "authenticated" (i.e. it has at least 1 role set on it) and which has the ``User`` object inside of it. -Inside this method, an encoder is needed to check the password's validity:: +Inside this method, the password encoder is needed to check the password's validity:: - $encoder = $this->encoderFactory->getEncoder($user); - $passwordValid = $encoder->isPasswordValid( - $user->getPassword(), - $token->getCredentials(), - $user->getSalt() - ); + $passwordValid = $this->encoder->isPasswordValid($user, $token->getCredentials()); -This is a service that is already available in Symfony and the password algorithm -is configured in the security configuration (e.g. ``security.yml``) under +This is a service that is already available in Symfony and it uses the password algorithm +that is configured in the security configuration (e.g. ``security.yml``) under the ``encoders`` key. Below, you'll see how to inject that into the ``TimeAuthenticator``. .. _cookbook-security-password-authenticator-config: @@ -157,7 +151,7 @@ Now, configure your ``TimeAuthenticator`` as a service: time_authenticator: class: Acme\HelloBundle\Security\TimeAuthenticator - arguments: ["@security.encoder_factory"] + arguments: ["@security.password_encoder"] .. code-block:: xml @@ -173,7 +167,7 @@ Now, configure your ``TimeAuthenticator`` as a service: - + @@ -188,7 +182,7 @@ Now, configure your ``TimeAuthenticator`` as a service: $container->setDefinition('time_authenticator', new Definition( 'Acme\HelloBundle\Security\TimeAuthenticator', - array(new Reference('security.encoder_factory')) + array(new Reference('security.password_encoder')) )); Then, activate it in the ``firewalls`` section of the security configuration