Skip to content

Added a SecurityUserValueResolver for controllers #18510

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

Merged
merged 1 commit into from
Jul 1, 2016
Merged

Added a SecurityUserValueResolver for controllers #18510

merged 1 commit into from
Jul 1, 2016

Conversation

linaori
Copy link
Contributor

@linaori linaori commented Apr 12, 2016

Q A
Branch? master
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? yes
Tests pass? yes
Fixed tickets ~
License MIT
Doc PR ~

This PR uses the new ArgumentResolver to inject a security user when the signature implies so. This is based on the docs code example and existing pr on the SFEB.

With the new example you can do the following:

// when a User is mandatory, e.g. behind firewall
public function fooAction(UserInterface $user)

// when a User is optional, e.g. where it can be anonymous
public function barAction(UserInterface $user = null)

This deprecates the Controller::getUser() method.

I have added it on a priority of 40 so it falls just under the RequestValueResolver. This is because it's already used and the initial performance is less of an impact.

There was a comment asking if the controller_argument.value_resolver tag name wasn't too long. If decided this tag should change before 3.1 is released, I will update it in here.

RequestValueResolver contains a small codestyle consistency fix.

@linaori
Copy link
Contributor Author

linaori commented Apr 12, 2016

Build failures unrelated to this change. For 5.5 see #18512 and regarding HHVM, I have no idea what it's complaining about.

@javiereguiluz
Copy link
Member

Yes, I think controller_argument.value_resolver is too long. What about using any of the alternatives @iltar proposed in symfony/symfony-docs#6422 (comment)?

  • controller.value_resolver
  • controller.argument_value_resolver
  • controller.argument_value

@wouterj
Copy link
Member

wouterj commented Apr 12, 2016

controller.argument_value_resolver at least sounds better than controller_argument.value_resolver (it isn't shorter though). If people think it's too long, I think argument.value_resolver or controller.argument_resolver should be used (unless I'm wrong, Argumentresolver can be used for all classes and not just controllers, right?)

@linaori
Copy link
Contributor Author

linaori commented Apr 12, 2016

@wouterj in theory it can be used for anything yes, you simply give it a callable and it will resolve. It's just called $controller and is Controller oriented (same for exception messages).

argument.value_resolver seems like the best choice in that PoV.

@@ -1,7 +1,7 @@
{% extends "::base.html.twig" %}

{% block body %}
Hello {{ app.user.username }}!<br /><br />
Hello {{ user.username }}!<br /><br />
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this should be changed, app.user is still a valid way to access the user in a template.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm I was in the assumption there was another location where this was tested, I'll add a second test to verify this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The CsrfFormLoginBundle also has after_login.html.twig test and still does Hello {{ app.user.username }}!

@DavidBadura
Copy link
Contributor

Isn't it a conflict with Doctrine ParamConverter? What happens if i have an User Entity that implements the Security UserInterface?

/**
 * @Route("/user/{user}")
 */
public function fooAction(User $user) {}

In Symfony <= 3.0 you get the User Entity based on the "user" Argument. But what i get with this change? The logged user? In this case we have a BC break.

Btw. why you deprecate Controller::getUser() and force to use the typehint method? Like ParamConverter it is nice to have, but shouldn't be necessary. The base concept of a controller is to transform a request into a response.

@jvasseur
Copy link
Contributor

@DavidBadura this resolver have a lower priority that the RequestValueResolver that would fill the $user argument with the value resolved by the Doctrine ParamConvertor, so no BC break here.

@linaori
Copy link
Contributor Author

linaori commented Apr 12, 2016

@DavidBadura @jvasseur correct: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml#L28

I've done this on purpose for 100% BC reasons and the fact that this way you can define the values through attributes if you already have them. This is how the ControllerResolver used to do it as well.

I've deprecated the controller method because it does exactly the same as this. The request variant was also deprecated and removed in 3.0. Imo this is a cleaner way of getting the User than calling a method and checking what the return was. Another issue I sometimes see coming by, is that people with anon. can't figure out why they get no user back. This way you can see that it's always an object and you can easily typehint which user object. This also gives proper auto-completion in IDEs.

* file that was distributed with this source code.
*/

namespace Symfony\Bundle\SecurityBundle\ValueResolver;
Copy link
Contributor

Choose a reason for hiding this comment

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

What about Symfony\Bundle\SecurityBundle\ArgumentValueResolver ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got my vote but I don't know if it's a good idea to put things directly in the bundle root

Copy link
Contributor

Choose a reason for hiding this comment

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

If there is no other files requiring a HttpKernel or Controller (sub)namespace there is no reason to keep a complex tree in a bundle IMHO.

@stof
Copy link
Member

stof commented Apr 12, 2016

Typehinting only the User class to get the current user is indeed confusing, as you might need to get a User based on the URL or the current logged-in user depending on the cases (and maybe even both in the same controller action)

@linaori
Copy link
Contributor Author

linaori commented Apr 12, 2016

@stof, that's only the case if you happen to use your Entity as security user object, which imo is a bad practice and is causing the confusion. Retrieving a security user (important to the security system) should imo never be done via the url.

@stof
Copy link
Member

stof commented Apr 12, 2016

that's only the case if you happen to use your Entity as security user object, which imo is a bad practice and is causing the confusion

But this is the case in most almost all Symfony projects I know of (the other ones using some in memory providers for very basic setup), and probably in the vast majority of Symfony projects out there that I haven't reviewed as almost any tutorial does it too (and most people asking for support on the security system describe such setup too).
So introducing a feature which would break the parameter resolution for at least 90% of projects out there is bad. You cannot just say "this should not be done this way".

Retrieving a security user (important to the security system) should imo never be done via the url.

When the security user object is your entity, wanting a User object can mean 2 different things:

  • wanting the current user (which should come from the security context indeed, not from the URL)
  • wanting the user object based on the URL to display info about this user (and then you almost always need a security check to be sure that the current user can view the info about this user)

I'm not talking about getting the current user based on a parameter in the URL. I'm talking about getting any User based on this parameter.
For instance, on Github, you can look at the profile page of any user, not only about yourselves.

@linaori
Copy link
Contributor Author

linaori commented Apr 12, 2016

@stof so what are you recommending? Would this be a useless feature? I know I won't use it myself because the Token is enough for me (getUsername() > fetch actual user/client object and inject that). It seems like there's still animo for this feature though.

If someone uses the ParamConverter, pretty much everything can collide with that, especially given the silent activation of the listener. The only "broken" thing would be that if it does collide, the ParamConverter would get precedence over this feature.

@wouterj
Copy link
Member

wouterj commented Apr 12, 2016

What about injecting the current user only when typehinting for UserInterface?

@linaori
Copy link
Contributor Author

linaori commented Apr 13, 2016

@wouterj that could work, as that interface defines your security user. In that case should I un-deprecate the getUser() method?

@wouterj
Copy link
Member

wouterj commented Apr 13, 2016

@iltar why, this is still a replacement for the getUser getter afaics?

@linaori
Copy link
Contributor Author

linaori commented Apr 13, 2016

In the end, yes. Conceptually it's a bit further away; Getting "my user which I know it is" or getting "a UserInterface". If it would be @return UserInterface|null, I'd say it matches 100%, but it has @return mixed at the moment. In the end there's no difference and the object will be the same.

fabpot added a commit that referenced this pull request Apr 15, 2016
This PR was merged into the 3.1-dev branch.

Discussion
----------

[HttpKernel] Renamed the argument resolver tag

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | not if merged before 3.1
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | ~
| License       | MIT
| Doc PR        | ~

Changed as discussed several times: #18510 (comment), symfony/symfony-docs#6422 (comment).

Commits
-------

cd10057 Renamed argument resolver tag, added test
symfony-splitter pushed a commit to symfony/framework-bundle that referenced this pull request Apr 15, 2016
This PR was merged into the 3.1-dev branch.

Discussion
----------

[HttpKernel] Renamed the argument resolver tag

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | not if merged before 3.1
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | ~
| License       | MIT
| Doc PR        | ~

Changed as discussed several times: symfony/symfony#18510 (comment), symfony/symfony-docs#6422 (comment).

Commits
-------

cd10057 Renamed argument resolver tag, added test
@linaori
Copy link
Contributor Author

linaori commented Apr 15, 2016

Tag name is updated to controller.argument_value_resolver. Failure in appveyor is unreleated.

@@ -20,6 +20,11 @@

<service id="security.token_storage" class="Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage" />

<service id="security.user_value_resolver" class="Symfony\Bundle\SecurityBundle\SecurityUserValueResolver">
Copy link
Member

Choose a reason for hiding this comment

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

should be private

@linaori
Copy link
Contributor Author

linaori commented Apr 18, 2016

@xabbuh I've updated all deprecation notices to state the UserInterface instead, should be clear now what to do instead of getUser().

@fabpot was this feature still in time for 3.1?

@fabpot
Copy link
Member

fabpot commented Jun 15, 2016

It can be in 3.2 now (the changelogs should be updated).

@stof Is it ok as is or do you still have concerns about this feature? (I must admit that I haven't re-read the whole thread)

@linaori
Copy link
Contributor Author

linaori commented Jun 17, 2016

PR is updated and passing

@fabpot
Copy link
Member

fabpot commented Jun 21, 2016

ping @stof

* @throws \LogicException If SecurityBundle is not available
*
* @see TokenInterface::getUser()
*/
protected function getUser()
{
@trigger_error(sprintf('%s is deprecated as of 3.2 and will be removed in 4.0. You can typehint your method argument with %s instead.', __METHOD__, UserInterface::class), E_USER_DEPRECATED);
Copy link
Member

Choose a reason for hiding this comment

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

Missing parentheses after the first %s.

@fabpot
Copy link
Member

fabpot commented Jul 1, 2016

@iltar can you rebase this one? If @stof does not have any other concern, I'd like to merge it soon. Thanks.

@linaori
Copy link
Contributor Author

linaori commented Jul 1, 2016

@fabpot done

@fabpot
Copy link
Member

fabpot commented Jul 1, 2016

Thank you @iltar.

@fabpot fabpot merged commit d341889 into symfony:master Jul 1, 2016
fabpot added a commit that referenced this pull request Jul 1, 2016
This PR was merged into the 3.2-dev branch.

Discussion
----------

Added a SecurityUserValueResolver for controllers

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | ~
| License       | MIT
| Doc PR        | ~

This PR uses the new `ArgumentResolver` to inject a security user when the signature implies so. This is based on the [docs code example](symfony/symfony-docs#6438 (comment)) and [existing pr on the SFEB](sensiolabs/SensioFrameworkExtraBundle#327).

With the new example you can do the following:
```php
// when a User is mandatory, e.g. behind firewall
public function fooAction(UserInterface $user)

// when a User is optional, e.g. where it can be anonymous
public function barAction(UserInterface $user = null)
```
This deprecates the `Controller::getUser()` method.

I have added it on a priority of 40 so it falls just under the `RequestValueResolver`. This is because it's already used and the initial performance is less of an impact.

There was a comment asking if the `controller_argument.value_resolver` tag name wasn't too long. If decided this tag should change before 3.1 is released, I will update it in here.

*`RequestValueResolver` contains a small codestyle consistency fix.*

Commits
-------

d341889 Added a SecurityUserValueResolver for controllers
@ad3n
Copy link

ad3n commented Jul 19, 2016

@iltar : just a question, can i do like this use this feature?

indexAction(UserInterface $user, Request $request) {}

@linaori
Copy link
Contributor Author

linaori commented Jul 19, 2016

@ad3n yes, that should be the way to use it. The sequence of arguments doesn't matter. I still have to write the documentation but that will be done before 3.2 is released (I hope).

@ad3n
Copy link

ad3n commented Jul 19, 2016

@iltar : 👍 thanks for confirmation

fabpot added a commit that referenced this pull request Oct 15, 2016
This PR was merged into the 3.2-dev branch.

Discussion
----------

Remove the new SecurityUserValueResolver

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no (the feature hasn't been released yet)
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | n/a
| License       | MIT
| Doc PR        | n/a

Hi guys!

This is a revert for #18510 (ping @iltar), which is a nice idea, but will have some big practical implications:

1) You are only allowed to type-hint the argument with `UserInterface` exactly. For the 90% of Symfony project's that user a User entity for their User, this will be weird: I'll receive a `UserInterface`, that immediately call methods on it that aren't in the interface (and also, your IDE won't give you auto-completion). And as #18510 mentions, we can't allow people to type-hint their concrete `User` class, because this will conflict with SensioFWExtraBundle ParamConverter if there is a user id in the URL

2) Deprecating and removing `$this->getUser()` in a controller is a step back. Where we can, let's make controllers and services act *more* like each other. You can't call `$this->getUser()` in a service, but at least if you look at this method in `Controller`, you can see that it uses `security.token_storage` - which is how you will get the User object if you need it from within services.

Sorry for being late on this original issue! It looked good to me at first :).

Cheers!

Commits
-------

da7daee Removing the Controller::getUser() deprecation
@fabpot fabpot mentioned this pull request Oct 27, 2016
wouterj added a commit to symfony/symfony-docs that referenced this pull request Nov 19, 2016
This PR was merged into the master branch.

Discussion
----------

Added docs mentioning UserInterface in action args

Added notes about the `UserInterface` added in symfony/symfony#18510, was waiting for the in-deprecation of the `getUser()` method: symfony/symfony#19452.

Commits
-------

7849fa2 Added docs mentioning UserInterface in action args
@linaori linaori deleted the feature/security-user-value-resolver branch February 8, 2019 13:38
nicolas-grekas added a commit that referenced this pull request Nov 9, 2022
… than `EntityValueResolver` (kbond)

This PR was merged into the 6.2 branch.

Discussion
----------

[SecurityBundle] Set `UserValueResolver`'s priority higher than `EntityValueResolver`

| Q             | A
| ------------- | ---
| Branch?       | 6.2
| Bug fix?      | no
| New feature?  | no
| Deprecations? | no
| Tickets       | n/a
| License       | MIT
| Doc PR        | n/a

`UserValueResolver`'s priority is currently `40` and `EntityValueResolver`'s priority is `110` (configured in doctrine-bundle).

Currently, to use the `CurrentUser` attribute and `MapEntity` (when `auto_mapping` is enabled), you need to do the following to have it work:

```php
public function postAction(
  #[CurrentUser]
  #[MapEntity(disabled: true)]
  User $user,
  Post $post
)
```

This removes this need for `#[MapEntity(disabled: true)]` but I'm not sure the larger impact of increasing the priority of `UserValueResolver`. Here is some context as to why the priorities are they way they are:
- doctrine/DoctrineBundle#1554 (comment)
- #18510

Commits
-------

48499b9 Set `UserValueResolver`'s priority higher than `EntityValueResolver`
symfony-splitter pushed a commit to symfony/security-bundle that referenced this pull request Nov 9, 2022
… than `EntityValueResolver` (kbond)

This PR was merged into the 6.2 branch.

Discussion
----------

[SecurityBundle] Set `UserValueResolver`'s priority higher than `EntityValueResolver`

| Q             | A
| ------------- | ---
| Branch?       | 6.2
| Bug fix?      | no
| New feature?  | no
| Deprecations? | no
| Tickets       | n/a
| License       | MIT
| Doc PR        | n/a

`UserValueResolver`'s priority is currently `40` and `EntityValueResolver`'s priority is `110` (configured in doctrine-bundle).

Currently, to use the `CurrentUser` attribute and `MapEntity` (when `auto_mapping` is enabled), you need to do the following to have it work:

```php
public function postAction(
  #[CurrentUser]
  #[MapEntity(disabled: true)]
  User $user,
  Post $post
)
```

This removes this need for `#[MapEntity(disabled: true)]` but I'm not sure the larger impact of increasing the priority of `UserValueResolver`. Here is some context as to why the priorities are they way they are:
- doctrine/DoctrineBundle#1554 (comment)
- symfony/symfony#18510

Commits
-------

48499b99c4 Set `UserValueResolver`'s priority higher than `EntityValueResolver`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.