Files
admin/_docs/02_document/system-flows.md
T
Oleksandr Bezdieniezhnykh c7b297de83
ci/woodpecker/push/01-test Pipeline failed
ci/woodpecker/push/02-build-push unknown status
refactor: remove deploy.cmd and update Dockerfile for health checks
- Deleted the deploy.cmd script as it was no longer needed.
- Updated Dockerfile to include curl for health checks and added a non-root user for improved security.
- Modified health check command to use curl for better reliability.
- Adjusted docker-compose.test.yml to reflect changes in health check configuration.
- Cleaned up appsettings.json and removed unused configuration properties.
- Removed Resource entity and related requests from the codebase as part of the architectural shift.
- Updated documentation to reflect the removal of hardware binding and related endpoints.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 08:47:21 +03:00

14 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.

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 Admin API, Auth, User Mgmt, Resource Mgmt High
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 Admin API, Auth, Resource Mgmt Medium
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)
F3 F1 (requires JWT) — (post-AZ-197: no hardware-binding dependency)
F5 F1 (requires JWT) F3 (uploaded resources are later downloaded)
F6 F1 (requires JWT)
F7 F1 (requires JWT, ApiAdmin role) F3 (user data)
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

Updated by AZ-197 (2026-05-13) — the hardware-binding precondition and the CheckHardwareHash / GetHWHash steps were removed; the encryption key is now derived from email + password only. The diagram below reflects the post-cycle-1 path.

Description

An authenticated user requests a resource file. The system derives a per-user encryption key from email + password, encrypts the file with AES-256-CBC, and streams the encrypted content.

Preconditions

  • User is authenticated (JWT)
  • Resource file exists on server

Sequence Diagram

sequenceDiagram
    participant Client
    participant API as Admin API
    participant Auth as AuthService
    participant Sec as Security
    participant RS as ResourcesService
    participant FS as Filesystem

    Client->>API: POST /resources/get {password, fileName}
    API->>Auth: GetCurrentUser()
    Auth-->>API: User
    API->>Sec: GetApiEncryptionKey(email, password)
    Sec-->>API: AES key string
    API->>RS: GetEncryptedResource(folder, fileName, key)
    RS->>FS: Read file
    FS-->>RS: FileStream
    RS->>Sec: EncryptTo(stream, key)
    Sec-->>RS: Encrypted MemoryStream
    RS-->>API: Stream
    API-->>Client: 200 OK (application/octet-stream)

Error Scenarios

Error Where Detection Recovery
Not authenticated API No/invalid JWT 401 Unauthorized
File not found ResourcesService FileStream throws 500 Internal Server Error

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

Description

An authenticated user downloads the latest Azaion Suite installer (production or staging).

Preconditions

  • User is authenticated (JWT)
  • Installer file exists on server

Sequence Diagram

sequenceDiagram
    participant Client
    participant API as Admin API
    participant Auth as AuthService
    participant RS as ResourcesService
    participant FS as Filesystem

    Client->>API: GET /resources/get-installer
    API->>Auth: GetCurrentUser()
    Auth-->>API: User (not null)
    API->>RS: GetInstaller(isStage: false)
    RS->>FS: Scan for AzaionSuite.Iterative*
    FS-->>RS: FileInfo
    RS-->>API: (name, FileStream)
    API-->>Client: 200 OK (application/octet-stream)

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.