Problem/Motivation
As reported in #2460969: Basic auth access check for rest views ends in 403 is fallen into the cracks. To fix this for views #2228141: Add authentication support to REST views and for Rest UI #2456283: AuthenticationManager::getSortedProviders is protected we need to introduce means to collect all Authentication Providers.
Proposed resolution
Introduce an Authentication Collector acting as the single source of information for both, the authentication manager and core/custom modules requiring information about available authentication providers.
In addition this frees AuthenticationManager from the responsibility of providing information about enabled providers. SoC FTW.
Remaining tasks
Review.
User interface changes
None.
API changes
None really. This adds back functionality prematurely removed in #2286971: Remove dependency of current_user on request and authentication manager but in a better place.
Beta phase evaluation
| Issue category | Bug because it adds back functionality prematurely removed (regression) |
|---|---|
| Issue priority | Not critical |
| Comment | File | Size | Author |
|---|---|---|---|
| #50 | interdiff-2490228-50.txt | 1.99 KB | damiankloip |
| #50 | 2490228-50.patch | 19.09 KB | damiankloip |
| #47 | interdiff-2490228-47.txt | 1.8 KB | damiankloip |
| #41 | interdiff-39-41.txt | 926 bytes | martin107 |
| #39 | interdiff-2490228-39.txt | 1.68 KB | damiankloip |
Comments
Comment #1
clemens.tolboomPlease give credit to @znerol + @mikey_p too.
Comment #2
andypostmaybe it needs add related issues from contrib and tag as contrib blocker (console at least)
Comment #3
clemens.tolboomThis blocks core #2228141: Add authentication support to REST views
This blocks contrib module Rest UI #2456283: AuthenticationManager::getSortedProviders is protected
This blocks contrib console module https://github.com/hechoendrupal/DrupalConsole http://drupalconsole.com/
Comment #4
dawehnerI'm curious, does that really belong into rest module and not into core itself?
It would be great to have some sort of test coverage.
Comment #5
clemens.tolboom@dawehner good point to put it into core instead of rest module.
It needs test coverage for sure. Hope someone steps up.
Comment #6
martin107 commented@dawehner regarding #4
it exists in core - today
core.services.yml
under the name 'authentication'
it does vacuum up all the authentication_provider's
but the results in the form of AuthenticationManager::getSortedProviders() are protected.
I am curious to know why it is protected... I can't see any security implications.
[ Side note - to be helpful - the todo above AuthenticationManager::getSortedProviders() is stale - the issue is fixed, but the comment just has not been removed ]
Comment #7
almaudoh commentedRe #4 and #5: Looks like we went round in a really big circle here.
See #2456303-23: AuthenticationManager needs interface and ::getProviderKeys where @znerol explained the rationale for making
AuthenticationManager::getSortedProviders()protected and the suggested solution #2456303-16: AuthenticationManager needs interface and ::getProviderKeys, which led to #2456283-4: AuthenticationManager::getSortedProviders is protected, which led to this issue.So maybe we need to review the rationale again though, even though we could do without the duplication of code.
Comment #8
martin107 commentedSo in summary
There were three options, in #2456303: AuthenticationManager needs interface and ::getProviderKeys
1) Revert the visibility of the getSortedProviders() to public.
2) Add Authentication Collector to Rest - which spawned this issue
3) The third option is just provide a new method, and I am just going to quote @znerol
I think we should have gone with something like option 3
@znerol from this comment https://www.drupal.org/node/2456303#comment-9844563
Comment #9
martin107 commentedI can do something about the stale todo from #6
#2505655: Remove stale todo in AuthenticationManager
Comment #10
damiankloip commentedIf we go with the original approach but have a core AuthenticationCollector. What do people think about that? Something like this (Could remove AuthenticationManagerInterface also).
I am not apposed to the metadata idea either, but seems like that shouldn't live on the AuthenticationManager, and if it didn't then it would need to container aware to be able to load the providers?
Comment #12
dawehnerI like that approach, given that its also similar to the approach done in
\Drupal\Core\Access\AccessManagerComment #13
martin107 commented1) Refactored AuthenticationManagerTest to reflect the new way of doing things
2) Fixed a gaggle of minor stray cs errors. - nothing major.
Comment #14
dawehner+1 to not add a mock! Mocking IMHO mostly makes sense if you are outside of your domain, this is still as part of it so that is totally fine.
Do we really need dedicated tests for this collector? I mean it can't be bad, but we have a lot of implicit test coverage already.
+1 on the code in general.
Comment #15
damiankloip commentedI would be inclined to add a unit test anyway, wont take long to do. I can do that in the morning if no one else does first.
We can still remove the
AuthenticationManagerInterfacetoo.Comment #16
almaudoh commentedWow!! I really like this approach - really clean. Just some nitpicks mostly:
Nit: newline
I know this is c/p code but I always hoped for an opportunity to change 'builders' to 'providers'
THe
This was removed in #2505655: Remove stale todo in AuthenticationManager
Doc first line...
Constructor doc
We can remove
AuthenticationManagerInterfacenow.Comment #17
-enzo- commentedI did some code style changes requested by @almaudoh using patch attached in comment #13 by @martin107
Comment #19
martin107 commentedHi enzo,
There is a missing semicolon at the end of $authentication_collector = new AuthenticationCollector()
I hope this helps.
Comment #20
almaudoh commentedAlso look like the patch at #17 is missing a really big hunk - the
AuthenticationCollectorclassComment #21
almaudoh commentedIn this patch:
1. Addressed nits in #16
2. Removed some dead code in
AuthenticationManager3. Removed
AuthenticationManagerInterface4. Added unit test for
AuthenticationCollectorComment #22
martin107 commented#21 is a good improvement,
The tests cover what is needed, they are simple to understand and are complete so +1
I just picked out a few inconsequential nits while running the new tests through phpcs.
I would be really good to get this in, as it is holding up a console project command I use on a daily basis.
If I can work on a separate issue in exchange for a review here... just let me know :)
Comment #23
znerol commentedUpdating the issue summary and adding beta evaluation.
Comment #24
znerol commentedI very much like the approach, feedback from #14, #15, #16 is included. This solution even narrows down the scope of
AuthenticationManagerto authentication - and nothing else. Information about the enabled providers can be retrieved from a separate service.I'm really glad we did not simply reverted the visibility of the
AuthenticationManager::getSortedProviders()method back to public. Thanks!Comment #25
znerol commentedComment #29
znerol commentedTwo consecutive test runs, no changes in between. Probably there is too much randomness in the test? Maybe this happens if there are duplicate priorities?
Comment #30
almaudoh commentedThat's interesting considering that it's the same sort function
krsort()being used for both the test and the actual code.Still passes in my local system using both PHPUnit and run-tests.sh
Comment #31
almaudoh commentedMy bad. The way the test was written made no provision for duplicate priorities. Adjusted the test a little to cover this scenario.
After some more trials, still getting random failures for duplicate priorities, so sticking to unique priorities in the test...
Comment #32
znerol commentedIn fact it wouldn't hurt if we'd test whether or not the authentication collector works properly with duplicated priorities. How about using a two-level array for the
$providersin the test?Edit: interdiff is against #22.
Comment #33
almaudoh commentedGreat idea! Same as what we have in the class implementation itself. Here's the patch, also incorporating tests against duplicated priorities.
Comment #34
znerol commentedThanks!
Comment #35
alexpottWe need to document the argument with an
@paramLooking at the code I don't get the point of storing the challengers and filters on the AuthenticationManager - in fact it now seems dangerous since if any code calls Authentication::addProvider() (for whatever reason) after the AuthenticationManager has been constructed the AuthenticationManager will behave unpredictably. If anything we should move this to AuthenticationCollector so that the list of challengers and filters can be maintained there.
Comment #36
damiankloip commented1. Done
2. Not sure I agree with moving that to the auth collector. That is stuff that is pretty much internal to the auth manager. We can simplify things slight I think, not collect the filters and challengers and just check inline, when needed. Auth is not something that is going to be called lots of times (hopefully just once). So if anything, this will save a few checks. It also gets around the problem you outlined above (even though the likelyhood is very minimal).
Comment #37
dawehnerCool, yeah I agree optimizing for the case of multiple authentications is wrong.
Comment #39
damiankloip commentedMhh, that's what the coverage is for. Missed that!
How about this? return the provider directly from getChallenger()?
Comment #40
dawehner+1 for returning objects directly. I'm curious whether this means we should better expand the test coverage for the AuthenticationManagerTest. Ideally it should have caused the problem, right?
Comment #41
martin107 commentedSo I have tightened up the documentation surrounding getChallenger() after #39
getChallenger() is private and only called by challengeException() -- all reasonable design decisions.
challengeException() is a preexisting segment of untested code ( I think ? ) My perspective is adding extra tests to confirm operation of the AuthenticationProviderChallengeInterface is a little out of scope.
I would like to move this "Contributed project blocker" along. So just let me know and I will open up and work on an issue to expanding test coverage.
Comment #42
damiankloip commentedFeel free to open up that issue, if we can open that and move this along, great. I think we have adequate coverage right now, especially with all the integration points these classes have. I don't see why it should block this right now.
Comment #43
almaudoh commentedSo does that mean RTBC then...
Comment #44
znerol commentedI understand that both
getProviderandgetChallengerare protected methods. Still there is a slight inconsistency if one returns an id while the other returns the object.Comment #45
damiankloip commentedYeah, I thought that too when doing the conversion. Would be good if they were consistent....
Comment #46
damiankloip commentedThe trouble is the check to isGlobal() and defaultFilter() iirc. They always want an ID regardless of whether a provider is loaded. We could implement SplObjectStorage or something?
I would say it's just easier to go back the other way, just return IDs?
Comment #47
damiankloip commentedComment #48
znerol commentedYeah, that is better.
I realize that this is a preexisting defect, but
AuthenticationManager::getProvider()may returnNULL. We should not attempt to authenticate in this case.Here, on the other hand, I suggest moving
AuthenticationCollector::getProviderfrom the condition into the block. We can trustgetChallengerto either returnNULLor a valid provider id.Comment #49
almaudoh commentedHit the same snag while working on that. +1 to #47.
Small docs nits:
1.
Add => Adds, test => tests
2.
s/@var $provider_id/@var/
Comment #50
damiankloip commentedComment #51
dawehnerThe feedback got addressed in the meantime.
Comment #52
alexpottCommitted bc3dad6 and pushed to 8.0.x. Thanks!
Comment #55
znerol commented