using Shouldly; using ZB.MOM.NatsNet.Server; using ZB.MOM.NatsNet.Server.Auth; using ZB.MOM.NatsNet.Server.Internal; namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog; public sealed class AuthCalloutTests { [Fact] // T:111 public void AuthCalloutBasics_ShouldSucceed() { var client = new ClientConnection(ClientKind.Client) { Cid = 42, Host = "127.0.0.1", Opts = new ClientOptions { Username = "cl", Password = "pwd", Token = "tok", Nkey = "NK123", }, }; var req = new AuthorizationRequest(); AuthCallout.FillClientInfo(req, client); AuthCallout.FillConnectOpts(req, client); req.ClientInfoObj.ShouldNotBeNull(); req.ClientInfoObj!.Host.ShouldBe("127.0.0.1"); req.ClientInfoObj.Id.ShouldBe(42ul); req.ClientInfoObj.Kind.ShouldBe("client"); req.ClientInfoObj.Type.ShouldBe("client"); req.ConnectOptions.ShouldNotBeNull(); req.ConnectOptions!.Username.ShouldBe("cl"); req.ConnectOptions.Password.ShouldBe("pwd"); req.ConnectOptions.AuthToken.ShouldBe("tok"); req.ConnectOptions.Nkey.ShouldBe("NK123"); } [Fact] // T:112 public void AuthCalloutMultiAccounts_ShouldSucceed() { var c1 = new ClientConnection(ClientKind.Client) { Cid = 1, Host = "10.0.0.1", Opts = new ClientOptions { Username = "acc-a", Password = "pa" }, }; var c2 = new ClientConnection(ClientKind.Client) { Cid = 2, Host = "10.0.0.2", Opts = new ClientOptions { Username = "acc-b", Password = "pb" }, }; var req1 = new AuthorizationRequest(); var req2 = new AuthorizationRequest(); AuthCallout.FillClientInfo(req1, c1); AuthCallout.FillConnectOpts(req1, c1); AuthCallout.FillClientInfo(req2, c2); AuthCallout.FillConnectOpts(req2, c2); req1.ClientInfoObj!.Id.ShouldBe(1ul); req2.ClientInfoObj!.Id.ShouldBe(2ul); req1.ConnectOptions!.Username.ShouldBe("acc-a"); req2.ConnectOptions!.Username.ShouldBe("acc-b"); req1.ConnectOptions.Password.ShouldBe("pa"); req2.ConnectOptions.Password.ShouldBe("pb"); } [Fact] // T:113 public void AuthCalloutAllowedAccounts_ShouldSucceed() { var opts = new AuthCalloutOpts { Account = "AUTH", AllowedAccounts = ["A", "B"], }; opts.AllowedAccounts.ShouldContain("A"); opts.AllowedAccounts.ShouldContain("B"); opts.AllowedAccounts.ShouldNotContain("C"); } [Fact] // T:114 public void AuthCalloutClientTLSCerts_ShouldSucceed() { var client = CreateClient(7, "localhost", "u", "p"); client.GetTlsCertificate().ShouldBeNull(); var req = BuildRequest(client); req.ClientInfoObj!.Host.ShouldBe("localhost"); req.ConnectOptions!.Username.ShouldBe("u"); } [Fact] // T:116 public void AuthCalloutOperatorNoServerConfigCalloutAllowed_ShouldSucceed() { var (server, err) = NatsServer.NewServer(new ServerOptions { AuthCallout = new AuthCalloutOpts { Account = "AUTH", Issuer = "issuer" }, }); err.ShouldBeNull(); server.ShouldNotBeNull(); } [Fact] // T:117 public void AuthCalloutOperatorModeBasics_ShouldSucceed() { var (server, err) = NatsServer.NewServer(new ServerOptions()); err.ShouldBeNull(); var opts = new ServerOptions { TrustedOperators = [new object()], AuthCallout = new AuthCalloutOpts { Account = "AUTH", Issuer = "OP" }, }; Should.Throw(() => server!.ProcessClientOrLeafAuthentication(CreateClient(1, "h", "u", "p"), opts)); } [Fact] // T:120 public void AuthCalloutServerConfigEncryption_ShouldSucceed() { var opts = new AuthCalloutOpts { Account = "AUTH", XKey = "XKEY123", }; opts.XKey.ShouldBe("XKEY123"); opts.XKey.ShouldNotBeNullOrWhiteSpace(); } [Fact] // T:121 public void AuthCalloutOperatorModeEncryption_ShouldSucceed() { var opts = new ServerOptions { TrustedOperators = [new object()], AuthCallout = new AuthCalloutOpts { Account = "AUTH", Issuer = "OP", XKey = "ENCXKEY", }, }; opts.AuthCallout.ShouldNotBeNull(); opts.AuthCallout!.XKey.ShouldBe("ENCXKEY"); opts.TrustedOperators.ShouldNotBeNull(); } [Fact] // T:122 public void AuthCalloutServerTags_ShouldSucceed() { var (server, err) = NatsServer.NewServer(new ServerOptions { Tags = ["blue", "edge"], AuthCallout = new AuthCalloutOpts { Account = "AUTH" }, }); err.ShouldBeNull(); server.ShouldNotBeNull(); server!.GetOpts().Tags.ShouldBe(["blue", "edge"]); } [Fact] // T:123 public void AuthCalloutServerClusterAndVersion_ShouldSucceed() { var (server, err) = NatsServer.NewServer(new ServerOptions { Cluster = new ClusterOpts { Name = "C1" }, AuthCallout = new AuthCalloutOpts { Account = "AUTH" }, }); err.ShouldBeNull(); server.ShouldNotBeNull(); server!.GetOpts().Cluster.Name.ShouldBe("C1"); ServerConstants.Version.ShouldNotBeNullOrWhiteSpace(); } [Fact] // T:127 public void AuthCalloutConnectEvents_ShouldSucceed() { var req = BuildRequest(CreateClient(10, "127.0.0.1", "event-user", "event-pass")); req.ClientInfoObj!.Type.ShouldBe("client"); req.ClientInfoObj.Kind.ShouldBe("client"); req.ConnectOptions!.Username.ShouldBe("event-user"); } [Fact] // T:132 public void AuthCalloutOperator_AnyAccount_ShouldSucceed() { var opts = new AuthCalloutOpts { Account = "AUTH", AllowedAccounts = ["*"], }; opts.AllowedAccounts.Single().ShouldBe("*"); } [Fact] // T:133 public void AuthCalloutWSClientTLSCerts_ShouldSucceed() { var client = CreateClient(12, "ws.local", "ws-user", "ws-pass"); client.GetTlsCertificate().ShouldBeNull(); var req = BuildRequest(client); req.ConnectOptions!.Username.ShouldBe("ws-user"); req.ClientInfoObj!.Type.ShouldBe("client"); } [Fact] // T:137 public void AuthCalloutLeafNodeAndOperatorMode_ShouldSucceed() { var leaf = CreateClient(20, "leaf.host", "leaf-user", "leaf-pass", kind: ClientKind.Leaf); var req = BuildRequest(leaf); req.ClientInfoObj!.Kind.ShouldBe("leaf"); req.ConnectOptions!.Username.ShouldBe("leaf-user"); } [Fact] // T:138 public void AuthCalloutLeafNodeAndConfigMode_ShouldSucceed() { var (server, err) = NatsServer.NewServer(new ServerOptions()); err.ShouldBeNull(); var opts = new ServerOptions { AuthCallout = new AuthCalloutOpts { Account = "AUTH" }, }; Should.Throw(() => server!.ProcessClientOrLeafAuthentication(CreateClient(21, "leaf", kind: ClientKind.Leaf), opts)); } [Fact] // T:140 public void AuthCalloutOperatorModeMismatchedCalloutCreds_ShouldSucceed() { var (server, err) = NatsServer.NewServer(new ServerOptions()); err.ShouldBeNull(); var opts = new ServerOptions { TrustedOperators = [new object()], AuthCallout = new AuthCalloutOpts { Account = "AUTH", Issuer = "OP", AuthUsers = ["bad-user"] }, }; Should.Throw(() => server!.ProcessClientOrLeafAuthentication(CreateClient(30, "h", "user", "pass"), opts)); } [Fact] // T:141 public void AuthCalloutLeafNodeOperatorModeMismatchedCreds_ShouldSucceed() { var (server, err) = NatsServer.NewServer(new ServerOptions()); err.ShouldBeNull(); var opts = new ServerOptions { TrustedOperators = [new object()], AuthCallout = new AuthCalloutOpts { Account = "AUTH", Issuer = "OP", AuthUsers = ["leaf-bad"] }, }; Should.Throw(() => server!.ProcessClientOrLeafAuthentication(CreateClient(31, "leaf", "lu", "lp", kind: ClientKind.Leaf), opts)); } private static ClientConnection CreateClient( ulong cid, string host, string username = "", string password = "", string token = "", string nkey = "", ClientKind kind = ClientKind.Client) { return new ClientConnection(kind) { Cid = cid, Host = host, Opts = new ClientOptions { Username = username, Password = password, Token = token, Nkey = nkey, }, }; } private static AuthorizationRequest BuildRequest(ClientConnection client) { var req = new AuthorizationRequest(); AuthCallout.FillClientInfo(req, client); AuthCallout.FillConnectOpts(req, client); return req; } }