diff --git a/CLAUDE.md b/CLAUDE.md index 19038be..7c510d4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -159,7 +159,7 @@ each project's **code-verified current state**, and the **gaps** between. See | Component | Status | Goal | Design | Implementation | |---|---|---|---|---| -| Auth (login / identity / authz) | Adopted. Feed carries **`0.3.0`** (2026-09-02; 5 packages, newest `ZB.MOM.WW.Auth.ApiKeys.SqlServer`). **All four apps pin `0.3.0`** (2026-09-03, all on their default branch and pushed — and all four are DEPLOYED at those tips on DEV vd03 and QA vq03/vq04 since 2026-09-10: OtOpcUa `b8b4c18f`, mxgw `4f931e4`, HistorianGateway `d8d890c`; ScadaBridge already had it on `main` via the `inbound-api-key-shared-store` work, `e45c4af9`). `0.2.1` is a functional fix, not housekeeping — `0.1.5` cannot log in against an AD domain root at all. `FallbackServers` (0.2.0) live-gated on the redundant local GLAuth pair. Version narrative, consumer matrix, `tools/LdapProbe`: [`ZB.MOM.WW.Auth/CLAUDE.md`](ZB.MOM.WW.Auth/CLAUDE.md). | Shared `ZB.MOM.WW.Auth` lib | [`components/auth/`](components/auth/) | [`ZB.MOM.WW.Auth/`](ZB.MOM.WW.Auth/) | +| Auth (login / identity / authz) | Adopted. Feed carries **`0.4.0`** (2026-10-01; opt-in user bind) — the four apps still pin `0.3.0` (2026-09-02; 5 packages, newest `ZB.MOM.WW.Auth.ApiKeys.SqlServer`). **0.4.0 (2026-10-01) — opt-in LDAP user bind:** adds `LdapOptions.BindMode` (`ServiceAccount` default | `User`), `UserBindTemplate` and `AcceptDomainQualifiedNames`; in `User` mode `LdapAuthService` binds as the signing-in user and reads their own groups on the same connection, so no service account is needed. Bind failures map to `BadCredentials` (bad password, unusable name, or bind result 49) or `GroupLookupFailed` (zero/multiple entries or no groups after a good bind), other bind/connect/search errors fail over to the next endpoint, and `ServiceAccount` stays the default so no existing consumer needs to change. First consumer: BTDB Manager; ScadaBridge/HistorianGateway/OtOpcUa/MxAccessGateway stay on `0.3.0` until they move to AD. **All four apps pin `0.3.0`** (2026-09-03, all on their default branch and pushed — and all four are DEPLOYED at those tips on DEV vd03 and QA vq03/vq04 since 2026-09-10: OtOpcUa `b8b4c18f`, mxgw `4f931e4`, HistorianGateway `d8d890c`; ScadaBridge already had it on `main` via the `inbound-api-key-shared-store` work, `e45c4af9`). `0.2.1` is a functional fix, not housekeeping — `0.1.5` cannot log in against an AD domain root at all. `FallbackServers` (0.2.0) live-gated on the redundant local GLAuth pair. Version narrative, consumer matrix, `tools/LdapProbe`: [`ZB.MOM.WW.Auth/CLAUDE.md`](ZB.MOM.WW.Auth/CLAUDE.md). | Shared `ZB.MOM.WW.Auth` lib | [`components/auth/`](components/auth/) | [`ZB.MOM.WW.Auth/`](ZB.MOM.WW.Auth/) | | UI Theme (layout / tokens / components) | Adopted. Feed `0.2.0`–`0.4.1`; **five** consumers (the ignitionoee router joined), all on `0.4.1` and pushed (2026-08-11); Every deployed wonder box (DEV vd03 + QA vq03/vq04) runs its repo tip as of 2026-09-10, so the deployed Theme is 0.4.1 everywhere. **On bump, TRIM the local `.btn` block, don't delete it** — mxgw's carried more than sizing. Narrative: [`ZB.MOM.WW.Theme/CLAUDE.md`](ZB.MOM.WW.Theme/CLAUDE.md). | Shared `ZB.MOM.WW.Theme` RCL | [`components/ui-theme/`](components/ui-theme/) | [`ZB.MOM.WW.Theme/`](ZB.MOM.WW.Theme/) | | Health (readiness / liveness / active-node) | **Adopted** — lib `0.3.0`, all 4 apps reference it + wire `MapZbHealth`. `0.2.1`/`0.3.0` changed the active tier's behaviour (standby now answers **503**; active = oldest `Up` member via `ClusterActiveNode`, not `RoleLeader` — the two diverge after any restart), and both Akka apps' private copies were deleted in its favour (merged + pushed). Narrative: [`ZB.MOM.WW.Health/CLAUDE.md`](ZB.MOM.WW.Health/CLAUDE.md). | Shared `ZB.MOM.WW.Health` lib | [`components/health/`](components/health/) | [`ZB.MOM.WW.Health/`](ZB.MOM.WW.Health/) | | Observability (metrics / traces / logs) | **Adopted** (libs `0.1.0`, on the feed; **all 4 apps** call `AddZbTelemetry`; `AddZbSerilog` in all but ScadaBridge, which keeps its own factory by design) | Shared `ZB.MOM.WW.Telemetry` lib + `.Serilog` | [`components/observability/`](components/observability/) | [`ZB.MOM.WW.Telemetry/`](ZB.MOM.WW.Telemetry/) | diff --git a/ZB.MOM.WW.Auth/Directory.Build.props b/ZB.MOM.WW.Auth/Directory.Build.props index 7e57b98..d8cc15d 100644 --- a/ZB.MOM.WW.Auth/Directory.Build.props +++ b/ZB.MOM.WW.Auth/Directory.Build.props @@ -5,12 +5,10 @@ enable enable latest - - 0.3.0 + + 0.4.0 true diff --git a/ZB.MOM.WW.Auth/README.md b/ZB.MOM.WW.Auth/README.md index 1fc55f3..2be3643 100644 --- a/ZB.MOM.WW.Auth/README.md +++ b/ZB.MOM.WW.Auth/README.md @@ -30,7 +30,16 @@ Authentication and authorisation libraries for the **ZB.MOM.WW SCADA family** (O ## Versioning -All five packages are versioned **lockstep** from `Directory.Build.props`. The current release is **0.3.0**. A single version bump in `Directory.Build.props` bumps all five packages simultaneously — consumers should reference the same version for all ZB.MOM.WW.Auth packages. +All five packages are versioned **lockstep** from `Directory.Build.props`. The current release is **0.4.0**. A single version bump in `Directory.Build.props` bumps all five packages simultaneously — consumers should reference the same version for all ZB.MOM.WW.Auth packages. + +### 0.4.0 — LDAP sign-in by user bind (opt-in) + +- **`LdapOptions.BindMode`** (`ServiceAccount` default | `User`), **`UserBindTemplate`** (`{0}` = the login name, e.g. `{0}@example.com` or `EXAMPLE\{0}`) and **`AcceptDomainQualifiedNames`** (default `true`). +- In `User` mode `LdapAuthService` binds as the user; success authenticates them and the same connection reads their entry (`UserNameAttribute`, `DisplayNameAttribute`, `GroupAttribute`). No `ServiceAccountDn` / `ServiceAccountPassword` is needed or validated. +- Failure mapping: empty password or an unusable name → `BadCredentials` with no network call (AD treats an empty-password simple bind as an unauthenticated success). Bind result 49 → `BadCredentials`, final — never retried on a fallback DC, so one wrong password is one lockout strike. Any other bind error, a connect failure or a search failure → the system-side bucket (`ServiceAccountBindFailed`) and the next endpoint. Zero/several entries or no groups after a good bind → `GroupLookupFailed`. +- `DOMAIN\name` / `name@suffix` are reduced to `name` (or refused when `AcceptDomainQualifiedNames` is `false`), so the result's `Username` has one spelling. +- No `Abstractions` contract change beyond the new enum and option properties; `ServiceAccount` mode is unchanged. +- `UserBindTemplate` output is used verbatim as the bind name, so use a UPN (`{0}@example.com`) or down-level (`EXAMPLE\{0}`) template; a DN template is not escaped. ### 0.3.0 — SQL Server API-key store (ScadaBridge central pair)