Skip to content

[Security][DX] Added SecurityPasswordType #35654

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed

[Security][DX] Added SecurityPasswordType #35654

wants to merge 4 commits into from

Conversation

pyrech
Copy link
Contributor

@pyrech pyrech commented Feb 9, 2020

Q A
Branch? master
Bug fix? no
New feature? yes
Deprecations? no
Tickets Fix #...
License MIT
Doc PR symfony/symfony-docs#...

Few days ago, I discussed with @lyrixx about how to simplify user password handling in EasyAdmin. Because currently if we want to make user's password administrable we have to create our own custom controller to interact with UserPasswordEncoder. For better DX we concluded that it would be better to add a native SecurityPasswordType in Symfony. So here we go:

  • This new form type inherits from PasswordType.
  • It automatically detects the user object from the parent form. This can be overridden with the option security_user.
  • If no data is submitted, the old password is kept.

I will see to add tests if the idea is accepted.

@carsonbot carsonbot added Status: Needs Review Security DX DX = Developer eXperience (anything that improves the experience of using Symfony) Feature labels Feb 9, 2020
Copy link
Member

@wouterj wouterj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this idea! I've left two minor comments.

@nicolas-grekas nicolas-grekas added this to the next milestone Feb 9, 2020
@lyrixx
Copy link
Member

lyrixx commented Feb 10, 2020

@pyrech pyrech requested a review from xabbuh as a code owner February 16, 2020 21:05
@pyrech
Copy link
Contributor Author

pyrech commented Feb 16, 2020

I moved the form type to the form component in a new SecurityExtension 👍

I also added some tests. I'm unsure if I need to add a dev-dependency on symfony/security inside symfony/form component and if yes, on which version : 5.1 but not released, will this break the CI?

@lyrixx
Copy link
Member

lyrixx commented Feb 17, 2020

I also added some tests. I'm unsure if I need to add a dev-dependency on symfony/security inside symfony/form component and if yes, on which version : 5.1 but not released, will this break the CI?

Yes :) IHMO you can target 3.4 because it works with 3.4 :)

public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('security_user', null);
$resolver->setAllowedTypes('security_user', [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the statement should be in one line

$resolver->setAllowedTypes('security_user', [
'null',
UserInterface::class,
]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we've recently added a fluent interface to define options, let's use it.

throw new InvalidConfigurationException(sprintf('You should either use "%s" inside a parent form where data is an instance of "%s" or specify the user in "security_user" option', self::class, UserInterface::class));
}

$securityUser = $parentData;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the aux variable can be avoided here. i.e. $securityUser = $event->getForm()->getParent()->getData()

$securityUser = $event->getForm()->getConfig()->getOption('security_user');

if (!$securityUser) {
$parentData = $event->getForm()->getParent()->getData();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the parent form cannot be null here, should we check?

<!-- SecurityExtension -->
<service id="form.extension.security" class="Symfony\Component\Form\Extension\Security\SecurityExtension">
<argument type="service" id="security.user_password_encoder" on-invalid="ignore"/>
<tag name="form.type_extension" />
Copy link
Member

@yceruto yceruto Feb 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The target class isn't a form type extension, but a "form extension" which doesn't need to be registered as service.

<tag name="form.type_extension" />
</service>
<service id="form.extension.security.type.security_password" class="Symfony\Component\Form\Extension\Security\Type\SecurityPasswordType">
<argument type="service" id="security.user_password_encoder" on-invalid="ignore"/>
Copy link
Member

@yceruto yceruto Feb 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class would be pointless without the required password encoder, I'd suggest make the argument mandatory and remove the whole service definition if "security" isn't enabled. See form.type_extension.form.validator as sample.

{
private $passwordEncoder;

public function __construct(UserPasswordEncoderInterface $passwordEncoder = null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, remove the nullable definition.

@yceruto
Copy link
Member

yceruto commented Feb 19, 2020

I like the idea too!

I'm wondering if this implementation fits better in a new "password type" extension rather than creating a new type and enable this feature through a new option "auto_encode".

I don't like much the idea to use the user instance. Instead, I would do what @wouterj has suggested here #35654 (comment).

@fabpot
Copy link
Member

fabpot commented Aug 11, 2020

@pyrech @lyrixx Have you read the comments from @yceruto? Any interest in resuming the work here?

Copy link
Member

@yceruto yceruto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After a second review I realized that this password encoding cannot be done before validation (that happens in POST_SUBMIT event on the root form) otherwise constraint validators will validate the encoded password (which would be wrong) rather than the plain password. Let's think about the Length() constraint for instance.

But, also changing the form data in POST_SUBMIT event is currently impossible, so I don't think the proposal is feasible this way.


$plainPassword = $event->getData();

$event->setData($plainPassword ? $this->passwordEncoder->encodePassword($securityUser, $plainPassword) : $securityUser->getPassword());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The submitted data cannot be changed this way before validating the plain password.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In effect, if the validation step fails, the encoded password will be visible to the end user.

@fabpot
Copy link
Member

fabpot commented Sep 1, 2020

@pyrech Can you have a look at the feedback?

@pyrech
Copy link
Contributor Author

pyrech commented Sep 1, 2020

I agree with the reviews that the current implementation is not ideal at all. I don't know how to improve it enough though so we might just close this PR 👍

@fabpot fabpot closed this Sep 1, 2020
@nicolas-grekas nicolas-grekas modified the milestones: next, 5.2 Oct 5, 2020
@Seb33300
Copy link
Contributor

Seb33300 commented Sep 1, 2021

I worked on a different approach here: #42807

@pyrech pyrech deleted the security-password-type branch January 7, 2023 18:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
DX DX = Developer eXperience (anything that improves the experience of using Symfony) Feature Security Status: Needs Work
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants