Symfony2:无需密码即可编辑用户


Symfony2: Edit user without having password

在我的应用程序中,只有admin用户可以创建用户,理论上也可以编辑用户。到目前为止,仅使用Symfony安全系统(没有FOSUserBundle管理—不需要它的复杂性),创建具有不同角色的用户就可以了。我完全不知道的挑战是如何在不知道用户密码的情况下编辑用户。我总是遇到预期的验证错误

密码不能为空

。如何完成编辑?我肯定遗漏了一些非常基本的东西。

编辑动作:

    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',
            );
   }

更新行动:

   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()));
        }        
用户表单:

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: ',
            ))
    ;
}

在我看到一个更优雅的解决方案之前,我是这样想的:

  1. 创建UserEditType表单类,包含除密码字段外的所有字段
  2. 将UserEditType分配给非Default的验证组
  3. 配置2中验证组的密码长度约束。
  4. 修改编辑和更新动作以使用UserEditType

现在用户可以编辑没有密码!

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'),
        ));
    }

用户实体中的密码:

 * @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"}
 * )

更新行动:

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',
    );
}

我在我的项目中也遇到了同样的问题。

我通过从表单中删除密码字段来解决这个问题,只是为了我的编辑操作。

UserController中,我改变了editAction:

//find the line where the form is created
$editForm = $this->createForm(new UserType($this->container), $entity)
        ->remove('password'); //add this to remove the password field

我这样做(未经测试的代码)

我的用户实体有一个映射到数据库的密码属性
它还有一个'plainPassword'属性,没有映射

class User {
  // mapped
  private string $username;
  // mapped
  private string $password;
  // not mapped - simple php property
  private string $plainPassword;
  
  // getters/setters
  ...
}

表单使用的是plainPassword属性,而不是映射的密码。

class UserType extends AbstractType {
  ...
  public function buildForm(FormBuilderInterface $builder, array $options)
  {
    $builder
      ->add('username', TextType::class)
      ->add('plainPassword', PasswordType::class, ['required' => false])
  }
  ...
}

然后在某个地方,控制器在这个例子中,我们检查plainPassword是否不为空-因此密码试图被更改。

public function updateUserAction(User $user, Request $request)
{
  $form = $this->formFactory->createForm(UserType::class, $user);
  if($request->getMethod() === 'POST') {
    $form->handleRequest($request);
    if($form->isSubmitted() && $form->isValid()) {
      if(0 !== strlen($user->getPlainPassword()) {
        $encoder = $this->encoderFactory->getPasswordHasher($user);
        $salt = rtrim(str_replace('+', '.', base64_encode(random_bytes(32))), '=');
        $user->setSalt($salt);
        $hashedPassword = $encoder->hash($user->getPlainPassword(), $user->getSalt());
        $user->setPassword($hashedPassword);
        $user->setPlainPassword(null);
      }
      $this->em->persist($user);
      $this->em->flush();
      return $this->redirectToRoute('something');
    }
  }
}

如果您想使用remove()函数,那么也应用于表单设置。至少在Symfony 3.3中是这样。这样,您将避免上述@pusle所述的密码确认:

        $form = $this->formFactory->createForm()->remove("current_password");
        $form->setData($user)->remove("current_password");

这里是FOSUserBundle的ProfileController中的整个方法。


public function editDiffAction($id, Request $request)
    {
        $userManager = $this->get('fos_user.user_manager');
        $user = $userManager->findUserBy(['id' => $id]);
        $event = new GetResponseUserEvent($user, $request);
        if (null !== $event->getResponse()) {
            return $event->getResponse();
        }
        $form = $this->formFactory->createForm()->remove("current_password");
        $form->setData($user)->remove("current_password");
        $form->handleRequest($request);
        if ($form->isValid()) {
            $event = new FormEvent($form, $request);
            $userManager = $this->get('fos_user.user_manager');
            $userManager->updateUser($user);
            $url = $this->generateUrl('fos_user_profile_show_diff', array('id' => $user->getId() ));
            $response = new RedirectResponse($url);
            return $response;
        }
        return $this->render('@FOSUser/Profile/edit_diff.html.twig', array(
            'form' => $form->createView(),
            'user_id' => $user->getId(),
        ));
    }

只需添加'disabled' => 'disabled',此字段将不会被考虑。