Answer product-owner round 1: 0 🔴, 2 🟡 (0 misread); say the old copy is refused and nothing redirects /admin/, show a profile-entry override

This commit is contained in:
2026-11-08 21:33:57 +01:00
parent 8324a9c59e
commit b8a510a1c5
4 changed files with 16 additions and 14 deletions
+6 -6
View File
@@ -21,10 +21,10 @@ set of ids, so the boot refuses an entry whose `id` another entry or nav node al
### The admin example plugin is now `auth-admin`, and its screens live at `/auth-admin/*` ### The admin example plugin is now `auth-admin`, and its screens live at `/auth-admin/*`
The Users, Groups, OAuth2-clients and Plugin-settings screens move from `/admin/*` to The Users, Groups, OAuth2-clients and Plugin-settings screens move from `/admin/*` to
`/auth-admin/*`. An installed `plugins/admin/` keeps working until you replace it: delete it, copy `/auth-admin/*`, and nothing redirects the old paths, so update any proxy rule, bookmark, alert or
`examples/plugins/auth-admin/` into `plugins/`, and move any `locales/plugins/admin/` to log query that matches `/admin/`. An installed `plugins/admin/` still declares `apiVersion` 0.4, so
`locales/plugins/auth-admin/`. The boot refuses the two installed together, since both declare the the boot refuses it: delete it, copy `examples/plugins/auth-admin/` into `plugins/`, and move any
nav section `admin`. `locales/plugins/admin/` to `locales/plugins/auth-admin/`.
### The `request` log line renames `method`, `path`, `status` and `ms`, and gains the caller, site and route ### The `request` log line renames `method`, `path`, `status` and `ms`, and gains the caller, site and route
@@ -152,7 +152,7 @@ Two administrators editing one user's or group's permissions no longer overwrite
form carries a token for the grants it showed. A save that would change something, made after those form carries a token for the grants it showed. A save that would change something, made after those
grants changed, is refused with "These permissions changed while you had the page open", and the page grants changed, is refused with "These permissions changed while you had the page open", and the page
shows the current grants. A POST with no token is refused the same way, so a script posting to shows the current grants. A POST with no token is refused the same way, so a script posting to
`/admin/users/:id/permissions` or `/admin/groups/:name/permissions` must first read the token from the `/auth-admin/users/:id/permissions` or `/auth-admin/groups/:name/permissions` must first read the token from the
page's `baseline` field. page's `baseline` field.
### The admin example confirms a permission save and reports a partial one ### The admin example confirms a permission save and reports a partial one
@@ -165,7 +165,7 @@ changes that landed still reach the user's live sessions at once when the revoca
A holder of `users:read` without `users:write` gets a "View" row action with an eye icon. The admin A holder of `users:read` without `users:write` gets a "View" row action with an eye icon. The admin
example's catalogs gain `admin.grants.partial`, `admin.grants.saved`, `admin.grants.stale` and example's catalogs gain `admin.grants.partial`, `admin.grants.saved`, `admin.grants.stale` and
`admin.users.view`, so a translated catalog mounted under `locales/plugins/admin/` must add them. `admin.users.view`, so a translated catalog mounted under `locales/plugins/auth-admin/` must add them.
### Every page carries a smaller icon sprite ### Every page carries a smaller icon sprite
+4 -4
View File
@@ -418,7 +418,7 @@ URL/path-safe (`isValidPluginId`: lowercase `a–z`, digits, dashes) and also na
`RESERVED_PLUGIN_IDS` are refused at discovery — the gated `dashboard`, the Kratos auth flows `RESERVED_PLUGIN_IDS` are refused at discovery — the gated `dashboard`, the Kratos auth flows
(`auth`, `login`, `logout`, `recovery`, `registration`, `settings`, `verification`) and their `error` page, the `oauth2` (`auth`, `login`, `logout`, `recovery`, `registration`, `settings`, `verification`) and their `error` page, the `oauth2`
provider routes, and `public` — since plugin routes resolve first and a folder claiming one would provider routes, and `public` — since plugin routes resolve first and a folder claiming one would
silently shadow a built-in. `auth-admin` is **not** reserved: the admin screens are themselves a plugin. silently shadow a built-in.
Installing a plugin is "drop the folder, restart"; removing one is "delete the folder, restart". Installing a plugin is "drop the folder, restart"; removing one is "delete the folder, restart".
@@ -1003,9 +1003,9 @@ The menu is **driven entirely by config** and assembled from two sources:
1. **Plugin fragments** — each plugin contributes its own `nav` (above). 1. **Plugin fragments** — each plugin contributes its own `nav` (above).
2. **A central override** — `config/menu.ts` (loaded by `src/ui/menu-config.ts`, validated at 2. **A central override** — `config/menu.ts` (loaded by `src/ui/menu-config.ts`, validated at
boot) — where the operator reorders, renames, groups, or hides items (by node `id`), hides or boot) — where the operator reorders, renames, groups or hides nav nodes and renames or hides
renames a profile-menu entry (by its `id`), and sets branding (app name, logo, default theme). The override always wins, applied before profile-menu entries, each by its `id`, and sets branding (app name, logo, default theme). The
the per-user filter. A clean clone needs no `config/menu.ts`; defaults apply. override always wins, applied before the per-user filter. A clean clone needs no `config/menu.ts`; defaults apply.
`config/` is an **empty drop-in mount point** (like `plugins/`): it ships empty, and you `config/` is an **empty drop-in mount point** (like `plugins/`): it ships empty, and you
supply `config/menu.ts` by copying the template ([`examples/config/menu.ts`](examples/config/menu.ts)) supply `config/menu.ts` by copying the template ([`examples/config/menu.ts`](examples/config/menu.ts))
+4 -2
View File
@@ -3,7 +3,8 @@
// Absent config = built-in defaults. // Absent config = built-in defaults.
// //
// Brand the app, reorder/rename/group/hide nav nodes and rename/hide profile-menu entries (by their // Brand the app, reorder/rename/group/hide nav nodes and rename/hide profile-menu entries (by their
// `id`) across all plugins — the override always wins, applied before the per-user permission filter. Every field is optional. // `id`) across all plugins — the override always wins, applied before the per-user permission
// filter. Every field is optional.
// See src/ui/menu-config.ts (types), src/ui/nav.ts (NavOverride), README → The menu system. // See src/ui/menu-config.ts (types), src/ui/nav.ts (NavOverride), README → The menu system.
import { defineMenu } from "#menu-config"; import { defineMenu } from "#menu-config";
@@ -16,7 +17,8 @@ export default defineMenu({
// theme: "auto", // default color theme: auto | light | dark // theme: "auto", // default color theme: auto | light | dark
}, },
// Operator override (rename → group → order → hide), keyed by node id. // Operator override (rename → group → order → hide), keyed by nav-node id; `rename` and `hide` also
// take a profile-menu entry's id, such as "scheduling:profile-mine".
override: { override: {
// rename: { people: "Staff" }, // node id → new label (or a catalog key) // rename: { people: "Staff" }, // node id → new label (or a catalog key)
// groups: [{ id: "admin", label: "Admin", children: ["users", "groups"] }], // groups: [{ id: "admin", label: "Admin", children: ["users", "groups"] }],
+2 -2
View File
@@ -1,8 +1,8 @@
# auth-admin — the plugin that administers users, groups, permissions and OAuth2 clients # auth-admin — the plugin that administers users, groups, permissions, OAuth2 clients and plugin settings
The Users / Groups / OAuth2-clients screens for running Plainpages itself, shipped as a **drop-in The Users / Groups / OAuth2-clients screens for running Plainpages itself, shipped as a **drop-in
example plugin** so a fresh clone has no admin GUI until you opt in. Copy this folder into `plugins/` example plugin** so a fresh clone has no admin GUI until you opt in. Copy this folder into `plugins/`
(it keeps the id and mount path `auth-admin`, so the screens live at `/auth-admin/*`) and restart: (its folder name `auth-admin` is its id and mount path, so the screens live at `/auth-admin/*`) and restart:
```bash ```bash
cp -r examples/plugins/auth-admin plugins/auth-admin cp -r examples/plugins/auth-admin plugins/auth-admin