diff --git a/http/webconfig_server.go b/http/webconfig_server.go index 05b24d0..46c1584 100644 --- a/http/webconfig_server.go +++ b/http/webconfig_server.go @@ -291,14 +291,11 @@ func (s *WebconfigServer) AuthValidationMiddleware(next http.Handler) http.Handl } else if authToken := getLoginTokenFromRequest(r); authToken != "" { if LoginToken, err := ValidateAndGetLoginToken(authToken); err != nil { log.Error(err.Error()) - //http.Error(w, "invalid auth token", http.StatusUnauthorized) - //Cheking and Setting DEV PROFILES to allow and display ALL the TABS - //ctx = context.WithValue(ctx, CTX_KEY_TOKEN, LoginToken) - permissions := getPermissions() - ctx = context.WithValue(ctx, CTX_KEY_PERMISSIONS, permissions) - //return + http.Error(w, "invalid auth token", http.StatusUnauthorized) + ctx = context.WithValue(ctx, CTX_KEY_TOKEN, LoginToken) + return } else { - //THIS IS LOGIN TOKEN SUCCESS CASE + // THIS IS LOGIN TOKEN SUCCESS CASE r.Header.Set(AUTH_SUBJECT, LoginToken.Subject) // Add UI token & permissions to request context diff --git a/openspec/changes/sat-rbac-capabilities-v2/.openspec.yaml b/openspec/changes/sat-rbac-capabilities-v2/.openspec.yaml new file mode 100644 index 0000000..b0dbbae --- /dev/null +++ b/openspec/changes/sat-rbac-capabilities-v2/.openspec.yaml @@ -0,0 +1,5 @@ +schema: spec-driven +created: 2026-05-14 +status: proposed +applies_to: + - openspec/specs/auth/auth-contract.md diff --git a/openspec/changes/sat-rbac-capabilities-v2/design.md b/openspec/changes/sat-rbac-capabilities-v2/design.md new file mode 100644 index 0000000..689fc27 --- /dev/null +++ b/openspec/changes/sat-rbac-capabilities-v2/design.md @@ -0,0 +1,150 @@ +# SAT RBAC v2 Design + +## Overview + +This document describes the architectural approach to implementing SAT RBAC v2 capability names and authorization logic in xconfadmin while maintaining full backward compatibility with legacy SAT behavior. + +## Design Principles + +1. **Routing-Based Selection**: If the `Authorization` header is present, select the SAT path; otherwise, if Xerxes token is present (header `token` or cookie `token`), select the Xerxes path. +2. **SAT Mode Detection**: On the SAT path, if SAT contains xconf-prefixed capabilities, authorize via SAT v2; otherwise, use legacy SAT unchanged. +3. **Deny-by-Default for SAT v2**: Any request that cannot be classified into (domain, access) requirements is denied. +4. **Route-Based Classification**: Domain and access level are determined from HTTP method and API route/path, not from request entity type. +5. **Backward Compatibility**: Existing SAT tokens without xconf-prefixed capabilities continue to work exactly as before. + +## Authorization Routing Selection + +The authorization system follows routing-based selection (Option C): + +``` +┌─────────────────────────────────┐ +│ Request arrives with credentials │ +└─────────────────┬───────────────┘ + │ + ┌──────────────────────────────┐ + │ Authorization header present?│ + └──────────────┬───────────────┘ + │ + ┌─────────┴─────────┐ + YES NO + │ │ + ┌───────────▼──────────────┐ ┌────────────────────────────┐ + │ Route to SAT auth path │ │ Xerxes token present │ + │ (deterministic selection)│ │ (header/cookie `token`)? │ + └───────────┬──────────────┘ └──────────────┬─────────────┘ + │ │ + ┌─────────▼──────────┐ ┌──────────┴──────────┐ + │ Has xconf: prefix? │ NO YES + └─────────┬──────────┘ │ │ + │ │ ┌─────────▼─────────┐ + ┌────────┴───────┐ │ │ Authorize via │ + │ │ │ │ Xerxes permissions │ + NO YES │ └───────────────────┘ + │ │ │ + ┌────▼──────────┐ ┌──▼────────────────────────┐ ┌──────────────────────┐ + │ Authorize via │ │ Authorize via SAT RBAC v2 │ │ Return 401 │ + │ legacy SAT │ │ (domain + access check) │ │ Unauthorized │ + │ (unchanged) │ └───────────────────────────┘ └──────────────────────┘ + └───────────────┘ +``` + +## Request Classification + +### Route-to-Domain Mapping + +Request domain is determined by matching the HTTP route/path against an ordered ruleset. The first matching rule determines the domain. + +Classification rules SHALL match on stable route substrings and/or route templates. The following is a representative seed set of patterns: +- `/queries/firmware`, `/firmware` → **core** +- `/dcm`, `/telemetry` → **core** +- `/tagging` → **tagging** +- `/roundrobinfilter` → **system** +- `/metrics` → **metrics** + +The ruleset ordering is critical because the first match wins. More specific routes must appear before more general patterns. + +**Note on Mapping Scope**: This seed set represents initial patterns. The authoritative route-to-domain classification will be maintained in a central mapping registry in code and extended iteratively as new endpoints are added. SAT v2 authorization remains deny-by-default for any request that cannot be classified. + +### Access-Level Determination + +Access level (readonly or readwrite) is determined by the following precedence: + +1. **Route Override**: If the request path contains the segment `/filtered`, treat as readonly (these are filtered search endpoints that use POST). +2. **HTTP Method**: Otherwise, classify by HTTP method: + - GET, HEAD → **readonly** + - POST, PUT, PATCH, DELETE → **readwrite** + +Post-based read endpoints (e.g., /filtered searches) are explicitly classified as readonly via the override pattern to avoid misclassification based on method alone. + +## Integration Architecture + +### Authorization Middleware Position + +The authorization logic is invoked after credential validation, with deterministic routing by credential type: + +``` +Request + │ + ├─> Credential Extraction & Validation + │ (Xerxes token, SAT token, etc.) + │ + ├─> If Authorization header exists -> SAT path + │ ├─> SAT v2 detection (xconf: prefix) + │ ├─> SAT RBAC v2 OR legacy SAT authorization + │ └─> Allow/Deny Decision + │ + ├─> Else if Xerxes token exists -> Xerxes authorization + │ + ├─> Else -> 401 Unauthorized + │ + └─> Handler Execution (if authorized) +``` + +### Capability Matching + +Given a classified request (domain, access), SAT RBAC v2 checks whether the SAT token contains a matching capability: + +- Request (core, readonly) requires any of: xconf:core:readonly, xconf:core:readwrite +- Request (core, readwrite) requires: xconf:core:readwrite +- Request (tagging, readonly) requires any of: xconf:tagging:readonly, xconf:tagging:readwrite +- Request (tagging, readwrite) requires: xconf:tagging:readwrite +- Request (system, readonly) requires any of: xconf:system:readonly, xconf:system:readwrite +- Request (system, readwrite) requires: xconf:system:readwrite +- Request (metrics, readonly) requires: xconf:metrics:readonly +- No readwrite functionality exists for the metrics domain (no xconf:metrics:readwrite). + +### Unclassifiable Requests + +If a request cannot be classified into (domain, access) requirements—for example, if a new API route is added but the classification rules are not yet updated—the request is denied with 403 Forbidden. + +This deny-by-default approach ensures that new endpoints are secure by default and prevents accidental authorization leakage. + +## Backward Compatibility + +Legacy SAT tokens (without xconf-prefixed capabilities) continue to work exactly as before: + +1. If no xconf: capabilities are detected, the authorization flow immediately falls back to legacy SAT logic. +2. Legacy SAT behavior (appType-based authorization, existing capability names) is unchanged. +3. No new validation rules are applied to legacy SAT tokens. +4. Operators can mix legacy and SAT v2 tokens in the same deployment; each is authorized according to its own rules. + +## HTTP Status Code Semantics + +The auth middleware uses the following status codes: + +- **401 Unauthorized**: Sent when credential extraction or validation fails (missing token, invalid signature, expired token, etc.). No authenticated identity is available. +- **403 Forbidden**: Sent when the request is authenticated but not authorized for the requested operation. This includes: + - SAT v2 capability mismatch (e.g., readonly SAT v2 token attempting a write) + - Unclassifiable SAT v2 requests (route/domain mapping missing) + - Any other authorization denial after successful authentication + +## Future Extensibility + +### Phase 2: Tenant/Partner Enforcement +Once domain-to-capability mapping is stable, Phase 2 will add tenant or partner scoping to authorization. Tenant/partner enforcement will be implemented using separate SAT claims (e.g., partner scope) and/or request metadata (e.g., tenantId header), independent of capability strings. The capability strings themselves (e.g., `xconf:core:readwrite`) will remain unchanged. + +### Metrics Domain +The metrics domain is read-only. The only supported capability for the metrics domain is `xconf:metrics:readonly`. No write capability exists for the metrics domain. + +### Additional Domains +As new domain areas emerge, new capability names (e.g., xconf:foo:readonly) can be added without affecting existing capabilities or fallback logic. diff --git a/openspec/changes/sat-rbac-capabilities-v2/proposal.md b/openspec/changes/sat-rbac-capabilities-v2/proposal.md new file mode 100644 index 0000000..99c219b --- /dev/null +++ b/openspec/changes/sat-rbac-capabilities-v2/proposal.md @@ -0,0 +1,70 @@ +Status: Proposed +Applied to: openspec/specs/auth/auth-contract.md + +## Why + +xconfadmin must introduce SAT RBAC v2 capability names while preserving all legacy SAT behavior for backward compatibility. SAT token shape is unchanged (capabilities list), but capability values now include a new xconf-prefixed namespace. We need a clear and deterministic routing-based authorization selection contract that supports SAT v2 when present on the SAT path, falls back to legacy SAT semantics on the SAT path when no xconf capability is present, and uses Xerxes authorization on its own path. + +This change documents the transition contract so implementation can safely evolve without breaking existing SAT clients. + +## What Changes + +- Define SAT RBAC v2 capability namespace and names: + - xconf:core:readonly / xconf:core:readwrite + - xconf:tagging:readonly / xconf:tagging:readwrite + - xconf:system:readonly / xconf:system:readwrite + - xconf:metrics:readonly +- Define SAT RBAC v2 detection: + - SAT v2 SHALL be detected by the presence of at least one capability with prefix "xconf:" +- Define routing-based authorization selection: + - If `Authorization` header is present: + - Run existing SAT validation logic. + - If SAT is valid: + - If SAT contains any capability starting with "xconf:", authorize using SAT RBAC v2. + - Else, authorize using legacy SAT behavior unchanged. + - Else, return 401 Unauthorized. + - Else, if token header/cookie `token` is present: + - Run existing Xerxes validation and authorization. + - Else, return 401 Unauthorized. +- Define initial SAT v2 domain mapping seed set: + - Core: firmware, firmware rules, firmware templates, features, feature rules, telemetry, dcm + - Metrics: penetration metrics, future metrics APIs + - System: download location round robin filter + - Tagging: all tagging APIs +- Define SAT v2 request classification: + - SAT v2 authorization SHALL classify requests by API route/path into one of {core, tagging, system, metrics}. + - SAT v2 domains are not equivalent to Xerxes entity types; classification is based on admin functionality (route/path), not entity. + - Classification SHALL use an ordered ruleset where the first matching rule determines the domain. + - Classification rules SHALL match on stable route substrings and/or route templates (when available) (e.g., /queries/firmware, /firmware, /dcm, /telemetry, /tagging, /roundrobinfilter, /metrics), with precedence determined by rule order. +- Define SAT v2 access classification: + - SAT v2 access level SHALL be determined using the following precedence: + 1. If the route matches a known read-only override pattern (e.g., path contains the segment "/filtered"), access SHALL be treated as "readonly". + 2. Else, HTTP method SHALL determine access: + - GET, HEAD → readonly + - POST, PUT, PATCH, DELETE → readwrite + - Certain endpoints use POST for read operations (e.g., filtered search APIs). These MUST be explicitly classified as readonly. +- Define SAT v2 deny-by-default behavior: + - If a request cannot be classified into (domain, access) requirements, SAT v2 authorization SHALL deny with 403. +- Define HTTP status semantics (Option A): + - 401 Unauthorized only for missing/invalid authentication. + - 403 Forbidden for authenticated-but-not-authorized requests, including unmapped SAT v2 operations/domains. + +## Non-Goals + +- No tenant or partner enforcement in this phase (phase 2). +- No changes to legacy SAT capability names or authorization semantics. +- No appType field in SAT RBAC v2. +- No redesign of Xerxes authentication or permission model. + +## Capabilities + +### Modified Capabilities +- `auth`: Authorization outcome semantics clarified to use 401 for authentication failures and 403 for authorization denials, including SAT v2 unmapped operations. +- `auth`: SAT RBAC v2 capability-name model, detection by `xconf:` prefix, ordered route/path classification, access classification, and routing-based selection contract across SAT and Xerxes credential paths. + +## Impact + +- Affected specs: openspec/specs/auth/auth-contract.md. +- Affected implementation areas (future apply phase): SAT validation/authorization middleware and API domain-to-capability mapping logic. +- API behavior impact: no endpoint shape changes; authorization outcomes become explicitly specified for SAT v2 capability evaluation. +- Compatibility impact: legacy SAT behavior remains unchanged unless SAT v2 capabilities are explicitly present. diff --git a/openspec/changes/sat-rbac-capabilities-v2/spec.md b/openspec/changes/sat-rbac-capabilities-v2/spec.md new file mode 100644 index 0000000..bc9ea92 --- /dev/null +++ b/openspec/changes/sat-rbac-capabilities-v2/spec.md @@ -0,0 +1,235 @@ +# SAT RBAC v2 Specification + +## Spec Delta Summary + +This change updates `openspec/specs/auth/auth-contract.md` with the +following normative clauses: + +- `Authorization Routing Selection`: if `Authorization` header is present, + SAT processing is selected; otherwise Xerxes is selected only when SAT + header is absent. +- `SAT RBAC v2 Detection`: SAT v2 SHALL be detected by presence of at + least one capability with prefix `xconf:`. +- `SAT RBAC v2 Domain Classification`: domain SHALL be determined by + ordered route/path rules; first matching rule MUST win. +- `SAT RBAC v2 Access Classification`: `/filtered` route override SHALL + classify as readonly before method-based mapping. +- `SAT RBAC v2 Deny-By-Default`: unclassifiable SAT v2 requests SHALL be + denied with `403 Forbidden`. +- `HTTP Status Semantics`: `401 Unauthorized` only for missing/invalid + authentication; `403 Forbidden` for authenticated authorization denials. +- `Metrics Domain Constraint`: metrics SHALL be readonly-only; no + `xconf:metrics:readwrite` functionality exists. + +## Definitions + +### SAT Token +A Structured Authorization Token (SAT) is a credential containing a list of capability strings. The token format is unchanged; this specification only defines new capability names. + +### Capability +A capability is an opaque string in the SAT token indicating a permission or privilege. Legacy capabilities are arbitrary strings; SAT v2 capabilities follow the pattern `xconf::`. + +### Domain +A domain is a logical grouping of xconfadmin APIs. Valid domains are: +- **core**: Firmware rules, firmware configs, firmware templates, features, feature control rules, telemetry profiles, telemetry rules, and DCM settings. +- **tagging**: Tagging APIs. +- **system**: System-level APIs (e.g., download location round robin filter). +- **metrics**: Metrics and analytics APIs. + +### Access Level +An access level describes the scope of operations permitted by a capability. +- **readonly**: Only read operations (GET, HEAD queries) are permitted. +- **readwrite**: Both read operations (GET, HEAD) and write operations (POST, PUT, PATCH, DELETE) are permitted. + +### SAT v2 Capability Names +The following capability names are defined for SAT RBAC v2: +- `xconf:core:readonly` - Read access to core domain +- `xconf:core:readwrite` - Read and write access to core domain +- `xconf:tagging:readonly` - Read access to tagging domain +- `xconf:tagging:readwrite` - Read and write access to tagging domain +- `xconf:system:readonly` - Read access to system domain +- `xconf:system:readwrite` - Read and write access to system domain +- `xconf:metrics:readonly` - Read access to metrics domain + +No readwrite functionality exists for the metrics domain in Phase 1 (no `xconf:metrics:readwrite`). + +### SAT v2 Detection +A SAT token is classified as SAT v2 if and only if it contains at least one capability with the prefix `xconf:`. + +A SAT token without any xconf-prefixed capabilities is treated as a legacy SAT token and authorized according to legacy SAT semantics. + +## Authorization Algorithm + +### Input +- HTTP method (GET, POST, HEAD, PUT, PATCH, DELETE) +- HTTP request path (e.g., `/queries/firmware`, `/dcm/devices`) +- Xerxes token (if present) +- SAT token (if present) + +### Output +- Authorization decision: ALLOW or DENY +- HTTP status code: 401 Unauthorized (auth failure) or 403 Forbidden (authz failure) + +### Routing-Based Selection Algorithm + +1. **SAT Path (Authorization Header Present)** + - If `Authorization` header is present, the request is treated as SAT and SAT processing is selected. + - If SAT token is valid and classified as SAT v2 (has xconf: prefix): + - Classify request into (domain, access) pair (see below) + - If request cannot be classified: + - DENY request + - Return 403 Forbidden + - Check SAT token capabilities for matching capability + - If matching capability exists: + - ALLOW request + - If no matching capability: + - DENY request + - Return 403 Forbidden + - If SAT token is valid and is NOT classified as SAT v2: + - Authorize using legacy SAT semantics (unchanged from prior xconfadmin behavior) + - If legacy SAT authorization allows: + - ALLOW request + - If legacy SAT authorization denies: + - DENY request + - Return 403 Forbidden + - If SAT token is missing/invalid on SAT path: + - DENY request + - Return 401 Unauthorized + +2. **Xerxes Path (Authorization Header Absent)** + - If `Authorization` header is absent and Xerxes token is present (header `token` or cookie `token`): + - If Xerxes validation fails: + - DENY request + - Return 401 Unauthorized + - Authorize based on Xerxes permissions + - If Xerxes permissions allow the operation: + - ALLOW request + - If Xerxes permissions do not allow the operation: + - DENY request + - Return 403 Forbidden + +3. **No Applicable Credentials** + - If neither SAT path nor Xerxes path applies: + - DENY request + - Return 401 Unauthorized + +## Request Classification + +### Algorithm + +Given an HTTP method and request path, classify the request as (domain, access): + +**Step 1: Determine Access Level** + +``` +if path contains segment "/filtered": + access = readonly +else if method in {GET, HEAD}: + access = readonly +else if method in {POST, PUT, PATCH, DELETE}: + access = readwrite +else: + # Unknown method; deny for safety + DENY with 403 +``` + +**Step 2: Determine Domain** + +Apply the following ruleset in order. The first matching rule determines the domain: + +| Rule # | Path Pattern | Domain | Notes | +|--------|-------------|--------|-------| +| 1 | contains `/metrics` | metrics | Metrics domain | +| 2 | contains `/roundrobinfilter` | system | Round robin filter for download locations | +| 3 | contains `/tagging` | tagging | Tagging APIs | +| 4 | contains `/telemetry` | core | Telemetry profiles and rules | +| 5 | contains `/dcm` | core | Device Configuration Management | +| 6 | contains `/queries/firmware` or `/firmware` | core | Firmware rules and configs | +| 7 | contains `/feature` | core | Feature and feature control rules | +| 8 | _default_ | _unclassified_ | No matching rule | + +If the default rule matches (no prior rules matched), the request is unclassifiable and SHALL be DENIED with 403. + +### Classification Examples + +| HTTP Method | Path | Domain | Access | Capabilities Required | +|-------------|------|--------|--------|----------------------| +| GET | /queries/firmware | core | readonly | xconf:core:readonly, xconf:core:readwrite | +| POST | /queries/firmware/filtered | core | readonly | xconf:core:readonly, xconf:core:readwrite | +| POST | /firmware | core | readwrite | xconf:core:readwrite | +| PUT | /dcm/device-settings | core | readwrite | xconf:core:readwrite | +| GET | /tagging/operations | tagging | readonly | xconf:tagging:readonly, xconf:tagging:readwrite | +| DELETE | /tagging/operations/123 | tagging | readwrite | xconf:tagging:readwrite | +| GET | /roundrobinfilter | system | readonly | xconf:system:readonly, xconf:system:readwrite | +| GET | /metrics/penetration | metrics | readonly | xconf:metrics:readonly | +| POST | /unknown-api | _unclassified_ | - | DENY 403 | + +## Capability Matching + +After classifying a request as (domain, access), check whether the SAT token contains at least one capability matching the requirement: + +**For readonly requests**: +- Required capabilities: `xconf::readonly` OR `xconf::readwrite` +- Both are acceptable because readwrite implies readonly + +**For readwrite requests**: +- Required capability: `xconf::readwrite` +- Only readwrite is acceptable; readonly is insufficient + +### Matching Examples + +| SAT Capabilities | Request | Result | +|------------------|---------|--------| +| `xconf:core:readonly` | GET /queries/firmware | ALLOW | +| `xconf:core:readonly` | POST /firmware | DENY 403 | +| `xconf:core:readwrite` | POST /firmware | ALLOW | +| `xconf:core:readwrite` | GET /queries/firmware | ALLOW | +| `xconf:tagging:readonly, xconf:core:readwrite` | GET /tagging/ops | ALLOW | +| `xconf:tagging:readonly, xconf:core:readwrite` | DELETE /tagging/ops/1 | DENY 403 | +| `xconf:metrics:readonly` | GET /metrics/penetration | ALLOW | +| `xconf:core:readonly` | POST /unknown-api | DENY 403 (unclassifiable) | + +## Backward Compatibility + +### Legacy SAT Tokens + +Tokens without any xconf-prefixed capability are treated as legacy SAT tokens and are authorized using the existing xconfadmin legacy SAT semantics. No new rules or restrictions are applied. + +Example: A SAT token with capabilities `["admin", "firmware-operator"]` (without xconf: prefix) is evaluated using legacy logic and is unaffected by SAT v2 authorization. + +### Migration Path + +1. **Phase 1 (Current)**: Operators issue both legacy SAT tokens and SAT v2 tokens. Legacy tokens work unchanged; SAT v2 tokens use new classification rules. +2. **Phase 2**: Tenant/partner enforcement is introduced using separate SAT claims and/or request metadata (e.g., tenantId header), independent of capability strings. +3. **Phase 3** (future): Legacy SAT tokens are deprecated and eventually removed. + +During the transition, no action is required by operators for existing tokens to continue working. + +## Error Responses + +### 401 Unauthorized +Returned when: +- No credentials (Xerxes or SAT) are provided +- Xerxes token validation fails +- SAT token signature validation fails +- SAT token is expired + +Response body SHALL include an error code and message suitable for debugging. + +### 403 Forbidden +Returned when: +- Xerxes token is valid but does not grant permission for the requested operation +- SAT RBAC v2 token does not contain a matching capability for the request (domain, access) +- SAT RBAC v2 request cannot be classified into a valid (domain, access) pair +- Legacy SAT authorization denies the request + +Response body SHALL include an error code and message suitable for debugging. + +## Implementation Notes + +- Route classification rules MUST be ordered as specified; the first matching rule determines the domain. +- The `/filtered` segment check MUST be performed before HTTP method inspection to correctly classify POST-based read operations. +- Capability matching MUST be case-sensitive (xconf:core:readwrite is not equivalent to xconf:CORE:READWRITE). +- When multiple domains could match a path (e.g., a path containing both `/telemetry` and `/tagging`), the first rule in the ruleset that matches SHALL determine the domain. +- SAT v2 authorization SHALL NOT alter or inspect the request entity; classification is based solely on HTTP method and route/path. +- The SAT v2 domains are independent of Xerxes entity types; a request to access a "firmware" entity via SAT v2 is classified by route (core domain) regardless of entity semantics. diff --git a/openspec/changes/sat-rbac-capabilities-v2/tasks.md b/openspec/changes/sat-rbac-capabilities-v2/tasks.md new file mode 100644 index 0000000..e4937ff --- /dev/null +++ b/openspec/changes/sat-rbac-capabilities-v2/tasks.md @@ -0,0 +1,37 @@ +## 1. Spec And Contract Updates +- [x] 1.1 Update `openspec/specs/auth/auth-contract.md` with normative SAT RBAC v2 requirements. +- [x] 1.2 Add routing-based selection requirements: Authorization header -> SAT path; else Xerxes path. +- [x] 1.3 Add SAT v2 detection requirement via `xconf:` capability prefix. +- [x] 1.4 Add route-based domain classification requirements with ordered rules. +- [x] 1.5 Add access classification requirements with `/filtered` override then HTTP method mapping. +- [x] 1.6 Add SAT v2 deny-by-default requirement for unclassifiable requests. +- [x] 1.7 Add HTTP status semantics requirement (`401` auth failure, `403` authz denial). +- [x] 1.8 Add metrics domain constraint (readonly only; no `xconf:metrics:readwrite`). + +## 2. Mapping Registry And Classification +- [ ] 2.1 Implement a central route-to-domain mapping registry used by SAT v2 authorization. +- [ ] 2.2 Ensure mapping evaluation is ordered and first-match-wins. +- [ ] 2.3 Seed registry with representative patterns for `core`, `tagging`, `system`, `metrics`. +- [ ] 2.4 Add explicit readonly override patterns for POST-based read endpoints (including `/filtered`). + +## 3. Authorization Flow Integration +- [ ] 3.1 Integrate routing-based selection in auth middleware: Authorization header selects SAT path; otherwise Xerxes path. +- [ ] 3.2 Add SAT v2 detection logic based on any capability prefixed with `xconf:`. +- [ ] 3.3 Ensure SAT v2 deny-by-default on unclassifiable `(domain, access)` requests. +- [ ] 3.4 Enforce metrics as readonly-only during SAT v2 capability checks. +- [ ] 3.5 Preserve legacy SAT behavior unchanged when SAT v2 is not detected. + +## 4. HTTP Semantics And Error Handling +- [ ] 4.1 Ensure `401` is returned only for missing/invalid authentication. +- [ ] 4.2 Ensure `403` is returned for authenticated-but-not-authorized requests. +- [ ] 4.3 Ensure SAT v2 classification and capability failures consistently return `403`. +- [ ] 4.4 Verify fail-fast termination remains enforced after `401`/`403` responses. + +## 5. Validation +- [ ] 5.1 Add tests for routing behavior (Authorization header selects SAT path; SAT v2 vs legacy SAT selection on SAT path; Xerxes path only when Authorization is absent). +- [ ] 5.2 Add tests for SAT v2 detection by `xconf:` prefix presence. +- [ ] 5.3 Add tests for ordered route classification and first-match behavior. +- [ ] 5.4 Add tests for access classification (`/filtered` override and method-based fallback). +- [ ] 5.5 Add tests for deny-by-default on unclassified SAT v2 routes. +- [ ] 5.6 Add tests for metrics readonly-only behavior and no readwrite functionality. +- [ ] 5.7 Add tests for `401` vs `403` semantics across auth/authz scenarios. diff --git a/openspec/specs/auth/auth-contract.md b/openspec/specs/auth/auth-contract.md index 78d39fd..ea670e7 100644 --- a/openspec/specs/auth/auth-contract.md +++ b/openspec/specs/auth/auth-contract.md @@ -9,11 +9,13 @@ This specification describes: - credential validation - identity resolution - authentication success and failure outcomes +- routing-based authorization selection across SAT v2, legacy SAT, and Xerxes +- SAT v2 request classification requirements - fail-fast termination after authentication or authorization failure This specification does not describe: - business-specific policy enforcement -- application-level authorization +- tenant or partner enforcement policy - downstream extensions or constraints ## Guarantees @@ -31,6 +33,88 @@ Authentication failures SHALL result in defined error categories. Failure handling is subject to the Fail-Fast Termination guarantee defined below. +### Authorization Routing Selection + +Authorization selection SHALL be deterministic and route credentials by +credential type, not by evaluating Xerxes and SAT in precedence order. + +Normative behavior: + +- If the `Authorization` header is present, the request SHALL be treated + as SAT-authenticated and SAT processing SHALL be selected. + - If SAT contains at least one capability with prefix `xconf:`, the + system SHALL authorize using SAT RBAC v2 semantics. + - If SAT does not contain any capability with prefix `xconf:`, the + system SHALL authorize using legacy SAT behavior unchanged. +- Else, if a Xerxes token is present (header `token` or cookie `token`), + the system SHALL authorize using Xerxes permissions. +- Else, the system SHALL return `401 Unauthorized`. + +When both SAT and Xerxes credentials are present, `Authorization`-header +routing MUST win; SAT selection SHALL be used and Xerxes SHALL NOT be +evaluated for that request. + +### SAT RBAC v2 Detection + +SAT RBAC v2 SHALL be detected by the presence of at least one SAT +capability string with prefix `xconf:`. + +SAT tokens without any `xconf:` capability SHALL be treated as legacy SAT. + +### SAT RBAC v2 Domain Classification + +For SAT RBAC v2 authorization, request classification SHALL be based on +API route/path (admin functionality), not entity type. + +SAT RBAC v2 domains are `core`, `tagging`, `system`, and `metrics`. + +Domain classification requirements: + +- The system SHALL classify requests using an ordered ruleset. +- The first matching rule MUST determine the domain. +- Rules SHALL match on stable route substrings and/or route templates + (when available), for example: `/queries/firmware`, `/firmware`, + `/dcm`, `/telemetry`, `/tagging`, `/roundrobinfilter`, `/metrics`. + +### SAT RBAC v2 Access Classification + +For SAT RBAC v2 authorization, access level SHALL be determined with this +precedence: + +1. Route override +2. HTTP method + +Normative behavior: + +- If the path contains the segment `/filtered`, access SHALL be + `readonly`. +- Otherwise, access SHALL be method-based: + - `GET`, `HEAD` => `readonly` + - `POST`, `PUT`, `PATCH`, `DELETE` => `readwrite` +- Endpoints that use `POST` for read behavior (such as filtered searches) + MUST be explicitly treated as `readonly` via the route override. + +### SAT RBAC v2 Deny-By-Default + +If a SAT RBAC v2 request cannot be classified into `(domain, access)` +requirements, authorization SHALL be denied with `403 Forbidden`. + +### Metrics Domain Constraint + +The metrics domain SHALL be read-only. + +- `xconf:metrics:readonly` is the only supported metrics capability. +- No readwrite functionality exists for the metrics domain (no + `xconf:metrics:readwrite`). + +### HTTP Status Semantics + +The system SHALL use: + +- `401 Unauthorized` only for missing or invalid authentication. +- `403 Forbidden` for authenticated-but-not-authorized requests, + including SAT RBAC v2 classification or capability denials. + ### Fail-Fast Termination @@ -41,9 +125,9 @@ request processing MUST terminate immediately. No downstream handler logic, middleware continuation, or post-failure side effects SHALL occur after such a failure. -This contract does not define authorization semantics; it specifies -fail-fast termination behavior when such failures are emitted -through this authentication boundary. +This contract defines authentication-boundary authorization semantics +for routing selection, classification, and failure handling; downstream +business policy remains outside scope. ## Extension Notice