Files
scadaproj/ldap_mapping.md

15 KiB

UG_WW_* → app role mapping (proposal)

Maps the Zimmer NAM UG_WW_* Active Directory groups onto the roles each ZB.MOM.WW app actually implements. Inputs: the 39-group inventory in ldap_setup.md (enumerated live 2026-08-12) and the standardized CANONICAL-ROLES.md.

Status: PROPOSAL. The mechanical parts (_RO_, _OPERATOR, APP_IDE, APP_ADMIN) are safe to adopt. Four items marked ⚠ DECIDE need a human answer before anyone writes them into a production config — they are listed in §6 and none of them can be resolved from the directory alone. Ignition's role set is deliberately out of scope.

⚠️ ignitionoee (router): ON HOLD (decision 2026-08-13) — do not set up its LDAP. Its role vocabulary is outside the canonical set (admin role is literally "Admin"), so no existing UG_WW_* group maps to it; wiring it to NAM AD requires minting new AD groups first. Revisit only once those groups exist and their mapping is decided.

1. The app role vocabularies (verified in source 2026-08-12)

Each app implements only a subset of the canonical six. This is the constraint that shapes everything below — there is no point mapping a group to a role an app does not have.

App Roles it actually implements Where the mapping lives
OtOpcUa (AdminUI) Viewer, Designer, Administrator Security:Ldap:GroupToRole (appsettings, merged with a DB overlay)
ScadaBridge Viewer, Operator, Verifier, Designer, Deployer, Administrator LdapGroupMapping table (database, not config)
MxAccessGateway Viewer, Administrator MxGateway:Dashboard:GroupToRole
HistorianGateway Viewer, Administrator Dashboard:GroupToRole

Three things follow that are easy to get wrong:

  • OtOpcUa's LDAP mapping governs the AdminUI only. Since Gitea #520 (2026-08-01) the OPC UA data plane does not use LDAP at all — a client presents an otua_ API key as an OPC UA UserName token. So a plant operator who only reads tags needs no AD group here; granting one gives them the configuration UI, which is not what anyone means by "operator access". The CANONICAL-ROLES.md row showing OtOpcUa data-plane bundles predates that change and no longer applies to LDAP.
  • Administrator is the literal string in all four apps. MxGateway's constant is named DashboardRoles.Admin but its value is "Administrator", and its validator rejects anything that is not Administrator or Viewer.
  • Every app fails closed on zero matched groups. A user whose groups match no entry is denied. So an unmapped group grants nothing — the mapping does not have to be exhaustive, and leaving a group out is a safe default rather than an omission.

2. Group family → canonical role

UG_WW_* family Canonical Confidence
APP_IDE_RO_<env>, BTMGR_RO_<env> Viewer firm — RO is unambiguous
<SITE>_<AREA>_OPERATOR Operator firm
BTMGR_<PLANT>_<env> Operator firm — batch execution is operate-level
<SITE>_<AREA>_ENGINEER Engineer firm canonically, but no app implements Engineer — see §6.1
APP_IDE_<env> Designer + Deployer DECIDED — see §2.1
APP_ADMIN_<env> Administrator firm
HIST_ADMIN_<env> Administrator, historian-scoped firm for HistorianGateway; see §4 for the others
SITE_ADMIN_<SITE> Administrator (scoped) — not Deployer ⚠ DECIDE — §6.2
<SITE>_<AREA>_SUPERVISOR Verifier? ⚠ DECIDE — §6.3
<SITE>_SMS_TESTER — ⚠ DECIDE — §6.4
CITRIX_<env> none — deliberately unmapped firm — see below

CITRIX_* is not a role and must not be mapped. It grants desktop/session access to a Citrix farm, not authority within an application. Mapping it would hand app privileges to everyone who merely has a published desktop — 12 people in QA alone — which is a privilege-escalation path disguised as a convenience.

2.1 APP_IDE carries both Designer and Deployer — DECIDED

The ArchestrA IDE is both the authoring surface and the deployment surface: templates are created there and pushed to runtime from the same tool, by the same person, in the same session. So APP_IDE_<env> maps to both canonical roles, and APP_IDE_RO_<env> maps to Viewer — read the Galaxy, change nothing, deploy nothing.

This closes §6.5: the AUTHOR/DEPLOY separation the canonical set describes does not exist in this estate, and rather than inventing one, the mapping reflects how the tooling actually works. It is a deliberate collapse, not an oversight.

Only ScadaBridge can express it. Its LdapGroupMapping is a row per (group, role) pair and its RoleMapper accumulates matches into a HashSet<string>, so two rows against the same group grant both roles. The other three apps bind GroupToRole as a Dictionary<string, string> — one role per group, structurally; a duplicate key is a config error, not a second grant. That is not a limitation in practice: none of those three implements Deployer at all, so there is nothing to lose.

Consequence for SITE_ADMIN_*: §6.2 previously recommended mapping it to a site-scoped Deployer. With Deployer now sourced from APP_IDE, that would mean two unrelated groups granting the same authority by different routes, and would leave "site admin" meaning nothing beyond what an IDE user already has. Administrator is the remaining reading — see the revised §6.2, which raises rather than settles the blast-radius question.

3. The environment axis is per-deployment, not per-role

DEV/QA/PROD is orthogonal to role and has no representation in CanonicalRole. Do not encode it into role names; that triples the role set and buys nothing.

Instead, each deployment maps only its own environment's groups. The mapping already lives in per-instance config, so this is free:

  • the PROD instance's config lists UG_WW_APP_ADMIN_PROD and never mentions _DEV
  • the DEV instance's config lists UG_WW_APP_ADMIN_DEV

This is load-bearing, not cosmetic. A single config listing all three environments would make every DEV administrator a PROD administrator, silently, with no error anywhere.

The blocks in §4 are written for PROD. For DEV/QA, substitute the suffix. Note CITRIX has no PROD group at all, which is further reason not to treat it as a role.

4. Per-app configuration

OtOpcUa — Security:Ldap:GroupToRole

"GroupToRole": {
  "UG_WW_APP_ADMIN_PROD":   "Administrator",
  "UG_WW_SITE_ADMIN_WW19":  "Administrator",
  "UG_WW_APP_IDE_PROD":     "Designer",
  "UG_WW_APP_IDE_RO_PROD":  "Viewer",
  "UG_WW_HIST_ADMIN_PROD":  "Viewer"
}

HIST_ADMIN gets Viewer because historization targets are authored in the OtOpcUa AdminUI: a historian admin needs to see how tags are historized without being able to change the address space. Plant-floor operator and engineer groups are deliberately absent — the AdminUI is a configuration surface, and their runtime access is API-key based (§1).

Replace WW19 with the site that owns the instance; see §6.2 before listing all eight.

ScadaBridge — LdapGroupMapping table

The only app with a role set rich enough to use most of the directory, and the only one where site scoping is real.

Group Role Scope
UG_WW_APP_ADMIN_PROD Administrator —
UG_WW_APP_IDE_PROD Designer and Deployer — two rows (§2.1) —
UG_WW_APP_IDE_RO_PROD Viewer —
UG_WW_BTMGR_RO_PROD Viewer —
UG_WW_SITE_ADMIN_<SITE> Administrator that site ⚠ §6.2
UG_WW_B19_SMS_OPERATOR Operator B19
UG_WW_B19_VitE_OPERATOR Operator B19
UG_WW_PON_PRESS_OPERATOR Operator PON
UG_WW_WN_ARCOM_OPERATOR Operator WN
UG_WW_WN_ARCOMXL_OPERATOR Operator WN
UG_WW_WN_SOLAR_OPERATOR Operator WN
UG_WW_WN_STERIS_OPERATOR Operator WN
UG_WW_BTMGR_BE_PROD Operator BE
UG_WW_BTMGR_MC_PROD Operator MC
UG_WW_WN_STERIS_SUPERVISOR Verifier? WN ⚠ §6.3
UG_WW_B19_VitE_ENGINEER ⚠ §6.1 B19
UG_WW_PON_PRESS_ENGINEER ⚠ §6.1 PON

The IDE grant is two rows against one group — this is the only place in the four apps where that is possible, and it is the row pair most likely to be entered as one:

INSERT INTO LdapGroupMapping (LdapGroupName, Role) VALUES
  ('UG_WW_APP_IDE_PROD', 'Designer'),
  ('UG_WW_APP_IDE_PROD', 'Deployer');

RoleMapper unions every matched row into a HashSet<string>, so the user comes out holding both. A single row grants only what it names — there is no implicit widening, and nothing will report the missing half.

MxAccessGateway — MxGateway:Dashboard:GroupToRole

"GroupToRole": {
  "UG_WW_APP_ADMIN_PROD":  "Administrator",
  "UG_WW_APP_IDE_PROD":    "Viewer",
  "UG_WW_APP_IDE_RO_PROD": "Viewer"
}

Only two roles exist, so Designer collapses to Viewer: an IDE author has a legitimate reason to browse Galaxy through the gateway dashboard, but no reason to administer API keys. The gRPC surface is API-key authenticated and is not governed by this mapping.

HistorianGateway — Dashboard:GroupToRole

"GroupToRole": {
  "UG_WW_HIST_ADMIN_PROD": "Administrator",
  "UG_WW_APP_ADMIN_PROD":  "Administrator",
  "UG_WW_APP_IDE_RO_PROD": "Viewer",
  "UG_WW_BTMGR_RO_PROD":   "Viewer"
}

HIST_ADMIN is the exact-fit group for this app — it is the one place in the directory whose name already means what the app's Administrator grants.

5. Blast radius

Worth doing the arithmetic before adopting, because these groups are larger than they look:

Grant People
APP_ADMIN_PROD → Administrator 5
SITE_ADMIN_* → Administrator, all 8 sites ~48
APP_IDE_PROD → Designer 4
all _OPERATOR groups → Operator ~87

Mapping all eight SITE_ADMIN_* groups to Administrator on all four apps grants roughly 48 people full administrative control of every app, including the ones whose site has nothing to do with the deployment. That is the single largest risk in this document, and it is why §6.2 exists.

6. Open questions — DECIDE before adopting

6.1 Engineer has no home ⚠

_ENGINEER maps cleanly to canonical Engineer (OBSERVE + OPERATE + TUNE), and no app implements it. OtOpcUa has Viewer/Designer/Administrator; ScadaBridge has Operator/Verifier but no Engineer; the two gateways have Viewer/Administrator.

Options: (a) map _ENGINEER → Operator and accept that engineers lose TUNE; (b) add an Engineer role to ScadaBridge; (c) leave unmapped, so the 18 people in the two _ENGINEER groups get nothing. Recommendation: (a) for now — it is the honest subset, grants no authority the group name does not imply, and can be widened later. Do not silently map Engineer to Administrator to "cover" TUNE.

6.2 SITE_ADMIN_* — how much authority, and how many sites? ⚠

Narrowed by §2.1, not settled. Deployer now comes from APP_IDE, so site-scoped Deployer is no longer available as the gentle reading of "site admin". What remains is Administrator (scoped to the site), Viewer, or leaving it unmapped — and those are very far apart.

This matters more than it did. There are eight of these groups at 6 members each, so Administrator is the maximum grant in the vocabulary being handed to as many as ~48 people (§5). If a "site admin" is really a local IT contact rather than an application owner, Administrator is badly wrong.

Second question, unchanged: which sites should a given deployment list? Every group has exactly 6 members and they look like the same handful of people, but that is inference from counts, not fact. Recommendation: list only the owning site, and confirm what the role means before granting Administrator to any of them.

6.3 _SUPERVISOR → Verifier? ⚠

UG_WW_WN_STERIS_SUPERVISOR (6 members) is the only _SUPERVISOR group. ScadaBridge's Verifier approves a two-person Secured Write that an Operator initiates, so a supervisor approving an operator's action is a plausible reading — but "supervisor" could equally mean a shift lead with no approval authority at all. Confirm with the group owner.

Whatever is decided, never map one group to both Operator and Verifier. The control requires two distinct principals; a person holding both could initiate and approve their own write, which removes the entire point of the mechanism while leaving it looking wired.

6.4 _TESTER ⚠

UG_WW_B19_SMS_TESTER (13 members) has no canonical equivalent and appears only in a QA context. Leave unmapped until someone states what it is for — 13 people is too many to guess at.

6.5 Deployer vs AUTHOR separation — RESOLVED

Closed by §2.1: APP_IDE grants both Designer and Deployer, because the ArchestrA IDE authors and deploys from the same tool and the same session. The canonical AUTHOR/DEPLOY split has no counterpart in this estate, and the mapping now records that as a decision rather than leaving it to be discovered later. Same class of accepted SoD collapse already noted in CANONICAL-ROLES.md.

7. Prerequisites

  1. Auth 0.2.1 adopted — DONE 2026-08-13. All four apps pin 0.2.1 (OtOpcUa master @ 08e33fc7, ScadaBridge main @ d28824d9, MxAccessGateway main @ 1cb14d2, HistorianGateway e42ff31, merged to main; all pushed to origin). The continuation-referral defect (ldap_setup.md) is closed at the app level, so this mapping is exercisable once configs are authored.
  2. UG_WW_BTMGR_RO_QA is empty. It appears in the QA variant of the ScadaBridge and HistorianGateway tables; mapping it grants nobody anything today. Harmless, but do not read a working config as evidence that the grant works.
  3. Verify with a real account before rollout. Every group in §4 was observed on one long-tenured architect account, which holds every admin-flavoured group and no operator group. The operator mappings are therefore untested against a real operator. ldapprobe --self-bind <operator-account> settles it in one run — and specifically whether such an account has any memberOf at all, since the library refuses a zero-group login (GroupLookupFailed) regardless of a correct password.
  4. New groups are provisioned by ServiceNow ticket (the group descriptions are TASK… ids). If §6 concludes that a group is missing — a Verifier counterpart, or an Engineer tier — that is a ticket, not a config change.