Files
admin/_docs/02_document/system-flows.md
T
Oleksandr Bezdieniezhnykh 3a925b9b0f
ci/woodpecker/push/01-test Pipeline failed
ci/woodpecker/push/02-build-push unknown status
refactor: remove obsolete resource download and installer endpoints
- Deleted the `POST /resources/get/{dataFolder?}` and `GET /resources/get-installer` endpoints as part of the architectural shift towards simplified resource management.
- Removed associated methods and configurations, including `ResourcesService.GetEncryptedResource`, `ResourcesService.GetInstaller`, and related properties in `ResourcesConfig`.
- Cleaned up environment variables and configuration files to reflect the removal of installer-related settings.
- Eliminated the `GetResourceRequest` DTO and its validator, along with the `WrongResourceName` error code.
- Updated documentation to clarify the changes in resource handling and the retirement of per-user file encryption.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 04:17:55 +03:00

13 KiB

Azaion Admin API — System Flows

Cycle 1 (2026-05-13) note — F4 (Hardware Check) was deleted by AZ-197; F3 no longer depends on hardware. Two new flows were added: F8 Detection Classes CRUD (AZ-513), F9 Device Auto-Provisioning (AZ-196). F10 OTA Update Check & Publish (AZ-183) was reverted later the same day after the security audit (finding F-1) — the OTA delivery model itself was deemed obsolete; see _docs/05_security/security_report.md for context. F3's narrative was updated to drop the hardware-check step.

Cycle 2 (2026-05-14) note — F3 (Encrypted Resource Download) and F6 (Installer Download) were removed entirely as obsolete. The encrypted-download support stack (Security.GetApiEncryptionKey, EncryptTo, DecryptTo, ResourcesService.GetEncryptedResource, ResourcesService.GetInstaller, GetResourceRequest, WrongResourceName (50)) and the installer config (SuiteInstallerFolder, SuiteStageInstallerFolder) all went with them. See _docs/02_document/architecture.md ADR-003 (retired).

Flow Inventory

# Flow Name Trigger Primary Components Criticality
F1 User Login POST /login Admin API, User Mgmt, Auth & Security High
F2 User Registration POST /users Admin API, User Mgmt High
F3 Encrypted Resource Download POST /resources/get REMOVED — cycle 2 (obsolete)
F4 Hardware Check POST /resources/check REMOVED — AZ-197
F5 Resource Upload POST /resources Admin API, Resource Mgmt Medium
F6 Installer Download GET /resources/get-installer REMOVED — cycle 2 (obsolete)
F7 User Management (CRUD) Various /users/* Admin API, User Mgmt Medium
F8 Detection Classes CRUD (AZ-513) POST/PATCH/DELETE /classes Admin API, DetectionClassService High
F9 Device Auto-Provisioning (AZ-196) POST /devices Admin API, User Mgmt High
F10 OTA Update Check & Publish POST /get-update + POST /resources/publish REMOVED — post-cycle-1 (AZ-183 reverted, see security audit F-1)

Flow Dependencies

Flow Depends On Shares Data With
F1 All other flows (produces JWT token)
F2 F1, F9 (creates user records — including device users via F9)
F5 F1 (requires JWT)
F7 F1 (requires JWT, ApiAdmin role)
F8 F1 (requires JWT, ApiAdmin role) UI Detection Classes table
F9 F1 (requires JWT, ApiAdmin role) F2 (writes a user row, but reuses RegisterUser end-to-end), F1 (provisioned devices later log in)

Flow F1: User Login

Description

A user submits email/password credentials. The system validates them against the database and returns a signed JWT token for subsequent authenticated requests.

Preconditions

  • User account exists in the database
  • User knows correct password

Sequence Diagram

sequenceDiagram
    participant Client
    participant API as Admin API
    participant US as UserService
    participant DB as PostgreSQL
    participant Auth as AuthService

    Client->>API: POST /login {email, password}
    API->>US: ValidateUser(request)
    US->>DB: SELECT user WHERE email = ?
    DB-->>US: User record
    US->>US: Compare password hash
    US-->>API: User entity
    API->>Auth: CreateToken(user)
    Auth-->>API: JWT string
    API-->>Client: 200 OK {token}

Error Scenarios

Error Where Detection Recovery
Email not found UserService.ValidateUser No DB record 409: NoEmailFound (code 10)
Wrong password UserService.ValidateUser Hash mismatch 409: WrongPassword (code 30)

Flow F2: User Registration

Description

An admin creates a new user account with email, password, and role.

Preconditions

  • Caller has ApiAdmin role
  • Email is not already registered

Sequence Diagram

sequenceDiagram
    participant Admin
    participant API as Admin API
    participant VAL as FluentValidation
    participant US as UserService
    participant DB as PostgreSQL

    Admin->>API: POST /users {email, password, role}
    API->>VAL: Validate RegisterUserRequest
    VAL-->>API: OK
    API->>US: RegisterUser(request)
    US->>DB: SELECT user WHERE email = ?
    DB-->>US: null (no duplicate)
    US->>US: Hash password (SHA-384)
    US->>DB: INSERT user (admin connection)
    DB-->>US: OK
    US-->>API: void
    API-->>Admin: 200 OK

Error Scenarios

Error Where Detection Recovery
Validation failure FluentValidation Email < 8 chars, bad format, password < 8 chars 400 Bad Request
Duplicate email UserService.RegisterUser Existing user found 409: EmailExists (code 20)

Flow F3: Encrypted Resource Download — REMOVED (cycle 2, 2026-05-14)

The POST /resources/get/{dataFolder?} endpoint and its supporting stack (Security.GetApiEncryptionKey, Security.EncryptTo, Security.DecryptTo, ResourcesService.GetEncryptedResource, GetResourceRequest DTO + validator, ExceptionEnum.WrongResourceName (50)) were removed as obsolete. Per-user file encryption is no longer part of the system; resource files are now stored as plain bytes and only ever leave the server through the upload (F5) and admin clear paths. ADR-003 in architecture.md was retired in the same change.


Flow F4: Hardware Check (REMOVED by AZ-197)

The hardware-fingerprint binding flow (POST /resources/check, UserService.CheckHardwareHash, Security.GetHWHash, error code 40 HardwareIdMismatch, error code 45 BadHardware) was removed entirely in cycle 1.

Reason: the threat the binding mitigated (credential reuse via desktop installers) was eliminated by the architectural shift to fTPM-secured Jetsons + browser-only SaaS access. See _docs/03_implementation/batch_06_report.md and the obsolete diagram diagrams/flows/flow_hardware_check.md.


Flow F5: Resource Upload

Description

An authenticated user uploads a file to a specified resource folder on the server.

Preconditions

  • User is authenticated (JWT)
  • File size <= 200 MB

Sequence Diagram

sequenceDiagram
    participant User
    participant API as Admin API
    participant RS as ResourcesService
    participant FS as Filesystem

    User->>API: POST /resources/{folder} (multipart/form-data)
    API->>RS: SaveResource(folder, file)
    RS->>FS: Create directory (if needed)
    RS->>FS: Delete existing file (same name)
    RS->>FS: Write file
    FS-->>RS: OK
    RS-->>API: void
    API-->>User: 200 OK

Flow F6: Installer Download — REMOVED (cycle 2, 2026-05-14)

The GET /resources/get-installer and GET /resources/get-installer/stage endpoints, the ResourcesService.GetInstaller method, the ResourcesConfig.SuiteInstallerFolder / SuiteStageInstallerFolder configuration properties, and their environment-variable rows in every config artifact (appsettings.json, .env.example, secrets/*.public.env, docker-compose.test.yml) were removed. The installer-shipping era is over in the target architecture (browser SaaS + fTPM Jetsons); installer artefacts are no longer served from the Admin API.


Flow F7: User Management (CRUD)

Description

Admin operations: list users, change role, enable/disable, update queue offsets, delete user. (The "set hardware" operation was removed by AZ-197 — see F4.)

Preconditions

  • Caller has ApiAdmin role (for most operations)

All operations follow the same pattern: API endpoint → UserService method → DbFactory.RunAdmin → PostgreSQL UPDATE/DELETE. Cache is invalidated for affected user keys after writes (the UpdateQueueOffsets path is the only remaining cache-invalidation site post-AZ-197).


Flow F8: Detection Classes CRUD (AZ-513, 2026-05-13)

Description

ApiAdmin manages the detection-class catalogue exposed to operators in the UI: create new entries, partial-merge edits, delete entries. The UI's existing add/delete affordances start working end-to-end once this flow exists; the in-place edit affordance arrives via UI cycle AZ-512.

Preconditions

  • Caller has ApiAdmin role (apiAdminPolicy)
  • detection_classes table exists in the admin DB

Sequence Diagram

sequenceDiagram
    participant Client
    participant API as Admin API
    participant VAL as FluentValidation
    participant DCS as DetectionClassService
    participant DB as PostgreSQL

    Client->>API: POST /classes {name, shortName, color, maxSizeM, photoMode?}
    API->>VAL: Validate CreateDetectionClassRequest
    VAL-->>API: OK / 400
    API->>DCS: Create(request)
    DCS->>DB: InsertWithInt32IdentityAsync (admin conn)
    DB-->>DCS: new id
    DCS-->>API: DetectionClass {id, …}
    API-->>Client: 200 OK {DetectionClass}

    Client->>API: PATCH /classes/{id} {…partial fields}
    API->>VAL: Validate UpdateDetectionClassRequest
    VAL-->>API: OK / 400
    API->>DCS: Update(id, request)
    alt id exists
        DCS->>DB: UPDATE row applying non-null fields (admin conn)
        DCS-->>API: DetectionClass
        API-->>Client: 200 OK {DetectionClass}
    else id missing
        DCS-->>API: null
        API-->>Client: 404 Not Found
    end

    Client->>API: DELETE /classes/{id}
    API->>DCS: Delete(id)
    DCS->>DB: DELETE WHERE id = ? (admin conn)
    alt deleted > 0
        DCS-->>API: true
        API-->>Client: 204 No Content
    else
        DCS-->>API: false
        API-->>Client: 404 Not Found
    end

Error Scenarios

Error Where Detection Recovery
Not authenticated API No JWT 401 Unauthorized
Wrong role API Non-ApiAdmin JWT 403 Forbidden
Validation failure FluentValidation Field bounds violated 400 Bad Request
Missing id (PATCH/DELETE) DetectionClassService Row not found 404 Not Found

Flow F9: Device Auto-Provisioning (AZ-196, 2026-05-13)

Description

ApiAdmin requests a fresh CompanionPC device user. The server allocates the next sequential serial (azj-NNNN), generates a 32-char hex password, persists the user with the SHA-384 hash, and returns the plaintext credentials exactly once. The provisioning script (out-of-tree) embeds the values into the device's device.conf.

Preconditions

  • Caller has ApiAdmin role (apiAdminPolicy)

Sequence Diagram

sequenceDiagram
    participant Admin
    participant API as Admin API
    participant US as UserService
    participant DB as PostgreSQL

    Admin->>API: POST /devices (no body)
    API->>US: RegisterDevice()
    US->>DB: SELECT TOP 1 email FROM users WHERE role = 'CompanionPC' ORDER BY created_at DESC
    DB-->>US: lastEmail (or null)
    US->>US: nextNumber = parse(lastEmail.suffix) + 1 (or 0)
    US->>US: serial = "azj-" + nextNumber.PadLeft(4)
    US->>US: password = ToHex(RandomBytes(16))   // 32 hex chars
    US->>DB: INSERT user {Email=serial@domain, PasswordHash=SHA384(password), Role=CompanionPC, IsEnabled=true} (admin conn)
    DB-->>US: OK
    US-->>API: RegisterDeviceResponse {Serial, Email, Password}
    API-->>Admin: 200 OK {Serial, Email, Password}

Error Scenarios

Error Where Detection Recovery
Not authenticated / wrong role API JWT missing or non-ApiAdmin 401 / 403
Email already exists UserService.RegisterUser (called by RegisterDevice) DB UNIQUE INDEX users_email_uidx violation translated to EmailExists (5) 409 — caller retries (the next call recomputes a fresh azj-NNNN)

Implementation noteRegisterDevice reuses UserService.RegisterUser for the row insert (post-security-audit consolidation, finding F-3). The users.email column has a UNIQUE INDEX (env/db/06_users_email_unique.sql); concurrent provisioning calls that race on the same serial surface the violation atomically.


Flow F10: OTA Update Check & Publish (REMOVED — post-cycle-1 revert)

The POST /get-update and POST /resources/publish endpoints, the IResourceUpdateService / ResourceUpdateService / ResourceColumnEncryption types, the Resource entity, the resources table, the apiUploaderPolicy, and the ResourcesConfig.EncryptionMasterKey field were all removed shortly after AZ-183 shipped.

Reasons:

  1. Security audit finding F-1 — /get-update was registered with .RequireAuthorization() (any authenticated caller) and returned the per-resource decrypted EncryptionKey in the response body, defeating the at-rest column encryption.
  2. The OTA delivery model is itself a leftover from the installer-shipping era; the target architecture (browser-only SaaS + fTPM-secured Jetsons) does not need it.

The apiUploaderPolicy definition was removed from Program.cs; the RoleEnum.ResourceUploader enum value remains as data (the seed uploader@azaion.com user still uses it for negative-auth tests) but is no longer wired to any endpoint.