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
@@ -20,6 +20,10 @@ public sealed class CookieAuthenticationStateProvider : AuthenticationStateProvi
private ClaimsPrincipal _current;
private Task? _pingLoop;
/// <summary>Initializes a new instance of the CookieAuthenticationStateProvider class.</summary>
/// <param name="initial">The initial claims principal from circuit boot.</param>
/// <param name="http">The HTTP client for authentication ping requests.</param>
/// <param name="logger">The logger for diagnostic messages.</param>
public CookieAuthenticationStateProvider(
ClaimsPrincipal initial,
HttpClient http,
@@ -30,6 +34,7 @@ public sealed class CookieAuthenticationStateProvider : AuthenticationStateProvi
_logger = logger;
}
/// <inheritdoc />
public override Task<AuthenticationState> GetAuthenticationStateAsync()
{
_pingLoop ??= Task.Run(() => PingLoopAsync(_cts.Token));
@@ -60,6 +65,7 @@ public sealed class CookieAuthenticationStateProvider : AuthenticationStateProvi
}
}
/// <inheritdoc />
public async ValueTask DisposeAsync()
{
_cts.Cancel();
@@ -4,6 +4,7 @@ public sealed class OtOpcUaCookieOptions
{
public const string SectionName = "Security:Cookie";
/// <summary>Gets or sets the cookie name.</summary>
public string Name { get; set; } = "OtOpcUa.Auth";
/// <summary>Idle sliding window, in minutes (default 30).</summary>
@@ -16,8 +16,12 @@ public static class AuthEndpoints
/// <summary>JSON body schema for API-side login callers (kept stable for tests).</summary>
public sealed record LoginRequest(string Username, string Password);
/// <summary>Response for a token issue request.</summary>
public sealed record TokenResponse(string Token);
/// <summary>Maps OtOpcUa authentication endpoints to the application route builder.</summary>
/// <param name="app">The endpoint route builder.</param>
/// <returns>The endpoint route builder for chaining.</returns>
public static IEndpointRouteBuilder MapOtOpcUaAuth(this IEndpointRouteBuilder app)
{
// The login endpoint serves two callers with different ergonomics:
@@ -8,7 +8,9 @@ public sealed class JwtOptions
/// <summary>HS256 signing key. Must be at least 32 bytes (256 bits) UTF-8.</summary>
public string SigningKey { get; set; } = string.Empty;
/// <summary>Gets or sets the JWT issuer identifier.</summary>
public string Issuer { get; set; } = "otopcua";
/// <summary>Gets or sets the JWT audience identifier.</summary>
public string Audience { get; set; } = "otopcua";
/// <summary>Default token expiry. Mirrors ScadaLink (15 min).</summary>
@@ -16,6 +16,9 @@ public sealed class JwtTokenService
private readonly JwtOptions _options;
private readonly ILogger<JwtTokenService> _logger;
/// <summary>Initializes a new instance of the JwtTokenService class.</summary>
/// <param name="options">The JWT options.</param>
/// <param name="logger">The logger for the service.</param>
public JwtTokenService(IOptions<JwtOptions> options, ILogger<JwtTokenService> logger)
{
_options = options.Value;
@@ -32,6 +35,11 @@ public sealed class JwtTokenService
}
}
/// <summary>Issues a JWT token for the specified user with the given roles.</summary>
/// <param name="displayName">The display name of the user.</param>
/// <param name="username">The username of the user.</param>
/// <param name="roles">The roles assigned to the user.</param>
/// <returns>The JWT token string.</returns>
public string Issue(string displayName, string username, IReadOnlyList<string> roles)
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.SigningKey));
@@ -55,6 +63,10 @@ public sealed class JwtTokenService
return new JwtSecurityTokenHandler().WriteToken(token);
}
/// <summary>Attempts to validate a JWT token and extract the claims principal.</summary>
/// <param name="token">The JWT token to validate.</param>
/// <param name="principal">The claims principal extracted from the token, or null if validation failed.</param>
/// <returns>True if the token is valid; otherwise false.</returns>
public bool TryValidate(string token, out ClaimsPrincipal? principal)
{
principal = null;
@@ -2,5 +2,10 @@ namespace ZB.MOM.WW.OtOpcUa.Security.Ldap;
public interface ILdapAuthService
{
/// <summary>Authenticates a user against the LDAP service.</summary>
/// <param name="username">The username to authenticate.</param>
/// <param name="password">The password to verify.</param>
/// <param name="ct">A cancellation token.</param>
/// <returns>A task that returns the authentication result.</returns>
Task<LdapAuthResult> AuthenticateAsync(string username, string password, CancellationToken ct = default);
}
@@ -15,6 +15,10 @@ public sealed class LdapAuthService(IOptions<LdapOptions> options, ILogger<LdapA
{
private readonly LdapOptions _options = options.Value;
/// <summary>Authenticates a user via LDAP bind and retrieves their group memberships and roles.</summary>
/// <param name="username">The username to authenticate.</param>
/// <param name="password">The password to validate against the LDAP directory.</param>
/// <param name="ct">A cancellation token to observe while waiting for the operation to complete.</param>
public async Task<LdapAuthResult> AuthenticateAsync(string username, string password, CancellationToken ct = default)
{
if (string.IsNullOrWhiteSpace(username))
@@ -130,6 +134,9 @@ public sealed class LdapAuthService(IOptions<LdapOptions> options, ILogger<LdapA
: $"cn={username},{_options.SearchBase}";
}
/// <summary>Escapes special characters in an LDAP filter string according to RFC 4515.</summary>
/// <param name="input">The unescaped string to escape.</param>
/// <returns>The escaped LDAP filter string.</returns>
internal static string EscapeLdapFilter(string input) =>
input.Replace("\\", "\\5c")
.Replace("*", "\\2a")
@@ -142,6 +149,8 @@ public sealed class LdapAuthService(IOptions<LdapOptions> options, ILogger<LdapA
/// group as an <c>ou=</c> RDN immediately above the user's <c>cn=</c>, so this recovers
/// the group name when <see cref="LdapOptions.GroupAttribute"/> is absent from the entry.
/// </summary>
/// <param name="dn">The distinguished name to extract the OU from.</param>
/// <returns>The extracted OU value, or null if no OU segment is found.</returns>
internal static string? ExtractOuSegment(string dn)
{
var segments = dn.Split(',');
@@ -154,6 +163,9 @@ public sealed class LdapAuthService(IOptions<LdapOptions> options, ILogger<LdapA
return null;
}
/// <summary>Extracts the value portion of the first RDN (relative distinguished name) from a DN.</summary>
/// <param name="dn">The distinguished name to extract from.</param>
/// <returns>The value of the first RDN.</returns>
internal static string ExtractFirstRdnValue(string dn)
{
var equalsIdx = dn.IndexOf('=');
@@ -9,9 +9,16 @@ public sealed class LdapOptions
{
public const string SectionName = "Authentication:Ldap";
/// <summary>Gets or sets a value indicating whether LDAP authentication is enabled.</summary>
public bool Enabled { get; set; } = true;
/// <summary>Gets or sets the LDAP server hostname.</summary>
public string Server { get; set; } = "localhost";
/// <summary>Gets or sets the LDAP server port.</summary>
public int Port { get; set; } = 3893;
/// <summary>Gets or sets a value indicating whether to use TLS for LDAP connection.</summary>
public bool UseTls { get; set; }
/// <summary>Dev-only escape hatch — must be <c>false</c> in production.</summary>
@@ -24,6 +31,7 @@ public sealed class LdapOptions
/// </summary>
public bool DevStubMode { get; set; }
/// <summary>Gets or sets the LDAP search base DN.</summary>
public string SearchBase { get; set; } = "dc=lmxopcua,dc=local";
/// <summary>
@@ -31,9 +39,14 @@ public sealed class LdapOptions
/// <c>cn={user},{SearchBase}</c> is attempted.
/// </summary>
public string ServiceAccountDn { get; set; } = string.Empty;
/// <summary>Gets or sets the service account password for LDAP authentication.</summary>
public string ServiceAccountPassword { get; set; } = string.Empty;
/// <summary>Gets or sets the LDAP attribute name for user display name.</summary>
public string DisplayNameAttribute { get; set; } = "cn";
/// <summary>Gets or sets the LDAP attribute name for group membership.</summary>
public string GroupAttribute { get; set; } = "memberOf";
/// <summary>
@@ -6,6 +6,10 @@ namespace ZB.MOM.WW.OtOpcUa.Security.Ldap;
/// </summary>
public static class RoleMapper
{
/// <summary>Maps LDAP groups to roles using the provided group-to-role mapping dictionary.</summary>
/// <param name="ldapGroups">The LDAP groups to map.</param>
/// <param name="groupToRole">The mapping dictionary from LDAP groups to roles.</param>
/// <returns>The list of roles corresponding to the LDAP groups.</returns>
public static IReadOnlyList<string> Map(
IReadOnlyCollection<string> ldapGroups,
IReadOnlyDictionary<string, string> groupToRole)
@@ -21,6 +21,9 @@ namespace ZB.MOM.WW.OtOpcUa.Security;
internal sealed class ConfigureJwtBearerFromTokenService(JwtTokenService tokenService)
: IPostConfigureOptions<JwtBearerOptions>
{
/// <summary>Configures JWT bearer options from the token service.</summary>
/// <param name="name">The options name.</param>
/// <param name="options">The JWT bearer options to configure.</param>
public void PostConfigure(string? name, JwtBearerOptions options)
{
if (name != JwtBearerDefaults.AuthenticationScheme) return;
@@ -36,6 +39,8 @@ public static class ServiceCollectionExtensions
/// tools, scripts). DataProtection keys persist to the shared ConfigDb so cookies survive
/// failover between nodes.
/// </summary>
/// <param name="services">The service collection.</param>
/// <param name="configuration">The application configuration.</param>
public static IServiceCollection AddOtOpcUaAuth(this IServiceCollection services, IConfiguration configuration)
{
services.AddOptions<JwtOptions>().Bind(configuration.GetSection(JwtOptions.SectionName));