-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Security] Configurable execution order for firewall listeners #37337
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
src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/SortFirewallListenersPass.php
Show resolved
Hide resolved
7e359df
to
1972786
Compare
I've added the logout listener to the array of authentication listeners and removed the sorting logic from I have a bit of an issue with I've tried to resolve the |
When I make this change: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php
index 95e3e6e345..226eb1a440 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php
@@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase;
use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension;
use Symfony\Bundle\SecurityBundle\SecurityBundle;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
+use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManager;
@@ -69,10 +70,9 @@ abstract class CompleteConfigurationTest extends TestCase
$configs = [];
foreach (array_keys($arguments[1]->getValues()) as $contextId) {
$contextDef = $container->getDefinition($contextId);
- $arguments = $contextDef->getArguments();
- $listeners[] = array_map('strval', $arguments['index_0']->getValues());
+ $listeners[] = array_map('strval', $contextDef->getArgument(0)->getValues());
- $configDef = $container->getDefinition((string) $arguments['index_3']);
+ $configDef = $container->getDefinition((string) $contextDef->getArgument(3));
$configs[] = array_values($configDef->getArguments());
}
@@ -637,6 +637,7 @@ abstract class CompleteConfigurationTest extends TestCase
$container = new ContainerBuilder();
$container->setParameter('kernel.debug', false);
+ $container->register('cache.system', \stdClass::class);
$security = new SecurityExtension();
$container->registerExtension($security);
@@ -645,7 +646,7 @@ abstract class CompleteConfigurationTest extends TestCase
$bundle->build($container); // Attach all default factories
$this->getLoader($container)->load($file);
- $container->getCompilerPassConfig()->setOptimizationPasses([]);
+ $container->getCompilerPassConfig()->setOptimizationPasses([new ResolveChildDefinitionsPass()]);
$container->getCompilerPassConfig()->setRemovingPasses([]);
$container->getCompilerPassConfig()->setAfterRemovingPasses([]);
$container->compile(); There is only one failing test in the
Seems like there are many new empty security listeners. |
@wouterj Nice, I'll try that and have a look at this failing test. |
src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php
Outdated
Show resolved
Hide resolved
Can you maybe checkout the Travis failures?
|
src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php
Outdated
Show resolved
Hide resolved
So I tried to reproduce these failing tests now for an hour or so. I don't get it. I'm unable to reproduce these failures. And then there's this build on 1bc5c7b: https://travis-ci.org/github/symfony/symfony/builds/700163046
These fail in But interestingly PHP: 7.3 + deps=high builds fine without any errors. How's that possible? I've deliberately introduced a failing test, but it doesn't fail 🤔 It feels as if the dependencies of this PR (specifically |
Oh, looking at the composer logs I think |
…ervice if invalid (chalasr) This PR was merged into the 4.4 branch. Discussion ---------- [SecurityBundle] Drop cache.security_expression_language service if invalid | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | - | License | MIT | Doc PR | - Definition should be removed when its parent (`cache.system`) does not exist. Spotted in #37337 Commits ------- bc96693 [SecurityBundle] Drop cache.security_expression_language definition if invalid
…rs (scheb) This PR was merged into the 5.2-dev branch. Discussion ---------- [Security] Let security factories add firewall listeners | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | Deprecations? | no | License | MIT | Doc PR | n/a Hello there, I'm the author of `scheb/two-factor-bundle`, which extends Symfony's security layer with two-factor authentication. I've been closely following the recent changes by @wouterj to rework the security layer with "authenticators" (great work!). While I managed to make my bundle work with authenticators, I see some limitations in the security layer that I'd like to address to make such extensions easier to implement. With the new authenticator-based security system, it is no longer possible to add a authentication listener to the firewall. The only way to do it is a dirty compiler pass, which extends the argument on the `security.firewall.map.context.[firewallName]` service (like I do in: https://github.com/scheb/2fa/blob/ed2ce9804b6a78fe539894e77038ab96a52f5c56/src/bundle/DependencyInjection/Compiler/AccessListenerCompilerPass.php). This is quite ugly and hacky, so I believe there should be an easier and clean way to add firewall-level listeners. This PR adds an interface, which may be implemented by security factories and lets them add additional listeners to the firewall. Why would you want to do that? There are certain use-cases that require extra logic to handle a request within the firewall. For example in my bundle, I need to handle the intermediate state between login and the completion of two-factor authentication. So ideally, I'm able to execute some code from the firewall right before `Symfony\Component\Security\Http\Firewall\AccessListener`. In the old security system, I could handle this in my authentication listener, which I had to implement anyways. With the new authenticator-based system this option is gone. In the ideal world, I could add a firewall listener and tell it to execute between `LogoutListener` and `AccessListener`. This is a draft, so I'd like to hear your opinion on this :) There's another issue, regarding the order of execution, which I'm addressing with #37337. Commits ------- 0a4fcea Add interface to let security factories add their own firewall listeners
6c9a2bb
to
7f05dc3
Compare
@@ -31,7 +31,8 @@ | |||
}, | |||
"conflict": { | |||
"symfony/event-dispatcher": "<4.3", | |||
"symfony/security-csrf": "<4.4" | |||
"symfony/security-csrf": "<4.4", | |||
"symfony/security-bundle": "<5.2" |
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.
Have added this because both symfony/security
and symfony/security-bundle
need to be installed in the "=>5.2" version to work properly together.
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.
security-http
should not be aware of security-bundle
, only security-bundle knows about security components and has version constraints for them
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.
Hmm, I'm wondering how would you ensure compatibility then? Combining a 5.1 symfony/security-bundle
with 5.2 symfony/security
would lead to an odd behavior.
@@ -285,6 +284,7 @@ private function createFirewalls(array $config, ContainerBuilder $container) | |||
->replaceArgument(1, $exceptionListener) | |||
->replaceArgument(2, $logoutListener) | |||
->replaceArgument(3, new Reference($configId)) | |||
->addTag('security.firewall_map_context') |
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.
Since #37368 has been merged, there now is a security.firewalls
parameter containing all firewall names. I propose to use that in the compiler pass instead of introducing a tag (see the referenced PR for a usage example).
Ryan tried to not introduce SecurityBundle "internal" tags, as this might confuse users. E.g. PHPstorm will auto complete this, so we should try to not clutter the autocompletion list with tags the user should never use. (in the end, we should maybe introduce the concept of internal tags?)
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.
Rebased and changed to use security.firewalls
now.
458d104
to
88d92a2
Compare
Hi @scheb! Is there anything missing from this PR, or can it be marked as ready-to-merge? I think it's a great feature to add to Symfony and feature freeze is only in a few weeks (so it's good to have a clear overview of what PRs can be ready before this deadline). |
@wouterj Technically it's ready and I'd like to see it in 5.2. It's still a draft, because I need feedback on this issue:
It breaks the logout feature when you have such a combination in your application. And since @chalasr said
I'm not sure how we could solve this. I tend to say the change to add the What do you think? |
What about removing the LogoutListener for now, merge this PR. Then, we create a PR for the LogoutListener and we can discuss the best option for BC. How does that sound? |
88d92a2
to
91388e8
Compare
Hello there! I've removed the change regarding the LogoutListener, as Fabien suggested. The failed builds have been unrelated to the changes. So anything stopping up from merging the PR? |
@scheb Thanks for the ping. Unfortunately, without a comment, there is no way I can see that something had changed here, except checking from time to time. |
Thank you @scheb. |
@fabpot Thank you for merging! :) |
This PR was merged into the 5.2-dev branch. Discussion ---------- [Security] Add some missing CHANGELOG entries | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | no | Deprecations? | no | Tickets | - | License | MIT | Doc PR | - Mentions #37337 in CHANGELOG files. Commits ------- 46ce480 [Security] Add some missing CHANGELOG entries
Hello there, I'm the author of
scheb/two-factor-bundle
, which extends Symfony's security layer with two-factor authentication. I've been closely following the recent changes by @wouterj to rework the security layer with "authenticators" (great work!). While I managed to make my bundle work with authenticators, I see some limitations in the security layer that I'd like to address to make such extensions easier to implement.In #37336 I've submitted a draft to let security factories add their own authentication listeners to the firewall. This PR is intended to address the issue of execution order. If you look at the
Firewall
classsymfony/src/Symfony/Component/Security/Http/Firewall.php
Lines 62 to 82 in f64f59a
authentication listeners are executed in the order of their creation. Additionally, there's hardcoded logic to execute
Symfony\Component\Security\Http\Firewall\AccessListener
always last and the logout listener second to last. I'd like to have a more flexible approach, to remove the hardcoded order and give authentication listeners the ability to determine their execution order.I've added an optional interface to provide a priority to sort all registered authenitication listeners. Sorting is done in a compiler pass, so no time is wasted at runtime.
This is a draft, so I'd like to hear your opinion on this :)