- 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>
6.4 KiB
User Management
Cycle 1 (2026-05-13) note — hardware-binding methods (
UpdateHardware,CheckHardwareHash) andSetHWRequestwere removed by AZ-197; theValidateUsererror set now includesUserDisabled;RegisterDevicewas added by AZ-196 to back the newPOST /devicesendpoint. Post-cycle-1 (security audit F-3):RegisterDevicenow reusesRegisterUserfor the row insert; the duplicate-row race was closed by adding a UNIQUE INDEX onusers.email(env/db/06_users_email_unique.sql) and translatingNpgsql.PostgresException(SqlState=23505)toBusinessException(EmailExists)insideRegisterUser.
1. High-Level Overview
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), System.Security.Cryptography.RandomNumberGenerator (device password entropy).
Downstream consumers: Admin API (endpoint handlers), Authentication (GetByEmail).
2. Internal Interfaces
Interface: IUserService
| Method | Input | Output | Async | Error Types |
|---|---|---|---|---|
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 |
UpdateQueueOffsets |
string email, UserQueueOffsets, CancellationToken |
void | Yes | None |
GetUsers |
string? searchEmail, RoleEnum? searchRole, CancellationToken |
IEnumerable<User> |
Yes | None |
ChangeRole |
string email, RoleEnum, CancellationToken |
void | Yes | None |
SetEnableStatus |
string email, bool, CancellationToken |
void | Yes | None |
RemoveUser |
string email, CancellationToken |
void | Yes | None |
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
Password: string (required) — validated: min 8 chars
Role: RoleEnum (required)
LoginRequest:
Email: string (required)
Password: string (required)
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)
Offsets: UserQueueOffsets (required)
3. External API Specification
N/A — exposed through Admin API component.
4. Data Access Patterns
Queries
| Query | Frequency | Hot Path | Index Needed |
|---|---|---|---|
| User by email (cached) | High | Yes | Yes |
| User list with filters | Medium | No | No |
| User insert (registration) | Low | No | No |
| User update (hardware, role, config, status) | Medium | No | No |
| User delete | Low | No | No |
Caching Strategy
| Data | Cache Type | TTL | Invalidation |
|---|---|---|---|
| User by email | In-memory (via ICache) | 4 hours | After UpdateQueueOffsets (only — UpdateHardware / CheckHardwareHash invalidations are gone with AZ-197) |
5. Implementation Details
State Management: Stateless — all state in PostgreSQL + in-memory cache.
Key Dependencies:
| Library | Version | Purpose |
|---|---|---|
| FluentValidation | 11.10.0 | Request validation (auto-discovered) |
Error Handling Strategy:
- Domain errors thrown as
BusinessExceptionwith specificExceptionEnumcodes. GetByEmailthrowsArgumentNullExceptionfor null/whitespace email.- Database errors propagate from
IDbFactory. - Write operations use
RunAdmin(admin connection); reads useRun(reader connection).
6. Extensions and Helpers
| Helper | Purpose | Used By |
|---|---|---|
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. RemoveUseris a hard delete, not soft delete.RegisterDevicereturns the plaintext password to the caller exactly once; if the provisioning script loses it, the device must be re-registered.- The
User.Hardwarecolumn is left in place but unused (AZ-197 chose to leave the column nullable rather than ship a migration).
Potential race conditions:
- Concurrent
RegisterUsercalls with the same email: both could pass the existence check before insert. Mitigated by database unique constraint on email (if one exists). - Concurrent
RegisterDevicecalls: both could read the same "most recent CompanionPC" row and try to claim the sameazj-NNNNserial. Mitigated by theusers.emailunique constraint — the loser will fail the insert. (Out of cycle-1 scope: a sequence-based serial allocator would eliminate the retry.)
Performance bottlenecks:
GetUsersloads full user objects includingUserConfigJSON; for large user bases, projection would be more efficient.
8. Dependency Graph
Must be implemented after: Data Layer, Security & Cryptography.
Can be implemented in parallel with: Resource Management.
Blocks: Authentication (uses GetByEmail), Admin API.
9. Logging Strategy
No explicit logging in UserService.
Modules Covered
Services/UserServiceCommon/Requests/LoginRequestCommon/Requests/RegisterUserRequestCommon/Requests/RegisterDeviceResponse(added cycle 1, AZ-196)Common/Requests/SetUserQueueOffsetsRequest
Removed cycle 1 (AZ-197): Common/Requests/SetHWRequest