namespace ZB.MOM.WW.OtOpcUa.Server.Security; /// /// LDAP settings for the OPC UA server's UserName token validator. Bound from /// appsettings.json OpcUaServer:Ldap. Defaults target the GLAuth dev instance /// (localhost:3893, dc=lmxopcua,dc=local) for the stock inner-loop setup. Production /// deployments are expected to point at Active Directory; see /// and the per-field xml-docs for the AD-specific overrides. /// /// /// Active Directory cheat-sheet: /// /// : one of the domain controllers, or the domain FQDN (will round-robin DCs). /// : 389 (LDAP) or 636 (LDAPS); use 636 + in production. /// : true. AD increasingly rejects plain-LDAP bind under LDAP-signing enforcement. /// : false. Dev escape hatch only. /// : DC=corp,DC=example,DC=com — your domain's base DN. /// : a dedicated service principal with read access to user + group entries /// (e.g. CN=OpcUaSvc,OU=Service Accounts,DC=corp,DC=example,DC=com). Never a privileged admin. /// : sAMAccountName (classic login name) or userPrincipalName /// (user@domain form). Default is uid which AD does not populate, so this override is required. /// : displayName gives the human name; cn works too but is less rich. /// : memberOf — matches AD's default. Values are full DNs /// (CN=<Group>,OU=...,DC=...); the authenticator strips the leading CN= RDN value and uses /// that as the lookup key in . /// : maps your AD group common-names to OPC UA roles — e.g. /// {"OPCUA-Operators" : "WriteOperate", "OPCUA-Engineers" : "WriteConfigure"}. /// /// /// Nested groups are not expanded — AD's tokenGroups / LDAP_MATCHING_RULE_IN_CHAIN /// membership-chain filter isn't used. Assign users directly to the role-mapped groups, or pre-flatten /// membership in your directory. If nested expansion becomes a requirement, it's an authenticator /// enhancement (not a config change). /// /// public sealed class LdapOptions { public bool Enabled { get; init; } = false; public string Server { get; init; } = "localhost"; public int Port { get; init; } = 3893; public bool UseTls { get; init; } = false; /// Dev-only escape hatch — must be false in production. public bool AllowInsecureLdap { get; init; } = true; public string SearchBase { get; init; } = "dc=lmxopcua,dc=local"; public string ServiceAccountDn { get; init; } = string.Empty; public string ServiceAccountPassword { get; init; } = string.Empty; public string DisplayNameAttribute { get; init; } = "cn"; public string GroupAttribute { get; init; } = "memberOf"; /// /// LDAP attribute used to match a login name against user entries in the directory. /// Defaults to uid (RFC 2307). Common overrides: /// /// sAMAccountName — Active Directory, classic NT-style login names (e.g. jdoe). /// userPrincipalName — Active Directory, email-style (e.g. jdoe@corp.example.com). /// cn — GLAuth + some OpenLDAP deployments where users are keyed by common-name. /// /// Used only when is non-empty (search-then-bind path) — /// direct-bind fallback constructs the DN as cn=<name>,<SearchBase> /// regardless of this setting and is not a production-grade path against AD. /// public string UserNameAttribute { get; init; } = "uid"; /// /// LDAP group → OPC UA role. Each authenticated user gets every role whose source group /// is in their membership list. Recognized role names (CLAUDE.md): ReadOnly (browse /// + read), WriteOperate, WriteTune, WriteConfigure, AlarmAck. /// public Dictionary GroupToRole { get; init; } = new(StringComparer.OrdinalIgnoreCase); }