mirror of
https://github.com/azaion/admin.git
synced 2026-06-21 19:11:10 +00:00
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>
This commit is contained in:
@@ -30,6 +30,8 @@
|
||||
### Entities
|
||||
|
||||
> **Cycle 1 (2026-05-13) note** — `DetectionClass` (AZ-513) entity was added. `Resource` (AZ-183) was added then removed in the same cycle (post-cycle-1 revert; security audit F-1 + the OTA delivery model itself was deemed obsolete). The `User.Hardware` column is left in place as a tombstone (nullable, unused) per AZ-197. A UNIQUE INDEX `users_email_uidx` was added on `users.email` (security audit F-3, `env/db/06_users_email_unique.sql`).
|
||||
>
|
||||
> **Cycle 2 (2026-05-14) note** — `ResourcesConfig.SuiteInstallerFolder` and `SuiteStageInstallerFolder` were removed along with the installer endpoints (`GET /resources/get-installer[/stage]`); the POCO is now a single-property class (`ResourcesFolder`).
|
||||
|
||||
```
|
||||
User:
|
||||
@@ -81,8 +83,7 @@ JwtConfig:
|
||||
|
||||
ResourcesConfig:
|
||||
ResourcesFolder: string
|
||||
SuiteInstallerFolder: string
|
||||
SuiteStageInstallerFolder: string
|
||||
# SuiteInstallerFolder / SuiteStageInstallerFolder removed in cycle 2 with the installer endpoints.
|
||||
# EncryptionMasterKey was added by AZ-183 and removed in the post-cycle-1 revert.
|
||||
```
|
||||
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
# Authentication & Security
|
||||
|
||||
> **Cycle 1 (2026-05-13) note** — AZ-197 simplified `GetApiEncryptionKey` to `(email, password)` and removed `GetHWHash` outright. The hardware-binding threat model that motivated those primitives is no longer in scope (fTPM-anchored Jetsons + browser SaaS).
|
||||
>
|
||||
> **Cycle 2 (2026-05-14) note** — `GetApiEncryptionKey`, `EncryptTo`, and `DecryptTo` were all removed along with the encrypted-download endpoint. `Security` is now a one-method utility (`ToHash`) that backs SHA-384 password hashing.
|
||||
|
||||
## 1. High-Level Overview
|
||||
|
||||
**Purpose**: JWT token creation/validation and cryptographic utilities (password hashing, AES file encryption/decryption).
|
||||
**Purpose**: JWT token creation/validation and password hashing (`Security.ToHash`).
|
||||
|
||||
**Architectural Pattern**: Service + static utility — `AuthService` is a DI-managed service for JWT operations; `Security` is a static class for cryptographic primitives.
|
||||
**Architectural Pattern**: Service + static utility — `AuthService` is a DI-managed service for JWT operations; `Security` is a static class with a single SHA-384 helper.
|
||||
|
||||
**Upstream dependencies**: Data Layer (JwtConfig, IUserService for GetByEmail), ASP.NET Core (IHttpContextAccessor).
|
||||
|
||||
**Downstream consumers**: Admin API (token creation on login, current user resolution), User Management (password hashing for both web users and provisioned devices), Resource Management (encryption key derivation, stream encryption).
|
||||
**Downstream consumers**: Admin API (token creation on login, current user resolution), User Management (password hashing for both web users and provisioned devices).
|
||||
|
||||
## 2. Internal Interfaces
|
||||
|
||||
@@ -26,11 +28,11 @@
|
||||
| Method | Input | Output | Description |
|
||||
|--------|-------|--------|-------------|
|
||||
| `ToHash` | `string` | `string` (Base64) | SHA-384 hash |
|
||||
| `GetApiEncryptionKey` | `string email, string password` | `string` (Base64) | Derives the per-user AES encryption key string. **Signature simplified by AZ-197** (`hardwareHash` parameter removed). |
|
||||
| `EncryptTo` | `Stream input, Stream output, string key, CancellationToken` | void | AES-256-CBC encrypt stream |
|
||||
| `DecryptTo` | `Stream encrypted, Stream output, string key, CancellationToken` | void | AES-256-CBC decrypt stream |
|
||||
|
||||
**Removed by AZ-197**: `GetHWHash(string hardware)` — no remaining callers in the post-cycle-1 codebase.
|
||||
**Removed**:
|
||||
- `GetHWHash(string hardware)` — removed by AZ-197 (cycle 1).
|
||||
- `GetApiEncryptionKey(string email, string password)` — removed in cycle 2 (no remaining callers after `POST /resources/get/{dataFolder?}` was deleted).
|
||||
- `EncryptTo` / `DecryptTo` extension methods — removed in cycle 2 (no remaining callers; the only consumer was `ResourcesService.GetEncryptedResource`, also deleted).
|
||||
|
||||
## 3. External API Specification
|
||||
|
||||
@@ -42,7 +44,7 @@ No direct database access. `AuthService.GetCurrentUser` delegates to `IUserServi
|
||||
|
||||
## 5. Implementation Details
|
||||
|
||||
**Algorithmic Complexity**: Encryption/decryption is O(n) where n is file size, streaming in 512 KB buffers.
|
||||
**Algorithmic Complexity**: SHA-384 hashing is O(n) where n is input length; in practice it operates on short password strings only.
|
||||
|
||||
**State Management**: `AuthService` is stateless (reads claims from HTTP context per request). `Security` is purely static.
|
||||
|
||||
@@ -54,7 +56,6 @@ No direct database access. `AuthService.GetCurrentUser` delegates to `IUserServi
|
||||
| Microsoft.AspNetCore.Authentication.JwtBearer | 10.0.3 | JWT middleware integration |
|
||||
|
||||
**Error Handling Strategy**:
|
||||
- `EncryptTo` throws `ArgumentNullException` for unreadable streams or empty keys.
|
||||
- JWT token creation does not throw (malformed config would cause runtime errors at middleware level).
|
||||
- `GetCurrentUser` returns null if claims are missing or user not found.
|
||||
|
||||
@@ -65,15 +66,12 @@ None — `Security` itself is a utility consumed by other components.
|
||||
## 7. Caveats & Edge Cases
|
||||
|
||||
**Known limitations**:
|
||||
- Password hashing uses SHA-384 without per-user salt or key stretching. Not resistant to rainbow table attacks. (Unchanged by cycle 1.)
|
||||
- The encryption-key salt is a hardcoded constant. (`Security.GetApiEncryptionKey` body — see `services_security.md`.)
|
||||
- Password hashing uses SHA-384 without per-user salt or key stretching. Not resistant to rainbow table attacks. (Unchanged by cycles 1 and 2.)
|
||||
- `GetCurrentUserEmail` assumes `ClaimTypes.Name` is always present; accessing a missing key would throw `KeyNotFoundException`.
|
||||
- AES encryption prepends IV as first 16 bytes — consumers must know this format.
|
||||
|
||||
**Removed in cycle 1**: hardware fingerprint hashing was a known weakness (static salt, no rotation); deleting it via AZ-197 also removed that attack surface.
|
||||
|
||||
**Performance bottlenecks**:
|
||||
- Large file encryption loads encrypted output into `MemoryStream` before sending — high memory usage for large files.
|
||||
**Removed in cycle 2**: per-user file encryption (`GetApiEncryptionKey` + `EncryptTo` + `DecryptTo`). The hardcoded encryption-key salt and the in-memory `MemoryStream` round-trip are no longer attack / performance surfaces in this codebase.
|
||||
|
||||
## 8. Dependency Graph
|
||||
|
||||
@@ -81,7 +79,7 @@ None — `Security` itself is a utility consumed by other components.
|
||||
|
||||
**Can be implemented in parallel with**: User Management (shared dependency on Data Layer).
|
||||
|
||||
**Blocks**: Admin API, Resource Management (uses encryption).
|
||||
**Blocks**: Admin API. (Resource Management no longer depends on this component after cycle 2 removed `EncryptTo` / `DecryptTo`.)
|
||||
|
||||
## 9. Logging Strategy
|
||||
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
# Resource Management
|
||||
|
||||
> **Cycle 1 (2026-05-13) note** — AZ-197 removed the `Hardware` field from `GetResourceRequest` and removed `CheckResourceRequest` and `POST /resources/check` entirely. AZ-183 introduced an OTA update path (`POST /get-update`, `POST /resources/publish`, `IResourceUpdateService`, `Resource` entity, `resources` table, `ResourcesConfig.EncryptionMasterKey`) but it was reverted later the same day after the security audit (finding F-1) — the OTA delivery model itself was deemed obsolete. The component is now back to filesystem-backed storage only.
|
||||
> **Cycle 1 (2026-05-13) note** — AZ-197 removed the `Hardware` field from `GetResourceRequest` and removed `CheckResourceRequest` and `POST /resources/check` entirely. AZ-183 introduced an OTA update path (`POST /get-update`, `POST /resources/publish`, `IResourceUpdateService`, `Resource` entity, `resources` table, `ResourcesConfig.EncryptionMasterKey`) but it was reverted later the same day after the security audit (finding F-1) — the OTA delivery model itself was deemed obsolete.
|
||||
>
|
||||
> **Cycle 2 (2026-05-14) note** — the encrypted-download endpoint (`POST /resources/get/{dataFolder?}`) and both installer endpoints (`GET /resources/get-installer[/stage]`) were removed as obsolete. With them went `ResourcesService.GetEncryptedResource` / `GetInstaller`, `GetResourceRequest` (and `WrongResourceName = 50`), and the `ResourcesConfig.SuiteInstallerFolder` / `SuiteStageInstallerFolder` properties + their env-var rows. The component is now upload + list + clear only and no longer depends on Authentication & Security for encryption primitives.
|
||||
|
||||
## 1. High-Level Overview
|
||||
|
||||
**Purpose**: filesystem-backed storage — upload, list, download (per-user AES-encrypted), folder clearing, installer distribution. Owned by `IResourcesService`.
|
||||
**Purpose**: filesystem-backed storage — upload, list, clear. Owned by `IResourcesService`.
|
||||
|
||||
**Architectural Pattern**: a single service over the local filesystem. No DB access, no cache.
|
||||
|
||||
**Upstream dependencies**: Data Layer (`ResourcesConfig`), Authentication & Security (encryption via `Security.EncryptTo`).
|
||||
**Upstream dependencies**: Data Layer (`ResourcesConfig`).
|
||||
|
||||
**Downstream consumers**: Admin API (resource endpoints).
|
||||
|
||||
@@ -18,22 +20,18 @@
|
||||
|
||||
| Method | Input | Output | Async | Error Types |
|
||||
|--------|-------|--------|-------|-------------|
|
||||
| `GetInstaller` | `bool isStage` | `(string?, Stream?)` | No | None (returns nulls if not found) |
|
||||
| `GetEncryptedResource` | `string? dataFolder, string fileName, string key, CancellationToken` | `Stream` | Yes | `FileNotFoundException` |
|
||||
| `SaveResource` | `string? dataFolder, IFormFile data, CancellationToken` | void | Yes | `BusinessException(NoFileProvided)` |
|
||||
| `ListResources` | `string? dataFolder, string? search, CancellationToken` | `IEnumerable<string>` | Yes | `DirectoryNotFoundException` |
|
||||
| `ClearFolder` | `string? dataFolder` | void | No | None |
|
||||
|
||||
**Input DTO**:
|
||||
```
|
||||
GetResourceRequest (post-AZ-197):
|
||||
Password: string (required, min 8 chars)
|
||||
FileName: string (required, not empty)
|
||||
// Hardware field removed by AZ-197.
|
||||
**Removed**:
|
||||
- `GetEncryptedResource` — removed in cycle 2 with the encrypted-download endpoint.
|
||||
- `GetInstaller` — removed in cycle 2 with the installer endpoints.
|
||||
|
||||
// CheckResourceRequest — REMOVED by AZ-197.
|
||||
// GetUpdateRequest, PublishResourceRequest — added by AZ-183, removed in the post-cycle-1 revert.
|
||||
```
|
||||
**Removed DTOs**:
|
||||
- `GetResourceRequest` — removed in cycle 2 (file deleted).
|
||||
- `CheckResourceRequest` — removed by AZ-197 (cycle 1).
|
||||
- `GetUpdateRequest`, `PublishResourceRequest` — removed in the post-cycle-1 AZ-183 revert.
|
||||
|
||||
## 3. External API Specification
|
||||
|
||||
@@ -49,7 +47,7 @@ N/A — exposed through Admin API.
|
||||
|
||||
### Storage Estimates
|
||||
|
||||
- **Filesystem**: AI models, DLLs, installers — potentially hundreds of MB per file.
|
||||
- **Filesystem**: AI models, DLLs, etc. — potentially hundreds of MB per file.
|
||||
|
||||
## 5. Implementation Details
|
||||
|
||||
@@ -61,32 +59,26 @@ N/A — exposed through Admin API.
|
||||
- `SaveResource` throws `BusinessException(NoFileProvided)` for null uploads.
|
||||
- Missing files/directories throw standard .NET I/O exceptions.
|
||||
- `ClearFolder` silently returns if directory doesn't exist.
|
||||
- `GetInstaller` returns `(null, null)` tuple if installer file is not found.
|
||||
|
||||
## 6. Extensions and Helpers
|
||||
|
||||
| Helper | Purpose | Used By |
|
||||
|--------|---------|---------|
|
||||
| `Security.EncryptTo` | AES stream encryption | `GetEncryptedResource` |
|
||||
| `Security.GetApiEncryptionKey(email, password)` | Per-user key derivation (post-AZ-197 — no hardware component) | Admin API (before calling `GetEncryptedResource`) |
|
||||
None remaining after the cycle-2 removal of `Security.EncryptTo` and `Security.GetApiEncryptionKey`.
|
||||
|
||||
## 7. Caveats & Edge Cases
|
||||
|
||||
**Known limitations** (security-audit findings):
|
||||
- **F-2 (High)** — no path traversal protection: `dataFolder` parameter is concatenated directly with `ResourcesFolder`. A malicious `dataFolder` like `../../etc` could access arbitrary filesystem paths. Filed as separate ticket.
|
||||
- `SaveResource` deletes existing file before writing — no versioning or backup.
|
||||
- `GetEncryptedResource` loads the entire encrypted file into a `MemoryStream` — memory-intensive for large files.
|
||||
- `ListResources` wraps a synchronous `DirectoryInfo.GetFiles` in `Task.FromResult` — not truly async.
|
||||
|
||||
**Performance bottlenecks**:
|
||||
- Full file encryption to memory before streaming response: memory usage scales with file size.
|
||||
- `ClearFolder` iterates and deletes files synchronously.
|
||||
|
||||
## 8. Dependency Graph
|
||||
|
||||
**Must be implemented after**: Data Layer (ResourcesConfig), Authentication & Security (encryption).
|
||||
**Must be implemented after**: Data Layer (ResourcesConfig).
|
||||
|
||||
**Can be implemented in parallel with**: User Management.
|
||||
**Can be implemented in parallel with**: User Management, Authentication & Security.
|
||||
|
||||
**Blocks**: Admin API.
|
||||
|
||||
@@ -101,6 +93,5 @@ N/A — exposed through Admin API.
|
||||
**Log storage**: console + rolling file (via Serilog configured in Program.cs).
|
||||
|
||||
## Modules Covered
|
||||
- `Services/ResourcesService`
|
||||
- `Common/Requests/GetResourceRequest` (post-AZ-197 — no `CheckResourceRequest`, no `Hardware` field)
|
||||
- `Common/Configs/ResourcesConfig` (the `EncryptionMasterKey` field added by AZ-183 was removed in the post-cycle-1 revert)
|
||||
- `Services/ResourcesService` (post-cycle-2 — only `SaveResource` / `ListResources` / `ClearFolder` remain)
|
||||
- `Common/Configs/ResourcesConfig` (post-cycle-2 — only `ResourcesFolder` remains)
|
||||
|
||||
@@ -50,12 +50,10 @@ Converts `BusinessException` to HTTP 409 JSON response: `{ ErrorCode: int, Messa
|
||||
| `/resources/{dataFolder?}` | POST | Authenticated | Uploads a file (up to 200 MB) |
|
||||
| `/resources/list/{dataFolder?}` | GET | Authenticated | Lists files |
|
||||
| `/resources/clear/{dataFolder?}` | POST | ApiAdmin | Clears folder |
|
||||
| `/resources/get/{dataFolder?}` | POST | Authenticated | Downloads encrypted resource (key derived from `email + password` only — no Hardware) |
|
||||
| `/resources/get-installer` | GET | Authenticated | Downloads production installer |
|
||||
| `/resources/get-installer/stage` | GET | Authenticated | Downloads staging installer |
|
||||
|
||||
**Removed by AZ-197**: `POST /resources/check` (was the hardware-binding side-effect probe).
|
||||
**Removed in post-cycle-1 revert**: `POST /get-update` and `POST /resources/publish` (AZ-183 reverted — security audit F-1; OTA delivery model itself obsolete).
|
||||
**Removed in cycle 2 (2026-05-14)**: `POST /resources/get/{dataFolder?}`, `GET /resources/get-installer`, `GET /resources/get-installer/stage` — all obsolete; the encrypted-download support stack (`Security.GetApiEncryptionKey` / `EncryptTo` / `DecryptTo`, `ResourcesService.GetEncryptedResource` / `GetInstaller`, `GetResourceRequest`, `WrongResourceName = 50`, `ResourcesConfig.SuiteInstallerFolder` / `SuiteStageInstallerFolder`) was removed with them. ADR-003 retired.
|
||||
|
||||
### Detection Classes
|
||||
| Endpoint | Method | Auth | Description |
|
||||
|
||||
Reference in New Issue
Block a user