mirror of
https://github.com/azaion/admin.git
synced 2026-06-21 14:21:09 +00:00
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>
This commit is contained in:
@@ -1,12 +1,14 @@
|
||||
# User Management
|
||||
|
||||
> **Cycle 1 (2026-05-13) note** — hardware-binding methods (`UpdateHardware`, `CheckHardwareHash`) and `SetHWRequest` were removed by AZ-197; the `ValidateUser` error set now includes `UserDisabled`; `RegisterDevice` was added by AZ-196 to back the new `POST /devices` endpoint. Post-cycle-1 (security audit F-3): `RegisterDevice` now reuses `RegisterUser` for the row insert; the duplicate-row race was closed by adding a UNIQUE INDEX on `users.email` (`env/db/06_users_email_unique.sql`) and translating `Npgsql.PostgresException(SqlState=23505)` to `BusinessException(EmailExists)` inside `RegisterUser`.
|
||||
|
||||
## 1. High-Level Overview
|
||||
|
||||
**Purpose**: Full user lifecycle management — registration, credential validation, hardware binding, role changes, account enable/disable, and deletion.
|
||||
**Purpose**: Full user lifecycle management — web-user registration, credential validation, role changes, account enable/disable, deletion, plus auto-provisioning of CompanionPC device users.
|
||||
|
||||
**Architectural Pattern**: Service layer — stateless business logic operating on the Data Layer through `IDbFactory`.
|
||||
|
||||
**Upstream dependencies**: Data Layer (IDbFactory, ICache, User entity), Security & Cryptography (hashing).
|
||||
**Upstream dependencies**: Data Layer (IDbFactory, ICache, User entity), Security & Cryptography (hashing), `System.Security.Cryptography.RandomNumberGenerator` (device password entropy).
|
||||
|
||||
**Downstream consumers**: Admin API (endpoint handlers), Authentication (GetByEmail).
|
||||
|
||||
@@ -16,18 +18,19 @@
|
||||
|
||||
| Method | Input | Output | Async | Error Types |
|
||||
|--------|-------|--------|-------|-------------|
|
||||
| `RegisterUser` | `RegisterUserRequest, CancellationToken` | void | Yes | `BusinessException(EmailExists)` |
|
||||
| `ValidateUser` | `LoginRequest, CancellationToken` | `User` | Yes | `BusinessException(NoEmailFound, WrongPassword)` |
|
||||
| `RegisterUser` | `RegisterUserRequest, CancellationToken` | void | Yes | `BusinessException(EmailExists)` — translated from `PostgresException(23505)` after the F-3 hardening |
|
||||
| `RegisterDevice` | `CancellationToken` | `RegisterDeviceResponse` | Yes | `BusinessException(EmailExists)` (propagated from `RegisterUser`) — added by AZ-196, refactored post-audit to call `RegisterUser` end-to-end |
|
||||
| `ValidateUser` | `LoginRequest, CancellationToken` | `User` | Yes | `BusinessException(NoEmailFound, WrongPassword, UserDisabled)` |
|
||||
| `GetByEmail` | `string? email, CancellationToken` | `User?` | Yes | `ArgumentNullException` |
|
||||
| `UpdateHardware` | `string email, string? hardware, CancellationToken` | void | Yes | None |
|
||||
| `UpdateQueueOffsets` | `string email, UserQueueOffsets, CancellationToken` | void | Yes | None |
|
||||
| `GetUsers` | `string? searchEmail, RoleEnum? searchRole, CancellationToken` | `IEnumerable<User>` | Yes | None |
|
||||
| `CheckHardwareHash` | `User, string hardware, CancellationToken` | `string` (hash) | Yes | `BusinessException(HardwareIdMismatch)` |
|
||||
| `ChangeRole` | `string email, RoleEnum, CancellationToken` | void | Yes | None |
|
||||
| `SetEnableStatus` | `string email, bool, CancellationToken` | void | Yes | None |
|
||||
| `RemoveUser` | `string email, CancellationToken` | void | Yes | None |
|
||||
|
||||
**Input DTOs**:
|
||||
**Removed by AZ-197**: `UpdateHardware`, `CheckHardwareHash`, and the private `UpdateLastLoginDate` helper.
|
||||
|
||||
**Input / Output DTOs**:
|
||||
```
|
||||
RegisterUserRequest:
|
||||
Email: string (required) — validated: min 8 chars, valid email format
|
||||
@@ -38,9 +41,10 @@ LoginRequest:
|
||||
Email: string (required)
|
||||
Password: string (required)
|
||||
|
||||
SetHWRequest:
|
||||
Email: string (required, validated: not empty)
|
||||
Hardware: string? (optional — null clears hardware)
|
||||
RegisterDeviceResponse (AZ-196):
|
||||
Serial: string ("azj-NNNN", zero-padded)
|
||||
Email: string ("azj-NNNN@azaion.com")
|
||||
Password: string (32-char hex, plaintext, exposed exactly once)
|
||||
|
||||
SetUserQueueOffsetsRequest:
|
||||
Email: string (required)
|
||||
@@ -67,7 +71,7 @@ N/A — exposed through Admin API component.
|
||||
|
||||
| Data | Cache Type | TTL | Invalidation |
|
||||
|------|-----------|-----|-------------|
|
||||
| User by email | In-memory (via ICache) | 4 hours | After UpdateHardware, UpdateQueueOffsets, CheckHardwareHash (first login) |
|
||||
| User by email | In-memory (via ICache) | 4 hours | After `UpdateQueueOffsets` (only — `UpdateHardware` / `CheckHardwareHash` invalidations are gone with AZ-197) |
|
||||
|
||||
## 5. Implementation Details
|
||||
|
||||
@@ -89,20 +93,21 @@ N/A — exposed through Admin API component.
|
||||
|
||||
| Helper | Purpose | Used By |
|
||||
|--------|---------|---------|
|
||||
| `Security.ToHash` | Password hashing (SHA-384) | RegisterUser, ValidateUser |
|
||||
| `Security.GetHWHash` | Hardware fingerprint hashing | CheckHardwareHash |
|
||||
| `Security.ToHash` | Password hashing (SHA-384) | RegisterUser, RegisterDevice, ValidateUser |
|
||||
| `RandomNumberGenerator.GetBytes(16)` + `Convert.ToHexString` | 32-char hex device password | RegisterDevice |
|
||||
| `QueryableExtensions.WhereIf` | Conditional LINQ filters | GetUsers |
|
||||
|
||||
## 7. Caveats & Edge Cases
|
||||
|
||||
**Known limitations**:
|
||||
- No pagination on `GetUsers` — returns all matching users.
|
||||
- `CheckHardwareHash` auto-stores hardware on first access (no explicit admin approval step).
|
||||
- `RemoveUser` is a hard delete, not soft delete.
|
||||
- `RegisterDevice` returns the plaintext password to the caller exactly once; if the provisioning script loses it, the device must be re-registered.
|
||||
- The `User.Hardware` column is left in place but unused (AZ-197 chose to leave the column nullable rather than ship a migration).
|
||||
|
||||
**Potential race conditions**:
|
||||
- Concurrent `RegisterUser` calls with the same email: both could pass the existence check before insert. Mitigated by database unique constraint on email (if one exists).
|
||||
- `CheckHardwareHash` first-login path: concurrent requests could trigger multiple hardware updates.
|
||||
- Concurrent `RegisterDevice` calls: both could read the same "most recent CompanionPC" row and try to claim the same `azj-NNNN` serial. Mitigated by the `users.email` unique constraint — the loser will fail the insert. (Out of cycle-1 scope: a sequence-based serial allocator would eliminate the retry.)
|
||||
|
||||
**Performance bottlenecks**:
|
||||
- `GetUsers` loads full user objects including `UserConfig` JSON; for large user bases, projection would be more efficient.
|
||||
@@ -123,5 +128,7 @@ No explicit logging in UserService.
|
||||
- `Services/UserService`
|
||||
- `Common/Requests/LoginRequest`
|
||||
- `Common/Requests/RegisterUserRequest`
|
||||
- `Common/Requests/SetHWRequest`
|
||||
- `Common/Requests/RegisterDeviceResponse` *(added cycle 1, AZ-196)*
|
||||
- `Common/Requests/SetUserQueueOffsetsRequest`
|
||||
|
||||
**Removed cycle 1 (AZ-197)**: `Common/Requests/SetHWRequest`
|
||||
|
||||
Reference in New Issue
Block a user