[AZ-529] [AZ-530] Cycle-2 documentation refresh

Refreshes _docs/02_document/ to reflect the cycle-2 auth-modernization
+ CMMC hardening landings (AZ-531..AZ-538). Authoritative source for
the ripple set is ripple_log_cycle2.md.

Covered:
- architecture.md (section 1 rewritten, ADRs 6-9 added)
- data_model.md (sessions, audit_events, user columns, migrations)
- system-flows.md (F1 rewritten; F11-F17 added; F2/F7/F9 minor)
- module-layout.md (cycle-2 sub-component table)
- diagrams/flows/flow_login.md (dual-token + MFA)
- components/{01_data_layer,03_auth_and_security,05_admin_api}
- modules/ (12 new, 8 modified — full Argon2id/ES256/MFA/refresh
  /mission/session/audit/jwks rollup)
- tests/{blackbox,security,traceability-matrix}

Step 13 (Update Docs) output for cycle 2.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-14 09:22:53 +03:00
parent c2c659ef62
commit a77b3f8a59
35 changed files with 3624 additions and 468 deletions
@@ -137,3 +137,99 @@ The encrypted-download and installer-download endpoints were removed as obsolete
| Cycle-2 AC-4 | `ExceptionEnum` no longer carries `WrongResourceName` (50); the gap is preserved | — | Build/CI invariant — verified by enum read |
| Cycle-2 AC-5 | `Azaion.Test` project no longer in solution; build is clean | — | Build invariant — `dotnet build Azaion.AdminApi.sln` clean post-cleanup |
| Cycle-2 AC-6 | E2E suite passes after the test deletions above | All e2e tests | Covered by Step 11 Run Tests post-cleanup (2026-05-14) |
## Cycle 2 Additions (2026-05-14) — Auth Modernization (AZ-529 + AZ-530)
Appended during the existing-code cycle 2 Test-Spec Sync (autodev Step 12) for the eight tasks delivered by the auth-modernization + CMMC-hardening epics. Rows below are namespaced by tracker ID; functional scenarios live in `blackbox-tests.md`, security-only invariants in `security-tests.md`. Existing AC/test IDs from earlier cycles are preserved unchanged.
### AZ-536 — Argon2id Password Hashing (epic AZ-530, 5 ACs)
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|-------|---------------------|----------|----------|
| AZ-536 AC-1 | New users get Argon2id hashes (PHC, m ≥ 64 MiB, t ≥ 3, p ≥ 1) | NFT-SEC-07 | Covered |
| AZ-536 AC-2 | Legacy SHA-384 hashes still validate | FT-P-24 | Covered |
| AZ-536 AC-3 | Successful legacy login transparently re-hashes to Argon2id | FT-P-25 | Covered |
| AZ-536 AC-4 | Wrong password fails for both formats with the same error code | FT-N-17 | Covered |
| AZ-536 AC-5 | Verify is constant-time (no remotely observable timing leak) | NFT-SEC-08 | Covered (with known suite-concurrency flake — see cycle-2 carry-forward F6) |
### AZ-537 — /login Rate Limit + Account Lockout (epic AZ-530, 6 ACs)
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|-------|---------------------|----------|----------|
| AZ-537 AC-1 | Per-IP rate limit triggers HTTP 429 with `Retry-After` | NFT-SEC-09 | Covered (legitimate environment-mismatch skip in shared-IP container env) |
| AZ-537 AC-2 | Per-account rate limit triggers HTTP 429 across IPs | NFT-SEC-10 | Covered |
| AZ-537 AC-3 | Account lockout after 10 failures returns 423 even on correct password | NFT-SEC-11 | Covered |
| AZ-537 AC-4 | Successful login resets `failed_login_count` and clears `lockout_until` | FT-P-26 | Covered |
| AZ-537 AC-5 | Lockout auto-expires after configured duration | FT-P-27 | Covered |
| AZ-537 AC-6 | Audit-log entry written on each lockout event | NFT-SEC-12 | Covered |
### AZ-538 — CORS HTTPS-Only + HSTS (epic AZ-530, 5 ACs)
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|-------|---------------------|----------|----------|
| AZ-538 AC-1 | HTTP origin gets no `Access-Control-Allow-Origin` header | NFT-SEC-13 | Covered |
| AZ-538 AC-2 | HTTPS origin preflight echoes credentials flag | FT-P-28 | Covered |
| AZ-538 AC-3 | HSTS header present in production responses | NFT-SEC-14 | Covered (legitimate Production-only environment-mismatch skip in dev test harness — verified by code inspection of `Program.cs UseHsts`) |
| AZ-538 AC-4 | HTTP request returns 307 to HTTPS in production | NFT-SEC-15 | Covered (legitimate Production-only environment-mismatch skip in dev test harness — verified by code inspection of `Program.cs UseHttpsRedirection`) |
| AZ-538 AC-5 | Development env unchanged (no redirect, no HSTS) | FT-P-29 | Covered |
### AZ-531 — Refresh-Token Flow (epic AZ-529, 5 ACs)
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|-------|---------------------|----------|----------|
| AZ-531 AC-1 | `/login` returns dual tokens, session row persisted | FT-P-30 | Covered |
| AZ-531 AC-2 | `/token/refresh` rotates refresh + chains via `parent_session_id` | FT-P-31 | Covered |
| AZ-531 AC-3 | Reuse-detection kills the entire session family | NFT-SEC-16 | Covered |
| AZ-531 AC-4 | Sliding window + 12 h absolute family expiry | FT-P-32 | Covered |
| AZ-531 AC-5 | Refresh tokens are opaque, hashed at rest, never logged in raw form | NFT-SEC-17 | Covered |
### AZ-532 — Asymmetric Signing + JWKS (epic AZ-529, 5 ACs)
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|-------|---------------------|----------|----------|
| AZ-532 AC-1 | Access tokens carry `alg=ES256` + `kid` | NFT-SEC-18 | Covered |
| AZ-532 AC-2 | `GET /.well-known/jwks.json` serves the active public key with cache headers | FT-P-33 | Covered |
| AZ-532 AC-3 | Two-key overlap during rotation (both JWKS entries valid) | FT-P-34 | Covered |
| AZ-532 AC-4 | JWKS never exposes private material | NFT-SEC-19 | Covered |
| AZ-532 AC-5 | alg-confusion forgery (HS256 with public key as secret) is rejected | NFT-SEC-20 | Covered |
### AZ-533 — Mission-Token Issuance for UAV (epic AZ-529, 6 ACs)
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|-------|---------------------|----------|----------|
| AZ-533 AC-1 | Mission token issued with correct lifetime (`planned_duration_h + 1h`) | FT-P-35 | Covered |
| AZ-533 AC-2 | Hard cap of 12 h enforced (HTTP 400 with cap message) | FT-N-19 | Covered |
| AZ-533 AC-3 | Mission token carries `mission_id`, `aircraft_id`, `aud`, `permissions`, `sid`, `jti` | FT-P-36 | Covered |
| AZ-533 AC-4 | Mission session auto-revoked when aircraft user reconnects | FT-P-37 | Covered |
| AZ-533 AC-5 | Endpoint requires authenticated session | FT-N-18 | Covered |
| AZ-533 AC-6 | MFA step-up required (`amr` must include `mfa`) | NFT-SEC-21 | **Spec only** — pending wire-up post-AZ-534 (cycle-2 carry-forward F1) |
### AZ-534 — TOTP-Based 2FA at Login (epic AZ-529, 6 ACs)
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|-------|---------------------|----------|----------|
| AZ-534 AC-1 | Enrollment returns secret + QR + 10 recovery codes | FT-P-38 | Covered |
| AZ-534 AC-2 | Confirm with valid TOTP completes enrollment | FT-P-39 | Covered |
| AZ-534 AC-3 | Two-step `/login``/login/mfa` flow; access-token `amr=["pwd","mfa"]` | FT-P-40 | Covered |
| AZ-534 AC-4 | Recovery code substitutes for TOTP and is single-use | FT-P-41 | Covered |
| AZ-534 AC-5 | Disable requires password + valid TOTP | FT-P-42 | Covered |
| AZ-534 AC-6 | TOTP secret encrypted at rest in `users.mfa_secret` | NFT-SEC-22 | Covered |
### AZ-535 — Logout + Revocation Surface (epic AZ-529, 5 ACs)
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|-------|---------------------|----------|----------|
| AZ-535 AC-1 | `POST /logout` revokes the current session and kills refresh | FT-P-43 | Covered |
| AZ-535 AC-2 | `POST /logout/all` revokes every session for the user | FT-P-44 | Covered |
| AZ-535 AC-3 | Admin can revoke any session by id; row records actor | FT-P-45 | Covered |
| AZ-535 AC-4 | `GET /sessions/revoked?since=…` returns recent, non-expired entries | FT-P-46 | Covered |
| AZ-535 AC-5 | `POST /logout` is idempotent (no second DB write) | FT-P-47 | Covered |
## Cycle 2 Coverage Update
| Category | Total Items | Covered | Not Yet Wired | Coverage % |
|----------|-----------|---------|---------------|-----------|
| Acceptance Criteria (cycle 2 — auth modernization) | 43 | 42 | 1 (AZ-533 AC-6 — pending wire-up F1) | 98% |
| Acceptance Criteria — combined total (baseline + cycle 1 + cycle 2 cleanup + cycle 2 auth) | 100 | 96 | 1 (F1) + 3 baseline restrictions still uncovered | 96% |
The single uncovered cycle-2 AC (AZ-533 AC-6) is documented in the cycle-2 implementation report as carry-forward item F1 — the `/sessions/mission` `amr=mfa` enforcement was deferred during AZ-533, became implementable once AZ-534 shipped, and is filed as a follow-up ticket to be picked up in a later cycle.