Skip to content

[6.x] Preserve unregistered permissions when saving a role#14975

Merged
jasonvarga merged 3 commits into
6.xfrom
preserve-unregistered-permissions
Jul 10, 2026
Merged

[6.x] Preserve unregistered permissions when saving a role#14975
jasonvarga merged 3 commits into
6.xfrom
preserve-unregistered-permissions

Conversation

@jasonvarga

@jasonvarga jasonvarga commented Jul 10, 2026

Copy link
Copy Markdown
Member

Saving a role in the Control Panel replaces its permissions with what the UI submitted. That payload only contains currently registered permissions, so anything else the role held gets silently dropped. This bites two cases:

  • Addon permissions — disable an addon, save any role, and its addon permissions are gone. Re-enabling doesn't bring them back.
  • Deprecated core permissions — ones unregistered but still honored for backward compatibility, lost on the next save.

Now update() merges any permission the role already holds that isn't in the registered tree back into what's saved. super is excluded (otherwise demoting a super role would resurrect it), and registered-but-unchecked permissions are still removed, so "Uncheck All" is unaffected. store() is untouched since a new role has nothing to preserve.

Accepted tradeoff

The registered tree resolves per-item permissions from collections, taxonomies, forms, etc. that currently exist. So deleting a collection makes its permissions (view blog entries) unregistered, and they'll now persist in the role's YAML instead of being pruned on save. Not a security concern — a permission naming a nonexistent collection grants nothing — and it means a deleted-then-recreated collection keeps its permissions. The cost is some potential YAML cruft, which beats trying to tell "unregistered because deprecated" apart from "unregistered because deleted".

Why this surfaced

In #14957 we're adjusting some permissions. Removing config form fields, but keeping it referenced in appropriate policies for backwards compatibility. But when you saved the role, you lost that permission if you weren't paying attention.

Note on the existing test

it_updates_a_role used fake handles (one, two, three) that were never registered, so they now count as unregistered and get preserved — breaking its full-replace assertion. Swapped for real core handles.

🤖 Generated with Claude Code

jasonvarga and others added 3 commits July 10, 2026 08:01
The roles CP payload is built from the registered permission tree, so any
permission a role held that wasn't currently registered - one from a disabled
addon, or a deprecated core permission - was silently dropped the first time
anyone saved that role.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Verified flattened()->map->value() yields the same resolved value set as
walking tree() - same values across all placeholder types (collections,
taxonomies, globals, assets, navs, forms), both populated and empty states.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jasonvarga jasonvarga enabled auto-merge (squash) July 10, 2026 18:20
@jasonvarga jasonvarga disabled auto-merge July 10, 2026 18:20
@jasonvarga jasonvarga enabled auto-merge (squash) July 10, 2026 18:21
@jasonvarga jasonvarga merged commit d9f2945 into 6.x Jul 10, 2026
22 checks passed
@jasonvarga jasonvarga deleted the preserve-unregistered-permissions branch July 10, 2026 18:25
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.

1 participant