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; }
}