64e3fbe035
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.
91 lines
4.3 KiB
C#
91 lines
4.3 KiB
C#
using Microsoft.Extensions.Logging.Abstractions;
|
|
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Host.OpcUa;
|
|
using ZB.MOM.WW.OtOpcUa.Security.Ldap;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Host.IntegrationTests;
|
|
|
|
/// <summary>
|
|
/// F13c — verifies <see cref="LdapOpcUaUserAuthenticator"/> faithfully translates
|
|
/// <see cref="ILdapAuthService"/> outcomes into <c>OpcUaUserAuthResult</c> and turns LDAP
|
|
/// backend exceptions into a denial rather than letting them escape into the SDK.
|
|
/// </summary>
|
|
public sealed class LdapOpcUaUserAuthenticatorTests
|
|
{
|
|
/// <summary>Verifies that successful LDAP authentication returns Allow result with user roles.</summary>
|
|
[Fact]
|
|
public async Task Authenticate_LDAP_success_returns_Allow_with_roles()
|
|
{
|
|
var ldap = new FakeLdap(new LdapAuthResult(true, "Alice", "alice", new[] { "configeditor" }, new[] { "ConfigEditor" }, null));
|
|
var sut = new LdapOpcUaUserAuthenticator(ldap, NullLogger<LdapOpcUaUserAuthenticator>.Instance);
|
|
|
|
var result = await sut.AuthenticateUserNameAsync("alice", "secret", CancellationToken.None);
|
|
|
|
result.Success.ShouldBeTrue();
|
|
result.DisplayName.ShouldBe("Alice");
|
|
result.Roles.ShouldBe(new[] { "ConfigEditor" });
|
|
}
|
|
|
|
/// <summary>Verifies that LDAP authentication failure returns Deny result with error text.</summary>
|
|
[Fact]
|
|
public async Task Authenticate_LDAP_failure_returns_Deny_with_error_text()
|
|
{
|
|
var ldap = new FakeLdap(new LdapAuthResult(false, null, "mallory", Array.Empty<string>(), Array.Empty<string>(), "Invalid username or password"));
|
|
var sut = new LdapOpcUaUserAuthenticator(ldap, NullLogger<LdapOpcUaUserAuthenticator>.Instance);
|
|
|
|
var result = await sut.AuthenticateUserNameAsync("mallory", "wrong", CancellationToken.None);
|
|
|
|
result.Success.ShouldBeFalse();
|
|
result.Error.ShouldBe("Invalid username or password");
|
|
}
|
|
|
|
/// <summary>Verifies that LDAP exceptions are converted to backend error denial results.</summary>
|
|
[Fact]
|
|
public async Task Authenticate_LDAP_exception_returns_backend_error_denial()
|
|
{
|
|
var ldap = new FakeLdap(_ => throw new InvalidOperationException("LDAP unreachable"));
|
|
var sut = new LdapOpcUaUserAuthenticator(ldap, NullLogger<LdapOpcUaUserAuthenticator>.Instance);
|
|
|
|
var result = await sut.AuthenticateUserNameAsync("anyone", "x", CancellationToken.None);
|
|
|
|
result.Success.ShouldBeFalse();
|
|
result.Error.ShouldNotBeNull();
|
|
result.Error.ShouldContain("backend");
|
|
}
|
|
|
|
/// <summary>Verifies that authentication falls back to username when LDAP omits display name.</summary>
|
|
[Fact]
|
|
public async Task Authenticate_falls_back_to_username_when_LDAP_omits_display_name()
|
|
{
|
|
var ldap = new FakeLdap(new LdapAuthResult(true, null, "alice", Array.Empty<string>(), new[] { "ReadOnly" }, null));
|
|
var sut = new LdapOpcUaUserAuthenticator(ldap, NullLogger<LdapOpcUaUserAuthenticator>.Instance);
|
|
|
|
var result = await sut.AuthenticateUserNameAsync("alice", "x", CancellationToken.None);
|
|
|
|
result.Success.ShouldBeTrue();
|
|
result.DisplayName.ShouldBe("alice");
|
|
}
|
|
|
|
/// <summary>Test fake implementation of LDAP authentication service.</summary>
|
|
private sealed class FakeLdap : ILdapAuthService
|
|
{
|
|
private readonly Func<string, LdapAuthResult> _handler;
|
|
|
|
/// <summary>Initializes the fake with a fixed result that ignores the username.</summary>
|
|
/// <param name="fixed_">The result to return for any authentication attempt.</param>
|
|
public FakeLdap(LdapAuthResult fixed_) => _handler = _ => fixed_;
|
|
|
|
/// <summary>Initializes the fake with a handler function for custom results.</summary>
|
|
/// <param name="handler">The handler to invoke with the username to produce a result.</param>
|
|
public FakeLdap(Func<string, LdapAuthResult> handler) => _handler = handler;
|
|
|
|
/// <summary>Authenticates a user asynchronously via the handler function.</summary>
|
|
/// <param name="username">The username to authenticate.</param>
|
|
/// <param name="password">The password (ignored by the fake).</param>
|
|
/// <param name="ct">Cancellation token for the operation.</param>
|
|
public Task<LdapAuthResult> AuthenticateAsync(string username, string password, CancellationToken ct = default)
|
|
=> Task.FromResult(_handler(username));
|
|
}
|
|
}
|