-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Security] Configuring a user checker per firewall #14721
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
Conversation
@@ -65,6 +65,7 @@ protected function createAuthProvider(ContainerBuilder $container, $id, $config, | |||
$container | |||
->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.dao')) | |||
->replaceArgument(0, new Reference($userProviderId)) | |||
->replaceArgument(1, new Reference('security.chain_user_checker.'.$id)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't this mean that the ChainUserChecker
is always injected into this class? For BC, if the new user_checkers
isn't specified, it should still inject the exact same UserChecker
class as it did before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's correct, I did that on purpose. The 4 services using this are most likely not going to break BC, all of them are abstract and only created/defined through the 4 factories here. As far as I know, it's very, very difficult to properly extend those classes and even if you do, it's their own mistake if they use the UserChecker
as interface instead of the interface as defined in all 4 classes.
All custom implementations will still use security.user_checker
and decorating that will still work as it's simply added as default when not defined. I don't see anything break here, but maybe you can give an example which I might have missed.
If I don't inject the ChainUserProvider
if it's not defined, I won't be able to add a default configuration (which will work as it's just the default user checker being used).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iltar Very well explained. Fwiw, I agree with you: the only BC break would be if you overrode these core services, then somehow type-hinted something internally in your overridden class to the concrete UserProvider
. And I don't believe that is really a BC break. So I agree with you - no issue here :)
@iltar I think we should add a bit more docs to |
* | ||
* @author Iltar van der Berg <ivanderberg@hostnet.nl> | ||
*/ | ||
final class ChainUserChecker implements UserCheckerInterface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there's a precedence in the core for this to be final
. We only use final
on classes like FormEvents
or classes that are basically nice factories to help you get started (e.g. PropertyAccess
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I designed this to not be an extension point. It's supposed to be a simple delegator. It also prevents people from looking for a way to extend it, while composition might be nicer (though, won't work for this particular case).
I found that this works rather nice for the core as it's not part of the public API: http://ocramius.github.io/blog/when-to-declare-classes-final/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well, it certainly can't hurt - someone could always ask us to not make it final in the future. Anyways, we'll see what others think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hope that by making it final, people will wonder why and ask (on IRC). We can then explain why it's final and help the developer with his design and make suggestions on what to do.
Docs are added, for some reason the tests timed out. Any idea how to rebuild without a push? :( |
@iltar Can you rebase to get rid of the merge commit? That's a blocker for the merge. |
@fabpot done |
'Symfony\\Bundle\\SecurityBundle\\Security\\FirewallContext', | ||
'Symfony\\Component\\HttpFoundation\\RequestMatcher', | ||
'Symfony\Component\Security\Http\Firewall', | ||
'Symfony\Component\Security\Core\SecurityContext', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As long as the SecurityContext
is a service, IMO yes. The Interface is missing though..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this cause a deprecation warning since that file will be included now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a merge conflict here so I thought I merged it correct, but yes it will throw a notice now.
Other than my last comment, 👍 |
@weaverryan I think it's fixed now, running gitbash with a non-syntax highlighting php |
Re-read the whole PR and the associated issue discussion. I'm 👍 on making the user checker configurable by firewall. I'm -0 for the addition of the chain user checker (or any other way to allow more than one user checker). Being able to configure the service responsible for user checking should be enough for the vast majority of use cases. If you really want/need to provide more than one user checker, you can still do the chain yourself. Or do you envision third party bundle to come with user checkers that you will mix with your own. |
@fabpot I think you're right that it will cover most cases, but configuring only 1 user-checker per firewall would be hard to extend for those who do want multiple user-checkers. They will have to start decorating the service, collect and inject their own user-checkers etc. Providing a small extension point in the configuration with 1 additional class will save people with that specific case a lot of time, which includes myself. |
I understand your point, but I'm still not convinced. It adds some complexity (and the security component does not need more complexity) for very edge cases. For the rare occasion where several user checkers are needed, the work is not that important, especially because you can now easily decorate a service in Symfony. Also, people will start asking for more, like being able to prioritize their user checkers and more. I'm still convinced that having more than one user checker is over-engineering we don't need. |
I think it's okay to ship a chain user checker implementation with the Security component. However, I am not convinced if it is really necessary to configure it as the default user checker in the SecurityBundle. |
It doesn't really matter if it's default or not, the end-user won't notice it. I just have the ChainChecker injected because it's less configuration to inject the default in there than make a conditional default service. I can change it but I don't see any benefit to add an extra conditional for that. Regarding complexity, it won't really add a complex layer and has a nice documentation part in the book. It's merely another configuration option which people will find if they search for custom user checkers. |
So, what's the status of this PR? For the record, I'm still against adding the chain user checker. |
For me this is as done as done can be. The @fabpot if you know another way of achieving this without making it more complex for the developer, I'm more than happy to write it. |
@iltar What about setting the default user checker argument in the config resource files to |
@xabbuh You mean this bit? ->arrayNode('user_checkers')
->defaultValue(array('security.user_checker')) #this default?
->info('A list of user checker service ids to use when authenticating users in this firewall.')
->prototype('scalar')->end()
->end() If I set this to null by default, no checker is defined and I would have to set this as default if the array is empty and add it in the pass. I thought that was what the default value was for here, to save time in the extension/compiler pass. |
@iltar I mean places like https://github.com/symfony/symfony/blob/2.8/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml#L31 which will be modified by the factories anyway. |
That can be done, but what would be the benefit of this? |
Imo it would increase readability for everyone trying to read and understand the code (you do not get the wrong impression that a single user checker is injected, for example). This can be handy when you try to debug and issue with the security system. Anyway, I am 👍 for this change. |
@xabbuh like this? |
@iltar Yes, I think that's better. |
I like this PR, and I've played around with the code. But the chain user checker is a tough one: this feature won't be used by many people, and it's easily implementable on your own using normal OO practices. @iltar had once told me that a bundle could now provider different user checkers that you could opt into. If that's the case, then I think that bundle could also provider the chain user checker and configuration for you to put whatever you want into it. @iltar I know you disagree with me, but I'm 👍 on this without the chain user checker. |
@weaverryan This service is what's making it possible to provide an array of user-checkers, otherwise you would be limited to one checker. I can update the code to work without, but it would add a bit of extra work for developers who do want to implement it. (A): Allow only 1 user-checker per firewall to be configured and remove the chain-user checker If I implement (A), I will change back the /cc @fabpot @weaverryan would this be an ok setup to complete this PR? |
If I follow, (A) would just be about making the user_checker service configurable, right? |
@fabpot correct |
👍 |
I currently have added Instead of creating a |
I agree with the 👍 |
And it looks like the test failures are unrelated and should be resolved with a rebase. So, a rebase might be good :) |
Also added the chain_user_checker to freshly added authenticators: - LDAP - Guard
@weaverryan yeah there were some issues with some tests hence I didn't rebase yet, good that you reminded me of it. Lets see how this turns out. |
@@ -369,6 +370,8 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a | |||
// Exception listener | |||
$exceptionListener = new Reference($this->createExceptionListener($container, $firewall, $id, $configuredEntryPoint ?: $defaultEntryPoint, $firewall['stateless'])); | |||
|
|||
$container->setAlias(new Alias('security.user_checker.'.$id, false), $firewall['user_checker']); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quick question: Why do we need to create aliases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was the only way I was able to get the user checker defined per firewall. I use an Alias because of the following configuration:
security:
firewalls:
admin:
pattern: ^/admin
user_checker: app.admin_user_checker
secured_area:
pattern: ^/
user_checker: app.user_checker
When the firewall part is being created, I couldn't get the user checker in there, I can only distinguish them based on $id
which is the firewall name. By creating an alias per firewall, I can see which is which.
security.user_checker.admin
->app.admin_user_checker
security.user_checker.secured_area
->app.user_checker
If you have a better idea, I can see if I can make that work instead.
Any clues what the error in travis might be? hhvm locally for me doesn't run stable enough (more errors than travis) and I don't have php7 to test with. |
The failing tests are not related to your changes (they are already failing on the |
Thank you @iltar. |
This PR was squashed before being merged into the 2.8 branch (closes #5530). Discussion ---------- [Cookbook, Security] Added user_checkers.rst | Q | A | ------------- | --- | Doc fix? | no | New docs? | yes | Applies to | ^2.8 | Fixed tickets | symfony/symfony/pull/14721 This PR should provide sufficient docs to get started with custom user checkers as a new feature. Commits ------- 89b20a8 [Cookbook, Security] Added user_checkers.rst
Changed my base branch to avoid issues, closed old PR
This pull request adds support for a configurable user checker per firewall. An example could be:
The above example will use the
UserChecker
defined asapp.user_checker
. If theuser_checker
option is left empty,security.user_checker
will be used. If theuser_checkers
option is not defined, it will fall back to the original behavior to not break backwards compatibility and will validate using the existingUserChecker
:security.user_checker
.I left the default argument in the service definitions to be
security.user_checker
to include backwards compatibility for people who for some reason don't have the extension executed. You can obtain the checker for a specific firewall by appending the firewall name to it. For the firewallsecured_area
, this would besecurity.user_checker.secured_area
.