Skip to content

test(cms): close the acceptance list of the dynamic per-path access control - #6753

Open
delchev wants to merge 1 commit into
masterfrom
test/cms-access-acceptance
Open

test(cms): close the acceptance list of the dynamic per-path access control#6753
delchev wants to merge 1 commit into
masterfrom
test/cms-access-acceptance

Conversation

@delchev

@delchev delchev commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Closes #6563.

Why this issue is still open

Its design shipped in #6566 (the store, the resolver, the guard, the endpoint, the dialog in both
Documents surfaces) and #6571 (the raw content path). #6566's body named both this issue and #6562
in a single closing sentence — a form GitHub only reads for the first reference, so this one was
never linked and never closed.

What is genuinely missing is the part of the issue nobody ships by accident: the acceptance list
it names — "Tests that must exist, because these are the failure modes of what is being replaced".
Three of the five were never written, and the two that guard the escapes and the kill switch guard
exactly the behaviour that keeps a restrictive rule from taking the platform down with it.

The issue's list Before Now
a grant/revoke takes effect on the next request with no sleep CmsAccessControlIT unchanged
a restricted folder disappears from a listing and its children are unreachable by direct path CmsAccessControlIT unchanged
a rule in tenant A does not affect tenant B missing CmsAccessControlIT.a_grant_in_one_tenant_does_not_govern_another
each of the three system-context escapes still succeeds under a restrictive rule missing DocumentAccessEvaluatorTest
DIRIGIBLE_CMS_ROLES_ENABLED=false disables enforcement wholesale missing DocumentAccessEvaluatorTest + CmsAccessControlIT.the_kill_switch_disables_enforcement_wholesale

The three escapes

Content seeding, a scheduled job, a workflow delegate minting a print snapshot and a message
listener storing an attachment all write to the CMS with no user and no roles behind them.
Denying them the moment an administrator restricts one folder would break seeding rather than
secure it — so enforcement engages only for a request, never in anonymous mode, and never when the
kill switch is off. That is three branches of an || that no test touched; each is now asserted
against a service that denies everything, so a passing case can only be the escape. The
anonymous case additionally asserts the enforcing answer first, so the static-mock setup cannot
carry the assertion after it.

Tenant isolation

The mechanism this replaced kept grants in the global DIRIGIBLE_SECURITY_ACCESS while every CMS
path is tenant-resolved, so one tenant's rule governed every tenant's like-named folder. The test
grants over HTTP in the default tenant, then asserts the same path is untouched inside a second,
provisioned tenant — and the reverse, a grant written inside that tenant that the default tenant
never sees, re-reading rather than trusting the cache so it asserts an absent row and not a stale
answer.

The last residue of the replaced mechanism

CmisSessionFactory still injected AccessVerifier — the .access/scope: CMIS path that this
design removed — and never used it; it was engine-cms's only reference to engine-security, so
the module dependency goes with it.

Deliberately not included

The legacy importer. The issue asks for a one-shot import of __internal/roles-access.json and
of scope: CMIS rows on first start per tenant. #6566 left it out with the reason stated there: the
only writer of that file was api/constraints.js, which nothing called, so the loop maintained an
empty {"constraints":[]} — the issue says so itself. Adding an importer now would be a code path
reading a file the platform no longer writes, for rows that in practice do not exist. Say the word
if an installation is known to carry rules and it is a small addition.

The CMIS option in the .access editor's scope dropdown. Nothing consults that scope any
more, so the option authors a rule that silently does nothing — but removing it would leave an
existing file's scope unmatched by any option in the select, which is a worse failure than the one
it fixes. Worth its own change, not this one.

Verification

  • DocumentAccessEvaluatorTest 5/5, CmsAccessResolutionTest 11/11 green on engine-cms.
  • CmsAccessControlIT 7/7 and DocumentsApiIT green, headless.
  • Full reactor mvn clean install -P quick-build green after the dependency removal.
  • mvn formatter:validate clean.

🤖 Generated with Claude Code

…ontrol

The design of #6563 shipped in #6566 and #6571; what never shipped is the
acceptance list the issue names - three of its five required tests.

A grant that restricts a folder must not restrict the platform's own writes:
content seeding, a scheduled job, a workflow delegate minting a print snapshot
and a message listener storing an attachment all reach the CMS with no user and
no roles behind them. That is three branches of one condition - no request,
anonymous mode, the kill switch - and no test touched any of them. Each is now
asserted against a service that denies everything, so a passing case can only be
the escape; the anonymous case asserts the enforcing answer first so the stubbing
cannot carry the assertion after it.

Grants are tenant data, which is the defect the design was written to fix: the
mechanism it replaced kept them in the global DIRIGIBLE_SECURITY_ACCESS while
every CMS path is tenant-resolved. The isolation is now asserted in both
directions across two provisioned tenants, re-reading rather than trusting the
cache so it asserts an absent row and not a stale answer. And the kill switch is
asserted end to end, over both the Documents API and the raw content path.

CmisSessionFactory still injected AccessVerifier - the .access / scope: CMIS path
this design removed - and never used it. It was engine-cms's only reference to
engine-security, so the module dependency goes with it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cms: dynamic per-path role access control (tenant-aware store, one resolver, UI in both Documents surfaces)

1 participant