2
votes

I want just to allow users to change their password in my application. I have built the a form to edit the password but the validation never pass because the password of the current user in the UserPassword constraint is always NULL:

<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <[email protected]>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Security\Core\Validator\Constraints;

use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;

class UserPasswordValidator extends ConstraintValidator
{
    private $securityContext;
    private $encoderFactory;

    public function __construct(SecurityContextInterface $securityContext, EncoderFactoryInterface $encoderFactory)
    {
        $this->securityContext = $securityContext;
        $this->encoderFactory = $encoderFactory;
    }

    /**
     * {@inheritdoc}
     */
    public function validate($password, Constraint $constraint)
    {
        if (!$constraint instanceof UserPassword) {
            throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\UserPassword');
        }

        $user = $this->securityContext->getToken()->getUser();

        if (!$user instanceof UserInterface) {
            throw new ConstraintDefinitionException('The User object must implement the UserInterface interface.');
        }

        $encoder = $this->encoderFactory->getEncoder($user);

        //I tried to print $user->getPassword from here and it is always NULL
        if (!$encoder->isPasswordValid($user->getPassword(), $password, $user->getSalt())) {
            $this->context->addViolation($constraint->message);
        }
    }
}

This is fhe form I'm using to change the password:

class UserPasswordEditType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('oldPassword', 'password', array(
            'constraints' => array(
                new UserPassword(array(
                        'message' => 'password_current.invalid',
                        'groups' => 'user-password-edit'
                    )
                ),
                new NotBlank(array(
                    'message' => 'not_blank',
                    'groups' => 'user-password-edit'
                ))
            ),
            'mapped' => false,
            'required' => true,
        ))
            ->add('password', 'repeated', array(
                'type' => 'password',
                'invalid_message' => 'password_repeat.invalid',
                'required' => true,
                'first_options' => array('label' => 'password.label'),
                'second_options' => array('label' => 'password_repeat.label'),
            ))
            ->add('save', 'submit', array(
                'label' => 'save.label'
            ));
    }

    public function getName()
    {
        return 'user_edit_password';
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'validation_groups' => array('user-password-edit'),
        ));
    }
}

This is a slice of the security.yml

  security:
      encoders: 
          Symfony\Component\Security\Core\User\User: plaintext
          XXX\PrivateApplication\Bundle\UserBundle\Entity\User: plaintext

  role_hierarchy:
      ROLE_ADMIN:       ROLE_USER
      ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

  providers:
      chain_provider:
        chain:
            providers: [in_memory, user_db]
      in_memory:
          memory:
              users:
                  API_DOC:  { password: @aaa, roles: [ 'ROLE_API_DOC' ] }
      user_db:
          entity: { class: XXX\PrivateApplication\Bundle\UserBundle\Entity\User, property: username }

Why the password of the logged user is always NULL from the constraint? If I print it from the controller it works... I don't use the FOSUserBundle.

Thank you

PS: I have found a similar question Using Symfony2 UserPassword validator in form type but without replies...

1
Did you ever find a solution to this? I am experiencing the same problem. - d0001

1 Answers

0
votes

Why the password of the logged user is always NULL from the constraint? If I print it from the controller it works...

The thing is, that in $password is your plain password from the form, not in getPassword()! You retrieve the 'signed in' user from the token of the security context and the encoded password is NULL. That means, that probably also getUsername() is NULL and getRoles() is just anonymous or guest (don't know it right now).

Then the login at all doesn't work and the token is only anonymous.