Skip to content

[RFC][Security] Simplify the User and UserChecker #26348

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
linaori opened this issue Mar 1, 2018 · 13 comments · Fixed by #40443
Closed

[RFC][Security] Simplify the User and UserChecker #26348

linaori opened this issue Mar 1, 2018 · 13 comments · Fixed by #40443
Labels
Feature Help wanted Issues and PRs which are looking for volunteers to complete them. Keep open RFC RFC = Request For Comments (proposals about features that you want to be discussed) Security

Comments

@linaori
Copy link
Contributor

linaori commented Mar 1, 2018

Q A
Bug report? no
Feature request? yes
BC Break report? no
RFC? yes
Symfony version 4.1

In #23508 the AdvancedUserInterface was deprecated and the UserChecker was made to support the User specifically. When 5.0 is released, that means that the UserChecker will effectively only work when the User is passed.

if (!$user instanceof AdvancedUserInterface && !$user instanceof User) {
return;
}
if ($user instanceof AdvancedUserInterface && !$user instanceof User) {
@trigger_error(sprintf('Calling %s with an AdvancedUserInterface is deprecated since Symfony 4.1. Create a custom user checker if you wish to keep this functionality.', __METHOD__), E_USER_DEPRECATED);
}

Some facts:

  • The User class is not used anywhere in the core except for the LdapUserProvider and InMemoryUserProvider.
  • The InMemoryUserProvider only supports the enabled feature of the AdvancedUserInterface.
  • The LdapUserProvider doesn't use any of the AdvancedUserInterface features.

RFC

In this RFC I would like to propose the following things to reduce the complexity:

  • Create an LdapUser, exclusively used by the LdapUserProvider. For BC it can extend the User for instanceof checks. Would only receive the currently used fields.
  • Create an InMemoryUser, exclusively used by the InMemoryUserProvider. For BC it can extend the User for instanceof checks. Would only receive the currently used fields.
  • Add a NullUserChecker, which features 0 checks. In 5.0 this would become the default UserChecker in the configuration.
  • Rename the current UserChecker to InMemoryUserChecker (BC: UserChecker extends InMemoryUserChecker). This UserChecker will no longer check anything but the enabled flag. Keep this user-checker as default in 4.x, but throw a deprecation if no user-checker is configured.
  • Deprecated the current User user class and instead document thoroughly how to create your own user class and checker.
  • Provide an easy-to-use UserInterface implementation for tests only.

Scenarios

I've been thinking about a few scenarios in which things will change for developers. Generally speaking, the impact should be rather small.

Using the User in custom UserProvider

Time to fix: roughly 15~30 minutes, requires proper documentation

In this scenario, the develop will have to carefully look at which features should be kept. A custom UserInterface implementation will have to be provided. If any of the AdvancedUserInterface domain-like features were used, these will have to be manually implemented, and a custom UserChecker has to be created and configured.

To do:

  • Write custom user class and configure it in the provider that would previously create a User
  • Write custom user checker and configure it in the firewall

Using the InMemoryUser and InMemoryUserProvider

Time to fix: roughly 2 minutes, proper deprecation should suffice

In this scenario, by default, the UserChecker would be configured by Symfony as default config. In 5.0 this would become the NullUserChecker, which does nothing. The InMemoryUser would be able to define an enabled field, which has to be checked by the InMemoryUserChecker. To fix this, configure the right user checker

To do:

  • Configure the user-checker option in your firewall to use the InMemoryUserChecker

Using the User in tests

Time to fix: roughly 5~30 minutes, depending on how often it's used

In this scenario, which I believe is more common, developers have used the User in their tests. This is also the case within Symfony. I think that it would be easiest to add a TestUser in a Test namespace and expose this (as opposed to tests in the Tests namespace).

To do:

  • Replace the usages of User by TestUser with the right namespace

If I've missed any scenarios, please let me know and I will add them. The goal of this RFC is to reduce domain logic in Symfony and having no ambiguous purposes for the User and User Checker. Additionally the NullUserChecker will simply do nothing, which is the default case with a custom user class as of 4.1.

@xabbuh xabbuh added Security Feature RFC RFC = Request For Comments (proposals about features that you want to be discussed) labels Mar 1, 2018
@Hanmac
Copy link
Contributor

Hanmac commented Mar 1, 2018

i am okay with that if the AdvancedUserInterface is removed.

what more changes need to be done for stuff like FOSUser or SonataUser?
they need to implement their wanted methods, and meed their own user checker?

@javiereguiluz
Copy link
Member

Iltar, what would be the impact for the most common use case? Simple app with no fancy needs that only wants a User class to represent their users which log in via a login form and don't have anything special: just a username + password + a bunch of properties (the username may be the email).

@linaori
Copy link
Contributor Author

linaori commented Mar 1, 2018

The most common use-case would probably be an entity with the UserInterface. If you wish to use the Symfony user, you have to have a customer user provider and custom logic to transform a username into that user. I believe that's actually more work than actually using an entity. However, my PoV is pretty limited as I've never seen the need to have either of the two options.

@stof
Copy link
Member

stof commented Mar 2, 2018

@javiereguiluz simple apps with no fancy needs don't need a user checker at all (just like they don't need to use the AdvancedUserInterface features today, and the built-in user checker is a no-op for non-advanced implementations)

@javiereguiluz
Copy link
Member

@stof yes ... and no. For example I consider the feature to mark a user account locked/expired/enabled (whatever you call it in your app) a very basic feature ... but it was provided in the AdvancedUserInterface.

I just wanted to say: OK if you want to improve the advanced case ... but please, don't make the simple case even harder. Thanks 🙏

@linaori
Copy link
Contributor Author

linaori commented Mar 2, 2018

For example I consider the feature to mark a user account locked/expired/enabled (whatever you call it in your app) a very basic feature ... but it was provided in the AdvancedUserInterface.

@javiereguiluz that case has already been made more "difficult" for custom user objects. The User in Symfony is final and none of the standard implementations have support for this. To use the User, you have to implement a lot more code. It would be easier to just create your own UserChecker and method for this. Writing those these few lines of code is maybe 5 minutes of work, as opposed to roughly 30 to 60 minutes (or more if you don't know how the security component works), just to write your own user provider.

@linaori
Copy link
Contributor Author

linaori commented Apr 13, 2018

Pinging @symfony/deciders whether or not I should work on this for Symfony 4.2+. It's a bit too much to do in a single PR and I don't want to do only half of the proposal 😄

I'm hoping to have covered all the upgrade paths in the initial post, but feel free to spew out more possible problems.

edit: seems like the deciders highlight is not working :(

@wouterj
Copy link
Member

wouterj commented Apr 21, 2018

  • I'm very much 👍 for most of the things you mention in here. In the end, I think the whole concept of user needs to be removed from the Security component. Doing this seems like a sensible step to make User and UserInterface less relevant.
  • I'm sure we can remove most of the methods from the UserInterface. However, I'm not so sure on how we can do things, while still perserving BC.

I just wanted to say: OK if you want to improve the advanced case ... but please, don't make the simple case even harder. Thanks pray

The simple case of a login form with just the User class will remain exactly the same. The only change is that users now get an InMemoryUser extending User (for BC) instead of User. So the only impact are the cases that explicitly typehint for User instead of UserInterface, which is never a good idea (and also redundant, as all methods of User are in the UserInterface).

The basic features of locking/enabling/whatever users has never been a simple feature in Symfony (that's why most people use FOSUserBundle). That's however completely unrelated to this RFC.

@linaori
Copy link
Contributor Author

linaori commented Apr 22, 2018

I'm sure we can remove most of the methods from the UserInterface. However, I'm not so sure on how we can do things, while still perserving BC.

Personally I would like to see the UserInterface (or the majority thereof) being used for the internal security process, I just would like to prevent it from being stored in the token, storing the identifier only. But something has to be created to fetch the current domain user (usually an entity), see #26971 (comment).

@carsonbot
Copy link

Thank you for this suggestion.
There has not been a lot of activity here for a while. Would you still like to see this feature?

@carsonbot
Copy link

Could I get an answer? If I do not hear anything I will assume this issue is resolved or abandoned. Please get back to me <3

@chalasr
Copy link
Member

chalasr commented Jan 4, 2021

Create an LdapUser, exclusively used by the LdapUserProvider. For BC it can extend the User for instanceof checks. Would only receive the currently used fields.

Done in #32824.
Help needed to make the remaining changes proposed here.

@carsonbot carsonbot removed the Stalled label Jan 4, 2021
@chalasr chalasr added the Help wanted Issues and PRs which are looking for volunteers to complete them. label Jan 4, 2021
@chalasr
Copy link
Member

chalasr commented Mar 12, 2021

See #40443

@fabpot fabpot closed this as completed in db87d72 Mar 16, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature Help wanted Issues and PRs which are looking for volunteers to complete them. Keep open RFC RFC = Request For Comments (proposals about features that you want to be discussed) Security
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants