feat(16.3-01): account token API with OpenAPI and phrase keys

- Add list/create/revoke/scopes handlers, validation, and soft revoke
- Regenerate admin OpenAPI and TS types; inventory and conformance cases
- Add server-side api_tokens phrase keys (en/pl)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-11 01:51:11 +02:00
parent 183583e0e9
commit ffdb5d00ca
13 changed files with 1430 additions and 49 deletions

View File

@@ -1,6 +1,151 @@
{
"components": {
"schemas": {
"cabana.AdminAPIToken": {
"properties": {
"created_at": {
"type": "string"
},
"expired": {
"type": "boolean"
},
"expires_at": {
"type": "string"
},
"hint": {
"type": "string"
},
"id": {
"type": "integer"
},
"last_used_at": {
"type": "string"
},
"name": {
"type": "string"
},
"revoked_at": {
"type": "string"
},
"scopes": {
"items": {
"$ref": "#/components/schemas/cabana.AdminAPITokenScope"
},
"type": "array"
}
},
"required": [
"created_at",
"expired",
"expires_at",
"hint",
"id",
"last_used_at",
"name",
"revoked_at",
"scopes"
],
"type": "object"
},
"cabana.AdminAPITokenCreated": {
"properties": {
"created_at": {
"type": "string"
},
"expired": {
"type": "boolean"
},
"expires_at": {
"type": "string"
},
"hint": {
"type": "string"
},
"id": {
"type": "integer"
},
"last_used_at": {
"type": "string"
},
"name": {
"type": "string"
},
"revoked_at": {
"type": "string"
},
"scopes": {
"items": {
"$ref": "#/components/schemas/cabana.AdminAPITokenScope"
},
"type": "array"
},
"token": {
"type": "string"
}
},
"required": [
"created_at",
"expired",
"expires_at",
"hint",
"id",
"last_used_at",
"name",
"revoked_at",
"scopes",
"token"
],
"type": "object"
},
"cabana.AdminAPITokenRequest": {
"properties": {
"expires": {
"type": "string"
},
"name": {
"type": "string"
},
"scopes": {
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"expires",
"name",
"scopes"
],
"type": "object"
},
"cabana.AdminAPITokenScope": {
"properties": {
"description": {
"type": "string"
},
"known": {
"type": "boolean"
},
"label": {
"type": "string"
},
"name": {
"type": "string"
},
"read_only": {
"type": "boolean"
}
},
"required": [
"description",
"known",
"label",
"name",
"read_only"
],
"type": "object"
},
"cabana.AdminAccountRequest": {
"properties": {
"current_password": {
@@ -322,6 +467,42 @@
],
"type": "object"
},
"cabana.Envelope-array_cabana_AdminAPIToken": {
"properties": {
"data": {
"items": {
"$ref": "#/components/schemas/cabana.AdminAPIToken"
},
"type": "array"
},
"meta": {
"$ref": "#/components/schemas/cabana.SuccessMeta"
}
},
"required": [
"data",
"meta"
],
"type": "object"
},
"cabana.Envelope-array_cabana_AdminAPITokenScope": {
"properties": {
"data": {
"items": {
"$ref": "#/components/schemas/cabana.AdminAPITokenScope"
},
"type": "array"
},
"meta": {
"$ref": "#/components/schemas/cabana.SuccessMeta"
}
},
"required": [
"data",
"meta"
],
"type": "object"
},
"cabana.Envelope-array_cabana_FileItem": {
"properties": {
"data": {
@@ -394,6 +575,36 @@
],
"type": "object"
},
"cabana.Envelope-cabana_AdminAPIToken": {
"properties": {
"data": {
"$ref": "#/components/schemas/cabana.AdminAPIToken"
},
"meta": {
"$ref": "#/components/schemas/cabana.SuccessMeta"
}
},
"required": [
"data",
"meta"
],
"type": "object"
},
"cabana.Envelope-cabana_AdminAPITokenCreated": {
"properties": {
"data": {
"$ref": "#/components/schemas/cabana.AdminAPITokenCreated"
},
"meta": {
"$ref": "#/components/schemas/cabana.SuccessMeta"
}
},
"required": [
"data",
"meta"
],
"type": "object"
},
"cabana.Envelope-cabana_AdminActionResult": {
"properties": {
"data": {
@@ -2176,7 +2387,7 @@
},
"securitySchemes": {
"BackendBearer": {
"description": "Backend admin bearer token. Send \"Bearer {access_token}\".",
"description": "Backend admin access token or personal API token (g15_…). Send \"Bearer {token}\". Account and token management routes refuse personal API tokens.",
"in": "header",
"name": "Authorization",
"type": "apiKey"
@@ -2193,7 +2404,7 @@
"paths": {
"/account": {
"put": {
"description": "Saves the signed-in admin's own first name, last name, email and subtitle; the row is the token subject, never a request value. Only the AdminAccountRequest keys are accepted: any other key (login, role_id, is_superuser, permissions, is_activated, id) is a 422 on that key and nothing is written. The email must be valid, 6 to 255 characters, and not equal (ignoring case) the email or login of another admin. A non-empty password needs current_password, at least 4 characters, at most 72 bytes and an equal password_confirmation; it ends every other session, and a cookie session receives a fresh cookie.",
"description": "Saves the signed-in admin's own first name, last name, email and subtitle; the row is the token subject, never a request value. Only the AdminAccountRequest keys are accepted: any other key (login, role_id, is_superuser, permissions, is_activated, id) is a 422 on that key and nothing is written. The email must be valid, 6 to 255 characters, and not equal (ignoring case) the email or login of another admin. A non-empty password needs current_password, at least 4 characters, at most 72 bytes and an equal password_confirmation; it ends every other session, and a cookie session receives a fresh cookie. Personal API tokens are refused (403 interactive_only).",
"requestBody": {
"content": {
"application/json": {
@@ -2398,6 +2609,240 @@
]
}
},
"/account/tokens": {
"get": {
"description": "Lists the signed-in admin's personal API tokens, newest first. Revoked tokens are hidden unless revoked=1. Personal API tokens are refused (403 interactive_only).",
"parameters": [
{
"description": "Include revoked tokens when 1 or true",
"in": "query",
"name": "revoked",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.Envelope-array_cabana_AdminAPIToken"
}
}
},
"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 API tokens",
"tags": [
"admin"
]
},
"post": {
"description": "Creates a personal API token for the signed-in admin. The plaintext token is returned exactly once in the 201 body. Personal API tokens are refused (403 interactive_only).",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.AdminAPITokenRequest"
}
}
},
"description": "Name, expiry and scopes",
"required": true
},
"responses": {
"201": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.Envelope-cabana_AdminAPITokenCreated"
}
}
},
"description": "Created"
},
"401": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Unauthorized"
},
"403": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Forbidden"
},
"422": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Unprocessable Entity"
}
},
"security": [
{
"BackendBearer": []
}
],
"summary": "Create an API token",
"tags": [
"admin"
]
}
},
"/account/tokens/scopes": {
"get": {
"description": "Lists framework and plugin scopes the signed-in admin may pick when creating a token. Personal API tokens are refused (403 interactive_only).",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.Envelope-array_cabana_AdminAPITokenScope"
}
}
},
"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 available API token scopes",
"tags": [
"admin"
]
}
},
"/account/tokens/{id}": {
"delete": {
"description": "Soft-revokes one of the signed-in admin's active personal API tokens. Personal API tokens are refused (403 interactive_only).",
"parameters": [
{
"description": "Token id",
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "integer"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.Envelope-cabana_AdminAPIToken"
}
}
},
"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"
}
},
"security": [
{
"BackendBearer": []
}
],
"summary": "Revoke an API token",
"tags": [
"admin"
]
}
},
"/auth/login": {
"post": {
"parameters": [