using Opc.Ua; namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.Security; /// /// A that additionally carries the LDAP-resolved roles /// () for the authenticated session. The SDK stores the /// instance we assign to ImpersonateEventArgs.Identity by reference onto /// Session.Identity/Session.EffectiveIdentity (no wrapping or copying), so a method /// handler can later recover the roles via /// (context as ISessionOperationContext)?.UserIdentity as RoleCarryingUserIdentity — the /// seam the inbound alarm ack/shelve AlarmAck gate (T18) reads. This type only carries the /// roles; it makes no auth decision. /// public sealed class RoleCarryingUserIdentity : UserIdentity { /// /// Initializes a new instance, building the base identity from the same UserName token the /// SDK decrypted and attaching the authenticator's resolved roles. /// /// The user identity token the SDK handed to the impersonation handler. /// The roles resolved by the authenticator (e.g. ReadOnly, AlarmAck). public RoleCarryingUserIdentity(UserIdentityToken token, IReadOnlyList roles) : this(token, roles, principal: string.Empty, apiKeyId: null) { } /// /// Initializes a new instance carrying the ACL principal and the authenticating key id alongside /// the roles (#520 stage 5). /// /// The user identity token the SDK handed to the impersonation handler. /// The roles resolved by the authenticator. /// The ACL principal this session authenticates as. /// The public id of the key that authenticated, or null for anonymous. public RoleCarryingUserIdentity( UserIdentityToken token, IReadOnlyList roles, string principal, string? apiKeyId) : base(token) { ArgumentNullException.ThrowIfNull(roles); // Freeze to a defensive copy so a caller mutating the source list can never retroactively // alter this session's granted roles, and so the gate's per-ack `.Contains(...)` runs over a // known-small frozen array. Roles = [.. roles]; Principal = principal ?? string.Empty; ApiKeyId = apiKeyId; } /// The roles the authenticator granted this session, used by downstream permission gates. public IReadOnlyList Roles { get; } /// /// The ACL principal this session authenticates as — the name NodeAcl rows key on, and what /// the permission trie matches. Empty when the authenticator predates the principal model, which /// the ACL gate treats as unauthorized. /// /// /// ⚠️ This is NOT . The base class derives that from /// the presented token, so for a key-bearing client it is the static username apikey — /// identical for every key in the fleet and useless as an identity. See /// . /// public string Principal { get; } /// /// Public id of the API key that authenticated this session, or null for anonymous. Lets an open /// session be re-checked against a later deployment that revoked the key, instead of the key /// remaining usable until the client happens to disconnect. /// public string? ApiKeyId { get; } }