docs: backfill XML documentation across 756 files
v2-ci / build (push) Failing after 1m43s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped

Adds <summary>, <param>, <typeparam>, and <inheritdoc/> tags to public
members surfaced by commentchecker — resolves 5,847 of 5,869 issues
(99.6%) across three /fixdocs passes.
This commit is contained in:
Joseph Doherty
2026-05-28 08:10:17 -04:00
parent f9fc7dd2e1
commit 64e3fbe035
756 changed files with 9876 additions and 96 deletions
@@ -15,12 +15,15 @@ public sealed record AuthorizationDecision(
AuthorizationVerdict Verdict,
IReadOnlyList<MatchedGrant> Provenance)
{
/// <summary>Gets a value indicating whether the authorization decision allows the operation.</summary>
public bool IsAllowed => Verdict == AuthorizationVerdict.Allow;
/// <summary>Convenience constructor for the common "no grants matched" outcome.</summary>
public static AuthorizationDecision NotGranted() => new(AuthorizationVerdict.NotGranted, []);
/// <summary>Allow with the list of grants that matched.</summary>
/// <param name="provenance">The list of grants that matched to allow the operation.</param>
/// <returns>An authorization decision with Allow verdict.</returns>
public static AuthorizationDecision Allowed(IReadOnlyList<MatchedGrant> provenance)
=> new(AuthorizationVerdict.Allow, provenance);
}
@@ -19,5 +19,8 @@ public interface IPermissionEvaluator
/// failure to <c>BadUserAccessDenied</c> per OPC UA Part 4 when the result is not
/// <see cref="AuthorizationVerdict.Allow"/>.
/// </summary>
/// <param name="session">The user session containing resolved LDAP groups and roles.</param>
/// <param name="operation">The OPC UA operation being requested.</param>
/// <param name="scope">The node address scope being accessed.</param>
AuthorizationDecision Authorize(UserAuthorizationState session, OpcUaOperation operation, NodeScope scope);
}
@@ -31,6 +31,8 @@ public sealed class PermissionTrie
/// session's <paramref name="ldapGroups"/>. Returns the matched-grant list; the caller
/// OR-s the flag bits to decide whether the requested permission is carried.
/// </summary>
/// <param name="scope">The node scope to match permissions for.</param>
/// <param name="ldapGroups">The user's LDAP group memberships.</param>
public IReadOnlyList<MatchedGrant> CollectMatches(NodeScope scope, IEnumerable<string> ldapGroups)
{
ArgumentNullException.ThrowIfNull(scope);
@@ -23,6 +23,7 @@ public sealed class PermissionTrieCache
new(StringComparer.OrdinalIgnoreCase);
/// <summary>Install a trie for a cluster + make it the current generation.</summary>
/// <param name="trie">The permission trie to install.</param>
public void Install(PermissionTrie trie)
{
ArgumentNullException.ThrowIfNull(trie);
@@ -32,6 +33,7 @@ public sealed class PermissionTrieCache
}
/// <summary>Get the current-generation trie for a cluster; null when nothing installed.</summary>
/// <param name="clusterId">The cluster identifier.</param>
public PermissionTrie? GetTrie(string clusterId)
{
ArgumentException.ThrowIfNullOrWhiteSpace(clusterId);
@@ -39,6 +41,8 @@ public sealed class PermissionTrieCache
}
/// <summary>Get a specific (cluster, generation) trie; null if that pair isn't cached.</summary>
/// <param name="clusterId">The cluster identifier.</param>
/// <param name="generationId">The generation identifier.</param>
public PermissionTrie? GetTrie(string clusterId, long generationId)
{
if (!_byCluster.TryGetValue(clusterId, out var entry)) return null;
@@ -46,10 +50,12 @@ public sealed class PermissionTrieCache
}
/// <summary>The generation id the <see cref="GetTrie(string)"/> shortcut currently serves for a cluster.</summary>
/// <param name="clusterId">The cluster identifier.</param>
public long? CurrentGenerationId(string clusterId)
=> _byCluster.TryGetValue(clusterId, out var entry) ? entry.Current.GenerationId : null;
/// <summary>Drop every cached trie for one cluster.</summary>
/// <param name="clusterId">The cluster identifier.</param>
public void Invalidate(string clusterId) => _byCluster.TryRemove(clusterId, out _);
/// <summary>
@@ -59,6 +65,8 @@ public sealed class PermissionTrieCache
/// class-typed entry) so a concurrent <see cref="Install"/> on the same cluster is never
/// silently overwritten.
/// </summary>
/// <param name="clusterId">The cluster identifier.</param>
/// <param name="keepLatest">The number of most recent generations to retain.</param>
public void Prune(string clusterId, int keepLatest = 3)
{
if (keepLatest < 1) throw new ArgumentOutOfRangeException(nameof(keepLatest), keepLatest, "keepLatest must be >= 1");
@@ -96,12 +104,18 @@ public sealed class PermissionTrieCache
// Class (not record) so TryUpdate in Prune uses reference equality for the CAS comparison.
private sealed class ClusterEntry(PermissionTrie current, IReadOnlyDictionary<long, PermissionTrie> tries)
{
/// <summary>Gets the current (latest) permission trie for the cluster.</summary>
public PermissionTrie Current { get; } = current;
/// <summary>Gets all cached tries for the cluster keyed by generation ID.</summary>
public IReadOnlyDictionary<long, PermissionTrie> Tries { get; } = tries;
/// <summary>Creates a cluster entry from a single trie.</summary>
/// <param name="trie">The permission trie to create the entry from.</param>
public static ClusterEntry FromSingle(PermissionTrie trie) =>
new(trie, new Dictionary<long, PermissionTrie> { [trie.GenerationId] = trie });
/// <summary>Creates a new entry with an additional trie, updating current if it's newer.</summary>
/// <param name="trie">The new permission trie to add.</param>
public ClusterEntry WithAdditional(PermissionTrie trie)
{
var next = new Dictionary<long, PermissionTrie>(Tries) { [trie.GenerationId] = trie };
@@ -14,6 +14,9 @@ public sealed class TriePermissionEvaluator : IPermissionEvaluator
private readonly PermissionTrieCache _cache;
private readonly TimeProvider _timeProvider;
/// <summary>Initializes a new instance of the TriePermissionEvaluator class.</summary>
/// <param name="cache">The permission trie cache.</param>
/// <param name="timeProvider">The time provider for checking authorization state staleness; if null, uses system time.</param>
public TriePermissionEvaluator(PermissionTrieCache cache, TimeProvider? timeProvider = null)
{
ArgumentNullException.ThrowIfNull(cache);
@@ -21,6 +24,11 @@ public sealed class TriePermissionEvaluator : IPermissionEvaluator
_timeProvider = timeProvider ?? TimeProvider.System;
}
/// <summary>Authorizes an operation against the user's session and node scope.</summary>
/// <param name="session">The user's authorization session.</param>
/// <param name="operation">The OPC UA operation to authorize.</param>
/// <param name="scope">The target node scope.</param>
/// <returns>An authorization decision indicating whether the operation is allowed.</returns>
public AuthorizationDecision Authorize(UserAuthorizationState session, OpcUaOperation operation, NodeScope scope)
{
ArgumentNullException.ThrowIfNull(session);
@@ -65,6 +73,8 @@ public sealed class TriePermissionEvaluator : IPermissionEvaluator
}
/// <summary>Maps each <see cref="OpcUaOperation"/> to the <see cref="NodePermissions"/> bit required to grant it.</summary>
/// <param name="op">The OPC UA operation.</param>
/// <returns>The required node permission for the operation.</returns>
public static NodePermissions MapOperationToPermission(OpcUaOperation op) => op switch
{
OpcUaOperation.Browse => NodePermissions.Browse,
@@ -63,6 +63,7 @@ public sealed record UserAuthorizationState
/// <see cref="AuthCacheMaxStaleness"/>. The evaluator short-circuits to NotGranted
/// whenever this is true.
/// </summary>
/// <param name="utcNow">The current UTC time.</param>
public bool IsStale(DateTime utcNow) => utcNow - MembershipResolvedUtc > AuthCacheMaxStaleness;
/// <summary>
@@ -70,6 +71,7 @@ public sealed record UserAuthorizationState
/// ceiling — a signal to the caller to kick off an async refresh, while the current
/// call still evaluates against the cached memberships.
/// </summary>
/// <param name="utcNow">The current UTC time.</param>
public bool NeedsRefresh(DateTime utcNow) =>
!IsStale(utcNow) && utcNow - MembershipResolvedUtc > MembershipFreshnessInterval;
}