Files
scadaproj/components/auth/current-state/otopcua/CURRENT-STATE.md
T

6.9 KiB
Raw Blame History

Auth — current state: OtOpcUa

Repo: ~/Desktop/OtOpcUa (Gitea lmxopcua). Stack: .NET 10, OPC Foundation UA stack. All paths below are relative to the repo root. Verified against source on 2026-06-01.

OtOpcUa has the richest auth of the three: OPC UA session-level identity, LDAP-backed authentication, transport security profiles, and a trie-based per-operation ACL system, plus a separate control-plane (Admin UI) auth stack.

1. Authentication

Three OPC UA identity-token types are accepted at session establishment (src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/OpcUaApplicationHost.cs, impersonation handler ~226288):

  • Anonymous — passes through without custom validation; gets no LDAP groups.
  • UserName/password (LDAP-backed) — the primary human path (see below).
  • X.509 certificate — validated at the secure-channel/PKI level; CN→role mapping not yet implemented.

UserName flow: the SDK decrypts the password with the server certificate, then IOpcUaUserAuthenticator.AuthenticateUserNameAsync() validates it.

  • Seam: src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Security/IOpcUaUserAuthenticator.cs
  • Prod impl: src/Server/ZB.MOM.WW.OtOpcUa.Host/OpcUa/LdapOpcUaUserAuthenticator.cs → wraps ILdapAuthService.
  • UserName tokens are always encrypted by the SDK (via the server cert) regardless of transport profile, so LDAP login works even on a None endpoint.

LDAP service (src/Server/ZB.MOM.WW.OtOpcUa.Security/Ldap/):

  • ILdapAuthService / LdapAuthService.cs — bind-then-search (or direct bind), re-bind as user DN to verify the password, read memberOf, strip leading CN=. GLAuth fallback extracts the primary group from the ou= RDN.
  • LdapOptions.cs — bound from config section Authentication.Ldap. Keys: Enabled, Server, Port (GLAuth 3893), UseTls, AllowInsecureLdap, SearchBase (dc=lmxopcua,dc=local), ServiceAccountDn, ServiceAccountPassword, UserNameAttribute (cn; AD→sAMAccountName), GroupAttribute (memberOf), DisplayNameAttribute, GroupToRole (dict), DevStubMode (dev-only: accepts any non-empty creds).
  • RoleMapper.cs — maps LDAP groups → control-plane AdminRole via GroupToRole.
  • Dev LDAP server: GLAuth at C:\publish\glauth\ (see C:\publish\glauth\auth.md).

2. Transport security

OpcUaServer config section; EnabledSecurityProfiles (default [None, Basic256Sha256-Sign, Basic256Sha256-SignAndEncrypt]; Aes128/Aes256 profiles also available), resolved by SecurityProfileResolver at startup. Server certificate auto-created under PkiStoreRoot (own/ issuer/ trusted/ rejected/). AutoAcceptUntrustedClientCertificates (default false). See docs/security.md.

  • Profile enum + policy build: OpcUaApplicationHost.cs (~1523, ~374410).

3. Authorization (data-plane ACLs — stays bespoke)

Bitmask permissions in src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Enums/NodePermissions.cs: Browse, Read, Subscribe, HistoryRead, WriteOperate, WriteTune, WriteConfigure, AlarmRead, AlarmAcknowledge, AlarmConfirm, AlarmShelve, MethodCall + bundles ReadOnly/Operator/Engineer/Admin. The three write tiers mirror Galaxy SecurityClassification (Operate/Tune/Configure).

  • Scope hierarchy: NodeAclScopeKind.csCluster, Namespace, UnsArea, UnsLine, Equipment, FolderSegment, Tag.
  • Grant entity: src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Entities/NodeAcl.cs(LdapGroup, ScopeKind, ScopeId, PermissionFlags), generation-versioned.
  • Evaluation: src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/TriePermissionEvaluator.cs, PermissionTrie.cs, PermissionTrieCache.cs, PermissionTrieBuilder.cs, IPermissionEvaluator.cs. Per-operation (OpcUaOperation enum); denials return BadUserAccessDenied.
  • Additive-only grants (no Deny) in Phase 6.2.

Control-plane (Admin UI) roles are independent of data-plane ACLs (design decision #150): AdminRole enum (ConfigViewer / ConfigEditor / FleetAdmin); LdapGroupRoleMapping entity maps groups→AdminRole. Cookie + JWT stack lives in src/Server/ZB.MOM.WW.OtOpcUa.Security/ (ServiceCollectionExtensions.cs, Endpoints/AuthEndpoints.cs/login,/logout,/ping; Jwt/JwtTokenService.cs; Blazor/CookieAuthenticationStateProvider.cs). DataProtection keys persisted in the Config DB so cookies survive failover.

4. Session / identity model (stays bespoke)

src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/UserAuthorizationState.cs: SessionId, ClusterId, LdapGroups, MembershipResolvedUtc, AuthGenerationId, MembershipVersion, MembershipFreshnessInterval (5 min, async refresh) and AuthCacheMaxStaleness (15 min, fail-closed). Sessions are generation-bound at sign-in so grant changes can't take effect mid-session. Auth is evaluated per request, not cached per session.

5. Secrets & config

Authentication.Ldap.ServiceAccountPassword should come from user-secrets/env, not source. DataProtection keys in Config DB table DataProtectionKeys. Docs: docs/security.md, docs/v2/acl-design.md.

6. Notable limits / TODOs

No explicit Deny grants (Phase 6.2); no nested-group expansion (relies on directory flattening); LDAP unreachable >15 min fails all sessions closed; X.509 CN→role mapping deferred; HistoryUpdate currently mapped to the HistoryRead bit.


Adoption plan → ZB.MOM.WW.Auth

Replace with the shared library:

  • LdapAuthService + LdapOptions + RoleMapperZB.MOM.WW.Auth.Ldap (ILdapAuthService, LdapAuthService, LdapOptions, LdapAuthResult) + IGroupRoleMapper<CanonicalRole>; OtOpcUa expands each canonical role into its AdminRole (control-plane) and NodePermissions (data-plane) per ../../spec/CANONICAL-ROLES.md. No first-class Deployer (publish ⊂ FleetAdmin).
  • Migrate config from Authentication.Ldap.* to the canonical schema in ../../spec/SPEC.md (notably UseTls → the canonical transport setting; UserNameAttribute keeps its name as the canonical one).
  • Control-plane Admin UI (Blazor, src/Server/ZB.MOM.WW.OtOpcUa.Security/): adopt ZB.MOM.WW.Auth.AspNetCore for the canonical cookie/claim conventions + DI helpers (ServiceCollectionExtensions.cs, Blazor/CookieAuthenticationStateProvider.cs, Endpoints/AuthEndpoints.cs). This is OtOpcUa's HTTP auth surface — distinct from the OPC UA data plane below.

Keep bespoke (thin adapter only):

  • IOpcUaUserAuthenticator / LdapOpcUaUserAuthenticator — keep as the OPC-UA-specific adapter that calls the shared ILdapAuthService.
  • ALL of §3 authZ (NodePermissions, ACL trie, NodeAcl), the control-plane AdminRole vocabulary, the JWT/DataProtection specifics, and §4 session model — domain-specific, not extracted (only the cookie/claim conventions are shared via .AspNetCore).
  • Transport security (§2) — OPC-UA-specific.

No API-key work — OtOpcUa has no API-key surface; it relies on OPC UA transport security instead.