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; /// /// F13c — verifies faithfully translates /// outcomes into OpcUaUserAuthResult and turns LDAP /// backend exceptions into a denial rather than letting them escape into the SDK. /// public sealed class LdapOpcUaUserAuthenticatorTests { [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.Instance); var result = await sut.AuthenticateUserNameAsync("alice", "secret", CancellationToken.None); result.Success.ShouldBeTrue(); result.DisplayName.ShouldBe("Alice"); result.Roles.ShouldBe(new[] { "ConfigEditor" }); } [Fact] public async Task Authenticate_LDAP_failure_returns_Deny_with_error_text() { var ldap = new FakeLdap(new LdapAuthResult(false, null, "mallory", Array.Empty(), Array.Empty(), "Invalid username or password")); var sut = new LdapOpcUaUserAuthenticator(ldap, NullLogger.Instance); var result = await sut.AuthenticateUserNameAsync("mallory", "wrong", CancellationToken.None); result.Success.ShouldBeFalse(); result.Error.ShouldBe("Invalid username or password"); } [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.Instance); var result = await sut.AuthenticateUserNameAsync("anyone", "x", CancellationToken.None); result.Success.ShouldBeFalse(); result.Error.ShouldNotBeNull(); result.Error.ShouldContain("backend"); } [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(), new[] { "ReadOnly" }, null)); var sut = new LdapOpcUaUserAuthenticator(ldap, NullLogger.Instance); var result = await sut.AuthenticateUserNameAsync("alice", "x", CancellationToken.None); result.Success.ShouldBeTrue(); result.DisplayName.ShouldBe("alice"); } private sealed class FakeLdap : ILdapAuthService { private readonly Func _handler; public FakeLdap(LdapAuthResult fixed_) => _handler = _ => fixed_; public FakeLdap(Func handler) => _handler = handler; public Task AuthenticateAsync(string username, string password, CancellationToken ct = default) => Task.FromResult(_handler(username)); } }