Problem/Motivation
We have entity render caching. But its cache keys & contexts don't take into account entity access, field access, nor node grants. Entities are only cached per role by default. Hence, any access checking that depends on something other than role will effectively be broken, and will be disclosing information to users that aren't allowed to see it.
Proposed resolution
#2429257: Bubble cache contexts provides the necessary infrastructure: it automatically bubbles cache contexts. Therefore this issue is merely a matter of ensuring that whenever we call $entity->access()
or $field->access()
, that we use the AccessResult
object we can get from there, and set the associated cache tags & cache contexts on the render array.
Remaining tasks
Ensure the right cache contexts & tags are set for:
entities (entity access) — see #49 + #61fields (field access)the entities rendered by entity reference field formatters
User interface changes
None.
API changes
None.
Internal (protected) API change: EntityReferenceFormatterBase::needsAccessCheck()
signatures modified, which affects all custom/contrib modules' Entity Reference formatter classes subclassing that class and overriding it (which is very rarely needed; in core itself, it only happens for file formatters).
Beta phase evaluation
Issue category | Bug because HEAD may show inaccessible information without this patch. |
---|---|
Issue priority | Critical because security bug. |
Prioritized changes | The main goal of this issue is security. |
Disruption | Disruptive for contributed and custom modules that subclass EntityReferenceFormatterBase and override its ::needsAccessCheck() method. |
Original report by effulgentsia
Opening this after discussions at DrupalCon this week.
Field access and entity access modules complicate entity render caching quite a bit, couple of examples:
1. An entity reference holds 3 referenced entities but the current user only has access to two.
2. A field access module is preventing two fields from being rendered at all.
Access modules can be based on arbitrary granularity - forcing it to be per-user, but also potentially by location or some other arbitrary criteria.
We could use JavaScript replacement for these in the same way as #1991684: Node history markers (comment & node "new" indicator, "x new comments" links) forces render caching to be per user but this moves actual content into js replacement, rather than more incidental items like new/updated/edit links etc.
Another option is to allow entity and field access modules to add to cache granularity themselves - since they know exactly what criteria is going to be needed. i.e. a per-role field access module needs to do nothing, because entity render caching is already per roie. But a per-organic group field access module needs a hash of the current user's organic group memberships - and only it knows that.
This may be possible with existing alter hooks (hook_entity_view_alter()?) but we ought to be able to test that it works correctly at least - and it'll need to be documented somewhere that entity/field access modules need to do this. If that's not enough we may need to add an extra step or move things around.