Rename the plugin-API permission gate to role
This commit is contained in:
@@ -15,7 +15,7 @@ What it demonstrates:
|
||||
`POST /scheduling/shifts` CSRF-verifies it (`ctx.verifyCsrf`) and forwards the create upstream,
|
||||
then POST-redirect-GET. The form body lives in the plugin's own `views/partials/shift-form.ejs`,
|
||||
reusing the core `field` partial.
|
||||
- **Permission-gated nav** — the "Shifts" nav leaf and routes are gated on `scheduling:read` /
|
||||
- **Role-gated nav** — the "Shifts" nav leaf and routes are gated on `scheduling:read` /
|
||||
`scheduling:write`; the whole "Scheduling" section is invisible to anyone without the grant.
|
||||
|
||||
The plugin holds **no state** — data lives upstream (README → *Stateless*). Handlers are thin and
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Reference plugin: a worked example of the contract — a list page that fetches upstream
|
||||
// data, a CSRF-guarded form that forwards a write upstream, and permission-gated nav. Copy this
|
||||
// data, a CSRF-guarded form that forwards a write upstream, and role-gated nav. Copy this
|
||||
// folder, rename it, point it at your own backend. Full contract: README.md → Building plugins.
|
||||
|
||||
import { definePlugin } from "#plugin-api";
|
||||
@@ -23,7 +23,7 @@ export default definePlugin({
|
||||
nav: [{
|
||||
children: [
|
||||
{ href: SCHEDULING_PATH, id: "scheduling:overview", label: "Overview", public: true },
|
||||
{ href: SHIFTS_PATH, id: "scheduling:shifts", label: "Shifts", permission: READ },
|
||||
{ href: SHIFTS_PATH, id: "scheduling:shifts", label: "Shifts", role: READ },
|
||||
],
|
||||
icon: "i-cal",
|
||||
id: "scheduling",
|
||||
@@ -31,17 +31,17 @@ export default definePlugin({
|
||||
}],
|
||||
|
||||
// Tokens this plugin introduces (docs + Keto seeding). Namespaced `<id>:<action>`.
|
||||
permissions: [
|
||||
{ description: "View shifts", token: READ },
|
||||
{ description: "Create and edit shifts", token: WRITE },
|
||||
roles: [
|
||||
{ description: "View shifts", name: READ },
|
||||
{ description: "Create and edit shifts", name: WRITE },
|
||||
],
|
||||
|
||||
// Mounted under /scheduling; `permission` gates before the handler runs. The overview is `public`
|
||||
// Mounted under /scheduling; `role` gates before the handler runs. The overview is `public`
|
||||
// (anyone may reach /scheduling, signed in or not); the rest need a role.
|
||||
routes: [
|
||||
{ handler: overview(), method: "GET", path: "/", public: true },
|
||||
{ handler: listShifts(upstream), method: "GET", path: "/shifts", permission: READ },
|
||||
{ handler: newShiftForm(), method: "GET", path: "/shifts/new", permission: WRITE },
|
||||
{ handler: createShift(upstream), method: "POST", path: "/shifts", permission: WRITE },
|
||||
{ handler: listShifts(upstream), method: "GET", path: "/shifts", role: READ },
|
||||
{ handler: newShiftForm(), method: "GET", path: "/shifts/new", role: WRITE },
|
||||
{ handler: createShift(upstream), method: "POST", path: "/shifts", role: WRITE },
|
||||
],
|
||||
});
|
||||
|
||||
@@ -10,8 +10,8 @@ import { can, CSRF_FIELD, GuardError, type PageChrome, parseListQuery, readFormB
|
||||
|
||||
export const SCHEDULING_PATH = "/scheduling"; // the plugin's public overview page
|
||||
export const SHIFTS_PATH = "/scheduling/shifts";
|
||||
export const READ = "scheduling:read"; // permission token gating the list + nav
|
||||
export const WRITE = "scheduling:write"; // permission token gating create
|
||||
export const READ = "scheduling:read"; // role name gating the list + nav
|
||||
export const WRITE = "scheduling:write"; // role name gating create
|
||||
|
||||
export interface Shift {
|
||||
id: string;
|
||||
|
||||
Reference in New Issue
Block a user