Today, I had to solve a problem which boggled my mind. I added a custom AccessCheck to the route 'entity.node.canonical', on top of the existing access check, and made it return AccessResult::neutral().
I was very confused to find that the AccessResult::allowed() from the original node check, and my AccessResult::neutral(), combined into a final AccessResult::neutral() which made the route throw a 403 Access Denied error.
I looked deeper into it and investigated the AccessResult::andIf() method that is used to combine these access checks.
The three big "if" cases of the method are:
isForbidden() in either ⇒ isForbidden()
otherwise, if isAllowed() in both ⇒ isAllowed()
otherwise, one of them is isNeutral() ⇒ isNeutral()
So, I feel like there's a logic problem here.
If "isNeutral()" is meant to be the "I don't care what happens with the access" response, why does it override "isAllowed()"?
Shouldn't the if statement
elseif ($this->isAllowed() && $other->isAllowed()) {
$result = static::allowed();
$merge_other = TRUE;
}
instead be...
elseif ($this->isAllowed() || $other->isAllowed()) {
to make logical sense?
This differentiation is already documented in this article, but it doesn't provide an explanation for why this happens.