Initial commit: scadaproj umbrella — sister-project index, auth component normalization (design + GAPS), and the built ZB.MOM.WW.Auth shared library (0.1.0, flattened in).
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
# Auth — gaps & adoption backlog
|
||||
|
||||
Divergence of each project from [`spec/SPEC.md`](spec/SPEC.md), and the ordered backlog to
|
||||
reach the shared `ZB.MOM.WW.Auth` library. Status legend: ⛔ gap · 🟡 partial · ✅ matches.
|
||||
|
||||
## Divergence vs spec
|
||||
|
||||
### §1 LDAP config schema
|
||||
|
||||
| Spec key | OtOpcUa | MxAccessGateway | ScadaBridge |
|
||||
|---|---|---|---|
|
||||
| Section nesting | 🟡 `Authentication:Ldap` (nested) | ✅ `MxGateway:Ldap` (nested) | ⛔ flat `ScadaBridge:Security:Ldap*` |
|
||||
| `Transport` enum | ⛔ `UseTls` bool | ⛔ `UseTls` bool | ✅ `LdapTransport` enum |
|
||||
| `AllowInsecure` | 🟡 `AllowInsecureLdap` | 🟡 `AllowInsecureLdap` | 🟡 `AllowInsecureLdap` (rename) |
|
||||
| `UserNameAttribute` | ✅ `UserNameAttribute` | ✅ `UserNameAttribute` | ⛔ `LdapUserIdAttribute` |
|
||||
| `GroupAttribute` | ✅ `memberOf` | ✅ `memberOf` | 🟡 `LdapGroupAttribute` (rename) |
|
||||
| dev `SearchBase` | `dc=lmxopcua,dc=local` | `dc=lmxopcua,dc=local` | `dc=scadabridge,dc=local` |
|
||||
|
||||
→ **Gap A1:** adopt the `Transport` enum in OtOpcUa + gateway (replace `UseTls`).
|
||||
→ **Gap A2:** ScadaBridge: nest keys + rename `LdapUserIdAttribute`→`UserNameAttribute`, `LdapGroupAttribute`→`GroupAttribute`.
|
||||
→ **Gap A3:** unify the **dev base DN** (`dc=lmxopcua` vs `dc=scadabridge`) — pick one shared GLAuth base.
|
||||
|
||||
### §2 bind-then-search
|
||||
|
||||
All three do bind-then-search; ScadaBridge has the most complete hygiene (RFC-4514 + filter
|
||||
escaping, per-op timeout, fail-closed on group lookup, username trim, service-account-bind
|
||||
distinction). 🟡 OtOpcUa/gateway: confirm each has filter escaping + fail-closed-on-group-lookup
|
||||
parity. → **Gap B1:** make ScadaBridge's hygiene the shared baseline; backfill any missing checks.
|
||||
|
||||
### §3 group→role mapping
|
||||
|
||||
⛔ **Mechanism split:** OtOpcUa + gateway map in **config** (`GroupToRole`); ScadaBridge maps in
|
||||
the **database** (`LdapGroupMapping`). → **Gap C1:** `IGroupRoleMapper<CanonicalRole>` must support both
|
||||
backings; ship a config-backed and a DB/delegate-backed mapper.
|
||||
|
||||
⛔ **Role vocabulary now standardized** to the canonical six ([`spec/CANONICAL-ROLES.md`](spec/CANONICAL-ROLES.md));
|
||||
native enforcement stays per-project. → **Gap C2:** implement the `canonical → native` expansion in each
|
||||
project. ⚠ Removing Auditor collapses ScadaBridge `AuditReadOnly`→Viewer and `Audit`→Administrator,
|
||||
losing its auditor/admin separation-of-duties (accepted). OtOpcUa lacks a first-class `Deployer`
|
||||
(publish ⊂ `FleetAdmin`); ScadaBridge has no `Operator`/`Engineer`; mxaccessgw no `Designer`/`Deployer` —
|
||||
each project assigns only the applicable subset.
|
||||
|
||||
### §4 API-key contract
|
||||
|
||||
| | OtOpcUa | MxAccessGateway | ScadaBridge |
|
||||
|---|---|---|---|
|
||||
| Has API keys | n/a (OPC UA transport security) | ✅ `mxgw_…`, SQLite, scopes + constraints | 🟡 `X-API-Key`, per-method approval (Inbound API only) |
|
||||
| Peppered HMAC-SHA256 | — | ✅ | ✅ |
|
||||
| Constant-time compare | — | ✅ | ✅ |
|
||||
| Token format `<prefix>_<id>_<secret>` | — | ✅ | ⛔ raw `X-API-Key` (no keyId/prefix structure) |
|
||||
| Audit log | — | ✅ append-only | 🟡 (verify) |
|
||||
|
||||
→ **Gap D1:** extract mxaccessgw's pipeline as `ZB.MOM.WW.Auth.ApiKeys`.
|
||||
→ **Gap D2:** ScadaBridge Inbound API adopts it; reconcile token format and model "per-method approval" as the opaque constraint policy.
|
||||
|
||||
### §5 cookie / claim conventions
|
||||
|
||||
⛔ Cookie names differ (`MxGatewayDashboard` vs `ZB.MOM.WW.ScadaBridge.Auth` vs OtOpcUa control-plane cookie); claim-type conventions differ. → **Gap E1:** define canonical claim types + cookie defaults in `ZB.MOM.WW.Auth.AspNetCore`; each app keeps its own cookie *name* but shares attributes/claims.
|
||||
|
||||
### §6 dev / secrets
|
||||
|
||||
✅ All never-log-secrets and pepper-external. 🟡 escape-hatch flag names vary. Covered by A3 (base DN) + E.
|
||||
|
||||
## Adoption backlog (ordered)
|
||||
|
||||
| # | Item | Projects | Priority | Effort | Risk | Notes |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 1 | Extract `ZB.MOM.WW.Auth.Ldap` from ScadaBridge's hardened impl | all 3 | High | M | Med | security-sensitive; needs strong tests before cutover |
|
||||
| 2 | Extract `ZB.MOM.WW.Auth.ApiKeys` from mxaccessgw Model A | gw, SB | High | M | Med | gateway adopts first (it's the source), then SB |
|
||||
| 3 | `IGroupRoleMapper<TRole>` seam + config & DB mappers (Gap C1) | all 3 | High | S | Low | unblocks per-project role retention |
|
||||
| 4 | Config migration to §1 schema (Gaps A1–A2) | all 3 | Med | S | Low | mechanical; do with #1 |
|
||||
| 5 | `ZB.MOM.WW.Auth.AspNetCore` claims/cookie conventions (Gap E1) | all 3 (UIs) | Med | S | Low | incl. OtOpcUa Blazor Admin UI control-plane |
|
||||
| 6 | Unify dev GLAuth base DN (Gap A3) | all 3 | Low | S | Low | dev-only; touches fixtures/infra |
|
||||
| 7 | Decide shared JWT/refresh helper vs per-project | SB (+?) | Low | S | Low | only if a 2nd project wants the same |
|
||||
| 8 | Adopt canonical roles: `canonical → native` mapping per project (Gap C2) | all 3 | Med | M | Med | governance (assign canonical role per LDAP group org-wide) + each project's expansion; SB audit roles collapse |
|
||||
|
||||
**Sequencing:** #3 first (cheap, unblocks), then #1 and #2 in parallel (independent libraries),
|
||||
then #4–#5 alongside cutover, then #6–#7 as cleanup. Each extraction lands behind tests in the
|
||||
source project before any consumer migrates. This stays consistent with the repos' loose coupling:
|
||||
adoption is opt-in per project, one consumer version-bump at a time.
|
||||
|
||||
## Decisions still open
|
||||
- Shared dev base DN value (A3).
|
||||
- Whether constraints stay opaque `object?` or get a small `IConstraintPolicy` (shared-contract Q3).
|
||||
- Shared JWT/refresh helper or not (#7).
|
||||
Reference in New Issue
Block a user