Permission names are <resource>:<action>; permissions come from plugin code #58

Merged
lilleman merged 12 commits from permission-naming into main 2026-08-05 18:04:34 +02:00
Owner

Permission names now follow <resource>:<action> everywhere, and the rule is enforced by the host at plugin discovery — a plugin gating on, or declaring, a badly-shaped name stops the boot like any other bad manifest. So it holds for every installed plugin, not just ones the admin GUI touches.

The catch-all admin permission is gone. Each admin screen is its own resource — users, groups, oauth2-clients — split into :read and :write, so a helpdesk account can hold users:read alone. That split is modelled in the UI, not just the gate: a read-only holder sees no create/save/delete affordance anywhere, and the Admin nav filters per screen (hold none of the three and the section disappears).

The Permissions screen is deleted. Permission names are declared in plugin code, so the host collects them into one catalog (ctx.declaredPermissions) and that fixed list is what the Users and Groups screens offer as a checkbox picker. The submitted set is the desired state; a crafted POST cannot grant an undeclared name, and a grant left behind by an uninstalled plugin is never silently revoked by an unrelated save.

Guards worth naming: you cannot revoke your own grants; a permission held through a group renders ticked-but-disabled rather than unticked (showing it unticked stated the opposite of the truth); and deleting a group now revokes the permissions it granted, instead of orphaning them to resurrect under a re-created name.

Two pre-existing bugs fixed on the way: the permission path validator was the group name regex with no colon, so /admin/permissions/scheduling:read already 404'd; and bootstrap never bind-mounted plugins/, so a dropped-in plugin's permissions were never seeded — the quick start now says docker compose up -d, which re-runs the one-shot.

Delivers both the permission-format and the permissions-as-a-list todos. Reviewed by systems-architect (3 rounds), product-owner-reviewer, and stability-reviewer (2 rounds); decisions recorded in AGENTS.md and README, deferred items in todo.md.

Permission names now follow `<resource>:<action>` everywhere, and the rule is enforced by the **host at plugin discovery** — a plugin gating on, or declaring, a badly-shaped name stops the boot like any other bad manifest. So it holds for every installed plugin, not just ones the admin GUI touches. The catch-all `admin` permission is gone. Each admin screen is its own resource — `users`, `groups`, `oauth2-clients` — split into `:read` and `:write`, so a helpdesk account can hold `users:read` alone. That split is modelled in the UI, not just the gate: a read-only holder sees no create/save/delete affordance anywhere, and the Admin nav filters per screen (hold none of the three and the section disappears). **The Permissions screen is deleted.** Permission names are declared in plugin code, so the host collects them into one catalog (`ctx.declaredPermissions`) and that fixed list is what the Users and Groups screens offer as a checkbox picker. The submitted set is the desired state; a crafted POST cannot grant an undeclared name, and a grant left behind by an uninstalled plugin is never silently revoked by an unrelated save. Guards worth naming: you cannot revoke your own grants; a permission held through a group renders ticked-but-disabled rather than unticked (showing it unticked stated the opposite of the truth); and deleting a group now revokes the permissions it granted, instead of orphaning them to resurrect under a re-created name. Two pre-existing bugs fixed on the way: the permission path validator was the *group* name regex with no colon, so `/admin/permissions/scheduling:read` already 404'd; and `bootstrap` never bind-mounted `plugins/`, so a dropped-in plugin's permissions were never seeded — the quick start now says `docker compose up -d`, which re-runs the one-shot. Delivers both the permission-format and the permissions-as-a-list todos. Reviewed by systems-architect (3 rounds), product-owner-reviewer, and stability-reviewer (2 rounds); decisions recorded in AGENTS.md and README, deferred items in todo.md.
lilleman marked the pull request as work in progress 2026-08-05 12:45:27 +02:00
lilleman changed title from WIP: Permission names are <resource>:<action>, replacing the catch-all admin permission to Permission names are <resource>:<action>; permissions come from plugin code 2026-08-05 15:33:58 +02:00
lilleman added 8 commits 2026-08-05 15:39:32 +02:00
lilleman force-pushed permission-naming from d626b3ae74 to 1f0956dd58 2026-08-05 15:39:32 +02:00 Compare
lilleman added 1 commit 2026-08-05 17:36:06 +02:00
lilleman added 1 commit 2026-08-05 17:48:59 +02:00
Todo updates
CI / full-gate (push) Successful in 2m42s
f76cd2a267
lilleman added 1 commit 2026-08-05 17:52:52 +02:00
lilleman added 1 commit 2026-08-05 18:00:41 +02:00
Record the stale-copy trap as accepted until the apiVersion freeze lifts
CI / full-gate (push) Successful in 2m41s
Mirror / github-mirror (push) Successful in 3s
2852722873
lilleman merged commit 2852722873 into main 2026-08-05 18:04:34 +02:00
lilleman deleted branch permission-naming 2026-08-05 18:04:34 +02:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: larvit/plainpages#58