Problem/Motivation
Follow-up to #2286971: Remove dependency of current_user on request and authentication manager. Revisit service construction. The AuthenticationManager currently hard-codes a list of global providers as a default argument to its constructor. Contrib authentication providers wishing to add an additional global provider need to alter the definition of the authentication manager from within a ServiceProvider class in order to add a new global provider. This may work if only one contrib module does this, it will likely break if multiple service provider classes try to alter the same argument of the definition.
Proposed resolution
- Modify the
TaggedHandlersPasscompiler pass to be able to inject additional extra parameters which the AuthenticationManager would use to define at compile time which providers are global. - Replace the
$idparameter with a$provider_idparameter on theaddProvidermethod and remove the undocumented convention that names of authentication provider services need to be prefixed withauthentication.
This also helps resolving #2456283: AuthenticationManager::getSortedProviders is protected in contrib and therefore renders #2456303: AuthenticationManager needs interface and ::getProviderKeys unnecessary.
Contrib modules known to be blocked by this issue:
Remaining tasks
Reviews
Commit
User interface changes
None
API changes
- Contrib can now add global authentication providers by setting the
globaltag parameter to TRUE in the service definition. (API addition) E.g. inmy_module.services.yml
my_module.authentication.custom_authentication: class: Drupal\my_module\Authentication\CustomAuthenticationProvider arguments: ['@injected_service_1', '@injected_service_2'] tags: - { name: authentication_provider, provider_id: 'provider_id', priority: 0, global: TRUE } - The provider id needs to be specified explicitely and is not derived from the service id anymore (i.e. the string used in the
_authroute option to specify which providers are allowed on a route)
Beta phase evaluation
| Issue category | Task |
|---|---|
| Issue priority | Normal because it doesn't affect the normal operation the authentication system |
| Prioritized changes | This issue is a follow-up from a critical issue and a blocker for contributed projects. It will improve contrib experience by easily allowing the implementation of alternate authentication providers and mechanisms. |
| Disruption | Disruptive for contributed and custom modules because third-party authentication providers will need to update their service definitions and addProvider function signatures. |
| Comment | File | Size | Author |
|---|---|---|---|
| #29 | interdiff.txt | 812 bytes | almaudoh |
| #29 | revisit_authentication-2432585-29.patch | 16.89 KB | almaudoh |
Comments
Comment #1
dawehnerOne simple idea berdir had in mind which we could start with is to put the default into a container parameter, so you can override it much easier.
Comment #2
znerol commentedTagged as contrib project blocker because at the moment a custom
ServiceProviderclass is necessary in order to add new global authentication providers.Comment #3
znerol commentedComment #4
clemens.tolboomWhat blocker is there without a patch? I came here from #2456303: AuthenticationManager needs interface and ::getProviderKeys
Rest UI uses authentication manager for sure in #2456283: AuthenticationManager::getSortedProviders is protected
Comment #5
znerol commentedThat works well for per-site use-cases. It does not help with contrib modules providing new default providers. Perhaps this should be configuration instead?
Comment #6
dawehnerFair, I'm just a bit worried as thing inits config potentially earlier ... do you think about putting that information still in the container but retrieve it form config while rebuilding the container?
Comment #7
almaudoh commentedComment #8
almaudoh commentedOk, so an initial patch to kick this off.
Started with a container parameter
%authentication.global_providers%which is then updated by theAuthenticationManager::addProvider()call to incorporate providers that have been tagged asglobal. This allows contrib to update the list of global (default) authentication providers.Had to modify the service collector
TaggedHandlersPassto allow additional custom arguments in the tagged service definitions.Nomenclature is based on existing code in HEAD.
Comment #9
znerol commentedI think we do not need the container parameter if we can set the global flag on the tag. That means we could remove the constructor completely.
I'd use
$extra_paramsor$additional_paramsas the name.We still need to use
ifhere because the preceding condition is targeting the type-hint, not the parameter name. Also change the conditions to use the identity operator (===), instead of equality.Comment #10
znerol commentedOh, and we probably should add test coverage to
TaggedHandlersPassTest.Comment #11
znerol commentedAnd we probably should add back the
AuthenticationManagerInterfacebecause of theaddProvider()method. I've removed it prematurely.Comment #12
almaudoh commentedThanks @znerol for the review
#9
1. Good point. Fixed.
2. Fixed
3. Fixed
Also added tests to
TaggedHandlersPassTestfor extra arguments and re-introduced theAuthenticationManagerInterfacewithaddProvider()method in there.Comment #14
almaudoh commentedFixed some docs typos, used more explicit variable names and adjusted
AuthenticationManagerTestto match the new interface.Comment #16
almaudoh commentedWas a bit too over-zealous with the variable renames.
Comment #17
znerol commentedThanks! Test coverage looks great now for the
TaggedServiceProvider.At devdays we also discuss the weird magic we use to derive the
provider_idfrom the service name (i.e. the service id of an authentication provider must follow the schemaauthentication.provider_idand thus cannot be prefixed with the module name). A better way to resolve that problem would be to add another extra parameterprovider_idand explicitly specify it in every service definition. E.g.:Thanks to the new extra parameters it will likely be enough to simply rename the
$idparamater to$provider_idinAuthenticationManager::addProvider. Thought that might be a BC break. If we want to avoid this, then we'd need to add the an optional$provider_idparameter and initialize it with the current magic ifNULL.An explicit
$provider_idon the service definition also would make it simpler for contrib to collect all provider ids (e.g. #2456283: AuthenticationManager::getSortedProviders is protected).Comment #18
cpj commented@znerol. #17.
I like the idea of an explicit
$provider_idto the current magic, so +1 for that.As you say, it might be a very minor BC without the fallback you suggest, but to avoid confusion, I personally think it's preferable to only support one way of defining the
$provider_id.Comment #19
almaudoh commented+1 also to explicit $provider_id. Dunno how many contrib modules will break, but explicitness is always better.
Comment #20
almaudoh commentedImplemented explicit
$provider_idand adjusted tests to match. Let's see what breaks.Comment #21
znerol commentedGreat!
Can you modify the function signature such that mandatory
$provider_idis before the optional$priority? Also specify in the@paramthat$priorityand$globalare not required (prefix with(optional)).Comment #22
almaudoh commentedTried that but it breaks because of how
TaggedHandlersPassis written. I will see how to make that work.Comment #23
znerol commentedI think we may want to merge the arguments before sorting.
Comment #24
almaudoh commentedYeah, it was easier than I thought. Also improved the test coverage.
Comment #25
almaudoh commentedSimplified the code.
Comment #26
znerol commentedI suspect that this overrides the default value of an optional extra paremeter. That parameter might then receive
NULLinstead of the default value.I do not intend to hold off the patch but it is a bit weird to have mandatory parameters after optional ones. In fact that makes the optional ones mandatory also. Perhaps add extra paremeters with default values?
Comment #27
almaudoh commentedYou're right. Didn't see that initially. To fix it we have to re-introduce the
else ifafterif ($param->getClass()) {since we assume that the only classed argument will be the handler itself, while others will be scalars.Also added test coverage for the default values.
Fixed.
Comment #29
almaudoh commentedMissed out the
early_translation_testauthentication provider. (I wonder how it managed to pass before).Comment #30
almaudoh commentedUpdated IS.
I removed this from the IS scope considering that is is easy to swap out the authentication manager service with a single authentication provider where needed. Correct me if I'm wrong :).
Comment #31
znerol commentedComment #32
znerol commentedIn fact it would be nice if we did that automatically. This might be easy to implement with a service alias: By default
authenticationpoints touser.authentication.cookie. If number of services with theauthentication_providertag is > 1, thenauthenticationpoints toauthentication.manager. This could be easily be implemented in a simple additional compiler pass.The benefit would be that the wast majority of sites would automatically run without the authentication manager.
Comment #33
andypostWe use a sort of that for language manager, while no language module available we use very primitive one that shipped with core
Comment #34
znerol commentedSo, I profiled how much #32 would bring us performance wise by hard-wiring the cookie authentication into
authenticationservice by hand. In fact we save 65 function calls and 30 kilobyte for authenticated users (wall time report does not look reliable). Difference for anonymous users was zero function calls. I had to hack xhprof-kitindex-perf.phpin order to inject the session cookie directly via$_COOKIEsuperglobal.That said I think the performance gain is too little to justify adding another compiler pass.
The changes in
TaggedHandlerPassare fully covered by unit tests, this is good to go.Comment #35
xjmThis looks good to me, and can be accepted during the beta potentially as a followup to #2286971: Remove dependency of current_user on request and authentication manager, and also if it is indeed blocking any contrib project (the summary doesn't indicate which).
However, we need a change record (or maybe an update to an existing change record). I think we also need a somewhat clearer picture of the scope of the disruption. As far as I can tell the disruption is the following things (none too major):
This adds a new interface, which theoretically could break extending implementations. However, since the method was already provided in the class, existing implementations would continue to work.
A parameter is being added to this method. However, the parameter is optional, so existing calls to the method would not break. Only I think any overridden implementations of it, since AFAIK PHP does not let child methods have fewer parameters than the parent.
The name of the service is being changed, so any references to it will need to change as well.
Test service, so OK to have a BC break.
The provider ID wasn't previously part of the tag... not sure what implication that has for BC.
Did I miss anything?
If we do update an existing change record, we should be sure to toggle the published field once the issue is fixed (uncheck -> save -> recheck -> save) so that a notification goes out on the feed about the API change. (Just mentioning this since I think it's been missed recently on what's probably the same CR.)
Comment #36
znerol commentedUpdated the issue summary and issue title.
I think we do not need a new change record for that, but update: https://www.drupal.org/node/2453449
AFAIK we do change record updates after commit, therefore resetting to RTBC.
Comment #37
xjm@znerol, we usually update the change records before commit with a note that the change is pending, like:
...but the process for it is not well documented and followed inconsistently. :P Thanks! And thanks for updating the disruption information with the details.
Comment #38
znerol commentedOk, thanks. I've added the pending api changes/additions to the change record.
Comment #40
xjmOkay, this makes sense to me, and I think the small disruption is merited to unblock the contrib modules listed.
As a contrib project blocker and a critical followup, this is a prioritized change during the beta per https://www.drupal.org/core/beta-changes. Committed and pushed to 8.0.x (with credit for reviewers). Thanks all!
Comment #41
znerol commentedThanks. Updated and (re-)published the change record.
Comment #42
znerol commentedComment #43
almaudoh commentedGreat!! Thanks @znerol, @xjm.