# Shared GLAuth Standardization — Design

> **Status:** IMPLEMENTED + verified 2026-06-04 (all 18 plan tasks). See `shared-glauth-on-35` memory.
> Plan: [`2026-06-04-shared-glauth-standardization.md`](2026-06-04-shared-glauth-standardization.md).
> **Scope:** dev/test only. Production stays on real corporate AD (out of scope).

## Goal

Consolidate the three sister projects (OtOpcUa, MxAccessGateway, ScadaBridge) onto **one shared
GLAuth dev directory** running on the shared Docker host **`10.100.0.35:3893`**, replacing the
three separate LDAP setups in use today. This is the natural endpoint of the Auth-component
normalization: all three already use the shared `ZB.MOM.WW.Auth.Ldap` library (search-then-bind)
and already default to the same base DN `dc=zb,dc=local`.

## Decisions (locked during brainstorming)

| Decision | Choice |
|---|---|
| Environments | **Dev/test only** (prod → real AD, untouched) |
| Consolidation depth | **Full** — every dev instance points at 35 |
| Transport | **Plaintext** (`Transport=None`, `AllowInsecure=true`) — trusted lab subnet |
| Source of truth | **`scadaproj/infra/glauth/`** (app-neutral, next to the other shared `ZB.MOM.WW.*` components) — Approach A |

## Architecture

```
        scadaproj/infra/glauth/            ← single source of truth (git)
        ├── config.toml                    (merged dc=zb,dc=local directory)
        ├── docker-compose.yml             (one `glauth` service, :3893)
        └── README.md
                  │  deploy on 10.100.0.35: docker compose up -d
                  ▼
        GLAuth @ 10.100.0.35:3893  ·  datastore=config  ·  baseDN dc=zb,dc=local  ·  ldaps=false
                  ▲                         ▲
   plaintext bind │ (None + AllowInsecure)  │
   ┌──────────────┴───────────┐   ┌─────────┴─────────────────────┐
   Mac / OrbStack             │   windev (10.100.0.48)
   • ScadaBridge :9000/:9100  │   • MxGateway (MxAccessGw svc)
   • OtOpcUa docker-dev        │   • OtOpcUa (OtOpcUa svc)
     (un-stubbed)
```

- One `glauth` container on `10.100.0.35:3893`, `datastore=config`, `baseDN=dc=zb,dc=local`, ldaps disabled.
- Every dev consumer: `Server=10.100.0.35`, `Port=3893`, `Transport=None`, `AllowInsecure=true`, `SearchBase=dc=zb,dc=local`.
- **Retired:** the `scadabridge-ldap` container (ScadaBridge `infra/docker-compose.yml`) and the windev-local glauth (`C:\publish\glauth`).
- **Consequences:** windev gains a runtime dependency on 35 for *new* logins (existing cookie sessions unaffected); deploying to 35 needs working access (see Prerequisites).

## The merged directory

One `dc=zb,dc=local` directory; group families partitioned into **non-overlapping gid ranges** (today
both existing GLAuth files reuse 5501–5505 — the collision to fix). **Each app maps only its own family
and ignores the rest**, so the families coexist with zero conflict.

**Groups**

| Family | Used by | Groups (gidnumber) |
|---|---|---|
| `SCADA-*` (55xx) | ScadaBridge roles (DB-mapped) | Admins 5501, Designers 5502, Deploy-All 5503, Deploy-SiteA 5504, Viewers 5505 |
| OPC-perm (560x) | OtOpcUa + MxGateway OPC-UA write model | ReadOnly 5601, WriteOperate 5602, WriteTune 5603, WriteConfigure 5604, AlarmAck 5605 |
| `Gw*` (561x) | MxGateway dashboard (config-mapped) | GwAdmin 5610, GwReader 5611 |
| `OtOpcUa-*` (57xx) | OtOpcUa AdminUI (DB-mapped) | Admins 5701, Designers 5702, Viewers 5703 |

`SCADA-*` keeps its canonical 55xx numbers (already deployed). The OPC/`Gw` groups move off the old
5501–5505/5510 into 56xx to clear the clash.

**Users** (all password `password`; uid ranges 50xx ScadaBridge / 51xx MxGateway / 52xx OtOpcUa)

- **`serviceaccount`** (5999, `cn=serviceaccount,dc=zb,dc=local`, `search *` capability) — the *single*
  bind account every app uses. Password `serviceaccount123`. ScadaBridge moves to it from `cn=admin`/`password`.
- **`multi-role`** (5005) — member of **every** group → all roles in all three apps (canonical cross-app QA login).
- **`admin`** (5001) — `SCADA-Admins` + `GwAdmin` + `OtOpcUa-Admins` → Administrator everywhere.
- Per-role testers: `designer`/`deployer`/`site-deployer` (ScadaBridge); `gwreader` (MxGateway Viewer);
  `otdesigner`/`otviewer` (OtOpcUa); `readonly`/`writeop`/`writetune`/`writeconfig`/`alarmack` (OPC perms).

## Per-app config changes

Each consumer changes only its LDAP `Server` (+ a few keys). Shared service account
`cn=serviceaccount,dc=zb,dc=local` / `serviceaccount123`.

- **ScadaBridge** (`docker/` + `docker-env2/`, central-node-a & -b `appsettings.Central.json`):
  `Ldap:Server` `scadabridge-ldap`→`10.100.0.35`; `ServiceAccountDn` `cn=admin`→`cn=serviceaccount`,
  `ServiceAccountPassword`→`serviceaccount123`. Rest unchanged (`SCADA-*` DB mappings already seeded).
  Retire the `ldap` service in `infra/docker-compose.yml`; sequenced-recreate central nodes.
- **OtOpcUa docker-dev** (`docker-dev/docker-compose.yml`, all host containers) — **the un-stub**:
  drop `Security__Ldap__DevStubMode=true`; add `Server=10.100.0.35`, `Port=3893`, `Transport=None`,
  `AllowInsecure=true`, `SearchBase=dc=zb,dc=local`, `ServiceAccountDn=cn=serviceaccount,…`,
  `ServiceAccountPassword=serviceaccount123`. Seed OtOpcUa DB mappings
  `OtOpcUa-Admins→Administrator`, `OtOpcUa-Designers→Designer`, `OtOpcUa-Viewers→Viewer` (system-wide).
- **MxGateway** (windev `C:\publish\mxaccessgw\Server\appsettings.json`): `Ldap:Server`
  `localhost`→`10.100.0.35`; `SearchBase` `dc=lmxopcua`→`dc=zb,dc=local`; `ServiceAccountDn`→`…dc=zb,dc=local`.
  `Transport=None`/`AllowInsecure=true` already migrated; `GroupToRole` (`GwAdmin`/`GwReader`) unchanged.
  Restart `MxAccessGw` (+ dependent `OtOpcUa` svc).
- **OtOpcUa (windev service)**: locate its deployed overlay; repoint `Server`→`10.100.0.35`,
  `SearchBase`→`dc=zb,dc=local`, service account, and switch dev transport `Ldaps`→`None`+`AllowInsecure`.
- **Then** stop/disable the windev-local `glauth` service.

## Rollout & rollback

Incremental; **the old glauths stay up until the very end**, so every step is reversible by pointing
`Server` back.

1. Stand up the shared glauth on 35 → verify via `ldapsearch` (bind `serviceaccount`; `multi-role`
   `memberOf` spans all families). Nothing repointed yet.
2. Prove reachability from an OrbStack container to `10.100.0.35:3893` (the linchpin) before any app edit.
3. ScadaBridge `:9000` → recreate → browser-verify `multi-role` = 4 roles. Then `:9100`.
4. OtOpcUa docker-dev → un-stub + repoint + seed → recreate → verify.
5. windev MxGateway (backup appsettings) → restart → verify. Then windev OtOpcUa overlay.
6. Only once all green: stop/disable `scadabridge-ldap` + the windev-local glauth.

**Rollback** per consumer: revert the one-line `Server` change (git revert on the Mac; `.bak` restore on
windev) and recreate/restart. Remove the shared glauth = `docker compose down` on 35.

## Testing & verification

- **LDAP layer:** `ldapsearch` bind `serviceaccount`; confirm each test user + `multi-role`'s `memberOf`
  across all four families; bind each user to confirm `password`.
- **Per-app browser (macbook Chrome):** ScadaBridge `:9000`/`:9100` `multi-role` → 4 roles (via
  `/auth/token`); OtOpcUa `:9200` → seeded roles; MxGateway `10.100.0.48:5130` → Administrator; windev OtOpcUa → AdminUI.
- **Role-gating spot-checks:** `gwreader`→MxGateway Viewer-only; `designer`→ScadaBridge design-only;
  `otviewer`→OtOpcUa read-only.
- **Negative:** wrong password rejected everywhere; a user in no family of an app → denied there.

## Prerequisites & open items (resolve in the plan)

1. **Access to `10.100.0.35`** — SSH from this Mac is currently refused (`Permission denied`/connection
   reset) and the windev→35 jump is administratively prohibited. Either re-authorize this Mac's key on 35,
   or the user runs the final `docker compose up -d`. Artifacts are portable either way.
2. **OtOpcUa group key shape** — confirm OtOpcUa maps on the **short RDN** (`OtOpcUa-Admins`) the shared
   lib returns vs the full-DN its `LdapGroupRoleMapping` entity comment shows, before seeding.
3. **OrbStack→LAN reachability** — verify ScadaBridge/OtOpcUa containers can reach `10.100.0.35:3893`
   early (likely fine; it's the linchpin). `log()` if any consumer can't reach 35 rather than silently failing.
4. **windev OtOpcUa config path** — discovery step (less is known about this deployment than MxGateway).

## Notes

- `scadaproj` is a plain-files umbrella that is *also* a local git repo; `infra/glauth/` lives here as the
  canonical source. Per-app config edits land on a `feat/*` branch per repo (merge on the user's go).
  windev edits are deployment-only with `.bak` backups (like the GroupToRole / LDAP-key migrations done
  2026-06-04); repo templates optionally aligned.
- Related memory: `multi-role-cross-app-test-user`, `mxgateway-windev-deploy`,
  `scadabridge-local-deploy-gotchas`, `auth-audit-normalization-in-progress`.
