Problem/Motivation
Currently, the only way to add authentication to a REST View is through RouteSubscriberBase->alterRoutes().
We could add the Authentication setting to REST Views so users could select the supported authentication methods for a particular Views display. It would look like the following screenshot:
The above view lists unpublished content in JSON format. Authentication has been set to Basic Auth, so requests to http://mysite.com/unpublished-content will require HTTP Basic authentication headers provided with a valid username and password. Note that Access has been set to Authenticated User role, since the default access requirement is "Access content", which is part of the Anonymous role. Alternatively, we could also set a permission that belongs to the Authenticated User role. I am not sure about how to make this more obvious to the site builder.
How to test
- Install Drupal and enable Rest, Hal, Basic Auth
- Add a Rest export display on /node
- Generate some frontpage nodes
curl --request GET http://drupal.d8/node?_format=hal_json
curl --request GET http://admin:admin@drupal.d8/node?_format=hal_json
gives access denied- Apply the patch
- Set basic auth as Authentication methods for the Rest Export display
curl --request GET http://admin:admin@drupal.d8/node?_format=hal_json
gives result.
Proposed resolution
Remaining tasks
Revert AuthenticationManager ::getSortedProviders to private once #2490228: Add Authentication Collector lands.
User interface changes
API changes
Comment | File | Size | Author |
---|---|---|---|
#155 | interdiff.txt | 1.23 KB | dawehner |
#155 | 2228141-155.patch | 23.63 KB | dawehner |
#153 | interdiff.txt | 611 bytes | dawehner |
#153 | 2228141-153.patch | 23.76 KB | dawehner |
#151 | 2228141-151.patch | 23.43 KB | dawehner |
Comments
Comment #1
juampynr CreditAttribution: juampynr commentedHere is a patch that addresses this issue.
Comment #3
dawehnerThis is really an interesting feature!
I would like to avoid executing any kind of code in the constructor. Wrap the logic in some additional method and we could be fine.
wait, you can select multiple?
Just use $this->t() instead
I would argue that you might want to put that onto PathPluginBase instead ... this could be helpful on html ones as well.
{@inheritdoc}
What does that one do?
Comment #4
juampynr CreditAttribution: juampynr commentedDone.
Yes, you can allow, for example, OAuth and Basic Auth. The Authentication Manager will try to authenticate the request with OAuth first (it has higher priority) and, if it failed, try with Basic Auth.
Addressed.
Makes sense. I will move it there in the next patch.
Addressed
Oh, nice catch! I realized this is not needed since you can set the Access requirements through the UI. I have removed it and added this requirement to the buildOptionsForm description.
Comment #5
juampynr CreditAttribution: juampynr commentedOops, I forgot to add the interdiff.
Comment #6
juampynr CreditAttribution: juampynr commentedOh nice! and my patch at #4 was empty. Yay juampy! #facepuml
Moving logic now to PathPluginBase as suggested by @dawehner so HTML displays can benefit from this feature too.
Comment #7
juampynr CreditAttribution: juampynr commentedMoved the logic to PathPluginBase and adapted REST, Page and Feed Views displays so they support it.
Will start adding tests now.
Comment #9
dawehnergood idea but you can just move that into the common base class
Just use ->setOption as this makes it a little bit easier to read and (maybe) does not override other ones.
Comment #10
juampynr CreditAttribution: juampynr commentedRe-rolling.
Comment #11
juampynr CreditAttribution: juampynr commentedAddressed @dawehner's suggestions:
1. Did changed it but I have to either touch this in Page and Feeds plugins or REST so the Authentication option is available to the three of them. the first two use Page category while REST uses Path category.
2. Fixed.
I need to continue reading PHPUnit tests until I get to the 'aha!' moment when I feel I am able to write them for this patch.
Comment #12
juampynr CreditAttribution: juampynr commentedForgot to add the interdiff for #11.
Comment #15
juampynr CreditAttribution: juampynr commentedRe-rolling.
Comment #16
juampynr CreditAttribution: juampynr commentedRe-rolling.
Comment #17
juampynr CreditAttribution: juampynr commentedComment #20
dawehnerIt is always a bad sign to execute any code during the construction time of objects. Better just inject the service and be done with it. Otherwise problems like circular dependencies could occur, which are both hard to track and potentially fix.
In general you could use the array typehinting, see https://github.com/php-fig/fig-standards/pull/169/files#diff-9a10a11696d...
On top of that it would be great to expand the unit test coverage for adding authentication as well.
Comment #22
juampynr CreditAttribution: juampynr commentedThanks @dawehner. Your suggestions make the logic even simpler.
I am struggling to add unit test coverage, but will give it another go tomorrow once I see the updated list of failing tests.
Comment #24
cloudbull CreditAttribution: cloudbull commentedAny update here ? or there is other approaches to have auth in REST views?
Many thanks guys
Comment #25
juampynr CreditAttribution: juampynr commentedI am sorry, I won't be able to contribute to this issue in the following weeks due to other commitments. I am happy to help when I am back though.
Comment #26
penyaskitoNo need to change status.
Comment #27
clemens.tolboomPatch rerolled.
core/modules/views/src/Plugin/views/display/PathPluginBase.php:433
To make this work I needed 'revert' #2456303: AuthenticationManager needs interface and ::getProviderKeys making the method public again. As suggested in #2456303-23: AuthenticationManager needs interface and ::getProviderKeys we need the code from Rest UI
Another hack is in core/modules/views/src/Plugin/views/display/PathPluginBase.php:67
Comment #29
clemens.tolboomThe unit test core/modules/rest/tests/src/Unit/CollectRoutesTest.php will still fail. What's wrong with the interdiff?
Comment #31
clemens.tolboomComment #32
clemens.tolboomThis is now a bug as views are not accessible through a Basic Auth rest call.
gives 403 forbidden.
Comment #33
clemens.tolboomIn #32 I made this a bug but I'm still not sure what the root cause is.
After following the 'How to test' steps requesting
curl --header 'Accept: application/hal+json' --request GET http://drupal.d8/node
shows a list of nodes but requesting as admincurl --user=admin:admin --header 'Accept: application/hal+json' --request GET http://drupal.d8/node
gives 'access denied'.Why is a anonymous allowed and admin not to view the result?
Comment #34
znerol CreditAttribution: znerol commentedThis is most likely because basic auth is not a global authentication provider while cookie auth is. Global authentication providers apply even when the route does not declare any authentication providers via
_auth
option, while non-global providers need to be explicitely set on the route.If a resource is requested with basic auth credentials, then the basic auth provider claims that request. This happens before routing. After routing it is checked whether the authentication provider is allowed on the selected route. This will result in a 403 if that check fails.
Comment #35
clemens.tolboom@znerol thanks for the explanation.
As [edit] #2403307: RPC endpoints for user authentication: log in, check login status, log out
#2228141: Add authentication support to REST views[/edit] is postponed to 8.1.x this means Rest export display are not accessible by authenticated users as the only option to authenticate is basic auth for 8.0.xSetting to major as this otherwise renders headless Drupal basically useless.
Comment #36
dawehner@clemens.tolboom
This link points to this issue, do you mind updating the URL :)
Comment #37
clemens.tolboom@dawehner sorry for the wrong link. Is now fixed.
I meant to link to #2403307: RPC endpoints for user authentication: log in, check login status, log out
Comment #38
dawehnerYeah, the blocking issue is now fixed!
Comment #39
almaudoh CreditAttribution: almaudoh commentedComment #40
almaudoh CreditAttribution: almaudoh commentedRerolled and updated for
AuthenticationCollector
Comment #42
almaudoh CreditAttribution: almaudoh commentedFixed the test fail.
Comment #43
almaudoh CreditAttribution: almaudoh commentedThe test fail was due to
array_filter()
getting aNULL
from the unit test. Seems brittle to me. Are there possible situations where::getOption('auth')
could be NULL?Comment #44
dawehnerInteresting, I would not have thought that we could simply also put auth onto the PathPluginBase level, this is great. Do you mind adapting the way how the code looks like to be more inline with the other default values?
Do you think we really need to filter both on storage and on rendering? What about filtering on storage?
+ we certainly need some form of config schema
We don't have any human readable names for them right? Well too bad
Won't this cause some form of escaping?
Comment #45
almaudoh CreditAttribution: almaudoh commented#44:
1. Done :)
2. Done. Added config schema also.
3. Yeah. Too bad.
4. This was c/p'ed from code directly above. Not sure of the escaping.
Also:
Since
$options['auth']
is now inPathPluginBase
I extended the implementation to thePage
display plugin as well; and made the$account_collector
argument in the constructor required (no more optional) - it causes a WTF when a user clicks on theAuthentication: foo
link on the UI and nothing shows up.I don't know how much of an API break this is (how many display plugins call
PathPluginBase::__construct();
).If it is too much of a break, we can put back the optional-ness of the argument, but with the caveat that the
PathPluginBase
sub-classes that callparent::__construct()
will be broken in the UI.Comment #47
almaudoh CreditAttribution: almaudoh commentedFixed test mocks to match changed
PathPluginBase::__construct()
method signatureComment #48
vivekvpandya CreditAttribution: vivekvpandya as a volunteer commented@almaudoh Permissions for RESTEXport on view works fine for me.
Comment #49
dawehnerJust in case you write another new mock somewhere, maybe consider using prophecy ... its just better to read.
We still need some proper tests ..
Comment #50
almaudoh CreditAttribution: almaudoh commentedIn which area specifically do we need more tests?
Comment #51
dawehnerSome form of integration test coverage would be great, so ensuring that basic auth for example works for such a configured view.
Comment #54
almaudoh CreditAttribution: almaudoh commentedRe-roll
Comment #55
vivekvpandya CreditAttribution: vivekvpandya as a volunteer commentedThe latest patch works fine for view based RESTExport .
Authentication with basic_auth provider works for all 3 access type namely None, Role and Permission. The results are as expected.
Comment #58
almaudoh CreditAttribution: almaudoh commented@dawehner: I've struggled with this without much headway. My knowledge of Views internals is limited. Would appreciate some pointers.
Comment #62
clemens.tolboomAs reported in [#2504433#5] using BASIC_AUTH seems to test for a token which seems completely wrong. I wonder whether this patch solves this. The issues summary suggest it works.
Comment #63
dawehnerIt would be still great if someone could write a proper test which uses an actual request to see whether the configuration got applied correctly.
Comment #64
clemens.tolboomApplying the patch is ok BUT the Authentication appears on every display which is not what we need.
@dawehner I agree we need integration tests. I'm similar puzzled as @almaudoh on where to start. Do you have some hints?
Comment #65
clemens.tolboomI've updated the step to test to using _format and can confirm the patch works so we can close #2504433: REST cookie auth overrides basic auth? as a duplicate of this issue.
Comment #66
clemens.tolboomThis should not be as it confuses our users right?
Comment #67
dawehnerYeah I don't think it makes sense on page displays, oh well, maybe we could have a flag on views displays to include it, because well, it makes logically sense on PathPluginBase, given that this is the location which deals with the routing integration.
Comment #68
damiankloip CreditAttribution: damiankloip at Tag1 Consulting commentedYes, split on this. It definitely only makes sense on the REST export display, but code wise, the PathPluginBase is not a bad spot for it!
Comment #69
eiriksmWorking on this
Comment #70
eiriksm- Moved the whole auth option thing to the rest display. Did not add a flag (as suggested in #67
- Added a simple test case for this.
Might maybe move this test to its own file. Comments on that?
Comment #71
eiriksmComment #73
eiriksmOops, took out a bit too much when removing some debug things from my test.
Comment #74
eiriksmComment #75
damiankloip CreditAttribution: damiankloip at Tag1 Consulting commentedThis might as well just go on one line:
$auth = $this->getOption('auth') ? implode(', ', $auth) : $this->t('No authentication is set')
IMO it's better to just have this in a small helper method, like getAuthOptions() or something. Not a major issue though.
Comment #76
dawehnerNitpick
I don't see an added assertion in this change. Let's add one.
Comment #77
eiriksm#75:
1 and 2: Fair enough. These were just copy-pasted from the earlier patch, but hard to disagree with that :)
#76
1. Fixed. Apparently my editor does not add new lines automatically on yaml files.
2. Added not only one, but 2 new assertions. Although this change stems from the earlier patch as well.
Comment #78
dawehnerThank you!
Comment #79
Wim LeersNit:
string[]
.Nit: why not
===
?What if no authentication is specified?
Perhaps it's obvious to everyone else, but it's not obvious to me what the behavior is in that case. I think it'd be good to have that documented?
Comment #80
dawehnerWell, we fallback to the default routing behaviour, which is configurable by default.
Comment #81
eiriksmPerhaps it is not obvious from reading the code, but from reading the patch it seems obvious to me that the route will be as before if auth is not added. That being said, it is not obvious from reading the code, I guess.
Just did your suggested changes and added one extra sentence about this.
Comment #82
almaudoh CreditAttribution: almaudoh commentedRe-RTBC since #79 is addressed.
Comment #83
dawehnerThis is wrong. We fallback to the default authentication mechanism, which is cookie based by default.
Comment #84
eiriksmComment #85
eiriksmJust used your wording for the updated comment :)
Comment #86
dawehnerthank you
Comment #87
catchThis looks great but don't we need an update to re-save existing REST views so that the default option gets saved? Otherwise it'll show up in diffs etc.
Comment #88
dawehnerFair point. The behaviour wouldn't change, as we still have the defaults merging, but I agree that its a good idea for the diffs.
Comment #89
eiriksmJust resaving them would not trigger any config diff. Just tried it. Should it?
The only way to trigger the diff would be to add the auth option (as empty) in the update hook. Should we do that? Attached patch with that solution.
Comment #90
dawehnerMh, currently views doesn't? Maybe it should though
Comment #91
eiriksmMaybe... Pretty sure that is a different issue though :p
Comment #93
eiriksmdoh
Comment #94
dawehnerOH yeah, this is why some automatic checking could be pretty useful. Variables should always be defined :)
PS: We could get rid of the
$view->set('display, $displays)
call, but I cannot really care about it, to be honest.Comment #95
alexpottDoesn't the hook_update_N implementation need a test?
Comment #96
mohit_aghera CreditAttribution: mohit_aghera as a volunteer and at Axelerant commentedAdding test case in views module for importing views related to rest configuration.
Comment #98
manojapare CreditAttribution: manojapare as a volunteer and at Valuebound commentedComment #99
manojapare CreditAttribution: manojapare as a volunteer and at Valuebound commentedComment #100
manojapare CreditAttribution: manojapare as a volunteer and at Valuebound commentedI have, rerolled agianst 8.1.x
Comment #102
manojapare CreditAttribution: manojapare as a volunteer and at Valuebound commentedMoved ViewsUpdateTest file as per namespace and fixed wrong include file path.
Comment #103
manojapare CreditAttribution: manojapare as a volunteer and at Valuebound commentedComment #104
dawehnerWe should adapt the calculate dependency method to include the auth providing module as well.
Is this really what we want to test? Don't we want to test that 'auth' was added to the stored view?
Comment #106
manojapare CreditAttribution: manojapare as a volunteer and at Valuebound commentedComment #107
mohit_aghera CreditAttribution: mohit_aghera as a volunteer and at Axelerant commentedAdding patch to make test case run.
Comment #108
mohit_aghera CreditAttribution: mohit_aghera as a volunteer and at Axelerant commentedComment #111
eiriksmWorking on this.
Comment #112
eiriksmHm, these update tests never finishes on my computer.
Please feel free to take over, someone with the ability to run them.
Comment #114
lokapujyaThere is something wrong with the test fixtures. This might still not be right, but something like this needs work.
Comment #116
lokapujyaRerolled for 8.2
Comment #118
lokapujyaGood that got rid of the fatal error. The test view was not getting loaded before. But there are now 3 different test fails.
Comment #119
lokapujyahmmm, what is this supposed to be doing?
Comment #120
dawehner@alexpott, @xjm, @timplunkett discussed this issue.
We agreed that this is a serious blocker for people using REST views behind authentication.
Comment #121
andypostIn #2575453: Remove message claiming defaults or make it work like its claim raised a good point about usage of authentication providers, suppose description should point that user should expect when nothing checked
Comment #122
dawehner@lokapujya
Do you think you can work on this patch? It would be really nice to land this
Comment #123
andypostCurrent implementation of REST resources skips routes without auth so why views implements fallback to cookie?
Why that different from rest module implementation?
\Drupal\rest\Routing\ResourceRoutes::alterRoutes()
Comment #124
dawehner@andypost
I see where you are coming from.
We certainly don't want to change the existing behaviour aka. no specific authentication is selected, as otherwise existing views out there would break.
Comment #126
lokapujyaComment #127
ashishdalviComment #128
Sonal.Sangale CreditAttribution: Sonal.Sangale at Blisstering Solutions commentedComment #129
Sonal.Sangale CreditAttribution: Sonal.Sangale at Blisstering Solutions commentedRerolled the patch
Comment #131
dawehnerJust some additional reviews.
This is a misleading function name, as it doesn't tell you that this is related to a custom auth method.
I'm wondering whether we could leverage guzzle for much less cruft.
You could make this a little bit nicer by using
AuthenticationCollectorInterface::class
or even use prophecy for mocking.I'm wondering whether we could provide the view as YML file instead, see
core/modules/views/tests/fixtures/update/argument-placeholder.php
as example. This would make it much more readable.This test is weird, why is there a view which already has basic_auth enabled?
Comment #132
Sonal.Sangale CreditAttribution: Sonal.Sangale at Blisstering Solutions commentedComment #133
dawehnerAddressed my own feedback. This feels RTBCable to be honest.
Comment #135
lokapujyaReviewing.
Comment #137
lokapujyaI think the rest_update_8202() name is correct.
Comment #139
dawehnerLet's fix things
Comment #140
LendudeThe patch in #139 seems to include the patch from #2721595: Simplify REST configuration (but those changes are not in the interdiff). Are those changes needed to make this pass? Is this blocked by that issue?
Comment #141
dawehnerOh no, yeah that was not planned. The interdiff should be able to apply to #137 though.
Comment #142
LendudeThis is #137 and the interdiff from #141 rolled into one. Only change I added was changing the name of the update test to something a little more descriptive then ViewsUpdateTest.
Comment #143
dawehnerThank you lendude!
Comment #144
juampynr CreditAttribution: juampynr at Lullabot commentedJust curious: What's the ampersand for?
Comment #145
dawehnerThis is a reference to the variable, so changes to $display end up in $displays.
Here is a more real life example:
Comment #146
alexpottShouldn't this be
!isset($display['display_options']['auth'])
Missing new line at end of file.
We need to ensure the plugin provides a dependency to the view. So that the view has a dependency on the module that provides the authentication method. This will need tests.
Weird number... here's the views.install example
We need the group thing and I think the update number should be 8200. @lokapujya why did you think it should be 8202?
Comment #147
lokapujyaI just meant that it should be 82xx. #2721595: [PP-1] Simplify REST configuration is going to add 8201 and 8202. So, you are right that it could be 8200.
Comment #148
Wim Leers#147: Whichever lands first gets the lowest number. So this should use 8200, the next one should use 8201, etc.
Comment #149
dawehnerFor now I'm just using the functionality which will be in #2308745: Remove rest.settings.yml, use rest_resource config entities
Comment #151
dawehnerJust a reroll basically :)
Comment #153
dawehnerLet's fix the failures.
Comment #154
Wim LeersWanted to RTBC, but found a few too many nits.
Trailing spaces. And actually, an unnecessary blank line before the closing brace. And missing a blank line after the closing brace.
I don't think this is necessary either?
Should also have
@group views
?8000 vs 0. Does not make sense.
This should be removed?
Comment #155
dawehnerIt actually does 8000 is the schema version and 0 is the weight.
Let's drop it.
We can just have one group statement at the moment, so let's with what the majority of files in that folder have (4 against 2)
Oh yeah totally not. I hate that git doesn't figure out those problems automatically.
Comment #156
Wim LeersOh… oops, lots of update tests then have this wrong!
This can now also be removed:
But that's a trivial thing that can happen on commit.
P.S.: wow, I thought the "-155.patch" was a typo, but it is not — gasp!
Comment #157
dawehnerYeah, but I guess just no code really cares about it.
It is Drupal ...
Comment #159
Wim LeersRandom fail:
Re-testing.
Comment #160
Wim LeersComment #161
alexpottLet's get a change record for the new key / value for auth in the view config.
Comment #162
dawehnerJust wrote the change record. @alexpott ... feel free to commit it.
Comment #163
alexpottCommitted 5987069 and pushed to 8.2.x. Thanks!
Fixes on commit.
Comment #165
Wim LeersI only noticed now, after this landed, that this patch only makes changes to
rest
module, yet then adds the upgrade path test toviews
module. That does not seem right?Comment #166
dawehnerNice catch!
Right, feel free to create a notice task to move the update path tests to rest module :)
Comment #167
Wim Leersi.e. this even has
rest_update_8202()
inrest.install
, but then\Drupal\views\Tests\Update\RestExportAuthUpdateTest
to test it…It's confusing when seeing rest_update_8202 and then not finding the corresponding upgrade path test.
+1, done: #2756527: Move test coverage for rest_update_8202() from core/modules/views to core/modules/rest.