mirror of
https://github.com/azaion/admin.git
synced 2026-04-22 06:46:33 +00:00
[AZ-189] Update coding and testing rules for clarity and consistency
- Revised coding standards to emphasize readability, meaningful comments, and test verification. - Adjusted test coverage thresholds to 75% for business logic and clarified expectations for test scenarios. - Enhanced guidelines for handling skipped tests, emphasizing the need for investigation and resolution. - Updated commit message format and length requirements for better adherence to Git conventions. Made-with: Cursor
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
# Dependencies Table
|
||||
|
||||
**Date**: 2026-04-16
|
||||
**Total Tasks**: 7
|
||||
**Total Complexity Points**: 29
|
||||
**Total Tasks**: 10
|
||||
**Total Complexity Points**: 37
|
||||
|
||||
| Task | Name | Complexity | Dependencies | Epic |
|
||||
|------|------|-----------|-------------|------|
|
||||
@@ -13,3 +13,6 @@
|
||||
| AZ-193 | resource_tests | 5 | AZ-189, AZ-190, AZ-192 | AZ-188 |
|
||||
| AZ-194 | security_tests | 3 | AZ-189, AZ-190 | AZ-188 |
|
||||
| AZ-195 | resilience_perf_tests | 5 | AZ-189, AZ-190 | AZ-188 |
|
||||
| AZ-183 | resources_table_update_api | 3 | None | AZ-181 |
|
||||
| AZ-196 | register_device_endpoint | 2 | None | AZ-181 |
|
||||
| AZ-197 | remove_hardware_id | 3 | None | AZ-181 |
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# Resources Table & Update Check API
|
||||
|
||||
**Task**: AZ-183_resources_table_update_api
|
||||
**Name**: Resources Table & Update Check API
|
||||
**Description**: Add Resources table to admin API PostgreSQL DB and implement POST /get-update endpoint for fleet OTA updates
|
||||
**Complexity**: 3 points
|
||||
**Dependencies**: None
|
||||
**Component**: Admin API
|
||||
**Tracker**: AZ-183
|
||||
**Epic**: AZ-181
|
||||
|
||||
## Problem
|
||||
|
||||
The fleet update system needs a server-side component that tracks published artifact versions and tells devices what needs updating. CI/CD publishes encrypted artifacts to CDN; the server must store metadata (version, URL, hash, encryption key) and serve it to devices on request.
|
||||
|
||||
## Outcome
|
||||
|
||||
- Resources table stores per-artifact metadata populated by CI/CD
|
||||
- Devices call POST /get-update with their current versions and get back only what's newer
|
||||
- Server-side memory cache handles 2000+ devices polling every 5 minutes without DB pressure
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
- Resources table migration (resource_name, dev_stage, architecture, version, cdn_url, sha256, encryption_key, size_bytes, created_at)
|
||||
- POST /get-update endpoint: accepts device's current versions + architecture + dev_stage, returns only newer resources
|
||||
- Server-side memory cache invalidated on CI/CD publish
|
||||
- Internal endpoint or direct DB write for CI/CD to publish new resource versions
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
**AC-1: Resources table created**
|
||||
Given the admin API database
|
||||
When the migration runs
|
||||
Then the Resources table exists with all required columns
|
||||
|
||||
**AC-2: Update check returns newer resources**
|
||||
Given Resources table has annotations version 2026-04-13
|
||||
When device sends POST /get-update with annotations version 2026-02-25
|
||||
Then response includes annotations with version, cdn_url, sha256, encryption_key, size_bytes
|
||||
|
||||
**AC-3: Current device gets empty response**
|
||||
Given device already has the latest version of all resources
|
||||
When POST /get-update is called
|
||||
Then response is an empty array
|
||||
|
||||
**AC-4: Memory cache avoids repeated DB queries**
|
||||
Given 2000 devices polling every 5 minutes
|
||||
When POST /get-update is called repeatedly
|
||||
Then the latest versions are served from memory cache, not from DB on every request
|
||||
|
||||
**AC-5: Cache invalidated on publish**
|
||||
Given a new resource version is published via CI/CD
|
||||
When the publish endpoint/function completes
|
||||
Then the next POST /get-update call returns the new version
|
||||
|
||||
## Constraints
|
||||
|
||||
- Must integrate with existing admin API (linq2db + PostgreSQL)
|
||||
- encryption_key column must be stored securely (encrypted at rest in DB or via application-level encryption)
|
||||
- Response must include encryption_key only over HTTPS with valid JWT
|
||||
@@ -0,0 +1,95 @@
|
||||
# Register Device Endpoint
|
||||
|
||||
**Task**: AZ-196_register_device_endpoint
|
||||
**Name**: POST /devices endpoint for auto device registration
|
||||
**Description**: Add POST /devices endpoint to admin API that auto-generates device serial, email, and password for CompanionPC users
|
||||
**Complexity**: 2 points
|
||||
**Dependencies**: None
|
||||
**Component**: Admin API
|
||||
**Tracker**: AZ-196
|
||||
**Epic**: AZ-181
|
||||
|
||||
## Problem
|
||||
|
||||
During Jetson manufacturing, each device needs a unique CompanionPC identity (serial, email, password). Currently the provisioning script generates the email client-side and calls POST /users. The serial/email format should be server-controlled so the admin API is the single source of truth for device numbering.
|
||||
|
||||
## Outcome
|
||||
|
||||
- Single POST /devices endpoint that requires no request body
|
||||
- Server auto-assigns the next sequential serial (azj-0000, azj-0001, ...)
|
||||
- Returns plaintext credentials so the provisioning script can embed them in device.conf
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
- `RegisterDeviceResponse` DTO in `Azaion.Common/Requests/` with `Serial`, `Email`, `Password` fields
|
||||
- `RegisterDevice` method on `IUserService` / `UserService`
|
||||
- `POST /devices` endpoint in `Program.cs` with `RequireAuthorization(apiAdminPolicy)`
|
||||
- Sequential serial assignment based on most recent CompanionPC user
|
||||
|
||||
### Excluded
|
||||
|
||||
- Changes to the provisioning shell script (handled in loader repo)
|
||||
- Removing old POST /users endpoint (still used for non-device users)
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Serial number logic
|
||||
|
||||
Email format: `azj-NNNN@azaion.com` where NNNN is zero-padded to 4 digits.
|
||||
|
||||
Constants at the top of `UserService`:
|
||||
|
||||
```csharp
|
||||
private const int SerialNumberStart = 4;
|
||||
private const int SerialNumberLength = 4;
|
||||
```
|
||||
|
||||
`RegisterDevice` implementation:
|
||||
|
||||
1. Query the single most recent CompanionPC user: `WHERE role = 'CompanionPC' ORDER BY created_at DESC LIMIT 1`
|
||||
2. If none found, next number is 0000; otherwise extract via `Substring(SerialNumberStart, SerialNumberLength)`, parse, increment
|
||||
3. Generate email `azj-{number:D4}@azaion.com`
|
||||
4. Generate random 32-char hex password: `Convert.ToHexString(RandomNumberGenerator.GetBytes(16)).ToLower()`
|
||||
5. Insert User with `Role = CompanionPC`, `IsEnabled = true`, password hashed via `ToHash()`
|
||||
6. Return `RegisterDeviceResponse { Serial, Email, Password (plaintext) }`
|
||||
|
||||
### Endpoint
|
||||
|
||||
```csharp
|
||||
app.MapPost("/devices",
|
||||
async (IUserService userService, CancellationToken cancellationToken)
|
||||
=> await userService.RegisterDevice(cancellationToken))
|
||||
.RequireAuthorization(apiAdminPolicy)
|
||||
.WithSummary("Creates a new device");
|
||||
```
|
||||
|
||||
No request body required.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
**AC-1: First device gets serial azj-0000**
|
||||
Given no CompanionPC users exist in the database
|
||||
When POST /devices is called with a valid ApiAdmin JWT
|
||||
Then the response contains `serial: "azj-0000"`, `email: "azj-0000@azaion.com"`, and a 32-char hex password
|
||||
|
||||
**AC-2: Sequential numbering**
|
||||
Given azj-0000 already exists
|
||||
When POST /devices is called again
|
||||
Then the response contains `serial: "azj-0001"`
|
||||
|
||||
**AC-3: User persisted with correct role**
|
||||
Given POST /devices returned successfully
|
||||
When the users table is queried
|
||||
Then a user exists with the returned email, Role=CompanionPC, IsEnabled=true
|
||||
|
||||
**AC-4: Password is hashed in DB**
|
||||
Given POST /devices returned a plaintext password
|
||||
When the users table is inspected
|
||||
Then PasswordHash contains the SHA-384 hash of the plaintext password, not the plaintext itself
|
||||
|
||||
**AC-5: Requires ApiAdmin authorization**
|
||||
Given a request without a JWT or with a non-ApiAdmin JWT
|
||||
When POST /devices is called
|
||||
Then 401 or 403 is returned
|
||||
@@ -0,0 +1,70 @@
|
||||
# Remove Hardware ID Binding
|
||||
|
||||
**Task**: AZ-197_remove_hardware_id
|
||||
**Name**: Remove hardware ID binding from resource flow
|
||||
**Description**: Remove CheckHardwareHash, UpdateHardware, HardwareService and simplify API encryption key derivation. Sealed Jetsons eliminate the credential-reuse threat this was protecting against.
|
||||
**Complexity**: 3 points
|
||||
**Dependencies**: None
|
||||
**Component**: Admin API, Loader
|
||||
**Tracker**: AZ-197
|
||||
**Epic**: AZ-181
|
||||
|
||||
## Problem
|
||||
|
||||
The `Hardware` field on `User` and the `CheckHardwareHash` flow were designed to bind a user account to a specific physical machine, preventing credential reuse across machines when users had desktop installers. With sealed Jetsons (secure boot, fTPM, no user filesystem access, no installers distributed), this threat no longer exists. The hardware binding adds unnecessary complexity and failure modes (HardwareIdMismatch on drive replacement, etc.).
|
||||
|
||||
## Outcome
|
||||
|
||||
- Simpler resource download flow without hardware fingerprint requirement
|
||||
- Simpler API encryption key derivation (email + password only)
|
||||
- Removal of dead code paths related to hardware binding
|
||||
- Fewer failure modes in production
|
||||
|
||||
## Scope
|
||||
|
||||
### Admin API changes
|
||||
|
||||
- Remove `CheckHardwareHash` and `UpdateHardware` from `IUserService` / `UserService`
|
||||
- Remove `PUT /users/hardware/set` endpoint from `Program.cs`
|
||||
- Simplify `POST /resources/get/{dataFolder}`: remove `request.Hardware` parameter, derive encryption key without hardware hash
|
||||
- Simplify `POST /resources/check`: remove hardware check entirely (or remove the endpoint if unused)
|
||||
- Update `Security.GetApiEncryptionKey` to not require `hardwareHash` parameter
|
||||
- Remove or deprecate `Security.GetHWHash`
|
||||
- Leave `User.Hardware` column nullable in DB (no migration needed, just stop writing/reading it)
|
||||
- Remove `SetHWRequest` DTO
|
||||
- Remove `HardwareIdMismatch` and `BadHardware` from `ExceptionEnum`
|
||||
|
||||
### Loader client changes
|
||||
|
||||
- Remove `HardwareService` class (`hardware_service.pyx`, `hardware_service.pxd`)
|
||||
- Update `api_client.pyx` `load_bytes`: stop gathering hardware info, stop sending `hardware` field in resource request
|
||||
- Update `security.pyx` `get_api_encryption_key`: remove `hardware_hash` parameter
|
||||
- Update `security_provider.py`, `tpm_security_provider.py`, `legacy_security_provider.py`: remove `get_hw_hash` and update `get_api_encryption_key` signature
|
||||
- Update `GetResourceRequest` validator to not require Hardware field
|
||||
|
||||
### Excluded
|
||||
|
||||
- Database migration to drop the `hardware` column (leave nullable, stop using it)
|
||||
- Changes to user registration or login flow
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
**AC-1: Resource download works without hardware**
|
||||
Given a provisioned device with valid email and password
|
||||
When the loader calls POST /resources/get without a hardware field
|
||||
Then the resource is returned and can be decrypted using email + password only
|
||||
|
||||
**AC-2: No hardware endpoints remain**
|
||||
Given the updated admin API
|
||||
When PUT /users/hardware/set is called
|
||||
Then 404 is returned
|
||||
|
||||
**AC-3: Encryption key derivation is simplified**
|
||||
Given the updated Security class
|
||||
When GetApiEncryptionKey is called
|
||||
Then it derives the key from email + password only (no hardware hash)
|
||||
|
||||
**AC-4: HardwareService removed from loader**
|
||||
Given the updated loader codebase
|
||||
When the build is run
|
||||
Then it compiles without hardware_service.pyx/pxd
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
## Current Step
|
||||
flow: existing-code
|
||||
step: 6
|
||||
name: Run Tests
|
||||
status: completed
|
||||
sub_step: 0
|
||||
step: 9
|
||||
name: Implement
|
||||
status: in_progress
|
||||
sub_step: 1 — Parse and compute batches
|
||||
retry_count: 0
|
||||
|
||||
Reference in New Issue
Block a user