# Module: Azaion.AdminApi.Program ## Purpose Application entry point: configures DI, middleware, authentication, authorization, CORS, Swagger, logging, and defines all HTTP endpoints using ASP.NET Core Minimal API. ## Public Interface (HTTP Endpoints) > **Cycle 1 (2026-05-13) note** — endpoint surface changed by AZ-513 (detection-class CRUD), AZ-196 (device auto-registration), AZ-197 (hardware-binding removal). AZ-183 (OTA update check + publish) 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. The table reflects the post-cycle-1 state including that revert. > > **Cycle 2 (2026-05-14) note** — three more endpoints were removed as obsolete: `POST /resources/get/{dataFolder?}`, `GET /resources/get-installer`, `GET /resources/get-installer/stage`. The encrypted-download support stack (`Security.GetApiEncryptionKey` / `EncryptTo` / `DecryptTo`, `ResourcesService.GetEncryptedResource` / `GetInstaller`, `GetResourceRequest` DTO, `WrongResourceName = 50` enum value, `ResourcesConfig.SuiteInstallerFolder` / `SuiteStageInstallerFolder`) went with them. ADR-003 in `architecture.md` was retired in the same change. | Method | Path | Auth | Summary | Cycle 1 origin | |--------|------|------|---------|----------------| | POST | `/login` | Anonymous | Validates credentials, returns JWT token | — | | POST | `/users` | ApiAdmin | Creates a new user | — | | POST | `/devices` | ApiAdmin | Creates a CompanionPC device user (auto serial / email / 32-hex password) | AZ-196 | | GET | `/users/current` | Any authenticated | Returns current user from JWT claims | — | | GET | `/users` | ApiAdmin | Lists users with optional email/role filters | — | | PUT | `/users/queue-offsets/set` | Any authenticated | Updates user's queue offsets | — | | PUT | `/users/{email}/set-role/{role}` | ApiAdmin | Changes a user's role | — | | PUT | `/users/{email}/enable` | ApiAdmin | Enables a user account | — | | PUT | `/users/{email}/disable` | ApiAdmin | Disables a user account | — | | DELETE | `/users/{email}` | ApiAdmin | Removes a user | — | | POST | `/resources/{dataFolder?}` | Any authenticated | Uploads a resource file | — | | GET | `/resources/list/{dataFolder?}` | Any authenticated | Lists files in a resource folder | — | | POST | `/resources/clear/{dataFolder?}` | ApiAdmin | Clears a resource folder | — | | POST | `/classes` | ApiAdmin | Creates a detection class | AZ-513 | | PATCH | `/classes/{id:int}` | ApiAdmin | Updates a detection class (partial-merge) | AZ-513 | | DELETE | `/classes/{id:int}` | ApiAdmin | Deletes a detection class | AZ-513 | ### Removed endpoints The following endpoints have been removed and now return `404`: | Method | Path | Removed in | Reason | |--------|------|------------|--------| | PUT | `/users/hardware/set` | cycle 1 (AZ-197) | hardware-binding feature deleted (no fielded clients in target architecture) | | POST | `/resources/check` | cycle 1 (AZ-197) | was the hardware-binding side-effect probe; no remaining purpose | | POST | `/get-update` | post-cycle-1 (AZ-183 reverted) | security audit F-1: endpoint disclosed plaintext per-resource encryption keys to any authenticated caller; the underlying installer-distribution flow is itself obsolete | | POST | `/resources/publish` | post-cycle-1 (AZ-183 reverted) | same revert as `/get-update` — the publish counterpart of the OTA flow | | POST | `/resources/get/{dataFolder?}` | cycle 2 (2026-05-14) | obsolete — per-user encrypted-download flow no longer used by any client; ADR-003 retired | | GET | `/resources/get-installer` | cycle 2 (2026-05-14) | obsolete — installer-shipping era is over (browser SaaS + fTPM Jetsons) | | GET | `/resources/get-installer/stage` | cycle 2 (2026-05-14) | same as `/resources/get-installer` | ## Internal Logic ### DI Registration - `IUserService` → `UserService` (Scoped) - `IAuthService` → `AuthService` (Scoped) - `IResourcesService` → `ResourcesService` (Scoped) - `IDetectionClassService` → `DetectionClassService` (Scoped) — added by AZ-513 - `IDbFactory` → `DbFactory` (Singleton) - `ICache` → `MemoryCache` (Scoped) - `LazyCache` via `AddLazyCache()` - FluentValidation validators auto-discovered from `RegisterUserValidator` assembly (also picks up `CreateDetectionClassRequest`, `UpdateDetectionClassRequest` validators introduced in cycle 1) - `BusinessExceptionHandler` registered as exception handler ### Middleware Pipeline 1. Swagger (dev only) 2. CORS (`AdminCorsPolicy`) 3. Authentication (JWT Bearer) 4. Authorization 5. URL rewrite: root `/` → `/swagger` 6. Exception handler ### Authorization Policies - `apiAdminPolicy`: requires `RoleEnum.ApiAdmin` role > The `apiUploaderPolicy` (`RoleEnum.ResourceUploader` OR `ApiAdmin`) was added by AZ-183 and removed in the same cycle when the OTA endpoints it guarded were retired (see "Removed in cycle 1" above). `RoleEnum.ResourceUploader` itself remains as a data value (the seed `uploader@azaion.com` still uses it) but is no longer wired to any endpoint policy. ### Configuration Sections - `JwtConfig` — JWT signing/validation - `ConnectionStrings` — DB connections - `ResourcesConfig` — file storage path (`ResourcesFolder`); the installer subfolders were dropped in cycle 2 along with the installer endpoints ### Kestrel - Max request body size: 200 MB (for file uploads) ### Logging - Serilog: console + rolling file (`logs/log.txt`) ### CORS - Allowed origins: `https://admin.azaion.com`, `http://admin.azaion.com` - All methods and headers allowed - Credentials allowed ## Dependencies All services, configs, entities, and request types from Azaion.Common and Azaion.Services. ## Consumers None — this is the application entry point. ## Data Models None defined here. ## Configuration Reads `JwtConfig`, `ConnectionStrings`, `ResourcesConfig` from `IConfiguration`. ## External Integrations - PostgreSQL (via DI-registered `DbFactory`) - Local filesystem (via `ResourcesService`) ## Security - JWT Bearer authentication with full validation (issuer, audience, lifetime, signing key) - Role-based authorization policies - CORS restricted to `admin.azaion.com` - Request body limit of 200 MB - Antiforgery disabled for resource upload endpoint - Password sent via POST body (not URL) ## Tests None directly; tested indirectly through integration tests.