Files
lmxopcua/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Security/RoleCarryingUserIdentity.cs
T
Joseph Doherty 826312befe docs: fill missing XML docs and strip tracking IDs from src comments
Clears CommentChecker findings in src/ (3,280 -> 209, the remainder being false
positives like PLC-5/IEEE-754/M0.0) by adding missing summaries/params/returns,
normalising inheritdoc usage, and removing Gitea/review tracking IDs while keeping
the rationale they annotated. Comment-only: comment-stripped sources are identical
to the parent commit.
2026-10-15 06:14:58 -04:00

72 lines
3.7 KiB
C#

using Opc.Ua;
namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.Security;
/// <summary>
/// A <see cref="UserIdentity"/> that additionally carries the LDAP-resolved roles
/// (<see cref="OpcUaUserAuthResult.Roles"/>) for the authenticated session. The SDK stores the
/// instance we assign to <c>ImpersonateEventArgs.Identity</c> by reference onto
/// <c>Session.Identity</c>/<c>Session.EffectiveIdentity</c> (no wrapping or copying), so a method
/// handler can later recover the roles via
/// <c>(context as ISessionOperationContext)?.UserIdentity as RoleCarryingUserIdentity</c> — the
/// seam the inbound alarm ack/shelve <c>AlarmAck</c> gate (T18) reads. This type only carries the
/// roles; it makes no auth decision.
/// </summary>
public sealed class RoleCarryingUserIdentity : UserIdentity
{
/// <summary>
/// Initializes a new instance, building the base identity from the same UserName token the
/// SDK decrypted and attaching the authenticator's resolved roles.
/// </summary>
/// <param name="token">The user identity token the SDK handed to the impersonation handler.</param>
/// <param name="roles">The roles resolved by the authenticator (e.g. <c>ReadOnly</c>, <c>AlarmAck</c>).</param>
public RoleCarryingUserIdentity(UserIdentityToken token, IReadOnlyList<string> roles)
: this(token, roles, principal: string.Empty, apiKeyId: null)
{
}
/// <summary>
/// Initializes a new instance carrying the ACL principal and the authenticating key id alongside
/// the roles.
/// </summary>
/// <param name="token">The user identity token the SDK handed to the impersonation handler.</param>
/// <param name="roles">The roles resolved by the authenticator.</param>
/// <param name="principal">The ACL principal this session authenticates as.</param>
/// <param name="apiKeyId">The public id of the key that authenticated, or null for anonymous.</param>
public RoleCarryingUserIdentity(
UserIdentityToken token, IReadOnlyList<string> 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;
}
/// <summary>The roles the authenticator granted this session, used by downstream permission gates.</summary>
public IReadOnlyList<string> Roles { get; }
/// <summary>
/// The ACL principal this session authenticates as — the name <c>NodeAcl</c> rows key on, and what
/// the permission trie matches. Empty when the authenticator predates the principal model, which
/// the ACL gate treats as unauthorized.
/// </summary>
/// <remarks>
/// ⚠️ <b>This is NOT <see cref="UserIdentity.DisplayName"/>.</b> The base class derives that from
/// the presented token, so for a key-bearing client it is the static username <c>apikey</c> —
/// identical for every key in the fleet and useless as an identity. See
/// <see cref="OpcUaUserAuthResult.Principal"/>.
/// </remarks>
public string Principal { get; }
/// <summary>
/// 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.
/// </summary>
public string? ApiKeyId { get; }
}