Symfony2: Edit user without having password

php, security, symfony

Solution

Until I see a more elegant solution, here's what I came up with:

- Create a UserEditType form class with all fields but the password field(s)

- Assign UserEditType to a validation group other than Default

- Configure the password length constraint to the validation group in 2.

- Modify the edit and update actions to use UserEditType

And now users can be edited without having the password!

UserEditType:

class UserEditType extends AbstractType {
    public function buildForm(FormBuilderInterface $builder, array $options) {
        $builder
                ->add('enabled', 'choice', array(
                    'choices' => array('Yes' => 'Yes', 'No' => 'No'),
                    'expanded' => true,
                    'multiple' => false,
                    'label' => 'Enabled: ',
                ))
                ->add('fname')
                ->add('sname')
                ->add('email')
                ->add('username')
                ->add('role', 'choice', array(
                    'choices' => array('ROLE_USER' => 'User', 'ROLE_ADMIN' => 'Admin'),
                    'expanded' => true,
                    'multiple' => false,
                    'label' => 'Group: ',
                ))
        ;
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver) {
        $resolver->setDefaults(array(
            'data_class' => 'Mana\ClientBundle\Entity\User',
            'validation_groups' => array('edit'),
        ));
    }

Password in User entity:

 * @ORM\Column(name="userpass", type="string", length=100, nullable=false)
 * @Assert\NotBlank(message="Password may not be empty")
 * @Assert\Length(
 *      min = "5",
 *      max = "12",
 *      minMessage = "Password must be at least 5 characters long",
 *      maxMessage = "Password cannot be longer than than 12 characters",
 *      groups = {"Default"}
 * )

Update action:

public function updateAction(Request $request, $id) {
    $em = $this->getDoctrine()->getManager();
    $user = $em->getRepository('ManaClientBundle:User')->find($id);

    $form = $this->createForm(new UserEditType(), $user);
    $form->bind($request);
    if ($form->isValid()) {
        $em->persist($user);
        $em->flush();
        return $this->redirect($this->generateUrl('user_main', array()));
    }
    return array(
        'form' => $form->createView(),
        'user' => $user,
        'title' => 'Edit user',
    );
}

Problem

In my application, only the admin user can create and, theoretically, edit users. So far, using only the Symfony security system (no FOSUserBundle management - its complexity is not required), creating users with varying roles is just fine. The challenge that totally escapes me is how to edit a user without knowing the user's password. I keep running into the expected validation error Password cannot be empty . How can editing be accomplished? I'm surely missing something very fundamental here. Edit action: ``` public function editAction($id) { $em = $this->getDoctrine()->getManager(); $user = $em->getRepository('ManaClientBundle:User')->find($id); $form = $this->createForm(new UserType(), $user); return array( 'form' => $form->createView(), 'user' => $user, 'title' => 'Edit user', ); } ``` Update action: ``` public function updateAction(Request $request, $id) { $em = $this->getDoctrine()->getManager(); $user = $em->getRepository('ManaClientBundle:User')->find($id); $originalPassword = $user->getPassword(); $form = $this->createForm(new UserType(), $user); $form->bind($request); if ($form->isValid()) { $plainPassword = $form->get('password')->getData(); if (!empty($plainPassword)) { //encode the password $encoder = $this->container->get('security.encoder_factory')->getEncoder($entity); //get encoder for hashing pwd later $tempPassword = $encoder->encodePassword($entity->getPassword(), $entity->getSalt()); $user->setPassword($tempPassword); } else { $user->setPassword($originalPassword); } $em->persist($user); $em->flush(); return $this->redirect($this->generateUrl('user_main', array())); } ``` User form: ``` public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('enabled', 'choice', array( 'choices' => array('Yes' => 'Yes', 'No' => 'No'), 'expanded' => true, 'multiple' => false, 'label' => 'Enabled: ', )) ->add('fname') ->add('sname') ->add('email') ->add('username') ->add('password', 'repeated', array( 'type' => 'password', 'invalid_message' => 'Password fields do not match', 'first_options' => array('label' => 'Password'), 'second_options' => array('label' => 'Repeat Password'), )) ->add('role', 'choice', array( 'choices' => array('ROLE_USER' => 'User', 'ROLE_ADMIN' => 'Admin'), 'expanded' => true, 'multiple' => false, 'label' => 'Group: ', )) ; } ```

Original source