Issue #3586311: Harden usage record and listing access checks.

Description

The usage record page (view ai context usage) rendered context item labels and full content without checking $context_item->access('view'), allowing usage-only viewers to read unpublished items they cannot otherwise access. Related fields on the same page and in the usage listing also rendered links without matching route or entity access.

This MR gates those disclosures behind the same access checks used elsewhere in the module, bubbles access-result cacheability on every branch, and adds functional tests for the record page, usage listing, and context item list operations.

Changes

Usage record controller (AiContextUsageRecordController)

  • Gate context item toLink() and the "Context item content (current)" details block on $context_item->access('view'), with Context item @id (no access) fallback.
  • Gate the agent field on ai_context.settings.agent_edit route access; show plain text when denied.
  • Show @type:@id (no access) when the target entity exists but view access is denied; show plain @type:@id when the entity cannot be loaded.
  • Bubble access-result cacheability for context item, target entity, and agent route checks via CacheableMetadata::applyTo().

Usage listing Views fields

  • AgentLinkField: apply the same agent edit route access check; render plain text when denied; bubble access cacheability on both allowed and denied branches.
  • ContextItemLinkField (new): gate the context item column on $context_item->access('view'); show Context item @id (no access) instead of an empty cell when denied; bubble access cacheability.
  • AiContextUsageViewsData: wire both custom field handlers for agent_id and context_item_id.

Access handler (AiContextItemAccessControlHandler)

  • Return cachePerUser() for unpublished item view checks whenever view ai context items is held, since access depends on ownership for non-owners too.

Context item list (AiContextItemListBuilder)

  • Gate the "Add subcontext" operation on create access for the current user.

Tests

  • AiContextUsageRecordAccessTest (new): unpublished context item hidden from usage-only viewer (label, content, target entity fallback); missing target entity shows plain type:id (not (no access)); published item visible to admin; agent link respects context edit route access on record page and usage listing; usage listing context item column respects view access; owner-based access cacheability bubbled to the response (user cache context).
  • AiContextItemListTest: "Add subcontext" operation requires create permission.

Behavior note

Users with only view ai context usage now see (no access) fallbacks for context items and target entities they cannot view, and plain-text agent IDs instead of configuration links. This is intended privilege separation.

Testing instructions

# From the ai_context module directory:
./lint.sh

# From the project root (DDEV):
ddev exec vendor/bin/phpunit \
  web/modules/contrib/ai_context/tests/src/Functional/AiContextUsageRecordAccessTest.php

ddev exec vendor/bin/phpunit \
  --filter testAddSubcontextOperationRequiresCreateAccess \
  web/modules/contrib/ai_context/tests/src/Functional/AiContextItemListTest.php

Manual checks (as admin and as a usage-only viewer):

  • /admin/config/ai/context/usage — context item and agent columns respect access.
  • A single usage record page — context item label/content, agent link, and target entity respect access.

Checklist

  • I have updated the MR title to use format: Issue #1234: My issue title.
  • I have updated the MR description to include: Closes #1234
  • I have performed a self-review of my own code
  • I have added or updated tests, or explained in the description why this change is not covered by tests
  • I have updated documentation for any new or changed functionality
  • I have written testing instructions and verified them locally
  • I have noted any required post-merge steps (config imports, cache rebuilds, manual changes)
  • This MR contains no breaking API or hook changes, or they are explicitly documented in the description

AI Compliance

Note

Check the one that best describes your usage, or leave all unchecked if AI was not significantly used.

  • AI Assisted Code
    Mainly written by a human; AI used for autocomplete or partial generation under full human supervision.

  • AI Generated Code

  • Vibe Coded

Closes #3586311 (closed)

Edited by Kristen Pol

Merge request reports

Loading