fix(cabana): safe account action URLs, per-section isolation and the prompted record action contract (B-WR-03, B-WR-05, B-WR-06)
- an account action URL is kept only when it is http(s) with a host or a same-origin path; anything else is dropped and logged without the URL - a failing Status marks its section error: true and a failing Applies drops only that action, instead of a 500 for the whole list - status text goes through localizeDetail (B-IN-03); the non-string payload refusal is the phrase key action_prompt_invalid (B-IN-02) - the record action annotations and pact's prompt doc say what happens (B-IN-05 text); admin.json and schema.d.ts regenerated
This commit is contained in:
@@ -147,7 +147,7 @@ Every signed-in administrator can edit their own profile on the My account scree
|
||||
- **Preferences:** the theme (system, light or dark) and the admin language an administrator picks in the SPA are saved through `PUT <prefix>/api/v1/account/preferences` and read with `GET <prefix>/api/v1/account/preferences`; `GET .../auth/me` returns them as `preferences`. The body may hold only `theme` and `locale` (`cabana.AdminPreferencesRequest`); any other key, including `id`, answers 422 on that key and nothing is written. Each present key is saved and an absent key keeps its value, so `{}` answers the current preferences. `theme` must be `system`, `light` or `dark`; `locale` must be `""` (follow the request locale) or one of the admin locales `GET /lang` lists in `meta.locales`; another value or a non-string answers 422 on that field. They are stored under `admin_theme` and `admin_locale` in `backend_users.metadata`, next to the subtitle; the defaults (`system`, `""`) are stored as absent keys, every other key survives a save, and metadata that is not a JSON object is never overwritten (422). Like the rest of My account the routes act only on the token's own administrator and refuse personal API tokens.
|
||||
- **API tokens:** a card below the profile form (never inside it — the page Save does not touch tokens) lists, creates and revokes only the signed-in administrator's own tokens. Creating asks for a name, an expiry (`30d`, `90d`, `365d` or `never`) and scopes from `GET /account/tokens/scopes`; the plaintext is shown once in the dialog for copying, then cleared. Revoke asks for confirmation. A "Show revoked tokens" toggle reloads the list with revoked rows. See [API tokens](#api-tokens) for the wire contract and scopes.
|
||||
|
||||
- **Plugin sections:** a plugin can add its own card below the API tokens, for something only the signed-in administrator does for themselves, such as linking their site account. The plugin implements `pact.HasAccountContributions` and returns `pact.AccountContribution` entries: a name, a label and description, an optional status line computed for the administrator, and buttons (`pact.AccountAction`) with an optional confirmation (danger variant included), extra permissions and an `Applies` rule. The SPA loads them from `GET <prefix>/api/v1/account/contributions` and runs a button through `POST <prefix>/api/v1/account/contributions/{name}/actions/{action}`, which calls the action's `Run` with the signed-in administrator's id inside a transaction (`cabana.TxFromContext`) and shows the returned message; a returned URL is opened in a new tab and shown as a link. Both routes refuse personal API tokens like the rest of My account, and a page without contributing plugins is unchanged.
|
||||
- **Plugin sections:** a plugin can add its own card below the API tokens, for something only the signed-in administrator does for themselves, such as linking their site account. The plugin implements `pact.HasAccountContributions` and returns `pact.AccountContribution` entries: a name, a label and description, an optional status line computed for the administrator, and buttons (`pact.AccountAction`) with an optional confirmation (danger variant included), extra permissions and an `Applies` rule. The SPA loads them from `GET <prefix>/api/v1/account/contributions` and runs a button through `POST <prefix>/api/v1/account/contributions/{name}/actions/{action}`, which calls the action's `Run` with the signed-in administrator's id inside a transaction (`cabana.TxFromContext`) and shows the returned message; a returned URL is opened in a new tab and shown as a link. Only an absolute `http`/`https` URL or a same-origin path is passed on; the admin drops anything else. A section whose status fails to load is still listed, marked `error: true`, and the other sections are unaffected. Both routes refuse personal API tokens like the rest of My account, and a page without contributing plugins is unchanged.
|
||||
|
||||
Plugins that show an administrator, such as an author box, read the subtitle with `cabana.BackendUser.Subtitle` and the picture with `cabana.BackendUserAvatar`, which returns the avatar file (or nil) for an administrator id.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user