// GLAuth integration test — opt-in only. // // Prerequisites // ------------- // 1. A running GLAuth instance (plaintext LDAP, no TLS). // A ready-made Docker Compose stack lives in the sibling repo: // ~/Desktop/ScadaBridge/infra/glauth // Start it with: docker compose up -d // Default listen address: localhost:3893 // // 2. Set the following environment variables before running: // ZB_LDAP_IT=1 (required — gates the test) // ZB_LDAP_SERVER=localhost (optional, default localhost) // ZB_LDAP_PORT=3893 (optional, default 3893) // ZB_LDAP_BASE=dc=lmxopcua,dc=local (optional) // ZB_LDAP_SVC_DN=cn=svc,dc=lmxopcua,dc=local (service-account DN) // ZB_LDAP_SVC_PW=svcpass (service-account password) // ZB_LDAP_USER=alice (test user login) // ZB_LDAP_PW=alicepass (test user password) // ZB_LDAP_USERATTR=cn (optional, default cn) // // Run command: // ZB_LDAP_IT=1 ZB_LDAP_SVC_DN=... ZB_LDAP_SVC_PW=... \ // ZB_LDAP_USER=... ZB_LDAP_PW=... \ // dotnet test tests/ZB.MOM.WW.Auth.Ldap.Tests \ // --filter "FullyQualifiedName~GLAuthIntegrationTests" // // Without ZB_LDAP_IT=1 the test is SKIPPED — it does not affect the normal CI run. using System.Net.Sockets; using ZB.MOM.WW.Auth.Abstractions.Ldap; using ZB.MOM.WW.Auth.Ldap; namespace ZB.MOM.WW.Auth.Ldap.Tests.Integration; public sealed class GLAuthIntegrationTests { /// /// Performs a real bind-then-search-then-bind against a live GLAuth instance. /// Verifies that authentication succeeds and that at least one LDAP group is returned. /// Skipped unless ZB_LDAP_IT=1 is set; skipped again if the server is unreachable. /// [SkippableFact] public async Task Authenticate_AgainstRealGLAuth_Succeeds() { // ------------------------------------------------------------------ opt-in gate Skip.IfNot( Environment.GetEnvironmentVariable("ZB_LDAP_IT") == "1", "Set ZB_LDAP_IT=1 and a reachable GLAuth to run."); // ------------------------------------------------------------------ read config var server = Environment.GetEnvironmentVariable("ZB_LDAP_SERVER") ?? "localhost"; var port = int.TryParse(Environment.GetEnvironmentVariable("ZB_LDAP_PORT"), out var p) ? p : 3893; var baseDn = Environment.GetEnvironmentVariable("ZB_LDAP_BASE") ?? "dc=lmxopcua,dc=local"; var svcDn = Environment.GetEnvironmentVariable("ZB_LDAP_SVC_DN") ?? ""; var svcPw = Environment.GetEnvironmentVariable("ZB_LDAP_SVC_PW") ?? ""; var user = Environment.GetEnvironmentVariable("ZB_LDAP_USER") ?? ""; var pw = Environment.GetEnvironmentVariable("ZB_LDAP_PW") ?? ""; var userAttr = Environment.GetEnvironmentVariable("ZB_LDAP_USERATTR") ?? "cn"; // ------------------------------------------------------------------ reachability probe try { using var tcp = new TcpClient(); // 3-second connect timeout to keep the test suite snappy when the server is absent var connectTask = tcp.ConnectAsync(server, port); if (!connectTask.Wait(TimeSpan.FromSeconds(3))) Skip.If(true, $"GLAuth not reachable at {server}:{port} (connect timed out)."); } catch (Exception ex) { Skip.If(true, $"GLAuth not reachable at {server}:{port}: {ex.Message}"); } // ------------------------------------------------------------------ build options var options = new LdapOptions { Enabled = true, Server = server, Port = port, Transport = LdapTransport.None, AllowInsecure = true, SearchBase = baseDn, ServiceAccountDn = svcDn, ServiceAccountPassword = svcPw, UserNameAttribute = userAttr, // GLAuth returns memberOf by default; keep the library default GroupAttribute = "memberOf", }; // ------------------------------------------------------------------ exercise the real service // Uses the public single-argument constructor, which wires up NovellLdapConnectionFactory // internally — no test seam involved. var svc = new LdapAuthService(options); var result = await svc.AuthenticateAsync(user, pw, default); // ------------------------------------------------------------------ assertions Assert.True(result.Succeeded, $"Authentication failed: {result.Failure} (server={server}:{port}, user={user})"); Assert.NotEmpty(result.Groups); } }