Problem/Motivation

hook_page_attachments() puts the current user's subscription into drupalSettings.subscriptionManager.currentUser.subscription on every page: plan_id, status, connector_plugin_id, the remote subscription_id and expiry. It declares no cacheability for that. Dynamic Page Cache stores a page by route and the cache contexts the page declares — the required ones are language, theme and permissions — so, on a route whose other content does not vary by user, the first authenticated user's subscription blob is stored with the page and served to every later user with the same permissions. On top of the leak, a subscription made after the page was cached never appears in the blob until the entry expires.

Reproduced in a browser test: two users with the same permissions request the same route (a 404 page, in Stark); the second receives the first one's remote subscription ID. On a real site most routes happen to carry a user context from something else — the account menu's Join/Upgrade link, the toolbar — which is why it has gone unnoticed, but nothing guarantees that, and a theme without the account menu, or a route rendered without blocks, exposes it.

Affects every release that has the attachment, which is all of them.

Proposed resolution

Remove the attachment and its hook. A front end that wants the current user's subscription calls api/my-subscription, which returns the same fields and more and is uncacheable by design. Declaring the attachment per user would fix the leak but cost every authenticated page the user cache context and a subscription lookup, for data nothing in the module reads.

The MR carries the browser test that demonstrated the leak in its history.

Remaining tasks

  • Review the MR (fix plus browser test).
  • Change record (behaviour: the blob is now per user and current; the tag means pages refresh when a subscription changes).
  • Review the MR.

User interface changes

None.

API changes

drupalSettings.subscriptionManager.currentUser.subscription is removed. Use GET /subscription-manager/api/my-subscription.

Data model changes

None.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

colan created an issue. See original summary.

colan’s picture

Title: The current user's subscription in drupalSettings is served to other users from Dynamic Page Cache » The current user's subscription in drupalSettings is served to other users from Dynamic Page Cache; remove the attachment
Issue summary: View changes

Implemented in the MR, in three commits, with a change of plan from the summary.

The first two commits do what the summary says: declare the user cache context and the subscription_list tag on the attachment, with a browser test — Dynamic Page Cache on, two users with the same permissions on the same 404 page in Stark — that fails on 1.0.x with the second user receiving the first one's remote subscription ID, and passes with the fix.

The third commit removes the attachment instead. Nothing in the module reads drupalSettings.subscriptionManager, and the known connectors and sites do not either; api/my-subscription already serves the same fields and more, uncacheably, to anyone with Manage own subscriptions. Kept and made cacheable, the blob would have cost every authenticated page the user cache context — one Dynamic Page Cache entry per user per route, all of them invalidated whenever any subscription changes — plus a subscription lookup per page render (#3616936: SubscribeMenuLink declares the user.roles cache context but varies per user, leaking Join/Upgrade labels across users trimmed that lookup; this removes it). That is a high price for a duplicate. Removing it closes the whole class of this bug rather than one instance. It is a breaking change for site JavaScript that read the setting, which is why it belongs in beta7 with the other breaking changes rather than later; the change record says what to fetch instead.

Gone with it: the hook_page_attachments() implementation and its unused current-user dependency, the PageAttachmentsQueryBudgetTest that existed to keep the lookup cheap, and the browser test from the second commit, which has nothing left to test. The README's decoupled section points at the API.

Suite: 199 Kernel tests, 6 Functional tests; phpcs clean. Change record drafted.

  • colan committed 5858d143 on 1.0.x
    Merge branch '3629912-page-attachments-cache' into '1.0.x'
    
    Resolve #...

  • colan committed 8a8dce47 on 1.0.x
    Issue #3629912: Stop attaching the current user's subscription to every...

  • colan committed 6f130a69 on 1.0.x
    Issue #3629912: Browser test that the drupalSettings subscription is per...

  • colan committed 7054577e on 1.0.x
    Issue #3629912: Vary the drupalSettings subscription by user and tag it...
colan’s picture

Status: Active » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.