feat(19.7-01): account contributions, prompted record actions and backend subject events
- pact.HasAccountContributions compiled at Activate; GET /account/contributions
and POST /account/contributions/{name}/actions/{action} behind interactiveOnly
(AccountContributionView, AccountContributionActionView, AccountActionResultView)
- RecordAction.prompt (RecordActionPrompt) for a pact.AdminRecordAction with a
Prompt; the record action route accepts payload only for such an action
(trimmed JSON string, non-empty, capped at max_length) and hands it to Run as
Value through CRUDService.RecordActionWithValue; a prompt without a label
fails boot
- fireSubjectChanged: pact.MCPSubjectChanged for revoked tokens, Administrators
saves that changed role, permissions or superuser, Roles saves that changed
grants, deactivation and deletion
- phrases backend::lang.form.action_prompt_required/_too_long (en, pl)
- smoke tests TestAccountContributionsSmoke and TestRecordActionPromptSmoke;
TestRecordActionPromptRun on Postgres; route inventories, CSRF walk and
OpenAPI conformance cases extended for the two routes
- regenerated admin/openapi/admin.json and admin/src/api/schema.d.ts; cabana
README, docs/backend/{admin-controllers,users-and-permissions}.md
This commit is contained in:
@@ -1,6 +1,75 @@
|
||||
{
|
||||
"components": {
|
||||
"schemas": {
|
||||
"cabana.AccountActionResultView": {
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"message",
|
||||
"url"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"cabana.AccountContributionActionView": {
|
||||
"properties": {
|
||||
"confirm": {
|
||||
"type": "string"
|
||||
},
|
||||
"danger": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"confirm",
|
||||
"danger",
|
||||
"label",
|
||||
"name"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"cabana.AccountContributionView": {
|
||||
"properties": {
|
||||
"actions": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/cabana.AccountContributionActionView"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"description": "Status is the section's line for the signed-in admin; \"\" when the\nsection has none.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"actions",
|
||||
"description",
|
||||
"label",
|
||||
"name",
|
||||
"status"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"cabana.AdminAPIToken": {
|
||||
"properties": {
|
||||
"created_at": {
|
||||
@@ -677,6 +746,24 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"cabana.Envelope-array_cabana_AccountContributionView": {
|
||||
"properties": {
|
||||
"data": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/cabana.AccountContributionView"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/cabana.SuccessMeta"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data",
|
||||
"meta"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"cabana.Envelope-array_cabana_AdminAPIToken": {
|
||||
"properties": {
|
||||
"data": {
|
||||
@@ -785,6 +872,21 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"cabana.Envelope-cabana_AccountActionResultView": {
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/components/schemas/cabana.AccountActionResultView"
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/cabana.SuccessMeta"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data",
|
||||
"meta"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"cabana.Envelope-cabana_AdminAPIToken": {
|
||||
"properties": {
|
||||
"data": {
|
||||
@@ -2361,6 +2463,14 @@
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"prompt": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/cabana.RecordActionPrompt"
|
||||
}
|
||||
],
|
||||
"description": "Prompt is present only for an action that asks for one text value in\nits confirm dialog (pact.AdminRecordAction.Prompt); the admin posts the\nvalue as the body key payload, a JSON string."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -2369,6 +2479,27 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"cabana.RecordActionPrompt": {
|
||||
"properties": {
|
||||
"comment": {
|
||||
"type": "string"
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"max_length": {
|
||||
"type": "integer"
|
||||
},
|
||||
"placeholder": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"label",
|
||||
"max_length"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"cabana.RecordEnvelope": {
|
||||
"properties": {
|
||||
"data": {
|
||||
@@ -3097,6 +3228,148 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/account/contributions": {
|
||||
"get": {
|
||||
"description": "Lists the sections plugins contribute to the signed-in admin's My account page: label, description, the status for this admin and the actions they may run that apply to them. Empty when no plugin contributes. Personal API tokens are refused (403 interactive_only).",
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/cabana.Envelope-array_cabana_AccountContributionView"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "OK"
|
||||
},
|
||||
"401": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Unauthorized"
|
||||
},
|
||||
"403": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Forbidden"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"BackendBearer": []
|
||||
}
|
||||
],
|
||||
"summary": "List My account contributions",
|
||||
"tags": [
|
||||
"admin"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/account/contributions/{name}/actions/{action}": {
|
||||
"post": {
|
||||
"description": "Runs one action of a contributed section for the signed-in admin. The action's permissions are checked (403) and its Applies rule again (409 when it no longer applies); the answer is the action's message and an optional URL to open. Personal API tokens are refused (403 interactive_only).",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Section name",
|
||||
"in": "path",
|
||||
"name": "name",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Action name",
|
||||
"in": "path",
|
||||
"name": "action",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/cabana.Envelope-cabana_AccountActionResultView"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "OK"
|
||||
},
|
||||
"401": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Unauthorized"
|
||||
},
|
||||
"403": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Forbidden"
|
||||
},
|
||||
"404": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Not Found"
|
||||
},
|
||||
"409": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Conflict"
|
||||
},
|
||||
"422": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Unprocessable Entity"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"BackendBearer": []
|
||||
}
|
||||
],
|
||||
"summary": "Run a My account contribution action",
|
||||
"tags": [
|
||||
"admin"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/account/preferences": {
|
||||
"get": {
|
||||
"description": "The signed-in admin's own UI preferences: theme (system, light or dark; system when not set) and locale (an admin locale code, or \"\" to follow the request locale). Interactive session only (403 interactive_only for personal API tokens).",
|
||||
|
||||
187
admin/src/api/schema.d.ts
vendored
187
admin/src/api/schema.d.ts
vendored
@@ -210,6 +210,152 @@ export interface paths {
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/account/contributions": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/**
|
||||
* List My account contributions
|
||||
* @description Lists the sections plugins contribute to the signed-in admin's My account page: label, description, the status for this admin and the actions they may run that apply to them. Empty when no plugin contributes. Personal API tokens are refused (403 interactive_only).
|
||||
*/
|
||||
get: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description OK */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.Envelope-array_cabana_AccountContributionView"];
|
||||
};
|
||||
};
|
||||
/** @description Unauthorized */
|
||||
401: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Forbidden */
|
||||
403: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/account/contributions/{name}/actions/{action}": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/**
|
||||
* Run a My account contribution action
|
||||
* @description Runs one action of a contributed section for the signed-in admin. The action's permissions are checked (403) and its Applies rule again (409 when it no longer applies); the answer is the action's message and an optional URL to open. Personal API tokens are refused (403 interactive_only).
|
||||
*/
|
||||
post: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
/** @description Section name */
|
||||
name: string;
|
||||
/** @description Action name */
|
||||
action: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description OK */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.Envelope-cabana_AccountActionResultView"];
|
||||
};
|
||||
};
|
||||
/** @description Unauthorized */
|
||||
401: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Forbidden */
|
||||
403: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Not Found */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Conflict */
|
||||
409: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Unprocessable Entity */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/account/preferences": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
@@ -5362,6 +5508,27 @@ export interface paths {
|
||||
export type webhooks = Record<string, never>;
|
||||
export interface components {
|
||||
schemas: {
|
||||
"cabana.AccountActionResultView": {
|
||||
message: string;
|
||||
url: string;
|
||||
};
|
||||
"cabana.AccountContributionActionView": {
|
||||
confirm: string;
|
||||
danger: boolean;
|
||||
label: string;
|
||||
name: string;
|
||||
};
|
||||
"cabana.AccountContributionView": {
|
||||
actions: components["schemas"]["cabana.AccountContributionActionView"][];
|
||||
description: string;
|
||||
label: string;
|
||||
name: string;
|
||||
/**
|
||||
* @description Status is the section's line for the signed-in admin; "" when the
|
||||
* section has none.
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
"cabana.AdminAPIToken": {
|
||||
created_at: string;
|
||||
expired: boolean;
|
||||
@@ -5569,6 +5736,10 @@ export interface components {
|
||||
scripts: string[];
|
||||
styles: string[];
|
||||
};
|
||||
"cabana.Envelope-array_cabana_AccountContributionView": {
|
||||
data: components["schemas"]["cabana.AccountContributionView"][];
|
||||
meta: components["schemas"]["cabana.SuccessMeta"];
|
||||
};
|
||||
"cabana.Envelope-array_cabana_AdminAPIToken": {
|
||||
data: components["schemas"]["cabana.AdminAPIToken"][];
|
||||
meta: components["schemas"]["cabana.SuccessMeta"];
|
||||
@@ -5593,6 +5764,10 @@ export interface components {
|
||||
data: components["schemas"]["cabana.SettingsEntry"][];
|
||||
meta: components["schemas"]["cabana.SuccessMeta"];
|
||||
};
|
||||
"cabana.Envelope-cabana_AccountActionResultView": {
|
||||
data: components["schemas"]["cabana.AccountActionResultView"];
|
||||
meta: components["schemas"]["cabana.SuccessMeta"];
|
||||
};
|
||||
"cabana.Envelope-cabana_AdminAPIToken": {
|
||||
data: components["schemas"]["cabana.AdminAPIToken"];
|
||||
meta: components["schemas"]["cabana.SuccessMeta"];
|
||||
@@ -6164,6 +6339,18 @@ export interface components {
|
||||
danger?: boolean;
|
||||
label: string;
|
||||
name: string;
|
||||
/**
|
||||
* @description Prompt is present only for an action that asks for one text value in
|
||||
* its confirm dialog (pact.AdminRecordAction.Prompt); the admin posts the
|
||||
* value as the body key payload, a JSON string.
|
||||
*/
|
||||
prompt?: components["schemas"]["cabana.RecordActionPrompt"];
|
||||
};
|
||||
"cabana.RecordActionPrompt": {
|
||||
comment?: string;
|
||||
label: string;
|
||||
max_length: number;
|
||||
placeholder?: string;
|
||||
};
|
||||
"cabana.RecordEnvelope": {
|
||||
data: components["schemas"]["cabana.AdminRecord"];
|
||||
|
||||
Reference in New Issue
Block a user