plan(phase1): ScadaBridge re-arch discovered architecture (CentralUI direct-repo + TransportExport) + C1-C5 decomposition + transport=exclude-keys
This commit is contained in:
@@ -189,11 +189,48 @@ keeps the build green at each step.
|
||||
secret (proven by test asserting `SecretHash.SequenceEqual` + unchanged `last_used_utc`). This is what lets C/D edit a key's
|
||||
method-scopes and toggle enabled WITHOUT re-issuing the token. **ScadaBridge must re-pin Auth packages 0.1.2 → 0.1.3.**
|
||||
- **C (management), D (CentralUI), E (retire SQL Server ApiKey + ApiMethod.ApprovedApiKeyIds migration + runbook/CHANGELOG)
|
||||
— IN PROGRESS (C next).** Mapping for C: `CreateApiKeyCommand` → `CreateKeyAsync` (keyId = `Guid.NewGuid().ToString("N")`,
|
||||
— IN PROGRESS.** Mapping: `CreateApiKeyCommand` → `CreateKeyAsync` (keyId = `Guid.NewGuid().ToString("N")`,
|
||||
DisplayName = name, scopes = `--methods`); `ListApiKeysCommand` → `ListKeysAsync` (enabled = `RevokedUtc is null`);
|
||||
`UpdateApiKeyCommand(IsEnabled)` → `SetEnabledAsync`; new set-scopes path → `SetScopesAsync`; `DeleteApiKeyCommand` →
|
||||
revoke-then-`DeleteKeyAsync`. All management message keys switch `int ApiKeyId` → `string KeyId`.
|
||||
|
||||
### Discovered architecture (CentralUI Explore, 2026-06-02) — expands C/D/E
|
||||
Two facts the original A–E spec missed:
|
||||
1. **CentralUI bypasses the ManagementActor.** `Components/Pages/Admin/ApiKeys.razor`, `ApiKeyForm.razor`, and
|
||||
`Components/Pages/Design/ApiMethodForm.razor` call `IInboundApiRepository` (SQL Server EF) **directly** — they do NOT
|
||||
send the `CreateApiKeyCommand`/etc. management messages. So there are **two** management entry points to rewire
|
||||
(CLI→ManagementActor uses the messages; CentralUI→repository uses the entities). Decoupling: introduce one app-side
|
||||
**`IInboundApiKeyAdmin` seam** over the library `ApiKeyAdminCommands`, and route BOTH CLI and CentralUI through it
|
||||
(DRY + single audit path). The message-contract change (int→string) touches only CLI+ManagementActor; the
|
||||
entity/repository change (`ApiKey.Id`, `ApiMethod.ApprovedApiKeyIds`) touches CentralUI + TransportExport.
|
||||
2. **TransportExport couples API keys + methods into config export/import** (`Components/Pages/Design/TransportExport.razor`
|
||||
+ `.razor.cs`, `HashSet<int>` selections, `ExportSelection`). With keys now in the library SQLite store (per-env pepper,
|
||||
secret-once), a key can't be exported/re-imported usefully. **Decision (user, 2026-06-02): EXCLUDE inbound API keys from
|
||||
transport — export API methods only; keys are re-created + method-scopes re-granted per environment.**
|
||||
|
||||
CentralUI blast radius (string keyId + scopes replace int Id + ApprovedApiKeyIds CSV): `Admin/ApiKeys.razor`,
|
||||
`Admin/ApiKeyForm.razor`, `Design/ApiMethodForm.razor` (approved-keys ↔ key-scopes), `Design/TransportExport.razor(.cs)`,
|
||||
`Design/ExternalSystems.razor` (uses method `int` id — methods STAY int in SQL Server, so unaffected for keys),
|
||||
`Dashboard.razor` (key count), test `Admin/ApiKeyFormAuditDrillinTests.cs`.
|
||||
|
||||
### C/D/E decomposition — 5 reviewed green sub-commits (user: "coordinated multi-commit now", 2026-06-02)
|
||||
- **C1** — re-pin ScadaBridge Auth 0.1.2→0.1.3; add app-side `IInboundApiKeyAdmin` seam (string-keyId model:
|
||||
Create(name,methods)→(keyId,token) / List / SetEnabled / SetMethods / Delete[=revoke+delete] / GetMethodsForKey /
|
||||
GetKeysForMethod) over the library facade; register `ApiKeyAdminCommands` + the seam in Host **and** CentralUI DI; seam
|
||||
unit tests. **Purely additive — build green.**
|
||||
- **C2** — Commons `Messages/Management/SecurityCommands.cs` contracts int→string keyId + add `Methods` + new
|
||||
`SetApiKeyMethodsCommand`; rewire ManagementActor handlers + CLI `security api-key` onto the seam; update ManagementActor
|
||||
tests. (CentralUI unaffected — it doesn't use these messages.)
|
||||
- **C3** — CentralUI `ApiKeys.razor`/`ApiKeyForm.razor`/`ApiMethodForm.razor` (+ Dashboard count) off `IInboundApiRepository`-
|
||||
for-keys onto the seam; string keyId; method-scope editing replaces `ApprovedApiKeyIds`; update bUnit test. (Methods stay
|
||||
in SQL Server; just stop using the `ApprovedApiKeyIds` column — dropped in C5.)
|
||||
- **C4** — TransportExport: remove API-key selection/export (methods-only); drop key `HashSet<int>` + `ExportSelection` keys;
|
||||
tests.
|
||||
- **C5 (=E)** — retire SQL Server `ApiKey` entity + DbContext reg + `IInboundApiRepository` key methods +
|
||||
`GetApprovedKeysForMethodAsync`; drop `ApiMethod.ApprovedApiKeyIds`; EF migration (drop ApiKeys table + column); delete
|
||||
residual `ApiKeyValidator`/`ApiKeyHasher`; runbook + CHANGELOG (breaking: re-issue keys, `X-API-Key`→`Authorization: Bearer`);
|
||||
full build+test sweep.
|
||||
|
||||
## Resolved decisions (2026-06-02)
|
||||
|
||||
- **Decision A — ScadaBridge inbound API keys depth → (a) FULL ADOPT.** Re-architect inbound-API auth to the
|
||||
|
||||
@@ -30,7 +30,14 @@
|
||||
{"id": 29, "subject": "Task 2.5: ScadaBridge rename→IAuditRedactor + AuditOutcome (#3) [high-risk]", "status": "pending", "blockedBy": [25]},
|
||||
|
||||
{"id": 30, "subject": "Task 3.1: Introduce IAuditActorAccessor seam", "status": "pending", "blockedBy": [9]},
|
||||
{"id": 31, "subject": "Task 3.2-3.4: Wire emit sites to Auth principal (#4)", "status": "pending", "blockedBy": [30]}
|
||||
{"id": 31, "subject": "Task 3.2-3.4: Wire emit sites to Auth principal (#4)", "status": "pending", "blockedBy": [30]},
|
||||
|
||||
{"id": 32, "subject": "Task 1.3-L: Extend Auth.ApiKeys admin store (SetScopes/SetEnabled) -> lib 0.1.3 (PUBLISHED)", "status": "completed", "blockedBy": []},
|
||||
{"id": 33, "subject": "Task 1.3-C1: ScadaBridge re-pin 0.1.3 + IInboundApiKeyAdmin seam (additive)", "status": "pending", "blockedBy": [32]},
|
||||
{"id": 34, "subject": "Task 1.3-C2: ManagementActor + CLI + Commons messages onto seam", "status": "pending", "blockedBy": [33]},
|
||||
{"id": 35, "subject": "Task 1.3-C3: CentralUI pages onto seam (string keyId + scopes)", "status": "pending", "blockedBy": [33]},
|
||||
{"id": 36, "subject": "Task 1.3-C4: TransportExport exclude API keys (methods-only)", "status": "pending", "blockedBy": [33, 35]},
|
||||
{"id": 37, "subject": "Task 1.3-C5 (=E): retire SQL Server ApiKey entity + EF migration + runbook", "status": "pending", "blockedBy": [34, 35, 36]}
|
||||
],
|
||||
"lastUpdated": "2026-06-02"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user