test(host): unreachable-directory activation fails bounded; document the SDK impersonation threading contract (03/S2)
This commit is contained in:
@@ -1,12 +1,17 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Server;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.Auth.Abstractions.Roles;
|
||||
using ZB.MOM.WW.OtOpcUa.Host.OpcUa;
|
||||
using ZB.MOM.WW.OtOpcUa.OpcUaServer;
|
||||
using ZB.MOM.WW.OtOpcUa.Security.Ldap;
|
||||
using LdapTransport = ZB.MOM.WW.Auth.Abstractions.Ldap.LdapTransport;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Host.IntegrationTests;
|
||||
|
||||
@@ -209,6 +214,51 @@ public sealed class LdapAuthResilienceTests
|
||||
ldap.Calls.ShouldBe(6, "a disabled circuit never fast-denies");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Outage-sim (offline-safe): the REAL <see cref="OtOpcUaLdapAuthService"/> + real shared library
|
||||
/// pointed at an UNROUTABLE host (a TCP blackhole — the same outage shape as "stop the GLAuth
|
||||
/// container"), driven through the REAL internal <see cref="OpcUaApplicationHost.HandleImpersonation"/>
|
||||
/// block-bridge. The activation must fail bounded (well under the SDK's default ~10-20 s stall) with
|
||||
/// <see cref="StatusCodes.BadIdentityTokenRejected"/> and no granted identity (03/S2).
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Activation_with_unreachable_directory_fails_within_bound()
|
||||
{
|
||||
var options = new LdapOptions
|
||||
{
|
||||
Enabled = true,
|
||||
DevStubMode = false,
|
||||
Server = "10.255.255.1", // RFC-blackhole: SYN goes nowhere
|
||||
Port = 3893,
|
||||
Transport = LdapTransport.None,
|
||||
AllowInsecure = true,
|
||||
SearchBase = "dc=zb,dc=local",
|
||||
ConnectionTimeoutMs = 500,
|
||||
AuthTimeoutMs = 1000,
|
||||
};
|
||||
|
||||
var ldap = new OtOpcUaLdapAuthService(Options.Create(options), NullLogger<OtOpcUaLdapAuthService>.Instance);
|
||||
var mapper = new FakeMapper(g => g.ToArray());
|
||||
var authenticator = new LdapOpcUaUserAuthenticator(
|
||||
ldap, ScopeFactoryWith(mapper), Options.Create(options),
|
||||
NullLogger<LdapOpcUaUserAuthenticator>.Instance);
|
||||
|
||||
var token = new UserNameIdentityToken { UserName = "alice", DecryptedPassword = Encoding.UTF8.GetBytes("secret") };
|
||||
var policy = new UserTokenPolicy(UserTokenType.UserName) { PolicyId = "username_basic256sha256" };
|
||||
var args = new ImpersonateEventArgs(token, policy, new EndpointDescription());
|
||||
|
||||
var sw = Stopwatch.StartNew();
|
||||
await Task.Run(() => OpcUaApplicationHost.HandleImpersonation(
|
||||
authenticator, args, NullLogger<object>.Instance), TestContext.Current.CancellationToken)
|
||||
.WaitAsync(TimeSpan.FromSeconds(10), TestContext.Current.CancellationToken);
|
||||
sw.Stop();
|
||||
|
||||
args.Identity.ShouldBeNull();
|
||||
args.IdentityValidationError.ShouldNotBeNull();
|
||||
args.IdentityValidationError.Code.ShouldBe(Opc.Ua.StatusCodes.BadIdentityTokenRejected);
|
||||
sw.Elapsed.ShouldBeLessThan(TimeSpan.FromSeconds(3));
|
||||
}
|
||||
|
||||
private static LdapAuthResult SystemFailure() =>
|
||||
new(false, null, "u", Array.Empty<string>(), Array.Empty<string>(), "Unexpected authentication error")
|
||||
{ IsSystemFailure = true };
|
||||
|
||||
Reference in New Issue
Block a user