diff --git a/docs/API.md b/docs/API.md index 7e450a4153..a38da4b494 100644 --- a/docs/API.md +++ b/docs/API.md @@ -356,19 +356,19 @@ Returns child resources for the parent ID. `GET /api/resources/{id}/metrics` Returns the resource metrics payload. -`POST /api/resources/{id}/link` +`POST /api/resources/{id}/link` (`monitoring:write`) Manually link two resources. ```json { "targetId": "resource-id", "reason": "optional note" } ``` -`POST /api/resources/{id}/unlink` +`POST /api/resources/{id}/unlink` (`monitoring:write`) Manually unlink two resources. ```json { "targetId": "resource-id", "reason": "optional note" } ``` -`POST /api/resources/{id}/report-merge` +`POST /api/resources/{id}/report-merge` (`monitoring:write`) Report an incorrect merge (creates exclusions). ```json { "sources": ["proxmox", "agent"], "notes": "optional note" } @@ -1130,9 +1130,15 @@ Supports actions: Callers need an admin session, or an API token with the `settings:write` scope; a token without that scope gets `403` with `error: "missing_scope"`. Identifier can be a username or IP address. An empty identifier returns `400 missing_identifier`. Success returns `{ "success": true, "message": "Lockout reset for admin" }`. ### Regenerate API Token (Admin) -`POST /api/security/regenerate-token` +`POST /api/security/regenerate-token` (admin, `settings:write`) -Returns a new raw token (shown once) and updates stored hashes: +Replaces every stored API token with one new full-access token. All other +tokens stop working. An API token may call this only if it has full-access +scope (`*`) and is not bound to a non-default organization; any other token +gets `403`, and should use `POST /api/security/tokens/{id}/rotate` to replace +just itself. + +Returns the new raw token (shown once) and updates stored hashes: ```json { "success": true, diff --git a/frontend-modern/public/docs/API.md b/frontend-modern/public/docs/API.md index 7e450a4153..a38da4b494 100644 --- a/frontend-modern/public/docs/API.md +++ b/frontend-modern/public/docs/API.md @@ -356,19 +356,19 @@ Returns child resources for the parent ID. `GET /api/resources/{id}/metrics` Returns the resource metrics payload. -`POST /api/resources/{id}/link` +`POST /api/resources/{id}/link` (`monitoring:write`) Manually link two resources. ```json { "targetId": "resource-id", "reason": "optional note" } ``` -`POST /api/resources/{id}/unlink` +`POST /api/resources/{id}/unlink` (`monitoring:write`) Manually unlink two resources. ```json { "targetId": "resource-id", "reason": "optional note" } ``` -`POST /api/resources/{id}/report-merge` +`POST /api/resources/{id}/report-merge` (`monitoring:write`) Report an incorrect merge (creates exclusions). ```json { "sources": ["proxmox", "agent"], "notes": "optional note" } @@ -1130,9 +1130,15 @@ Supports actions: Callers need an admin session, or an API token with the `settings:write` scope; a token without that scope gets `403` with `error: "missing_scope"`. Identifier can be a username or IP address. An empty identifier returns `400 missing_identifier`. Success returns `{ "success": true, "message": "Lockout reset for admin" }`. ### Regenerate API Token (Admin) -`POST /api/security/regenerate-token` +`POST /api/security/regenerate-token` (admin, `settings:write`) -Returns a new raw token (shown once) and updates stored hashes: +Replaces every stored API token with one new full-access token. All other +tokens stop working. An API token may call this only if it has full-access +scope (`*`) and is not bound to a non-default organization; any other token +gets `403`, and should use `POST /api/security/tokens/{id}/rotate` to replace +just itself. + +Returns the new raw token (shown once) and updates stored hashes: ```json { "success": true, diff --git a/scripts/tests/test_api_reference_routes.py b/scripts/tests/test_api_reference_routes.py index 8093ac18f3..02cdd2e2c9 100644 --- a/scripts/tests/test_api_reference_routes.py +++ b/scripts/tests/test_api_reference_routes.py @@ -79,6 +79,20 @@ ROUTE_ANCHORS = [ ("Token callers also need `settings:write` on every profile route, including GET requests", "internal/api/router_routes_registration.go", 'r.mux.Handle("/api/admin/profiles/", RequireAdmin(r.config, RequireScope(config.ScopeSettingsWrite,'), + # Everything other than GET or HEAD on the /api/resources/ prefix needs the write scope. + ("`POST /api/resources/{id}/link` (`monitoring:write`)", + "internal/api/router_routes_monitoring.go", + "if req.Method != http.MethodGet && req.Method != http.MethodHead {\n\t\t\tscope = config.ScopeMonitoringWrite"), + ("`POST /api/resources/{id}/unlink` (`monitoring:write`)", + "internal/api/router_routes_monitoring.go", + "if req.Method != http.MethodGet && req.Method != http.MethodHead {\n\t\t\tscope = config.ScopeMonitoringWrite"), + ("`POST /api/resources/{id}/report-merge` (`monitoring:write`)", + "internal/api/router_routes_monitoring.go", + "if req.Method != http.MethodGet && req.Method != http.MethodHead {\n\t\t\tscope = config.ScopeMonitoringWrite"), + # Regeneration replaces the whole inventory, so a token caller must be instance-wide. + ("An API token may call this only if it has full-access\nscope (`*`) and is not bound to a non-default organization", + "internal/api/security_setup_fix.go", + "!tokenHasInstanceWideAuthority(callerToken)"), ("`PUT /api/notifications/email` (admin, `settings:write`)", "internal/api/alerting/notifications.go", 'case path == "/email" && r.Method == http.MethodPut:\n\t\tif !requireAnyScope(config.ScopeSettingsWrite'),