Skip to content

[6.x] Hide permissions covered by a broader permission#14970

Merged
jasonvarga merged 13 commits into
6.xfrom
hidden-by-permissions
Jul 9, 2026
Merged

[6.x] Hide permissions covered by a broader permission#14970
jasonvarga merged 13 commits into
6.xfrom
hidden-by-permissions

Conversation

@jasonvarga

@jasonvarga jasonvarga commented Jul 9, 2026

Copy link
Copy Markdown
Member

Permissions may now declare one or more permissions that hide them in the roles UI:

$this->permission('view {container} assets')->hiddenBy('configure asset containers');
$this->permission('edit {form} form')->hiddenBy(['configure forms', 'edit forms']);

When any of the hiding permissions are checked, the permission and its children disappear from the roles UI. Unchecking brings them back, with their checked state intact.

CleanShot 2026-07-09 at 16 29 46

This is purely visual. It doesn't grant anything, and it doesn't establish a hierarchy between permissions. It just avoids showing redundant checkboxes, cleaning up the UI.

The concept is cherry-picked from #14957, where it was introduced as supersededBy as part of Forms 2. It's extracted here so it can land on its own, renamed, and widened to accept multiple permissions.

Core permissions

The configure permissions already grant every ability their policies guard, eg. configure collections -> view {collection} entries.

Fixes coming along for the ride

Publish state on new entries

EntriesController::create() resolved canManagePublishState from the raw publish {collection} entries permission, which sidesteps EntryPolicy and falls through to Gate::after. Users able to configure collections were denied the publish state toggle, despite the policy granting them everything. It now goes through the policy, matching the edit page.

Permission descriptions

Permissions using replacements(), and their children, are rebuilt when the tree is generated. That rebuild never copied the description across, so publish {collection} entries and reorder {collection} entries have never shown theirs in the roles UI, despite having one in lang/en/permissions.php. Fixed alongside the same bug affecting hiddenBy.

Tests

Nothing asserted that the configure permissions actually grant the abilities behind the permissions we're now hiding. Added user_with_configure_permission_can_do_it_all to EntryPolicyTest and TermPolicyTest, and view/edit coverage to GlobalSetVariablesPolicyTest. Navs and assets were already covered.

duncanmcclean and others added 13 commits July 9, 2026 13:45
Permissions can declare a broader permission that supersedes them. The
roles UI hides superseded permissions while the superseder is checked.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Covers hiding a superseded permission (and its children) once the
superseding permission is checked, and leaving it visible otherwise.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When creating an entry, the ability was resolved from the raw
"publish {collection} entries" permission, which sidesteps the policy.
Users able to configure collections were denied the publish state
toggle, despite the policy granting them everything.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The configure permissions already grant every ability their policies
guard, so the per-item permissions they cover no longer need to be
shown in the roles UI alongside them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The hiding is purely visual, so name it for what it does rather than
implying the hidden permission has been replaced. A permission may now
be hidden by any one of several permissions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The roles UI hides permissions that a configure permission covers, so
the coverage needs pinning down. Entries and terms had no such tests,
and globals only covered creating and deleting variables.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Unlike the other configure permissions, "configure addons" governs a
different resource to the addon settings permissions it would hide.
Whether it should grant access to every addon's settings is a separate
question to this one.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The tree is self-recursive, so there is only one call site to thread it
through. A prop keeps the component standalone and makes the data flow
visible, rather than reaching for an ancestor's provide.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The children of a permission with replacements are rebuilt when the tree
is generated, which dropped their hiddenBy. Core only declares it on
top level permissions, but addons may declare it anywhere.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Permissions with replacements, and their children, are rebuilt when the
tree is generated, which dropped their descriptions. The publish and
reorder entry permissions have never shown theirs in the roles UI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The roles form needs to know which permissions are visible too, so the
predicate shouldn't live in the tree component.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The buttons walked the whole tree, so a group could read as "Check All"
with every visible box already ticked, and checking would grant
permissions the user never saw.

Checking now only affects visible permissions, using the checked state
from before anything is mutated. Unchecking clears the group outright,
since unchecking a broader permission reveals the ones it was hiding.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jasonvarga jasonvarga merged commit 0d93232 into 6.x Jul 9, 2026
24 checks passed
@jasonvarga jasonvarga deleted the hidden-by-permissions branch July 9, 2026 20:37
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.

2 participants