Align the authorization vocabulary: User, Group, Permission #43

Merged
lilleman merged 14 commits from role-vocabulary into main 2026-08-03 17:44:52 +02:00
Owner

Aligns the authorization vocabulary and documents the model. Supersedes #40, whose commits are included here.

The model is now Identity → Group → Permission.

  • Identity (was our User) matches Kratos, which owns that record. Keto subjects are identity:<kratos-id>. It collided with the Identity DTO #plugin-api re-exports from kratos-admin.ts — that one is the full Kratos record and keeps the plain name; ours is the JWT projection, now SessionIdentity, reached as ctx.identity.
  • Permission (was Role) follows RBAC: a permission is one operation ("read shifts"), a role is a bundle of them. A route gates on one operation, so it gates on a permission; a bundle is just a group with several grants, and groups nest. There is no Role namespace. The Keto relation is granted (Permission:x#granted@identity:y); Group keeps members.
  • Group unchanged. Resource unchanged — the separate per-row tier, and the only place Ory's own sense of "permission" (permits: view/edit/delete) applies.

Manifest surface: route.permission, navNode.permission, permissions: [{ name, description }], PermissionDecl. JWT claim and ctx field are permissions. HOST_API_VERSION stays 1.0.0 — unreleased, no plugin authors to break. No data migration: keto-migrate runs Keto's own bundled schema migrations and our tuples are runtime data, so docker compose down -v covers it.

UI labels stay in ordinary words — the admin menu says "Users", not "Identities", per Nielsen's heuristic #2. Not a rename of an Ory concept: Ory's docs state it uses "identity" interchangeably with "users"/"accounts". Same split as chrome.user/ShellUser (avatar view-model) vs SessionIdentity (entity). Recorded in AGENTS.md so it is not re-litigated.

README gains ## Identities, groups & permissions before Building plugins, since a manifest's permission: gate is unreadable without the model: entity table, a worked graph over three people, a per-route can/cannot walkthrough, the trap that a per-row Resource grant never widens a coarse gate, and the point that Keto ships no namespaces at all — all four are ours.

The Security model section is cut 70 → 36 lines. Its threat table restated code readable at its source (alg allowlist, frame-ancestors, nosniff, localPath, ORY_TIMEOUT_SEC) or duplicated the config table. What remains is only what a deployment gets wrong without it: the private network as the sole guard on the Ory APIs, signed-not-encrypted claims, the 30-day Kratos session behind the ~10m JWT, and non-instant offboarding. The CSRF opt-in footgun moved next to ctx.verifyCsrf.

Aligns the authorization vocabulary and documents the model. Supersedes #40, whose commits are included here. **The model is now `Identity → Group → Permission`.** - **`Identity`** (was our `User`) matches Kratos, which owns that record. Keto subjects are `identity:<kratos-id>`. It collided with the `Identity` DTO `#plugin-api` re-exports from `kratos-admin.ts` — that one is the full Kratos record and keeps the plain name; ours is the JWT projection, now `SessionIdentity`, reached as `ctx.identity`. - **`Permission`** (was `Role`) follows RBAC: a permission is one operation ("read shifts"), a role is a *bundle* of them. A route gates on one operation, so it gates on a permission; a bundle is just a group with several grants, and groups nest. There is no `Role` namespace. The Keto relation is `granted` (`Permission:x#granted@identity:y`); Group keeps `members`. - **`Group`** unchanged. **`Resource`** unchanged — the separate per-row tier, and the only place Ory's own sense of "permission" (`permits`: view/edit/delete) applies. Manifest surface: `route.permission`, `navNode.permission`, `permissions: [{ name, description }]`, `PermissionDecl`. JWT claim and `ctx` field are `permissions`. `HOST_API_VERSION` stays 1.0.0 — unreleased, no plugin authors to break. No data migration: `keto-migrate` runs Keto's own bundled schema migrations and our tuples are runtime data, so `docker compose down -v` covers it. **UI labels stay in ordinary words** — the admin menu says "Users", not "Identities", per Nielsen's heuristic #2. Not a rename of an Ory concept: Ory's docs state it uses "identity" interchangeably with "users"/"accounts". Same split as `chrome.user`/`ShellUser` (avatar view-model) vs `SessionIdentity` (entity). Recorded in AGENTS.md so it is not re-litigated. **README gains `## Identities, groups & permissions`** before Building plugins, since a manifest's `permission:` gate is unreadable without the model: entity table, a worked graph over three people, a per-route can/cannot walkthrough, the trap that a per-row `Resource` grant never widens a coarse gate, and the point that Keto ships no namespaces at all — all four are ours. **The Security model section is cut 70 → 36 lines.** Its threat table restated code readable at its source (alg allowlist, `frame-ancestors`, `nosniff`, `localPath`, `ORY_TIMEOUT_SEC`) or duplicated the config table. What remains is only what a deployment gets wrong without it: the private network as the sole guard on the Ory APIs, signed-not-encrypted claims, the 30-day Kratos session behind the ~10m JWT, and non-instant offboarding. The CSRF opt-in footgun moved next to `ctx.verifyCsrf`.
lilleman changed title from Rename the plugin permission gate to role and document the model to Align the authorization vocabulary with Ory: permission→role, User→Identity 2026-08-03 12:19:13 +02:00
lilleman changed title from Align the authorization vocabulary with Ory: permission→role, User→Identity to Align the authorization vocabulary: Identity, Group, Permission 2026-08-03 17:04:19 +02:00
lilleman changed title from Align the authorization vocabulary: Identity, Group, Permission to Align the authorization vocabulary: User, Group, Permission 2026-08-03 17:16:09 +02:00
lilleman added 14 commits 2026-08-03 17:42:01 +02:00
lilleman force-pushed role-vocabulary from e66c530b0a to c30cd95ebd 2026-08-03 17:42:01 +02:00 Compare
lilleman merged commit c30cd95ebd into main 2026-08-03 17:44:52 +02:00
lilleman deleted branch role-vocabulary 2026-08-03 17:44:52 +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#43