Problem/Motivation
#2772413: REST GET fails on entity/taxonomy_vocabulary/{id} 403 Forbidden with error uncovered that it can be very difficult even for a Drupal expert to figure out why you're getting a 403 for a particular REST resource.
Yet … we want Drupal to be adopted as the storage back-end by JavaScript developers — i.e. by non-Drupalists. How are they going to figure this out?
Proposed resolution
Our 403 responses for REST routes need to list a reason for this 403. And in fact, #2681911: REST requests without X-CSRF-Token header: unhelpful response significantly hinders DX, should receive a 401 response introduced that very infrastructure! We just need to use it more broadly now.
Remaining tasks
Also letAccessResultNeutralimplement\Drupal\Core\Access\AccessResultReasonInterface.Ensure it's done for all permission checking, and\Drupal\Core\Access\AccessResult::allowedIfHasPermission(s)()in particular.Test coverage.
User interface changes
None.
API changes
None. (AccessResultNeutral now implements AccessResultReasonInterface just like AccessResultForbidden already does, but this breaks no APIs.)
Data model changes
None.
| Comment | File | Size | Author |
|---|---|---|---|
| #122 | 2808233-122.patch | 49.32 KB | wim leers |
| #117 | 2808233-117.patch | 49.32 KB | dawehner |
| #110 | interdiff.txt | 1.54 KB | dawehner |
| #110 | 2808233-110.patch | 49.24 KB | dawehner |
| #106 | interdiff.txt | 14.51 KB | dawehner |
Comments
Comment #2
wim leersComment #3
dawehnerOne thing we might should also look into is to pass along the reason when merging access results together
Comment #4
wim leersYep, absolutely! But we already do that. At least for
AccessResult::andIf(). We still need to do that for::orIf(). This issue will probably need to add that.Comment #5
dysrama commentedAdded is a patch for better error messages. This is the first work I've ever done on D8, so I hope I haven't misunderstood the issue.
I tried to look at the AccessResultReasonInterface, but the reason seems always to be empty for the entity access checks?
Comment #6
dawehnerThis is certainly adding the messages in all various places. This is really nice. It is a small incremental improvement.
Comment #7
Grayside commentedNot sure if it belongs here, but a standardized approach to thinking about contextualized error messages might look at the Problem Details RFC. One thing I absolutely love is when error messages are directly affiliated with a URL to "canonical" troubleshooting documentation. These are probably second-order considerations, but what should be decided is whether there is a point at which changing the error responses themselves constitute a breaking change.
Comment #8
dawehner@Grayside
Nice research! I'm totally in favour of adopting that, maybe we could do in its own issue. Just improving the exception message itself is worth doing so.
Comment #9
Grayside commentedOkay. #2811483: Adopt Problem Details RFC for REST Error Responses
Comment #10
dawehnerIMHO expanding the tests would still make sort of sense.
Comment #11
wim leersFurther confirmation we need this: #2810603: PATCH taxonomy term.
Comment #12
gnugetI will work on this.
Comment #13
wim leersAwesome, thanks @gnuget! I'll provide reviews :)
Comment #14
gnugetI'm stuck on this.
While I in my local this is working:
In the tests I got this error instead:
I will continue working on this today.
Comment #15
wim leersThe test result is a HTML response. That's suspicious.
There are three possible explanations that I can think of:
\Drupal\Tests\BrowserTestBase::installDrupal()enables verbose error logging by default.?_format. In fact, I see you have?_format=hal+jsonin the curl request. Which is wrong, it must be?_format=hal_json.Acceptheader in addition to the?_formatquery string. I've seen a similar problem in #2737719: EntityResource: Provide comprehensive test coverage: for every entity type, every format, every method.Hope this helps unblock you!
Comment #16
gnugetHi Wim
Thanks for your help, I was missing the query string (duh!)
And now I was able to test: delete, update and create but I think found a problem with READ:
The
Drupal\rest\Tests\ReadTestclass tests the following entities:And
config_test,taxonomy_vocabulary,blockanduser_rolestill return an empty body when the user hasn't permissions:Response body: {"message":""}So there is still a place where we need to add a better error message.
I also changed hal+json to hal_json in a few places (sorry if this is out of the scope but it was just 1 character! I can revert this change if necessary).
I attached my progress so far, I will continue working on this.
Thanks!
Comment #17
chi commentedIn some cases REST 403 responses come from outside REST module. For instance I just spent some time on debugging 403 error that was thrown in the onKernelRequestFilterProvider. The error message was as follows:
{message: ""}. Can we fix this as well in this ticket?Comment #18
wim leers#16: I think it may be better to postpone this issue on #2737719: EntityResource: Provide comprehensive test coverage: for every entity type, every format, every method then, because that issue is introducing all-new test coverage. Or, rather, spend minimal effort on updating the existing tests, instead just ensure that all 403 responses return useful information. So then when #2737719 lands, this issue will just have to update the existing test coverage :)
#17: Yes, exactly! That's definitely intended to be solved by this issue as well.
Comment #19
gnugetOk, I will wait until #2737719: EntityResource: Provide comprehensive test coverage: for every entity type, every format, every method be fixed.
Thanks!
Comment #20
wim leersOk, then I'm marking this postponed for now. Thanks for your help so far!
Comment #21
wim leersAn issue was opened precisely about the lacking information in 403 responses: #2831251: Trying to create nodes via REST with ajax POST request returns 403 response.
Comment #22
wim leers#2737719: EntityResource: Provide comprehensive test coverage: for every entity type, every format, every method landed!
Comment #23
tedbowRe-rolling
Comment #24
tedbowOk here is re-roll and update
The only changes from the previous patch still applicable are in \Drupal\rest\Plugin\rest\resource\EntityResource
Since a lot of files removed in #2737719: EntityResource: Provide comprehensive test coverage: for every entity type, every format, every method
I did update the errors to also include the entity type since it probably would make debugging easier because you could bundle names shared across entity types.
I wasn't sure why RoleJsonBasicAuthTest wasn't using JsonBasicAuthWorkaroundFor2805281Trait so updated that b/c it deals with the message.
I also added a couple more @todo point this issue in places where we have an empty string message in the tests haven't figured out where they come from yet but should be addressed here.
Comment #26
chi commented@tedbow, can you also update onKernelRequestFilterProvider mentioned in #17.
Comment #27
wim leersIt's hardcoded. So that's clear. No need for this comment.
This is confusing "authentication" with "authorization". The message being added here is related to authorization, not authentication.
We need to update
CookieResourceTestTrait::assertResponseWhenMissingAuthentication()to not assert the response body.Only
\Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait::assertResponseWhenMissingAuthenticationshould assert the response body, because it has its own authentication-related error message.Hence we don't even need to add this
$messageparameter.That was probably just an oversight, yes.
Comment #28
dawehnerIs it just me that its weird that 3 of the messages mention the bundle but one doesn't? Is there a specific reason for that?
Comment #29
gnugetI will comeback to work on this.
My progress so far:
Lets see what the bot says and in my next patch will try to fix any broken test.
Regards.
Comment #30
chi commentedI would just copy a sentence from the method description.
Comment #32
wim leers#29: yay, thanks for picking this up again! :)
The used authentication method is not allowed on this route.
These are inconsistent. Let's change them to:
These changes can be reverted.
What I said in #27.2 is still accurate:
In other words: the second parameter must be
FALSE, not$message.Surely this can be addressed here now?
The
@todois now done and hence can be removed!But the expected message is incorrect; it doesn't match the actual message we will get.
These changes are wrong. They remove the assertion completely when an entity type has no bundle. Let's revert these changes.
Comment #33
gnugetI think covered all the comments in this new patch.
Let's see what the bot says.
Comment #34
wim leersLooks great, thanks! :) Eagerly awaiting testbot results…
Comment #36
wim leersSome of those failures are because you didn't update
CookieResourceTestTraityet.You need to change it from
to
Comment #37
gnugetDuh!
I attach a new patch with this change.
Also, it's curious how the message is still empty under some circunstances.
Wonder if this change will fix those, if not I will look more in deep today.
Comment #39
gnugetI've been trying to figure it out why the messages return empty without luck, In my manual tests the response is sent correctly, the message is there but in the tests it is empty.
And it's hard debugging the tests because it seems to once the request to the endpoint is made none of my
print_r()calls are printed, so not sure where the things went wrong (unless my debug messages aren't printed because the empty messages don't come fromEntityResource.phpand Drupal send the empty messages before to touch the REST module.Will continue digging on this, it result harder than expected.
In the meanwhile I fixed the test related with the
CookieResourceTestTraitComment #40
gnugetComment #43
dawehnerA lot of those failures are caused by a limit due to the BC layer we have. The BC layer of having specific rest permissions causes a worse error message, which honestly is totally fine, as long we document it. This change should fix a good amount of the remaining test failures.
Comment #45
gnugetGreat dawehner!!!
I've spent a good amount of time trying to figure it out why it was returning an empty response.
I just apply the same change as you did for GET but for the other verbs.
Hoping to this is going to fix all the tests.
Comment #46
dawehner@gnuget
Yeah I primarily tried to enable you to continue to work on this issue. I hope this helped :)
Comment #48
gnugetUps! I forgot one.
New patch attached.
Comment #50
gnugetLet's try again. (I added the fix in the wrong line in my previous patch -_-)
Comment #52
gnugetI might need help explaining this but this is what I learned from my last patch:
That error comes from:
In theory at this point the resource hasn't even been provisioned, so the answer shouldn't come from the REST module and that is why it is empty (that is what Drupal returns when a non-provisioned resource is requested).
But for some tests it actually returns:
And that is because of Dawehner addition at #43 in the EntityAccessCheck.php file.
But what I cannot explain is why this answer is not returned for all the tested resources which haven't been provisioned otherwise the patch I made on #45 should work.
What I will try with this patch is undo the changes made on EntityAccessCheck.php file. and check if in this way the answer is consistent between all the tests.
Comment #53
gnugetAnd the answer was consistent, not sure what is next, is this ready? I need to re-add the change added By #34?
Should create a follow up to make drupal return a useful answer when the endpoint hasn't been enabled in the rest module?
:-) let me know.
Regards and thanks.
Comment #54
dawehnerI'm happy if you revert them, conceptually though it makes sense to have this message in
EntityAccessCheck...Comment #55
dawehnerI wonder whether we should take into account the case of non bundleable entities: Currently the message would look like
You are not authorized to create this user entity of bundle userSo FALSE means we don't compare the response body at all. Is there a reason we cannot generate the expected one? Maybe at least at a comment about it.
Comment #56
wim leers// Requests needing cookie authentication but missing it results in a 403 response. The cookie authentication mechanism sets no response message.(Contrast this with Basic Auth, which sends a 401 response, and a particular message.)Comment #57
gnugetAnd which would be the best way to address #55.1?
Checking if the entity is bundleable and if is not then provide a different message (and update the tests accordinly)?
Comment #58
dawehnerYou could create a helper function which takes an entity, a message without bundles and a message with bundles and returns the appropriate message.
Comment #59
gnugetSounds great, will work on that.
Thanks.
Comment #60
wim leersFor #55 and later.
Comment #61
gnugetReroll of 52 because #2805281: ?_format=hal_json error responses are application/json, yet should be application/hal+json has been fixed.
I'm still working on this.
Comment #62
gnugetOk, Here a patch which address #55
Comment #63
dawehnerSo this means we don't have any rest test coverage for non bundleable entity types?
Comment #64
dawehnerThe latest patch totally works for me ...
Comment #65
wim leersThis is getting very close now! :) Great work, @gnuget! I have mostly nits:
This has no test coverage. Let's update
\Drupal\basic_auth\Tests\Authentication\BasicAuthTestto test this.Or alternatively (which would be even better), is if you would implement a custom authentication provider and trigger that one from
EntityResourceTest, and then verify you get a 403 response with this message.A test auth provider would look like this:
i.e. sending a
test_authentication_providerrequest header would trigger this authentication provider to apply, but it wouldn't apply to the routed request, hence triggering this exception.Supernit: single quotes instead of double quotes, for consistency with the other code there.
Supernit: two newlines should be one.
This comment is wrong.
There should be no newline between the two
@params, there should be one before the@return.s/$action/$operation/
That's consistent with
\Drupal\Core\Entity\EntityAccessControlHandlerInterface::access.s/In case we have a BC layer,/When using the bc_entity_resource_permissions BC layer,/
Keeping the tag for the first remark.
#63: huh? The patch before #62 always had the bundle in the message, even if it matched the entity type ID (i.e. even entity types without bundles return something for
bundle: they return the entity type ID, see\Drupal\Core\Entity\Entity::bundle()). And since #62, we have more accurate messages. So, I think all is fine? Correct me if I'm wrong :)Comment #66
wim leersAfter posting #65, I went back to the issue summary to read what I originally wrote. I wrote this in the remaining tasks:
Neither of those have been done yet. Which is why the patch has this:
in several places.
It's also why we have
this hardcoding a certain message. Which is missing the point. It's merely giving a message, but it really doesn't help the end user in a sufficient way: the point of this issue was that the error messages should say exactly what is missing, when possible.
This message would be fine as a default. But what we really want, is that we check if
$entity_accesshas a reason$entity_access->getReason()(see\Drupal\Core\Access\AccessResultForbidden::getReason()+\Drupal\Core\Access\AccessResultReasonInterface).That reason should be set automatically.
So, for example, for
Vocabularyconfig entities, what determines access isEntityAccessControlHandler::checkAccess(), which usesVocabulary'sadmin_permission(administer taxonomy)to determine whether access must be granted or not:
In other words,
AccessResult::allowedIfHasPermission()must be modified to callsetReason()if access is not allowed. It can set a sensible reason automatically, because it knows which permission is necessary.In other words, what should happen here is:
AccessResultNeutralto implement\Drupal\Core\Access\AccessResultReasonInterface\Drupal\Core\Access\AccessResult::allowedIfHasPermission()to set a sensible reason when access is not allowed.EntityResource::get()etc. check if a reason is set and use that. So:throw new AccessDeniedHttpException($entity_access->getReason() ?: $this->accessDeniedExceptionMessage($entity, $operation).That way, you get what you have built so far if no reason is set, and otherwise you get a more helpful, more detailed reason.
Comment #67
gnugetOk, Here another try!
This basically address:
Also, I tried for a couple hours to use a custom provider as 65.1 suggested but I didn't find a way to switch to
TestAuthdirectly onEntityResourceTestBasethe alternative I guess is to add this provider on every entity resource test class (eg. NodeJsonCookieTest::$auth), so I can have that provider available on the test or something like that?For now, I just added the test directly on:
\Drupal\basic_auth\Tests\Authentication\BasicAuthTestI can give it another try with some guidance if you want @Wim Leers.For 65.7 and 66 I will provide another patch.
Regards!
(I will put the status as Needs Review so I can know to all the tests are working, I will switch back to needs work when it finished)
Comment #68
gnugetComment #69
gnugetOk, this new patch tackle 65.7 and 66.
Comment #71
gnugetOk, round 2.
Comment #73
tedbowForgot to un-assign myself.
Comment #74
gnugetAfter to pass a few hours with this. It seems to we will need to introduce a new abstract method on
ResourceTestBasecalled: getHttpMethodPermission (or something like that) which will return the permission necessary to access the resource, so we can match the error response with what we are expecting.For instance:
Here we explicitly need to know the user needs the "access content" permission in order to see the content and actually, we are granting that permission to the user at
TermResourceTestBase::setUpAuthorizationSo having
getHttpMethodPermissionwe can updateTermResourceTestBase::setUpAuthorizationin this way:and
getHttpMethodPermissionwill look something like:And finally we can do something like this on
EntityResourceTestBase (line 306):In order to do this we need to update a ton of classes and tests (which I'm super happy to do it if necessary) but first I want to know your opinion so I can be sure to I'm on the correct path.
Thanks!
Comment #75
dawehnerIMHO we should get these changes done in its own issue first, and ensure we have good unit test coverage for those.
Given that its most probably always a permission issue, I'm wondering whether we could move this additional method into a later issue. You know, its all about having small steps at a time, to actually achieve progress.
Comment #76
wim leersOnce it's ready, yes. But let's first prove here that it actually helps solve the problem that this issue wants to solve :)
But, yes, once this is looking good, I agree it'd be good to land that separately. Although we also didn't do that for #2681911: REST requests without X-CSRF-Token header: unhelpful response significantly hinders DX, should receive a 401 response (which added this to
AccessResultForbidden).Comment #77
gnugetSo, #74 is in the correct path? or should I change something?
Thanks! and regards.
Comment #78
lukedekker commentedRunning the patch from #74, I'm getting an error
Call to a member function isAllowed() on a non-object in /core/modules/rest/src/Plugin/resource/EntityResource.php Line 145.->access() defaults to return_as_object = FALSE.
I believe line 144 should be changed to
$entity_access = $entity->access('create', NULL, TRUE);I've only tested with POST, but this is likely the case for all methods. (Unless I'm running into some other issue.)
Comment #79
gnugetHi lukedekker
Yes, this still has issues but I want to know if the changes to I suggested on #74 are correct before to continue fixing the rest of the tests.
Thanks for your review.
Comment #80
lukedekker commentedGnuget,
Absolutely understand. My last comment was meant as more of an FYI/when you get there sort of thing.
Thanks everyone for all of their work on this. I'm a noob to REST in D8 and this patch saved me hours of banging my head against the wall.
Comment #81
tedbow@gnuget #74 sounds like a good idea to me.
We could even have EntityResourceTestBase::assertResource403ResponseForMethod($method, $response)
Maybe something like
To avoid having "The {$this->getHttpMethodPermission($method')} permission is required" in bunch of places.
But I like the idea in general.
Comment #82
wim leersFirst, let's get this patch to green again. The most common failure is
Failed asserting that 500 is identical to 403.. On my local machine (on PHP 7), I get a more helpful error message:The problem lies here:
The original code was returning a boolean. But now you need the access result object, to be able to access the reason.
So you need to call not:
but:
Just like
EntityResource::get()was already doing.Once you do that, those failures are fixed. In other words: a tiny, tiny thing you missed :)
Comment #84
wim leers#67: that addressed all points of #65, except #65.7 — thanks! :)
I can help you with the
TestAuthauthentication provider plusEntityResourceTestBaseadditions. Did that in this reroll.Comment #85
wim leers#69 + #71:
Let's put single quotes around the permission, for legibility. (Did that for you in point 6.)
s/Forbidden/Neutral/
Unnecessary
\nadditions in several places in this file. Can you please revert those?Unused, please remove this.
This kind of unnecessary
\naddition in multiple places here. Please revert those.AccessResult::allowedIfHasPermissions(). I did that for you here.AccessResultNeutralTest, look atAccessResultForbiddenTestfor an example. (Which #2681911: REST requests without X-CSRF-Token header: unhelpful response significantly hinders DX, should receive a 401 response added.)AccessResult::orIf()andAccessResult::andIf().#74 This is interesting. This happens when it's not the
admin_permissionon the entity type annotation that matters, but another permission, which we cannot know, but which is determined by the logic of the entity type's access control handler. It could even be a combination of multiple permissions.This is happening for:
access contentfor GET)post commentsfor POST)entity_testentities (no message yet forGETBasically, it's happening for every entity type EXCEPT nodes, because that uses the "node access" (node grants) system.
In other words: access may be denied because of a permission which the current user does not have… but it may also be because of other reasons. This is why the approach proposed in #74 is mostly sound, but not entirely.
Interesting that it turns out this is by far the trickiest part of this patch! So, what do we need? We need to be able to specify the expected 403 message when a 403 happens due to a lack of authorization. We don't want to do an assertion; because the assertion can be automated via
assertResourceErrorResponse(). What we need is the expected message parameter for that method. So I thinkabstract protected function getExpectedUnauthorizedAccessMessage($method)would make sense. ThenEntityResourceTestBasecan provide a default implementation, which can be exactly your current implementation:Then for example
BlockResourceTestBasecan override it like this:IOW: it becomes easy to override the default behavior for just a single HTTP method, or a subset, or all. And if an entity type has the default behavior, well, then you don't need to do anything.
#78 + #79 + #80: oops, I already fixed that in #82 :)
#81: that won't work either, for the same reason #74 won't work.
Comment #86
wim leersNW for:
Back to you, @gnuget, you're now fully unblocked again :)
Comment #87
wim leersAlso, we should really get this into Drupal 8.3. Without this, developers will have to continue to run into these painful 403 responses without any idea on how to get it to work.
When it lands, it's worthy of the release notes, so tagging .
This also blocks e.g. JSON API from having good DX when it comes to 403 responses, so also tagging .
Comment #88
gnugetOk, I will focus on this today, I will do my best so we can include it on 8.3.x
Thanks.
Comment #89
wim leersWooot! Thanks, @gnuget :)
Comment #90
gnugetThis still is work in progress.
This patch almost addresses everything but I need the testbot's help so I can know which entities require overwriting the getExpectedUnauthorizedAccessMessage method, so I will let the bot to fail in those cases.
(And 85.8 is still pending)
Regards.
Comment #92
wim leersThat looks like another big step in the right direction!
Comment #94
gnugetThe only pending thing is #85.8 (not sure where put these tests)
I found that drupal was still sending several empty responses on different places I update all of them.
Let's see what the testbot says.
Comment #95
dawehnerIMHO the additional complexity of one vs. many should not be handled here. From my point of view this is not worth it.
I extended the test coverage and I think I found a bug, we don't merge message correctly on OR. See the failing test.
Comment #97
gnugetThanks for your review dawehner.
I won't be able to work on this during the weekend. If there is someone participating on the weekend sprint and want to help finishing this issue feel free to take over it I would love to see this on 8.3.x
Thanks again.
Comment #98
dawehnerThis fixes the additional test I wrote in #95. We still need to talk about the 1 vs. many IMHO.
Comment #99
gnuget@Wim Leers added that part he can explain better why it was added there.
Regards and thanks.
Comment #100
mradcliffeThis probably needs a security review to confirm that we are not falling into a information disclosure vulnerability.
It may not be good to leak what exactly caused the user to be denied access, but it is very useful when debugging. For instance, Drupal's current Access Denied page, which does not show why a user was denied access.
400 messages for validation are desirable, but 403 and 500 should be fairly limited as a malicious actor should not get much information as to what to target next. I do not know if mentioning permission names or entity type names is information disclosure or not, but this should be assessed before committing.
Comment #101
dawehnerThis is indeed a good point. Maybe this should just be enabled somehow but we should not expose the information by default.
Comment #102
chi commentedThis adds error level condition to ExceptionJsonSubscriber. I am not sure if we can fix this globally, not only for JSON HTTP errors.
Comment #104
dawehnerI'm not entirely convinced that this is the right way to solve it.
error_displayableis really about php errors and exceptions (aka. server side errors), rather than what we deal with here which are client side errors. I'm wondering whether this should be actually a configuration for the serialization exception subscriber. Alternative this might be a container parameter, much like cacheability cache headers.Comment #105
wim leersAwesome progress here!
I basically only have nits remaining. This patch is approaching RTBC status. It looks great (besides the few rough edges — see my review below) and delivers the huge DX improvement that this issue was created for :)
#94:
This is not a reason. It's a useless message.
This change should be reverted.
Apparently @dawehner agrees in #95.
Yay!
Yay!
This does not make sense. An operation is not a permission. Let's write a better message. And, in fact, let's remove this altogether, because it's impossible to determine the reason for access not being allowed here. The reason must be specified in the place that is disallowing access.
s/lets/let's/
Or, actually, just remove this comment. The code is trivial, the comment is a unnecessary.
YAY!
All of these would also be more legible with
switch.This is again an unhelpful reason.
In this case, I think it makes sense to remove the comment and use its text as the reason.
#95++ for pointing out the lack of test coverage + bug that you found by fixing the test coverage — this is why I said in #85.8
#100: disclosing what the requirements are to be able to access something is not information disclosure. If Drupal's security depended on this, we'd be in the questionable land of security through obscurity. Let's also not forget that 99% of Drupal sites don't use custom access control schemes; they use Drupal's permissions. So for 99% of these responses, the reason that we now send back in the response actually is something any attacker could have found by reading the Drupal source code.
Finally, let's not forget that this is only for API (REST/JSON API/GraphQL) requests, i.e. for non-HTML requests. Most routes/controllers only allow HTML. HTML error responses do not show these reasons. And even if they did, the above about security through obscurity still applies.
So this is unchanged:
#102: let's revert this change. For the reasons given in #104, this change does not make sense.
#98: this is the patch that I now need to review.
Thoroughly reviewed this, the logic looks perfect. I have zero remarks.
Well, there is one remark: the first point in my review of #94, for
allowedIfHasPermissions(). But that's trivially fixed.Should be removed, as I said in my review of #94.
Generates a fallback access denied message, in case no specific reason is set.
The disallowed entity operation.
@return stringShould be protected.
Also,
generateFallbackAccessDeniedMessagewould be a better name.Let's change this to:
Much shorter, much easier to read.
Unnecessary whitespace changes in these places, let's not make whitespace changes!
As I said in my review of #94, these would be better as
switchstatements.Again echoing what I said in my review of #94.
Comment #106
dawehnerThank you for the great review wim!
I 100% agree, good point. By doing what is in the patch right now, we effectively throw away information.]
]
+1
If someone also cares about that level of security, they can implement their own exception rendering.
Comment #108
wim leersRTBC once it comes back green, this just needs a trivial fix now!
Comment #109
mradcliffeThanks for the review, Wim.
Comment #110
dawehnerHere are the remaining test failures
Comment #111
wim leersThanks!
Comment #112
wim leersComment #114
wim leersRandom fail in
Drupal\node\Tests\Views\FrontPageTest?Comment #115
gnugetThis looks great!
Just want to say THANKS to all of you for helping me to work on this, I've learned a lot about how the REST module work and the access system.
Regards.
Comment #116
alexpottThis patch looks sensible. Imo we should consider making the reason required but that is a D9 change.
Needs a reroll.
Comment #117
dawehnerThank you for pointing it out. Here is a quick reroll. (There has been just one conflict).
Comment #118
wim leers+1!
Reviewed #117 (and diffed it with the previous RTBC patch). Trivial conflict resolved correctly. Back to RTBC.
Comment #119
xjmNote that issues like this should be targeted against 8.4.x, but can be considered for backport once they are committed to that branch. See the updated alpha release policy. Thanks!
Comment #120
wim leers#119: apologies!
Comment #121
alexpottI'm sorry I missed that there was no change record for this issue. We totally need one - especially if we're pointing to this in the release notes. If people don't know to set a reason the contrib projects which use neutral access results won't make the necessary change.
One thing this change has me thinking about is how reasons are merged.
In this example, doesn't the end user actually want both the reasons? Setting back to needs review to get an answer to this.
Noticed a minor coding standards issue that should be addressed prior to commit.
Comment #122
wim leersChange record created: https://www.drupal.org/node/2849066.
Regarding reason merging: if we go down the path of listing all reasons involved, we hugely complicate the logic (and the reason returned in the 403 response) for very little benefit. How do we present 5 reasons that have been AND-ed together in a clear manner? Just listing the first reason is causing access to not be allowed, the developer can fix that. And perhaps there will be another reason, but that's okay: the developer can then next fix that reason too. And so on.
There's one more reason, and probably it's even more important: access results are evaluated lazily. See
\Drupal\Tests\Core\Access\AccessResultTest::testAndIf(). You can see that in the last several assertions:In other words: the reason in the second, third, etc access result object would not even be considered as soon as the first is disallowing access!
Fixed the coding standards issue and actually found one more.
Comment #123
dawehnerWorks for me
Comment #124
xjmThis a contrib soft blocker as a bad DX issue.
Comment #125
alexpottCommitted and pushed 7f59f9c to 8.4.x and 448e8c2 to 8.3.x. Thanks!
@Wim Leers thanks for answering the reason issue. I think what you suggest makes sense.
Comment #128
wim leersYAYAYAYAYAYAY! This is such a big DX win! Thanks! :)
Comment #129
wim leersCR published: https://www.drupal.org/node/2849066
Comment #130
jacov commentedThank you!
Comment #132
effulgentsia commentedPer #127, this was committed to 8.3.x, so retroactively updating the version attribute.
Comment #133
wim leersIssue to make JSON API also use this: #2930231: JSON API 403 errors don't tell the user *why* access is not granted: requires deep Drupal understanding to figure out.
Comment #134
wim leersWhat this did for entity access, #2938035: When PATCHing a field is disallowed, no reason is given for *why* this happens does for field access.
Comment #135
wim leersComment #136
wim leersAnd also created #2938053: AccessResult::orIf() fails to retain the reason if both operands are neutral or forbidden, but the first contains a reason and the second one does not, which fixes a minor bug that was introduced here.