0
votes

I am stuck on how to change passwords by form submission in symfony - and I have no idea how to solve it, since it seems to behave differently.

So first of all - I am building a custom form, where a user enters their old password, and a new one twice. This is my FormType:

UserPasswordType.php

<?php

namespace App\Form;

use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;

class UserPasswordType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            //basically just a unmapped password field
            ->add('password_current', PasswordType::class, [
                'label' => 'user.password.current', 
                'mapped' => false,
                /*'constraints' => new UserPassword()*/
            ])
            //RepeatedType to get a confirm password field
            ->add('password', RepeatedType::class, [
                'type' => PasswordType::class,
                'first_name' => 'new', 
                'first_options' => ['label' => 'user.password.new'], 
                'second_name' => 'confirm', 
                'second_options' => ['label' => 'user.password.confirm']
            ])
            ->add('save', SubmitType::class, ['label' => 'app.save']);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => User::class
        ]);
    }
}

And here is my controller:

    /**
     * @Route("/user/settings", name="app_user_settings", defaults={"_locale":"%locale%"})
     * @IsGranted("ROLE_USER")
     */
    public function user_settings(Request $request, UserPasswordEncoderInterface $passwordEncoder)
    {
        $user = $this->getUser();
        $form = $this->createForm(UserPasswordType::class, $user);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $em = $this->getDoctrine()->getManager();

            //earlier: $current = $request->get('password_current'); - but does not work anymore?
            $current = $form['password']->getData();
            if ($passwordEncoder->isPasswordValid($user, $current)) { //fails always?
                $confirm = $form['password_confirm']->getData();
                $user->setPassword($passwordEncoder->encodePassword($user, $confirm));

                $em->persist($user);
                $em->flush();

                $this->addFlash('success', 'status.success.text');
                return $this->redirectToRoute('app_user');
            }
            $this->addFlash('danger', 'status.danger.text');
        }

        return $this->render('user/settings/index.html.twig', ['form' => $form->createView()]);
    }

So here are my problems:

  • for some reason $current = $request->get('password_current') stopped working - it always returns null

  • $passwordEncoder->isPasswordValid($user, $current) also stopped working - was fine earlier now it literally always returns false

  • the constraint UserPassword() in my formtype on password_current also keeps failing validation, even though the password is correct

  • the logged-in user who tries to change their password keeps getting kicked (logged) out when trying to change the password (the form gets reloaded first, then after any action he returns to the login) - but just when I check the old password - without checking the old password the user stays logged in and gets the success flash message

Sadly I am really confused right now, because most of my stuff worked yesterday. Anyone knows how to approach those issues? Thanks

1

1 Answers

1
votes

Well, I finally found the solution. I mapped the form type to the entity. So whenever I enter a new password, my "old" password actually is the new one. This makes every validation fail.

So just make a unmapped form type like this:

FormType

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('password_current', PasswordType::class, ['label' => 'user.password.current', 'constraints' => new UserPassword()])
            ->add('password', RepeatedType::class, ['type' => PasswordType::class, 'first_name' => 'new', 'first_options' => ['label' => 'user.password.new'], 'second_name' => 'confirm', 'second_options' => ['label' => 'user.password.confirm']])
            ->add('save', SubmitType::class, ['label' => 'app.save']);
    }

    /* Remove entire function
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => User::class
        ]);
    }*/

Controller

    //remove $user in createForm function
    //$form = $this->createForm(UserPasswordType::class, $user);
    $form = $this->createForm(UserPasswordType::class);

Funny side-effect. This also removes all other issues I had.