I am working on a simple reset password system with Symfony4.
I don't know why isPasswordValid() always return false?
I am using Bcrypt to Hash the password
Here is some code of Security.yaml :
security:
encoders:
App\Entity\User:
algorithm: bcrypt
I don't know why isPasswordValid() Always returns false.
I manually tried this:
$pass="000000000";
dump($encoder->isPasswordValid($user, $pass));
die();
and it dump false..
Here is the function I wrote on the controller:
/**
* #Route("/password", name="change_pass", methods={"GET","POST"})
* #IsGranted("ROLE_USER")
*/
public function edit(Request $request,UserPasswordEncoderInterface $encoder): Response
{
$user = $this->getUser();
$form = $this->createForm(ResetPassType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$oldPassword = $request->request->get('reset_pass')['oldPassword'];
$newPassword = $user->getPassword();
if ($encoder->isPasswordValid($user, $oldPassword)) {
$hash = $encoder->encodePassword($user,$newPassword);
$user->setPassword($hash);
$this->getDoctrine()->getManager()->flush();
$this->addFlash('success', 'Your password is succesfully changed');
}else {
$this->addFlash('fail', 'old password is wrong');
}
}
$this->getDoctrine()->getManager()->refresh($user);
return $this->render('consultant/changepass.html.twig', [
'form' => $form->createView(),
]);
}
Here is the form ResetPassType :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('oldPassword', PasswordType::class, [
'mapped' => false,])
->add('password',PasswordType::class)
;
}
Short answer: you are using the new password as the old password's hash.
Explanation:
I guess you bound your form to your user class.
The password field in your form is mapped, meaning it updates your user's password property.
Therefore, as the property should contain the hash of the "old password" but contains your new password, it can't validate.
Solution and improvements:
The quickest solution would be to "unmap" the password field or to remove the data_class form option, and to replace a line in your controller:
// before: $newPassword = $user->getPassword();
$newPassword = $form->get('password')->getData();
I'd suggest to:
unbind the form to your user class
use the UserPassword validation constraint for your oldPassword field
fetch the form data using $form->getData() instead of accessing the $request manually
You will change your form because if you keep the field password, the password of the user will be changed by form too, so the right way is :
-in your Form :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('oldPassword', PasswordType::class, [
'mapped' => false,])
->add('newPassword',PasswordType::class, [
'mapped' => false,])
;
}
and then in your controller change the way you get the new password
$oldPassword = $request->request->get('reset_pass')['oldPassword'];
$newPassword = $request->request->get('reset_pass')['newPassword'];
Have fun :)
Related
I executed the make:crud command for generate some files for the entity user.
All works like a charm, but I have one problem when I edit a user.
When I edit a user, I can :
change the password and save
or
don't modify the password and save
From the generated user 'edit' controller:
/**
* #Route("/{id}/edit", name="user_edit", methods={"GET","POST"})
*/
public function edit(Request $request, User $user): Response
{
$form = $this->createForm(UserType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// HERE : How can I check if the password was changed ?
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('user_index', [
'id' => $user->getId(),
]);
}
return $this->render('user/edit.html.twig', [
'user' => $user,
'form' => $form->createView(),
'title' => 'edit userr'
]);
}
How can I check if the password was changed ? The user variable contains the new password value...
If the password is new, I must use the encoder service. If not, I just must update the user with new data
For this it helps to have a class variable in your User that is not written to the database. In your form you can then use this variable to temporarily store the updated password in, that you then encode and remove. This is what the eraseCredentials()-method in the UserInterface is for.
For example in your User you could have
class User implements UserInterface
{
private $plainPassword;
// ...
public function setPlainPassword(string $plainPassword)
{
$this->plainPassword = $plainPassword;
}
public function getPlainPassword()
{
return $this->plainPassword;
}
public function eraseCredentials()
{
$this->plainPassword = null;
}
}
Notice how private $plainPassword does not have any ORM-annotations, that means it will not be stored in the database. You can however use validation constraints, e.g. if you want to make sure that passwords have a minimum length or a certain complexity. You will still require the original password field that stores the encrypted password.
You then add this field to your user update-form instead of the actual password field. In your controller you can then only check if the new plainPassword-field was filled out and then read the value, encode it and replace the actual password field.
if ($form->isSubmitted() && $form->isValid()) {
$user = $form->getData();
if ($user->getPlainPassword() !== null) {
$user->setPassword($this->userPasswordEncoder->encode(
$user->getPlainPassword(),
$user
);
}
// ...
Another way of doing this, without adding this "helper"-property to the user is using an unmapped form field:
# UserForm
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
// ...
->add('plainPassword', PasswordType::class, ['mapped' => false])
;
}
The controller will look similar, only you fetch the data from the unmapped field instead of from the user:
$form->get('plainPassword')->getData();
Introduction
I have been trying to figure out how to create a reset password form that's governed by username value.
The Error
Path Message Invalid value Violation
data.email This value should not be blank. null
ConstraintViolation {#945 ▼
-message: "This value should not be blank."
-messageTemplate: "This value should not be blank."
-parameters: [▶]
-plural: null
-root: Form {#620 ▶}
-propertyPath: "data.email"
-invalidValue: null
-constraint: NotBlank {#477 …}
-code: "c1051bb4-d103-4f74-8988-acbcafc7fdc3"
-cause: null
}
What's expected
Update my User Object with the new password.
My Code
ForgotController.php
I know this probably isn't the correct way to get the password, but searching Symfony 4 forgotten password form brings up symfony2.4 posts which aren't relevant to my version
<?php
namespace App\Controller\User;
use App\Entity\User;
use App\Form\User\ChangePasswordType;
use App\Repository\UserRepository;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class ForgotController extends Controller
{
public function forgot(Request $request, UserPasswordEncoderInterface $encoder)
{
$entityManager = $this->getDoctrine()->getManager();
$changePassword = $request->request->get('change_password');
$username = $changePassword['username'];
$password = $changePassword['plainPassword']['first'];
$user = $entityManager->getRepository(User::class)->findBy(['username' => $username]);
$userEntity = new User();
if (!$user) {
$this->addFlash('danger', 'User not found for '. $username);
}
$form = $this->createForm(ChangePasswordType::class, $userEntity);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
try {
$pass = $encoder->encodePassword($userEntity, $password);
$userEntity->setPassword($pass);
$entityManager->flush();
$this->addFlash('success', 'Password Changed!');
} catch (Exception $e) {
$this->addFlash('danger', 'Something went skew-if. Please try again.');
}
return $this->redirectToRoute('login');
}
return $this->render('user/forgot.html.twig', array('form' => $form->createView()));
}
}
ChangePasswordType.php
<?php
namespace App\Form\User;
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\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ChangePasswordType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('username', TextType::class)
->add('plainPassword', RepeatedType::class, array(
'type' => PasswordType::class,
'first_options' => array('label' => 'New Password'),
'second_options' => array('label' => 'Repeat New Password')
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => User::class
));
}
}
forgot.html.twig
{% include 'builder/header.html.twig' %}
<div class="user-container" id="user-content">
{% block body %}
{% include 'builder/notices.html.twig' %}
<div class="user-container">
<i class="fas fa-user-edit fa-5x"></i>
</div>
<hr />
{{ form_start(form) }}
{{ form_row(form.username, { 'attr': {'class': 'form-control'} }) }}
{{ form_row(form.plainPassword.first, { 'attr': {'class': 'form-control'} }) }}
{{ form_row(form.plainPassword.second, { 'attr': {'class': 'form-control'} }) }}
<div class="register-btn-container">
<button class="btn btn-danger" id="return-to-dash-btn" type="button">Cancel!</button>
<button class="btn btn-primary" type="submit">Update!</button>
</div>
{{ form_end(form) }}
{% endblock %}
</div>
{% include 'builder/footer.html.twig' %}
I'm not sure why email is even being mentioned unless it's trying to insert a new user into a database but it shouldn't be trying to do that based on my controller? How can I go about adding a forgot password form that's identified by username?
Since your change password form only needs two fields we will use an array instead of a user entity. Need a slight tweak to ChangePasswordType:
// ChangePasswordType
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
//'data_class' => User::class
));
}
Here is a working forgot action:
public function forgot(
Request $request,
UserPasswordEncoderInterface $encoder,
UserRepository $userRepository)
{
$userInfo = ['username' => null, 'plainPassword' => null];
$form = $this->createForm(ChangePasswordType::class, $userInfo);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$userInfo = $form->getData();
$username = $userInfo['username'];
$plainPassword = $userInfo['plainPassword'];
$user = $userRepository->findOneBy(['username' => $username]);
if ($user === null) {
$this->addFlash('danger', 'Invalid username');
return $this->redirectToRoute('forgot');
}
$password = $encoder->encodePassword($user, $plainPassword);
$user->setPassword($password);
$userRepository->flush();
return $this->redirectToRoute('login');
}
return $this->render('user/forgot.html.twig', array('form' => $form->createView()));
}
UserRepository is injected which get's rid of all the doctrine nonsense. There is one caveat here that I'll get back to.
We build the userInfo array and let the form processing do it's thing. We really don't want to mess with getting attributes directly from the request object if we don't have to.
Then we get our actual user entity to update. Notice the use of findOneBy instead of findBy. We check to make sure username is valid. If you really wanted to get fancy then you could add a validation constraint to the form to do this check automatically.
I got rid of all the try/catch stuff. It just clutters up your code. By this point if an exception is thrown then it is truly exceptional and can be handled by the default exception handlers.
You got the password encoder stuff just right.
And then instead of $entityManager->flush() I used $userRepository->flush(); Out of the box there is no flush method on repositories so you need to add one:
// UserRepository
public function flush()
{
$this->_em->flush();
}
I personally like dealing with just repositories and not the entity manager. But if want, you can just go back and inject the manager instead of the repository. Your call.
And as mentioned in the comments, you do want to add some security to prevent users from changing other users passwords.
Implement something along the lines of below - I have left pieces out like the templates and routing. This is just to help you along.
Form 1: ForgottenUserType - use enters only username/email and submits
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add("username", null, array(
"label" => "Email",
"attr" => array(
"class" => "form-control",
"id" => "basic-url",
"placeholder" => "Email address for your account"
),
"constraints" => array(
new Email(array("message" => "Invalid Email"))
)
));
}
Form 2: ChangePasswordFormType - user enters & repeats new password.
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder
->add('plainPassword', RepeatedType::class, array(
'type' => PasswordType::class,
'required' => false,
'first_options' => array('label' => 'New password'),
'second_options' => array('label' => 'Confirm new password'),
'invalid_message' => 'The password fields must match.',
))
;
}
Controller: ResetPasswordController - handles user lookup request of Form 1 and Password reset request for Form 2:
<?php
namespace App\Controller\User;
use App\Entity\User;
use App\Form\User\ChangePasswordType;
use App\Repository\UserRepository;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class ResetPasswordController extends Controller
{
/**
* Action for when a user has forgotten their password, to request ForgottenUser form
*
* #param Request $request
*/
public function requestAction(Request $request)
{
$tmpUser = new User();
$entityManager = $this->getDoctrine()->getManager();
$form = $this->createForm(ForgottenUserType::class, $tmpUser);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$user = $entityManager->getRepository(User::class)->findBy(['username' => $tmpUser->getUsername()]);
if ($user) {
//check and set token
if (null === $user->getConfirmationToken()) {
/** #var $tokenGenerator TokenGeneratorInterface */
$token = md5(uniqid($user->getUsername(), true)); //some unique token (you can create a nicer token generator in standalone class with a service)
$user->setConfirmationToken($token);
$user->setPasswordRequestedAt(new \DateTime());
$em->persist($user);
$em->flush();)
$this->addFlash('Info', 'If user is found, you will receive an email with further instructions.');
//send email using swiftmailer & include url with token
}
} else {
//return to requestAction.
}
}
//request template contains the ForgottenUserType form
return $this->render(":path/to/template:request.html.twig", array(
"forgotten_form" => $form->createView()
));
}
/**
* Reset user password.
*
* #param Request $request
* #param $token
*/
public function resetAction(Request $request, $token)
{
$entityManager = $this->getDoctrine()->getManager();
$user = $entityManager->getRepository(User::class)->findBy(['confirmationToken' => $token]);
if (null === $user) {
return new RedirectResponse($this->generateUrl('resetting_request')); //to requestAction above. / create route
}
$form = $this->createForm(ChangePasswordFormType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$user->SetConfirmationToken(null);
$user->setPasswordRequestedAt(null);
$entityManager->persist($user);
$entityManager->flush()
$this->addFlash("success", "Your password has been reset, log in now.");
$url = $this->generateUrl('app.login'); //route to login page
$response = new RedirectResponse($url);
return $response;
}
//reset template contains the ChangePasswordFormType form
return $this->render(':path/to/forgottenpasswordtemplate:reset.html.twig', array(
'token' => $token,
'form' => $form->createView(),
));
}
}
repository with issue
I have a form for entity User with email field:
->add('email', EmailType::class, [
'constraints' => [
new NotBlank(),
new Email([
'checkMX' => true,
])
],
'required' => true
])
when i'm editing email to something like test#gmail.com1 and submit form it shows me error "This value is not a valid email address." THat's ok, but after that symfony populate wrong email into token and when i'm going to any other page or just reload page, i'm getting this:
WARNING security Username could not be found in the selected user
provider.
i think question is: why symfony populate wrong Email that failed validation into token and how i could prevent it?
controller:
public function meSettingsAction(Request $request)
{
$user = $this->getUser();
$userUnSubscribed = $this->getDoctrine()->getRepository('AppBundle:UserUnsubs')->findOneBy(
[
'email' => $user->getEmail(),
]
);
$form = $this->createForm(UserSettingsType::class, $user);
$form->get('subscribed')->setData(!(bool)$userUnSubscribed);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/**
* #var $user User
*/
$user = $form->getData();
/** #var UploadedFile $avatar */
$avatar = $request->files->get('user_settings')['photo'];
$em = $this->getDoctrine()->getManager();
if ($avatar) {
$avatar_content = file_get_contents($avatar->getRealPath());
$avatarName = uniqid().'.jpg';
$oldAvatar = $user->getPhoto();
$user
->setState(User::PHOTO_STATE_UNCHECKED)
->setPhoto($avatarName);
$gearmanClient = $this->get('gearman.client');
$gearmanClient->doBackgroundDependsOnEnv(
'avatar_content_upload',
serialize(['content' => $avatar_content, 'avatarName' => $avatarName, 'oldAvatar' => $oldAvatar])
);
}
$subscribed = $form->get('subscribed')->getData();
if ((bool)$userUnSubscribed && $subscribed) {
$em->remove($userUnSubscribed);
} elseif (!(bool)$userUnSubscribed && !$subscribed) {
$userUnSubscribed = new UserUnsubs();
$userUnSubscribed->setEmail($form->get('email')->getData())->setTs(time());
$em->persist($userUnSubscribed);
}
$user->setLastTs(time());
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
$this->get('user.manager')->refresh($user);
return $this->redirectToRoute('me');
}
return $this->render(
':user:settings.html.twig',
[
'form' => $form->createView(),
]
);
}
UPD:
it works fine if i change in OAuthProvider:
/**
* #param \Symfony\Component\Security\Core\User\UserInterface $user
*
* #return \Symfony\Component\Security\Core\User\UserInterface
*/
public function refreshUser(UserInterface $user)
{
return $this->loadUserByUsername($user->getName());
}
to:
/**
* #param \Symfony\Component\Security\Core\User\UserInterface $user
*
* #return \Symfony\Component\Security\Core\User\UserInterface
*/
public function refreshUser(UserInterface $user)
{
return $this->userManager($user->getId());
}
but it seems to be dirty hack.
Thanks.
Your user token seems to be updated by the form, even if the email constraint stop the flush.
Can you check if your form past the isValid function ?
You can maybe try to avoid it with an event listener or a validator.
With an event SUBMIT you should be able to check the email integrity, and then add a FormError to avoid the refreshUser.
This is a tricky one, thanks to the repository it was easier to isolate the problem. You are binding the user object form the authentication token to the createForm() method. After the
$form->handleRequest($request)
call the email off the token user object is updated.
I first thought to solve this by implementing the EquatableInterface.html in the User entity but this did not work, as the compared object already had the wrong email address set.
It may also be useful to implement the EquatableInterface interface, which defines a method to check if the user is equal to the current user. This interface requires an isEqualTo() method.)
Than I thought about forcing a reload of the user from the db and resetting the security token, but in the it came to my mind, that it might be sufficient to just refresh the current user object from the database in case the form fails:
$this->get('doctrine')->getManager()->refresh($this->getUser());`
In your controller, this would solve your issue.
/**
* #Route("/edit_me", name="edit")
* #Security("has_role('ROLE_USER')")
*/
public function editMyselfAction(Request $request) {
$form = $this->createForm(User::class, $this->getUser());
if ($request->isMethod(Request::METHOD_POST)) {
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$user = $form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
} else {
$this->get('doctrine')->getManager()->refresh($this->getUser());
}
}
return $this->render(':security:edit.html.twig',['form' => $form->createView()]);
}
Alternative solution
The issue at the Symfony repository resulted in some valuable input about Avoiding Entities in Forms and
Decoupling Your Security User which provides a more complex approach for a solution to your problem.
on my profile update form i have fullname password and confirm password fields . currently my validations are works only for
empty fields and password mismatch. but how can i add password charactor limits validations as well ?
password should be min:5 and max 15 charactors.
please advice.
public function changePasswordPost()
{
$user = Auth::user();
if (Input::get('password')) {
if (Input::get('password') !== Input::get('confirm_password')) {
return Redirect::route('admin-change-password')->with('error', 'Password field is not identical to Confirm Password.');
}
$user->update();
return Redirect::route('admin-change-password')->with('success', 'You have successfully updated login details.');
}
return Redirect::route('admin-change-password')->with('error', 'Input Missing');
}
You need to do something like this:
use Validator;
public function changePasswordPost(Request $request)
{
$user = Auth::user();
if ($request->get('password')) {
if (($request->get('password') !== $request->get('confirm_password')) ||
(Validator::make($request->all(), ['password' => 'min:5|max:15'])->fails())) {
return redicrect()->route('admin-change-password')->with('error', 'Password field is not identical to Confirm Password.');
}
$user->update();
return redirect()->route('admin-change-password')->with('success', 'You have successfully updated login details.');
}
return redirect()->route('admin-change-password')->with('error', 'Input Missing');
}
I haven't tested this code but the point is that you need to use Validator class from laravel. Note that I have changed some of the stuff to use laravel-5.1 friendly API.
Note that you can get cleaner code by adding Validation before you do anything. Something like this:
public function changePasswordPost(Request $request)
{
/**
* This basically captures your password matching
* and password length cases in a compact way so
* you don't need all the if statements.
*/
$validation = Validator::make($request->all(),
['password' => 'required|min:5|max:15',
'confirm_password' => 'required|same:password']);
if ($validation->fails())
{
response()->redirect('admin-change-password')->with('error', 'bad input');
}
/**
* Here you do the rest of the processing like updating the database.
*/
}
In my Symfony2 application I created a custom form to edit objects of a User class. Users have a password property which contains a hash of the user's password. For obvious reasons, I do not want to echo this property's value into a field. However, I want a password field on my form so that when editing a user it is possible to change the user's password.
This password field should behave as follows:
If the user has a password set, then the field should contain ********.
If the user has no password set, then the field should be empty.
(It turns out the previous two points are impossible to achieve with my current architecture, so instead I am going for:) When the page is loaded, the field should be empty, regardless of whether the user has a password set.
If the field is posted with content, then the user's password should be set to the hashed value of the field.
If the field is posted empty, the user's password should not be changed and, more importantly, not cleared.
I thought of implementing this with a custom data transformer. However, the data transformer does not provide me with a way to skip updating the user's password property when the password field is posted empty.
Where do I need to extend the framework to add custom logic deciding which fields should be updated?
UPDATE
This is the legacy code I am trying to replace:
/* SomeController.php */
$pass = $request->get('password');
if (strlen($pass) >= 5 && strlen($pass) <= 16) {
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword($pass, $user->getSalt());
$user->setPassword($password);
}
I can live with removing the string length checks. All I want to check is whether something has been entered.
As you can see, I can not simply move this code to a data transformer as it needs to access the $user which is the user we are currently editing. I don't think it is a good idea to create a service providing this value.
Just insert a control directly into your entity method and use data transformer (as you have insight)
So your entity will be
class User
{
//some properties and methods here
public function setPassword($pwd = null) {
if (null !== $pwd) {
$this->password = //do actions here like hash or whatever
}
//don't update the password
}
}
If you want to take advantage only of DataTransformers, you could still do what you need that way
use Symfony\Component\DependencyInjection\ContainerInterface;
class PasswordTransformer implements DataTransformerInterface
{
private $ci;
public function __construct(ContainerInterface $ci) {
$this->ci = $ci;
}
//...
public function reverseTransform($form_password) {
if (!$form_password) {
//return password already stored
return $this->ci->get('security.context')
->getToken()
->getUser()
->getPassword();
}
}
}
Of course you need to inject service_container service into you data transformer (or better, you should inject it into your form type's selector and pass to DataTransformer constructor as follows:
services:
your.bundle.password_selector_type:
class: Your\Bundle\Form\Type\PasswordSelectorType
arguments: ["#security.context"]
tags:
- { name: form.type, alias: password_selector_type }
For the form part, you should take a look a this widget.
http://symfony.com/doc/current/reference/forms/types/repeated.html
It provides an easy way to ask and treat confirmation on a field (it also hide values with stars when you set type to password).
$builder->add('password', 'repeated', array(
'type' => 'password',
'invalid_message' => 'The password fields must match.',
'options' => array('attr' => array('class' => 'password-field')),
'required' => true,
'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Repeat Password')));
It will check first and second options to be equal. If that's the case then your form will be considered as valid. Otherwise, invalid_message will be displayed and first field will set to the content typed by user while confirmation field (second option) will be emptied.
You can add some logic afterwards like hashing the password to finally persist your entity. (Extracting it in a form handler would be a good practice).
Here is what I came up with for now but I am not happy with the solution as it involves custom form processing in the controller. However, so far this is the only way I have found to make it work.
My form class adds an unmapped field for the user's password:
class UserType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('username')
->add('displayName')
->add('password', 'password', ['mapped' => false]);
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array('data_class' => 'MyCompany\MyBundle\Entity\User'));
}
public function getName() {
return 'user';
}
}
This field is then processed manually in my controller class:
class UserAdminController extends Controller {
public function editUserAction($userId, Request $request) {
$user = $this->getDoctrine()->getRepository('MyCompanyMyBundle:User')->findOneById($userId);
$form = $this->createForm('user', $user);
$form->handleRequest($request);
if ($form->isValid()) {
$newPassword = $form['password']->getData();
if ($newPassword !== "") {
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword($newPassword, $user->getSalt());
$user->setPassword($password);
}
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
}
return $this->render(
"MyCompanyMyBundle:Admin/Management/User:Edit.html.php",
[
"form" => $form->createView()
]
);
}
}