// Copyright 2015-2025 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package test

import (
	"bufio"
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"net"
	"net/url"
	"os"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/nats-io/nats.go"

	"github.com/nats-io/nats-server/v2/server"
)

var noOpErrHandler = func(_ *nats.Conn, _ *nats.Subscription, _ error) {}

func TestTLSConnection(t *testing.T) {
	srv, opts := RunServerWithConfig("./configs/tls.conf")
	defer srv.Shutdown()

	endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port)
	nurl := fmt.Sprintf("tls://%s:%s@%s/", opts.Username, opts.Password, endpoint)
	nc, err := nats.Connect(nurl)
	if err == nil {
		nc.Close()
		t.Fatalf("Expected error trying to connect to secure server")
	}

	// Do simple SecureConnect
	nc, err = nats.Connect(fmt.Sprintf("tls://%s/", endpoint))
	if err == nil {
		nc.Close()
		t.Fatalf("Expected error trying to connect to secure server with no auth")
	}

	// Now do more advanced checking, verifying servername and using rootCA.

	nc, err = nats.Connect(nurl, nats.RootCAs("./configs/certs/ca.pem"))
	if err != nil {
		t.Fatalf("Got an error on Connect with Secure Options: %+v\n", err)
	}
	defer nc.Close()

	subj := "foo-tls"
	sub, _ := nc.SubscribeSync(subj)

	nc.Publish(subj, []byte("We are Secure!"))
	nc.Flush()
	nmsgs, _, _ := sub.Pending()
	if nmsgs != 1 {
		t.Fatalf("Expected to receive a message over the TLS connection")
	}
}

// TestTLSInProcessConnection checks that even if TLS is enabled on the server,
// that an in-process connection that does *not* use TLS still connects successfully.
func TestTLSInProcessConnection(t *testing.T) {
	srv, opts := RunServerWithConfig("./configs/tls.conf")
	defer srv.Shutdown()

	nc, err := nats.Connect("", nats.InProcessServer(srv), nats.UserInfo(opts.Username, opts.Password))
	if err != nil {
		t.Fatal(err)
	}
	defer nc.Close()

	if nc.TLSRequired() {
		t.Fatalf("Shouldn't have required TLS for in-process connection")
	}

	if _, err = nc.TLSConnectionState(); err == nil {
		t.Fatal("Should have got an error retrieving TLS connection state")
	}
}

func TestTLSClientCertificate(t *testing.T) {
	srv, opts := RunServerWithConfig("./configs/tlsverify.conf")
	defer srv.Shutdown()

	nurl := fmt.Sprintf("tls://%s:%d", opts.Host, opts.Port)

	_, err := nats.Connect(nurl)
	if err == nil {
		t.Fatalf("Expected error trying to connect to secure server without a certificate")
	}

	// Load client certificate to successfully connect.
	certFile := "./configs/certs/client-cert.pem"
	keyFile := "./configs/certs/client-key.pem"
	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		t.Fatalf("error parsing X509 certificate/key pair: %v", err)
	}

	// Load in root CA for server verification
	rootPEM, err := os.ReadFile("./configs/certs/ca.pem")
	if err != nil || rootPEM == nil {
		t.Fatalf("failed to read root certificate")
	}
	pool := x509.NewCertPool()
	ok := pool.AppendCertsFromPEM([]byte(rootPEM))
	if !ok {
		t.Fatalf("failed to parse root certificate")
	}

	config := &tls.Config{
		Certificates: []tls.Certificate{cert},
		ServerName:   opts.Host,
		RootCAs:      pool,
		MinVersion:   tls.VersionTLS12,
	}

	copts := nats.GetDefaultOptions()
	copts.Url = nurl
	copts.Secure = true
	copts.TLSConfig = config

	nc, err := copts.Connect()
	if err != nil {
		t.Fatalf("Got an error on Connect with Secure Options: %+v\n", err)
	}
	nc.Flush()
	defer nc.Close()
}

func TestTLSClientCertificateHasUserID(t *testing.T) {
	srv, opts := RunServerWithConfig("./configs/tls_cert_id.conf")
	defer srv.Shutdown()
	nurl := fmt.Sprintf("tls://%s:%d", opts.Host, opts.Port)
	nc, err := nats.Connect(nurl,
		nats.ClientCert("./configs/certs/client-id-auth-cert.pem", "./configs/certs/client-id-auth-key.pem"),
		nats.RootCAs("./configs/certs/ca.pem"))
	if err != nil {
		t.Fatalf("Expected to connect, got %v", err)
	}
	defer nc.Close()
}

func TestTLSClientCertificateCheckWithAllowedConnectionTypes(t *testing.T) {
	conf := createConfFile(t, []byte(
		`
		listen: "127.0.0.1:-1"
		tls {
			cert_file: "./configs/certs/server-cert.pem"
			key_file:  "./configs/certs/server-key.pem"
			timeout: 2
			ca_file:   "./configs/certs/ca.pem"
			verify_and_map: true
		}
		authorization {
			users = [
				{user: derek@nats.io, permissions: { publish:"foo" }, allowed_connection_types: ["WEBSOCKET"]}
			]
		}
	`))
	s, o := RunServerWithConfig(conf)
	defer s.Shutdown()

	nurl := fmt.Sprintf("tls://%s:%d", o.Host, o.Port)
	nc, err := nats.Connect(nurl,
		nats.ClientCert("./configs/certs/client-id-auth-cert.pem", "./configs/certs/client-id-auth-key.pem"),
		nats.RootCAs("./configs/certs/ca.pem"))
	if err == nil {
		if nc != nil {
			nc.Close()
		}
		t.Fatal("Expected connection to fail, it did not")
	}
}

func TestTLSClientCertificateCNBasedAuth(t *testing.T) {
	srv, opts := RunServerWithConfig("./configs/tls_cert_cn.conf")
	defer srv.Shutdown()
	nurl := fmt.Sprintf("tls://%s:%d", opts.Host, opts.Port)
	errCh1 := make(chan error)
	errCh2 := make(chan error)

	// Using the default permissions
	nc1, err := nats.Connect(nurl,
		nats.ClientCert("./configs/certs/tlsauth/client.pem", "./configs/certs/tlsauth/client-key.pem"),
		nats.RootCAs("./configs/certs/tlsauth/ca.pem"),
		nats.ErrorHandler(func(_ *nats.Conn, _ *nats.Subscription, err error) {
			errCh1 <- err
		}),
	)
	if err != nil {
		t.Fatalf("Expected to connect, got %v", err)
	}
	defer nc1.Close()

	// Admin permissions can publish to '>'
	nc2, err := nats.Connect(nurl,
		nats.ClientCert("./configs/certs/tlsauth/client2.pem", "./configs/certs/tlsauth/client2-key.pem"),
		nats.RootCAs("./configs/certs/tlsauth/ca.pem"),
		nats.ErrorHandler(func(_ *nats.Conn, _ *nats.Subscription, err error) {
			errCh2 <- err
		}),
	)
	if err != nil {
		t.Fatalf("Expected to connect, got %v", err)
	}
	defer nc2.Close()

	err = nc1.Publish("foo.bar", []byte("hi"))
	if err != nil {
		t.Fatal(err)
	}
	_, err = nc1.SubscribeSync("foo.>")
	if err != nil {
		t.Fatal(err)
	}
	nc1.Flush()

	sub, err := nc2.SubscribeSync(">")
	if err != nil {
		t.Fatal(err)
	}
	nc2.Flush()
	err = nc2.Publish("hello", []byte("hi"))
	if err != nil {
		t.Fatal(err)
	}
	nc2.Flush()

	_, err = sub.NextMsg(1 * time.Second)
	if err != nil {
		t.Fatalf("Error during wait for next message: %s", err)
	}

	// Wait for a couple of errors
	var count int
Loop:
	for {
		select {
		case err := <-errCh1:
			if err != nil {
				count++
			}
			if count == 2 {
				break Loop
			}
		case err := <-errCh2:
			if err != nil {
				t.Fatalf("Received unexpected auth error from client: %s", err)
			}
		case <-time.After(2 * time.Second):
			t.Fatalf("Timed out expecting auth errors")
		}
	}
}

func TestTLSClientCertificateSANsBasedAuth(t *testing.T) {
	// In this test we have 3 clients, one with permissions defined
	// for SAN 'app.nats.dev', other for SAN 'app.nats.prod' and another
	// one without the default permissions for the CN.
	srv, opts := RunServerWithConfig("./configs/tls_cert_san_auth.conf")
	defer srv.Shutdown()
	nurl := fmt.Sprintf("tls://%s:%d", opts.Host, opts.Port)
	defaultErrCh := make(chan error)
	devErrCh := make(chan error)
	prodErrCh := make(chan error)

	// default: Using the default permissions (no SANs)
	//
	//    Subject: CN = www.nats.io
	//
	defaultc, err := nats.Connect(nurl,
		nats.ClientCert("./configs/certs/sans/client.pem", "./configs/certs/sans/client-key.pem"),
		nats.RootCAs("./configs/certs/sans/ca.pem"),
		nats.ErrorHandler(func(_ *nats.Conn, _ *nats.Subscription, err error) {
			defaultErrCh <- err
		}),
	)
	if err != nil {
		t.Fatalf("Expected to connect, got %v", err)
	}
	defer defaultc.Close()

	// dev: Using SAN 'app.nats.dev' with permissions to its own sandbox.
	//
	//    Subject: CN = www.nats.io
	//
	//    X509v3 Subject Alternative Name:
	//        DNS:app.nats.dev, DNS:*.app.nats.dev
	//
	devc, err := nats.Connect(nurl,
		nats.ClientCert("./configs/certs/sans/dev.pem", "./configs/certs/sans/dev-key.pem"),
		nats.RootCAs("./configs/certs/sans/ca.pem"),
		nats.ErrorHandler(func(_ *nats.Conn, _ *nats.Subscription, err error) {
			devErrCh <- err
		}),
	)
	if err != nil {
		t.Fatalf("Expected to connect, got %v", err)
	}
	defer devc.Close()

	// prod: Using SAN 'app.nats.prod' with all permissions.
	//
	//    Subject: CN = www.nats.io
	//
	//    X509v3 Subject Alternative Name:
	//        DNS:app.nats.prod, DNS:*.app.nats.prod
	//
	prodc, err := nats.Connect(nurl,
		nats.ClientCert("./configs/certs/sans/prod.pem", "./configs/certs/sans/prod-key.pem"),
		nats.RootCAs("./configs/certs/sans/ca.pem"),
		nats.ErrorHandler(func(_ *nats.Conn, _ *nats.Subscription, err error) {
			prodErrCh <- err
		}),
	)
	if err != nil {
		t.Fatalf("Expected to connect, got %v", err)
	}
	defer prodc.Close()

	// No permissions to publish or subscribe on foo.>
	err = devc.Publish("foo.bar", []byte("hi"))
	if err != nil {
		t.Fatal(err)
	}
	_, err = devc.SubscribeSync("foo.>")
	if err != nil {
		t.Fatal(err)
	}
	devc.Flush()

	prodSub, err := prodc.SubscribeSync(">")
	if err != nil {
		t.Fatal(err)
	}
	prodc.Flush()
	err = prodc.Publish("hello", []byte("hi"))
	if err != nil {
		t.Fatal(err)
	}
	prodc.Flush()

	// prod: can receive message on wildcard subscription.
	_, err = prodSub.NextMsg(1 * time.Second)
	if err != nil {
		t.Fatalf("Error during wait for next message: %s", err)
	}

	// dev: enough permissions to publish to sandbox subject.
	devSub, err := devc.SubscribeSync("sandbox.>")
	if err != nil {
		t.Fatal(err)
	}
	devc.Flush()
	err = devc.Publish("sandbox.foo.bar", []byte("hi!"))
	if err != nil {
		t.Fatal(err)
	}
	// both dev and prod clients can receive message
	_, err = devSub.NextMsg(1 * time.Second)
	if err != nil {
		t.Fatalf("Error during wait for next message: %s", err)
	}
	_, err = prodSub.NextMsg(1 * time.Second)
	if err != nil {
		t.Fatalf("Error during wait for next message: %s", err)
	}

	// default: no enough permissions
	_, err = defaultc.SubscribeSync("sandbox.>")
	if err != nil {
		t.Fatal(err)
	}
	defaultSub, err := defaultc.SubscribeSync("public.>")
	if err != nil {
		t.Fatal(err)
	}
	defaultc.Flush()
	err = devc.Publish("public.foo.bar", []byte("hi!"))
	if err != nil {
		t.Fatal(err)
	}
	_, err = defaultSub.NextMsg(1 * time.Second)
	if err != nil {
		t.Fatalf("Error during wait for next message: %s", err)
	}

	// Wait for a couple of errors
	var count int
Loop:
	for {
		select {
		case err := <-defaultErrCh:
			if err != nil {
				count++
			}
			if count == 3 {
				break Loop
			}
		case err := <-devErrCh:
			if err != nil {
				count++
			}
			if count == 3 {
				break Loop
			}
		case err := <-prodErrCh:
			if err != nil {
				t.Fatalf("Received unexpected auth error from client: %s", err)
			}
		case <-time.After(2 * time.Second):
			t.Fatalf("Timed out expecting auth errors")
		}
	}
}

func TestTLSClientCertificateTLSAuthMultipleOptions(t *testing.T) {
	srv, opts := RunServerWithConfig("./configs/tls_cert_san_emails.conf")
	defer srv.Shutdown()
	nurl := fmt.Sprintf("tls://%s:%d", opts.Host, opts.Port)
	defaultErrCh := make(chan error)
	devErrCh := make(chan error)
	prodErrCh := make(chan error)

	// default: Using the default permissions, there are SANs
	// present in the cert but they are not users in the NATS config
	// so the subject is used instead.
	//
	//    Subject: CN = www.nats.io
	//
	//    X509v3 Subject Alternative Name:
	//        DNS:app.nats.dev, DNS:*.app.nats.dev
	//
	defaultc, err := nats.Connect(nurl,
		nats.ClientCert("./configs/certs/sans/dev.pem", "./configs/certs/sans/dev-key.pem"),
		nats.RootCAs("./configs/certs/sans/ca.pem"),
		nats.ErrorHandler(func(_ *nats.Conn, _ *nats.Subscription, err error) {
			defaultErrCh <- err
		}),
	)
	if err != nil {
		t.Fatalf("Expected to connect, got %v", err)
	}
	defer defaultc.Close()

	// dev: Using SAN to validate user, even if emails are present in the config.
	//
	//    Subject: CN = www.nats.io
	//
	//    X509v3 Subject Alternative Name:
	//        DNS:app.nats.dev, email:admin@app.nats.dev, email:root@app.nats.dev
	//
	devc, err := nats.Connect(nurl,
		nats.ClientCert("./configs/certs/sans/dev-email.pem", "./configs/certs/sans/dev-email-key.pem"),
		nats.RootCAs("./configs/certs/sans/ca.pem"),
		nats.ErrorHandler(func(_ *nats.Conn, _ *nats.Subscription, err error) {
			devErrCh <- err
		}),
	)
	if err != nil {
		t.Fatalf("Expected to connect, got %v", err)
	}
	defer devc.Close()

	// prod: Using SAN '*.app.nats.prod' with all permissions, which is not the first SAN.
	//
	//    Subject: CN = www.nats.io
	//
	//    X509v3 Subject Alternative Name:
	//        DNS:app.nats.prod, DNS:*.app.nats.prod
	//
	prodc, err := nats.Connect(nurl,
		nats.ClientCert("./configs/certs/sans/prod.pem", "./configs/certs/sans/prod-key.pem"),
		nats.RootCAs("./configs/certs/sans/ca.pem"),
		nats.ErrorHandler(func(_ *nats.Conn, _ *nats.Subscription, err error) {
			prodErrCh <- err
		}),
	)
	if err != nil {
		t.Fatalf("Expected to connect, got %v", err)
	}
	defer prodc.Close()

	// No permissions to publish or subscribe on foo.>
	err = devc.Publish("foo.bar", []byte("hi"))
	if err != nil {
		t.Fatal(err)
	}
	_, err = devc.SubscribeSync("foo.>")
	if err != nil {
		t.Fatal(err)
	}
	devc.Flush()

	prodSub, err := prodc.SubscribeSync(">")
	if err != nil {
		t.Fatal(err)
	}
	prodc.Flush()
	err = prodc.Publish("hello", []byte("hi"))
	if err != nil {
		t.Fatal(err)
	}
	prodc.Flush()

	// prod: can receive message on wildcard subscription.
	_, err = prodSub.NextMsg(1 * time.Second)
	if err != nil {
		t.Fatalf("Error during wait for next message: %s", err)
	}

	// dev: enough permissions to publish to sandbox subject.
	devSub, err := devc.SubscribeSync("sandbox.>")
	if err != nil {
		t.Fatal(err)
	}
	devc.Flush()
	err = devc.Publish("sandbox.foo.bar", []byte("hi!"))
	if err != nil {
		t.Fatal(err)
	}
	// both dev and prod clients can receive message
	_, err = devSub.NextMsg(1 * time.Second)
	if err != nil {
		t.Fatalf("Error during wait for next message: %s", err)
	}
	_, err = prodSub.NextMsg(1 * time.Second)
	if err != nil {
		t.Fatalf("Error during wait for next message: %s", err)
	}

	// default: no enough permissions
	_, err = defaultc.SubscribeSync("sandbox.>")
	if err != nil {
		t.Fatal(err)
	}
	defaultSub, err := defaultc.SubscribeSync("public.>")
	if err != nil {
		t.Fatal(err)
	}
	defaultc.Flush()
	err = devc.Publish("public.foo.bar", []byte("hi!"))
	if err != nil {
		t.Fatal(err)
	}
	_, err = defaultSub.NextMsg(1 * time.Second)
	if err != nil {
		t.Fatalf("Error during wait for next message: %s", err)
	}

	// Wait for a couple of errors
	var count int
Loop:
	for {
		select {
		case err := <-defaultErrCh:
			if err != nil {
				count++
			}
			if count == 3 {
				break Loop
			}
		case err := <-devErrCh:
			if err != nil {
				count++
			}
			if count == 3 {
				break Loop
			}
		case err := <-prodErrCh:
			if err != nil {
				t.Fatalf("Received unexpected auth error from client: %s", err)
			}
		case <-time.After(2 * time.Second):
			t.Fatalf("Timed out expecting auth errors")
		}
	}
}

func TestTLSRoutesCertificateCNBasedAuth(t *testing.T) {
	// Base config for the servers
	optsA := LoadConfig("./configs/tls_cert_cn_routes.conf")
	optsB := LoadConfig("./configs/tls_cert_cn_routes.conf")
	optsC := LoadConfig("./configs/tls_cert_cn_routes_invalid_auth.conf")

	// TLS map should have been enabled
	if !optsA.Cluster.TLSMap || !optsB.Cluster.TLSMap || !optsC.Cluster.TLSMap {
		t.Error("Expected Cluster TLS verify and map feature to be activated")
	}

	routeURLs := "nats://localhost:9935, nats://localhost:9936, nats://localhost:9937"

	optsA.Host = "127.0.0.1"
	optsA.Port = 9335
	optsA.Cluster.Name = "xyz"
	optsA.Cluster.Host = optsA.Host
	optsA.Cluster.Port = 9935
	optsA.Routes = server.RoutesFromStr(routeURLs)
	optsA.NoSystemAccount = true
	srvA := RunServer(optsA)
	defer srvA.Shutdown()

	optsB.Host = "127.0.0.1"
	optsB.Port = 9336
	optsB.Cluster.Name = "xyz"
	optsB.Cluster.Host = optsB.Host
	optsB.Cluster.Port = 9936
	optsB.Routes = server.RoutesFromStr(routeURLs)
	optsB.NoSystemAccount = true
	srvB := RunServer(optsB)
	defer srvB.Shutdown()

	optsC.Host = "127.0.0.1"
	optsC.Port = 9337
	optsC.Cluster.Name = "xyz"
	optsC.Cluster.Host = optsC.Host
	optsC.Cluster.Port = 9937
	optsC.Routes = server.RoutesFromStr(routeURLs)
	optsC.NoSystemAccount = true
	srvC := RunServer(optsC)
	defer srvC.Shutdown()

	// srvC is not connected to srvA and srvB due to wrong cert
	checkClusterFormed(t, srvA, srvB)

	nc1, err := nats.Connect(fmt.Sprintf("%s:%d", optsA.Host, optsA.Port), nats.Name("A"))
	if err != nil {
		t.Fatalf("Expected to connect, got %v", err)
	}
	defer nc1.Close()

	nc2, err := nats.Connect(fmt.Sprintf("%s:%d", optsB.Host, optsB.Port), nats.Name("B"))
	if err != nil {
		t.Fatalf("Expected to connect, got %v", err)
	}
	defer nc2.Close()

	// This client is partitioned from rest of cluster due to using wrong cert.
	nc3, err := nats.Connect(fmt.Sprintf("%s:%d", optsC.Host, optsC.Port), nats.Name("C"))
	if err != nil {
		t.Fatalf("Expected to connect, got %v", err)
	}
	defer nc3.Close()

	// Allowed via permissions to broadcast to other members of cluster.
	publicSub, err := nc1.SubscribeSync("public.>")
	if err != nil {
		t.Fatal(err)
	}

	// Not part of cluster permissions so message is not forwarded.
	privateSub, err := nc1.SubscribeSync("private.>")
	if err != nil {
		t.Fatal(err)
	}
	// This ensures that srvA has received the sub, not srvB
	nc1.Flush()

	checkFor(t, time.Second, 15*time.Millisecond, func() error {
		if n := srvB.NumSubscriptions(); n != 1 {
			return fmt.Errorf("Expected 1 sub, got %v", n)
		}
		return nil
	})

	// Not forwarded by cluster so won't be received.
	err = nc2.Publish("private.foo", []byte("private message on server B"))
	if err != nil {
		t.Fatal(err)
	}
	err = nc2.Publish("public.foo", []byte("public message from server B to server A"))
	if err != nil {
		t.Fatal(err)
	}
	nc2.Flush()
	err = nc3.Publish("public.foo", []byte("dropped message since unauthorized server"))
	if err != nil {
		t.Fatal(err)
	}
	nc3.Flush()

	// Message will be received since allowed via permissions
	_, err = publicSub.NextMsg(1 * time.Second)
	if err != nil {
		t.Fatalf("Error during wait for next message: %s", err)
	}
	msg, err := privateSub.NextMsg(500 * time.Millisecond)
	if err == nil {
		t.Errorf("Received unexpected message from private sub: %+v", msg)
	}
	msg, err = publicSub.NextMsg(500 * time.Millisecond)
	if err == nil {
		t.Errorf("Received unexpected message from private sub: %+v", msg)
	}
}

func TestTLSGatewaysCertificateCNBasedAuth(t *testing.T) {
	// Base config for the servers
	optsA := LoadConfig("./configs/tls_cert_cn_gateways.conf")
	optsB := LoadConfig("./configs/tls_cert_cn_gateways.conf")
	optsC := LoadConfig("./configs/tls_cert_cn_gateways_invalid_auth.conf")

	// TLS map should have been enabled
	if !optsA.Gateway.TLSMap || !optsB.Gateway.TLSMap || !optsC.Gateway.TLSMap {
		t.Error("Expected Cluster TLS verify and map feature to be activated")
	}

	gwA, err := url.Parse("nats://localhost:9995")
	if err != nil {
		t.Fatal(err)
	}
	gwB, err := url.Parse("nats://localhost:9996")
	if err != nil {
		t.Fatal(err)
	}
	gwC, err := url.Parse("nats://localhost:9997")
	if err != nil {
		t.Fatal(err)
	}

	optsA.Host = "127.0.0.1"
	optsA.Port = -1
	optsA.Gateway.Name = "A"
	optsA.Gateway.Port = 9995

	optsB.Host = "127.0.0.1"
	optsB.Port = -1
	optsB.Gateway.Name = "B"
	optsB.Gateway.Port = 9996

	optsC.Host = "127.0.0.1"
	optsC.Port = -1
	optsC.Gateway.Name = "C"
	optsC.Gateway.Port = 9997

	gateways := make([]*server.RemoteGatewayOpts, 3)
	gateways[0] = &server.RemoteGatewayOpts{
		Name: optsA.Gateway.Name,
		URLs: []*url.URL{gwA},
	}
	gateways[1] = &server.RemoteGatewayOpts{
		Name: optsB.Gateway.Name,
		URLs: []*url.URL{gwB},
	}
	gateways[2] = &server.RemoteGatewayOpts{
		Name: optsC.Gateway.Name,
		URLs: []*url.URL{gwC},
	}
	optsA.Gateway.Gateways = gateways
	optsB.Gateway.Gateways = gateways
	optsC.Gateway.Gateways = gateways

	server.SetGatewaysSolicitDelay(100 * time.Millisecond)
	defer server.ResetGatewaysSolicitDelay()

	srvA := RunServer(optsA)
	defer srvA.Shutdown()

	srvB := RunServer(optsB)
	defer srvB.Shutdown()

	srvC := RunServer(optsC)
	defer srvC.Shutdown()

	// Because we need to use "localhost" in the gw URLs (to match
	// hostname in the user/CN), the server may try to connect to
	// a [::1], etc.. that may or may not work, so give a lot of
	// time for that to complete ok.
	waitForOutboundGateways(t, srvA, 1, 5*time.Second)
	waitForOutboundGateways(t, srvB, 1, 5*time.Second)

	nc1, err := nats.Connect(srvA.Addr().String(), nats.Name("A"))
	if err != nil {
		t.Fatalf("Expected to connect, got %v", err)
	}
	defer nc1.Close()

	nc2, err := nats.Connect(srvB.Addr().String(), nats.Name("B"))
	if err != nil {
		t.Fatalf("Expected to connect, got %v", err)
	}
	defer nc2.Close()

	nc3, err := nats.Connect(srvC.Addr().String(), nats.Name("C"))
	if err != nil {
		t.Fatalf("Expected to connect, got %v", err)
	}
	defer nc3.Close()

	received := make(chan *nats.Msg)
	_, err = nc1.Subscribe("foo", func(msg *nats.Msg) {
		select {
		case received <- msg:
		default:
		}
	})
	if err != nil {
		t.Error(err)
	}
	_, err = nc3.Subscribe("help", func(msg *nats.Msg) {
		nc3.Publish(msg.Reply, []byte("response"))
	})
	if err != nil {
		t.Error(err)
	}

	go func() {
		for range time.NewTicker(10 * time.Millisecond).C {
			if nc2.IsClosed() {
				return
			}
			nc2.Publish("foo", []byte("bar"))
		}
	}()

	select {
	case <-received:
	case <-time.After(2 * time.Second):
		t.Error("Timed out waiting for gateway messages")
	}

	msg, err := nc2.Request("help", []byte("should fail"), 100*time.Millisecond)
	if err == nil {
		t.Errorf("Expected to not receive any messages, got: %+v", msg)
	}
}

func TestTLSVerifyClientCertificate(t *testing.T) {
	srv, opts := RunServerWithConfig("./configs/tlsverify_noca.conf")
	defer srv.Shutdown()

	nurl := fmt.Sprintf("tls://%s:%d", opts.Host, opts.Port)

	// The client is configured properly, but the server has no CA
	// to verify the client certificate. Connection should fail.
	nc, err := nats.Connect(nurl,
		nats.ClientCert("./configs/certs/client-cert.pem", "./configs/certs/client-key.pem"),
		nats.RootCAs("./configs/certs/ca.pem"))
	if err == nil {
		nc.Close()
		t.Fatal("Expected failure to connect, did not")
	}
}

func TestTLSConnectionTimeout(t *testing.T) {
	opts := LoadConfig("./configs/tls.conf")
	opts.TLSTimeout = 0.25

	srv := RunServer(opts)
	defer srv.Shutdown()

	// Dial with normal TCP
	endpoint := net.JoinHostPort(opts.Host, fmt.Sprintf("%d", opts.Port))
	conn, err := net.Dial("tcp", endpoint)
	if err != nil {
		t.Fatalf("Could not connect to %q", endpoint)
	}
	defer conn.Close()

	// Read deadlines
	conn.SetReadDeadline(time.Now().Add(2 * time.Second))

	// Read the INFO string.
	br := bufio.NewReader(conn)
	info, err := br.ReadString('\n')
	if err != nil {
		t.Fatalf("Failed to read INFO - %v", err)
	}
	if !strings.HasPrefix(info, "INFO ") {
		t.Fatalf("INFO response incorrect: %s\n", info)
	}
	wait := time.Duration(opts.TLSTimeout * float64(time.Second))
	time.Sleep(wait)
	// Read deadlines
	conn.SetReadDeadline(time.Now().Add(2 * time.Second))
	tlsErr, err := br.ReadString('\n')
	if err == nil && !strings.Contains(tlsErr, "-ERR 'Secure Connection - TLS Required") {
		t.Fatalf("TLS Timeout response incorrect: %q\n", tlsErr)
	}
}

// Ensure there is no race between authorization timeout and TLS handshake.
func TestTLSAuthorizationShortTimeout(t *testing.T) {
	opts := LoadConfig("./configs/tls.conf")
	opts.AuthTimeout = 0.001

	srv := RunServer(opts)
	defer srv.Shutdown()

	endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port)
	nurl := fmt.Sprintf("tls://%s:%s@%s/", opts.Username, opts.Password, endpoint)

	// Expect an error here (no CA) but not a TLS oversized record error which
	// indicates the authorization timeout fired too soon.
	_, err := nats.Connect(nurl)
	if err == nil {
		t.Fatal("Expected error trying to connect to secure server")
	}
	if strings.Contains(err.Error(), "oversized record") {
		t.Fatal("Corrupted TLS handshake:", err)
	}
}

func TestClientTLSAndNonTLSConnections(t *testing.T) {
	s, opts := RunServerWithConfig("./configs/tls_mixed.conf")
	defer s.Shutdown()

	surl := fmt.Sprintf("tls://%s:%d", opts.Host, opts.Port)
	nc, err := nats.Connect(surl, nats.RootCAs("./configs/certs/ca.pem"))
	if err != nil {
		t.Fatalf("Failed to connect with TLS: %v", err)
	}
	defer nc.Close()

	// Now also make sure we can connect through plain text.
	nurl := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port)
	nc2, err := nats.Connect(nurl)
	if err != nil {
		t.Fatalf("Failed to connect without TLS: %v", err)
	}
	defer nc2.Close()

	// Make sure they can go back and forth.
	sub, err := nc.SubscribeSync("foo")
	if err != nil {
		t.Fatalf("Error subscribing: %v", err)
	}
	sub2, err := nc2.SubscribeSync("bar")
	if err != nil {
		t.Fatalf("Error subscribing: %v", err)
	}
	nc.Flush()
	nc2.Flush()

	nmsgs := 100
	for i := 0; i < nmsgs; i++ {
		nc2.Publish("foo", []byte("HELLO FROM PLAINTEXT"))
		nc.Publish("bar", []byte("HELLO FROM TLS"))
	}
	// Now wait for the messages.
	checkFor(t, time.Second, 10*time.Millisecond, func() error {
		if msgs, _, err := sub.Pending(); err != nil || msgs != nmsgs {
			return fmt.Errorf("Did not receive the correct number of messages: %d", msgs)
		}
		if msgs, _, err := sub2.Pending(); err != nil || msgs != nmsgs {
			return fmt.Errorf("Did not receive the correct number of messages: %d", msgs)
		}
		return nil
	})

}

func stressConnect(t *testing.T, wg *sync.WaitGroup, errCh chan error, url string, index int) {
	defer wg.Done()

	subName := fmt.Sprintf("foo.%d", index)

	for i := 0; i < 33; i++ {
		nc, err := nats.Connect(url, nats.RootCAs("./configs/certs/ca.pem"))
		if err != nil {
			errCh <- fmt.Errorf("Unable to create TLS connection: %v\n", err)
			return
		}
		defer nc.Close()

		sub, err := nc.SubscribeSync(subName)
		if err != nil {
			errCh <- fmt.Errorf("Unable to subscribe on '%s': %v\n", subName, err)
			return
		}

		if err := nc.Publish(subName, []byte("secure data")); err != nil {
			errCh <- fmt.Errorf("Unable to send on '%s': %v\n", subName, err)
		}

		if _, err := sub.NextMsg(2 * time.Second); err != nil {
			errCh <- fmt.Errorf("Unable to get next message: %v\n", err)
		}

		nc.Close()
	}

	errCh <- nil
}

func TestTLSStressConnect(t *testing.T) {
	opts, err := server.ProcessConfigFile("./configs/tls.conf")
	if err != nil {
		panic(fmt.Sprintf("Error processing configuration file: %v", err))
	}
	opts.NoSigs, opts.NoLog = true, true

	// For this test, remove the authorization
	opts.Username = ""
	opts.Password = ""

	// Increase ssl timeout
	opts.TLSTimeout = 2.0

	srv := RunServer(opts)
	defer srv.Shutdown()

	nurl := fmt.Sprintf("tls://%s:%d", opts.Host, opts.Port)

	threadCount := 3

	errCh := make(chan error, threadCount)

	var wg sync.WaitGroup
	wg.Add(threadCount)

	for i := 0; i < threadCount; i++ {
		go stressConnect(t, &wg, errCh, nurl, i)
	}

	wg.Wait()

	var lastError error
	for i := 0; i < threadCount; i++ {
		err := <-errCh
		if err != nil {
			lastError = err
		}
	}

	if lastError != nil {
		t.Fatalf("%v\n", lastError)
	}
}

func TestTLSBadAuthError(t *testing.T) {
	srv, opts := RunServerWithConfig("./configs/tls.conf")
	defer srv.Shutdown()

	endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port)
	nurl := fmt.Sprintf("tls://%s:%s@%s/", opts.Username, "NOT_THE_PASSWORD", endpoint)

	_, err := nats.Connect(nurl, nats.RootCAs("./configs/certs/ca.pem"))
	if err == nil {
		t.Fatalf("Expected error trying to connect to secure server")
	}
	if strings.ToLower(err.Error()) != nats.ErrAuthorization.Error() {
		t.Fatalf("Expected and auth violation, got %v\n", err)
	}
}

func TestTLSConnectionCurvePref(t *testing.T) {
	srv, opts := RunServerWithConfig("./configs/tls_curve_pref.conf")
	defer srv.Shutdown()

	if len(opts.TLSConfig.CurvePreferences) != 1 {
		t.Fatal("Invalid curve preference loaded.")
	}

	if opts.TLSConfig.CurvePreferences[0] != tls.CurveP256 {
		t.Fatalf("Invalid curve preference loaded [%v].", opts.TLSConfig.CurvePreferences[0])
	}

	endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port)
	nurl := fmt.Sprintf("tls://%s:%s@%s/", opts.Username, opts.Password, endpoint)
	nc, err := nats.Connect(nurl)
	if err == nil {
		nc.Close()
		t.Fatalf("Expected error trying to connect to secure server")
	}

	// Do simple SecureConnect
	nc, err = nats.Connect(fmt.Sprintf("tls://%s/", endpoint))
	if err == nil {
		nc.Close()
		t.Fatalf("Expected error trying to connect to secure server with no auth")
	}

	// Now do more advanced checking, verifying servername and using rootCA.

	nc, err = nats.Connect(nurl, nats.RootCAs("./configs/certs/ca.pem"))
	if err != nil {
		t.Fatalf("Got an error on Connect with Secure Options: %+v\n", err)
	}
	defer nc.Close()

	subj := "foo-tls"
	sub, _ := nc.SubscribeSync(subj)

	nc.Publish(subj, []byte("We are Secure!"))
	nc.Flush()
	nmsgs, _, _ := sub.Pending()
	if nmsgs != 1 {
		t.Fatalf("Expected to receive a message over the TLS connection")
	}
}

type captureSlowConsumerLogger struct {
	dummyLogger
	ch    chan string
	gotIt bool
}

func (l *captureSlowConsumerLogger) Noticef(format string, v ...any) {
	msg := fmt.Sprintf(format, v...)
	if strings.Contains(msg, "Slow Consumer") {
		l.Lock()
		if !l.gotIt {
			l.gotIt = true
			l.ch <- msg
		}
		l.Unlock()
	}
}

func TestTLSTimeoutNotReportSlowConsumer(t *testing.T) {
	oa, err := server.ProcessConfigFile("./configs/srv_a_tls.conf")
	if err != nil {
		t.Fatalf("Unable to load config file: %v", err)
	}

	// Override TLSTimeout to very small value so that handshake fails
	oa.Cluster.TLSTimeout = 0.0000001
	sa := RunServer(oa)
	defer sa.Shutdown()

	ch := make(chan string, 1)
	cscl := &captureSlowConsumerLogger{ch: ch}
	sa.SetLogger(cscl, false, false)

	ob, err := server.ProcessConfigFile("./configs/srv_b_tls.conf")
	if err != nil {
		t.Fatalf("Unable to load config file: %v", err)
	}

	sb := RunServer(ob)
	defer sb.Shutdown()

	// Watch the logger for a bit and make sure we don't get any
	// Slow Consumer error.
	select {
	case e := <-ch:
		t.Fatalf("Unexpected slow consumer error: %s", e)
	case <-time.After(500 * time.Millisecond):
		// ok
	}
}

func TestTLSHandshakeFailureMemUsage(t *testing.T) {
	for i, test := range []struct {
		name   string
		config string
	}{
		{
			"connect to TLS client port",
			`
				port: -1
				%s
			`,
		},
		{
			"connect to TLS route port",
			`
				port: -1
				cluster {
					port: -1
					%s
				}
			`,
		},
		{
			"connect to TLS gateway port",
			`
				port: -1
				gateway {
					name: "A"
					port: -1
					%s
				}
			`,
		},
	} {
		t.Run(test.name, func(t *testing.T) {
			content := fmt.Sprintf(test.config, `
				tls {
					cert_file: "configs/certs/server-cert.pem"
					key_file: "configs/certs/server-key.pem"
					ca_file: "configs/certs/ca.pem"
					timeout: 5
				}
			`)
			conf := createConfFile(t, []byte(content))
			s, opts := RunServerWithConfig(conf)
			defer s.Shutdown()

			var port int
			switch i {
			case 0:
				port = opts.Port
			case 1:
				port = s.ClusterAddr().Port
			case 2:
				port = s.GatewayAddr().Port
			}

			varz, _ := s.Varz(nil)
			base := varz.Mem
			buf := make([]byte, 1024*1024)
			for i := 0; i < 100; i++ {
				conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port))
				if err != nil {
					t.Fatalf("Error on dial: %v", err)
				}
				conn.SetWriteDeadline(time.Now().Add(10 * time.Millisecond))
				conn.Write(buf)
				conn.Close()
			}

			varz, _ = s.Varz(nil)
			newval := (varz.Mem - base) / (1024 * 1024)
			// May need to adjust that, but right now, with 100 clients
			// we are at about 20MB for client port, 11MB for route
			// and 6MB for gateways, so pick 50MB as the threshold
			if newval >= 50 {
				t.Fatalf("Consumed too much memory: %v MB", newval)
			}
		})
	}
}

func TestTLSClientAuthWithRDNSequence(t *testing.T) {
	for _, test := range []struct {
		name   string
		config string
		certs  nats.Option
		err    error
		rerr   error
	}{
		// To generate certs for these tests:
		// USE `regenerate_rdns_svid.sh` TO REGENERATE
		//
		// ```
		// openssl req -newkey rsa:2048  -nodes -keyout client-$CLIENT_ID.key -subj "/C=US/ST=CA/L=Los Angeles/OU=NATS/O=NATS/CN=*.example.com/DC=example/DC=com" -addext extendedKeyUsage=clientAuth -out client-$CLIENT_ID.csr
		// openssl x509 -req -extfile <(printf "subjectAltName=DNS:localhost,DNS:example.com,DNS:www.example.com") -days 1825 -in client-$CLIENT_ID.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out client-$CLIENT_ID.pem
		// ```
		//
		// To confirm subject from cert:
		//
		// ```
		// openssl x509 -in client-$CLIENT_ID.pem -text | grep Subject:
		// ```
		//
		{
			"connect with tls using full RDN sequence",
			`
                                port: -1
                                %s

                                authorization {
                                  users = [
                                    { user = "CN=localhost,OU=NATS,O=NATS,L=Los Angeles,ST=CA,C=US,DC=foo1,DC=foo2" }
                                  ]
                                }
                        `,
			// C = US, ST = CA, L = Los Angeles, O = NATS, OU = NATS, CN = localhost, DC = foo1, DC = foo2
			nats.ClientCert("./configs/certs/rdns/client-a.pem", "./configs/certs/rdns/client-a.key"),
			nil,
			nil,
		},
		{
			"connect with tls using full RDN sequence in original order",
			`
				port: -1
				%s

				authorization {
				  users = [
				    { user = "DC=foo2,DC=foo1,CN=localhost,OU=NATS,O=NATS,L=Los Angeles,ST=CA,C=US" }
				  ]
				}
			`,
			// C = US, ST = CA, L = Los Angeles, O = NATS, OU = NATS, CN = localhost, DC = foo1, DC = foo2
			nats.ClientCert("./configs/certs/rdns/client-a.pem", "./configs/certs/rdns/client-a.key"),
			nil,
			nil,
		},
		{
			"connect with tls using partial RDN sequence has different permissions",
			`
				port: -1
				%s

				authorization {
				  users = [
				    { user = "DC=foo2,DC=foo1,CN=localhost,OU=NATS,O=NATS,L=Los Angeles,ST=CA,C=US" },
				    { user = "CN=localhost,OU=NATS,O=NATS,L=Los Angeles,ST=CA,C=US,DC=foo1,DC=foo2" },
				    { user = "CN=localhost,OU=NATS,O=NATS,L=Los Angeles,ST=CA,C=US",
                                      permissions = { subscribe = { deny = ">" }} }
				  ]
				}
			`,
			// C = US, ST = California, L = Los Angeles, O = NATS, OU = NATS, CN = localhost
			nats.ClientCert("./configs/certs/rdns/client-b.pem", "./configs/certs/rdns/client-b.key"),
			nil,
			errors.New("nats: timeout"),
		},
		{
			"connect with tls and RDN sequence partially matches",
			`
				port: -1
				%s

				authorization {
				  users = [
				    { user = "DC=foo2,DC=foo1,CN=localhost,OU=NATS,O=NATS,L=Los Angeles,ST=CA,C=US" }
				    { user = "CN=localhost,OU=NATS,O=NATS,L=Los Angeles,ST=CA,C=US,DC=foo1,DC=foo2" }
				    { user = "CN=localhost,OU=NATS,O=NATS,L=Los Angeles,ST=CA,C=US"},
				  ]
				}
			`,
			// Cert is:
			//
			// C = US, ST = CA, L = Los Angeles, O = NATS, OU = NATS, CN = localhost, DC = foo3, DC = foo4
			//
			// but it will actually match the user without DCs so will not get an error:
			//
			// C = US, ST = CA, L = Los Angeles, O = NATS, OU = NATS, CN = localhost
			//
			nats.ClientCert("./configs/certs/rdns/client-c.pem", "./configs/certs/rdns/client-c.key"),
			nil,
			nil,
		},
		{
			"connect with tls and RDN sequence does not match",
			`
				port: -1
				%s

				authorization {
				  users = [
				    { user = "CN=localhost,OU=NATS,O=NATS,L=Los Angeles,ST=CA,C=US,DC=foo1,DC=foo2" },
				    { user = "DC=foo2,DC=foo1,CN=localhost,OU=NATS,O=NATS,L=Los Angeles,ST=CA,C=US" }
				  ]
				}
			`,
			// C = US, ST = California, L = Los Angeles, O = NATS, OU = NATS, CN = localhost, DC = foo3, DC = foo4
			//
			nats.ClientCert("./configs/certs/rdns/client-c.pem", "./configs/certs/rdns/client-c.key"),
			errors.New("nats: Authorization Violation"),
			nil,
		},
		{
			"connect with tls and RDN sequence with space after comma not should matter",
			`
				port: -1
				%s

				authorization {
				  users = [
				    { user = "DC=foo2, DC=foo1, CN=localhost, OU=NATS, O=NATS, L=Los Angeles, ST=CA, C=US" }
				  ]
				}
			`,
			// C=US/ST=California/L=Los Angeles/O=NATS/OU=NATS/CN=localhost/DC=foo1/DC=foo2
			//
			nats.ClientCert("./configs/certs/rdns/client-a.pem", "./configs/certs/rdns/client-a.key"),
			nil,
			nil,
		},
		{
			"connect with tls and full RDN sequence respects order",
			`
				port: -1
				%s

				authorization {
				  users = [
				    { user = "DC=com, DC=example, CN=*.example.com, O=NATS, OU=NATS, L=Los Angeles, ST=CA, C=US" }
				  ]
				}
			`,
			//
			// C = US, ST = CA, L = Los Angeles, OU = NATS, O = NATS, CN = *.example.com, DC = example, DC = com
			//
			nats.ClientCert("./configs/certs/rdns/client-d.pem", "./configs/certs/rdns/client-d.key"),
			nil,
			nil,
		},
		{
			"connect with tls and full RDN sequence with added domainComponents and spaces also matches",
			`
				port: -1
				%s

				authorization {
				  users = [
				    { user = "CN=*.example.com,OU=NATS,O=NATS,L=Los Angeles,ST=CA,C=US,DC=example,DC=com" }
				  ]
				}
			`,
			//
			// C = US, ST = CA, L = Los Angeles, OU = NATS, O = NATS, CN = *.example.com, DC = example, DC = com
			//
			nats.ClientCert("./configs/certs/rdns/client-d.pem", "./configs/certs/rdns/client-d.key"),
			nil,
			nil,
		},
		{
			"connect with tls and full RDN sequence with correct order takes precedence over others matches",
			`
				port: -1
				%s

				authorization {
				  users = [
				    { user = "CN=*.example.com,OU=NATS,O=NATS,L=Los Angeles,ST=CA,C=US,DC=example,DC=com",
                                        permissions = { subscribe = { deny = ">" }} }

				    # This should take precedence since it is in the RFC2253 order.
				    { user = "DC=com,DC=example,CN=*.example.com,O=NATS,OU=NATS,L=Los Angeles,ST=CA,C=US" }

				    { user = "CN=*.example.com,OU=NATS,O=NATS,L=Los Angeles,ST=CA,C=US",
                                        permissions = { subscribe = { deny = ">" }} }
				  ]
				}
			`,
			//
			// C = US, ST = CA, L = Los Angeles, OU = NATS, O = NATS, CN = *.example.com, DC = example, DC = com
			//
			nats.ClientCert("./configs/certs/rdns/client-d.pem", "./configs/certs/rdns/client-d.key"),
			nil,
			nil,
		},
		{
			"connect with tls and RDN includes multiple CN elements",
			`
				port: -1
				%s

				authorization {
				  users = [
				    { user = "DC=com,DC=acme,OU=Organic Units,OU=Users,CN=jdoe,CN=123456,CN=John Doe" }
                                  ]
				}
			`,
			//
			// OpenSSL: -subj "/CN=John Doe/CN=123456/CN=jdoe/OU=Users/OU=Organic Units/DC=acme/DC=com"
			// Go:       CN=jdoe,OU=Users+OU=Organic Units
			// RFC2253:  DC=com,DC=acme,OU=Organic Units,OU=Users,CN=jdoe,CN=123456,CN=John Doe
			//
			nats.ClientCert("./configs/certs/rdns/client-e.pem", "./configs/certs/rdns/client-e.key"),
			nil,
			nil,
		},
		{
			"connect with tls and DN includes a multi value RDN",
			`
				port: -1
				%s

				authorization {
				  users = [
				    { user = "CN=John Doe,DC=DEV+O=users,DC=OpenSSL,DC=org" }
                                  ]
				}
			`,
			//
			// OpenSSL: -subj "/DC=org/DC=OpenSSL/DC=DEV+O=users/CN=John Doe" -multivalue-rdn
			// Go:       CN=John Doe,O=users
			// RFC2253:  CN=John Doe,DC=DEV+O=users,DC=OpenSSL,DC=org
			//
			nats.ClientCert("./configs/certs/rdns/client-f.pem", "./configs/certs/rdns/client-f.key"),
			nil,
			nil,
		},
		{
			"connect with tls and DN includes a multi value RDN but there is no match",
			`
				port: -1
				%s

				authorization {
				  users = [
				    { user = "CN=John Doe,DC=DEV,DC=OpenSSL,DC=org" }
                                  ]
				}
			`,
			//
			// OpenSSL: -subj "/DC=org/DC=OpenSSL/DC=DEV+O=users/CN=John Doe" -multivalue-rdn
			// Go:       CN=John Doe,O=users
			// RFC2253:  CN=John Doe,DC=DEV+O=users,DC=OpenSSL,DC=org
			//
			nats.ClientCert("./configs/certs/rdns/client-f.pem", "./configs/certs/rdns/client-f.key"),
			errors.New("nats: Authorization Violation"),
			nil,
		},
		{
			"connect with tls and DN includes a multi value RDN that are reordered",
			`
				port: -1
				%s

				authorization {
				  users = [
				    { user = "CN=John Doe,O=users+DC=DEV,DC=OpenSSL,DC=org" }
                                  ]
				}
			`,
			//
			// OpenSSL: -subj "/DC=org/DC=OpenSSL/DC=DEV+O=users/CN=John Doe" -multivalue-rdn
			// Go:       CN=John Doe,O=users
			// RFC2253:  CN=John Doe,DC=DEV+O=users,DC=OpenSSL,DC=org
			//
			nats.ClientCert("./configs/certs/rdns/client-f.pem", "./configs/certs/rdns/client-f.key"),
			nil,
			nil,
		},
	} {
		t.Run(test.name, func(t *testing.T) {
			content := fmt.Sprintf(test.config, `
				tls {
					cert_file: "configs/certs/rdns/server.pem"
					key_file: "configs/certs/rdns/server.key"
					ca_file: "configs/certs/rdns/ca.pem"
					timeout: 5
					verify_and_map: true
				}
			`)
			conf := createConfFile(t, []byte(content))
			s, opts := RunServerWithConfig(conf)
			defer s.Shutdown()

			nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
				test.certs,
				nats.RootCAs("./configs/certs/rdns/ca.pem"),
				nats.ErrorHandler(noOpErrHandler),
			)
			if test.err == nil && err != nil {
				t.Errorf("Expected to connect, got %v", err)
			} else if test.err != nil && err == nil {
				t.Errorf("Expected error on connect")
			} else if test.err != nil && err != nil {
				// Error on connect was expected
				if test.err.Error() != err.Error() {
					t.Errorf("Expected error %s, got: %s", test.err, err)
				}
				return
			}
			defer nc.Close()

			nc.Subscribe("ping", func(m *nats.Msg) {
				m.Respond([]byte("pong"))
			})
			nc.Flush()

			_, err = nc.Request("ping", []byte("ping"), 250*time.Millisecond)
			if test.rerr != nil && err == nil {
				t.Errorf("Expected error getting response")
			} else if test.rerr == nil && err != nil {
				t.Errorf("Expected response")
			}
		})
	}
}

func TestTLSClientAuthWithRDNSequenceReordered(t *testing.T) {
	for _, test := range []struct {
		name   string
		config string
		certs  nats.Option
		err    error
		rerr   error
	}{
		{
			"connect with tls and DN includes a multi value RDN that are reordered",
			`
				port: -1
				%s

				authorization {
				  users = [
				    { user = "DC=org,DC=OpenSSL,O=users+DC=DEV,CN=John Doe" }
                                  ]
				}
			`,
			//
			// OpenSSL: -subj "/DC=org/DC=OpenSSL/DC=DEV+O=users/CN=John Doe" -multivalue-rdn
			// Go:       CN=John Doe,O=users
			// RFC2253:  CN=John Doe,DC=DEV+O=users,DC=OpenSSL,DC=org
			//
			nats.ClientCert("./configs/certs/rdns/client-f.pem", "./configs/certs/rdns/client-f.key"),
			nil,
			nil,
		},
		{
			"connect with tls and DN includes a multi value RDN that are reordered but not equal RDNs",
			`
				port: -1
				%s

				authorization {
				  users = [
				    { user = "DC=org,DC=OpenSSL,O=users,CN=John Doe" }
                                  ]
				}
			`,
			//
			// OpenSSL: -subj "/DC=org/DC=OpenSSL/DC=DEV+O=users/CN=John Doe" -multivalue-rdn
			// Go:       CN=John Doe,O=users
			// RFC2253:  CN=John Doe,DC=DEV+O=users,DC=OpenSSL,DC=org
			//
			nats.ClientCert("./configs/certs/rdns/client-f.pem", "./configs/certs/rdns/client-f.key"),
			errors.New("nats: Authorization Violation"),
			nil,
		},
		{
			"connect with tls and DN includes a multi value RDN that are reordered but not equal RDNs",
			`
				port: -1
				%s

				authorization {
				  users = [
				    { user = "DC=OpenSSL, DC=org, O=users, CN=John Doe" }
                                  ]
				}
			`,
			//
			// OpenSSL: -subj "/DC=org/DC=OpenSSL/DC=DEV+O=users/CN=John Doe" -multivalue-rdn
			// Go:       CN=John Doe,O=users
			// RFC2253:  CN=John Doe,DC=DEV+O=users,DC=OpenSSL,DC=org
			//
			nats.ClientCert("./configs/certs/rdns/client-f.pem", "./configs/certs/rdns/client-f.key"),
			errors.New("nats: Authorization Violation"),
			nil,
		},
	} {
		t.Run(test.name, func(t *testing.T) {
			content := fmt.Sprintf(test.config, `
				tls {
					cert_file: "configs/certs/rdns/server.pem"
					key_file: "configs/certs/rdns/server.key"
					ca_file: "configs/certs/rdns/ca.pem"
					timeout: 5
					verify_and_map: true
				}
			`)
			conf := createConfFile(t, []byte(content))
			s, opts := RunServerWithConfig(conf)
			defer s.Shutdown()

			nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
				test.certs,
				nats.RootCAs("./configs/certs/rdns/ca.pem"),
			)
			if test.err == nil && err != nil {
				t.Errorf("Expected to connect, got %v", err)
			} else if test.err != nil && err == nil {
				t.Errorf("Expected error on connect")
			} else if test.err != nil && err != nil {
				// Error on connect was expected
				if test.err.Error() != err.Error() {
					t.Errorf("Expected error %s, got: %s", test.err, err)
				}
				return
			}
			defer nc.Close()

			nc.Subscribe("ping", func(m *nats.Msg) {
				m.Respond([]byte("pong"))
			})
			nc.Flush()

			_, err = nc.Request("ping", []byte("ping"), 250*time.Millisecond)
			if test.rerr != nil && err == nil {
				t.Errorf("Expected error getting response")
			} else if test.rerr == nil && err != nil {
				t.Errorf("Expected response")
			}
		})
	}
}

func TestTLSClientSVIDAuth(t *testing.T) {
	for _, test := range []struct {
		name   string
		config string
		certs  nats.Option
		err    error
		rerr   error
	}{
		{
			"connect with tls using certificate with URIs",
			`
				port: -1
				%s

				authorization {
				  users = [
				    {
                                      user = "spiffe://localhost/my-nats-service/user-a"
                                    }
				  ]
				}
			`,
			nats.ClientCert("./configs/certs/svid/client-a.pem", "./configs/certs/svid/client-a.key"),
			nil,
			nil,
		},
		{
			"connect with tls using certificate with limited different permissions",
			`
				port: -1
				%s

				authorization {
				  users = [
				    {
                                      user = "spiffe://localhost/my-nats-service/user-a"
                                    },
				    {
                                      user = "spiffe://localhost/my-nats-service/user-b"
                                      permissions = { subscribe = { deny = ">" }}
                                    }
				  ]
				}
			`,
			nats.ClientCert("./configs/certs/svid/client-b.pem", "./configs/certs/svid/client-b.key"),
			nil,
			errors.New("nats: timeout"),
		},
		{
			"connect with tls without URIs in permissions will still match SAN",
			`
				port: -1
				%s

				authorization {
				  users = [
				    {
                                      user = "O=SPIRE,C=US"
                                    }
				  ]
				}
			`,
			nats.ClientCert("./configs/certs/svid/client-b.pem", "./configs/certs/svid/client-b.key"),
			nil,
			nil,
		},
		{
			"connect with tls but no permissions",
			`
				port: -1
				%s

				authorization {
				  users = [
				    {
                                      user = "spiffe://localhost/my-nats-service/user-c"
                                    }
				  ]
				}
			`,
			nats.ClientCert("./configs/certs/svid/client-a.pem", "./configs/certs/svid/client-a.key"),
			errors.New("nats: Authorization Violation"),
			nil,
		},
	} {
		t.Run(test.name, func(t *testing.T) {
			content := fmt.Sprintf(test.config, `
				tls {
					cert_file: "configs/certs/svid/server.pem"
					key_file: "configs/certs/svid/server.key"
					ca_file: "configs/certs/svid/ca.pem"
					timeout: 5
                                        insecure: true
					verify_and_map: true
				}
			`)
			conf := createConfFile(t, []byte(content))
			s, opts := RunServerWithConfig(conf)
			defer s.Shutdown()

			nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
				test.certs,
				nats.RootCAs("./configs/certs/svid/ca.pem"),
				nats.ErrorHandler(noOpErrHandler),
			)
			if test.err == nil && err != nil {
				t.Errorf("Expected to connect, got %v", err)
			} else if test.err != nil && err == nil {
				t.Errorf("Expected error on connect")
			} else if test.err != nil && err != nil {
				// Error on connect was expected
				if test.err.Error() != err.Error() {
					t.Errorf("Expected error %s, got: %s", test.err, err)
				}
				return
			}
			defer nc.Close()

			nc.Subscribe("ping", func(m *nats.Msg) {
				m.Respond([]byte("pong"))
			})
			nc.Flush()

			_, err = nc.Request("ping", []byte("ping"), 250*time.Millisecond)
			if test.rerr != nil && err == nil {
				t.Errorf("Expected error getting response")
			} else if test.rerr == nil && err != nil {
				t.Errorf("Expected response")
			}
		})
	}
}

func TestTLSPinnedCertsClient(t *testing.T) {
	tmpl := `
	host: localhost
	port: -1
	tls {
		ca_file: "configs/certs/ca.pem"
		cert_file: "configs/certs/server-cert.pem"
		key_file: "configs/certs/server-key.pem"
		# Require a client certificate and map user id from certificate
		verify: true
		pinned_certs: ["%s"]
	}`

	confFileName := createConfFile(t, []byte(fmt.Sprintf(tmpl, "aaaaaaaa09fde09451411ba3b42c0f74727d61a974c69fd3cf5257f39c75f0e9")))
	srv, o := RunServerWithConfig(confFileName)
	defer srv.Shutdown()

	if len(o.TLSPinnedCerts) != 1 {
		t.Fatal("expected one pinned cert")
	}

	opts := []nats.Option{
		nats.RootCAs("configs/certs/ca.pem"),
		nats.ClientCert("./configs/certs/client-cert.pem", "./configs/certs/client-key.pem"),
	}

	nc, err := nats.Connect(srv.ClientURL(), opts...)
	if err == nil {
		nc.Close()
		t.Fatalf("Expected error trying to connect without a certificate in pinned_certs")
	}

	os.WriteFile(confFileName, []byte(fmt.Sprintf(tmpl, "bf6f821f09fde09451411ba3b42c0f74727d61a974c69fd3cf5257f39c75f0e9")), 0660)
	if err := srv.Reload(); err != nil {
		t.Fatalf("on Reload got %v", err)
	}
	// reload pinned to the certs used
	nc, err = nats.Connect(srv.ClientURL(), opts...)
	if err != nil {
		t.Fatalf("Expected no error, got: %v", err)
	}
	nc.Close()
}

type captureWarnLogger struct {
	dummyLogger
	receive chan string
}

func newCaptureWarnLogger() *captureWarnLogger {
	return &captureWarnLogger{
		receive: make(chan string, 100),
	}
}

func (l *captureWarnLogger) Warnf(format string, v ...any) {
	l.receive <- fmt.Sprintf(format, v...)
}

func (l *captureWarnLogger) waitFor(expect string, timeout time.Duration) bool {
	for {
		select {
		case msg := <-l.receive:
			if strings.Contains(msg, expect) {
				return true
			}
		case <-time.After(timeout):
			return false
		}
	}
}

func TestTLSConnectionRate(t *testing.T) {
	config := `
		listen: "127.0.0.1:-1"
		tls {
			cert_file: "./configs/certs/server-cert.pem"
			key_file:  "./configs/certs/server-key.pem"
			connection_rate_limit: 3
		}
	`

	confFileName := createConfFile(t, []byte(config))

	srv, _ := RunServerWithConfig(confFileName)
	logger := newCaptureWarnLogger()
	srv.SetLogger(logger, false, false)
	defer srv.Shutdown()

	var err error
	count := 0
	for count < 10 {
		var nc *nats.Conn
		nc, err = nats.Connect(srv.ClientURL(), nats.RootCAs("./configs/certs/ca.pem"))

		if err != nil {
			break
		}
		nc.Close()
		count++
	}

	if count != 3 {
		t.Fatalf("Expected 3 connections per second, got %d (%v)", count, err)
	}

	if !logger.waitFor("connections due to TLS rate limiting", time.Second) {
		t.Fatalf("did not log 'TLS rate limiting' warning")
	}
}

func TestTLSPinnedCertsRoute(t *testing.T) {
	tmplSeed := `
	host: localhost
	port: -1
	cluster {
		port: -1
		pool_size: -1
		tls {
			ca_file: "configs/certs/ca.pem"
			cert_file: "configs/certs/server-cert.pem"
			key_file: "configs/certs/server-key.pem"
		}
	}`
	// this server connects to seed, but is set up to not trust seeds cert
	tmplSrv := `
	host: localhost
	port: -1
	cluster {
		port: -1
		routes = [nats-route://localhost:%d]
		tls {
			ca_file: "configs/certs/ca.pem"
			cert_file: "configs/certs/server-cert.pem"
			key_file: "configs/certs/server-key.pem"
			# Require a client certificate and map user id from certificate
			verify: true
			# expected to fail the seed server
			pinned_certs: ["%s"]
		}
	}`

	confSeed := createConfFile(t, []byte(tmplSeed))
	srvSeed, o := RunServerWithConfig(confSeed)
	defer srvSeed.Shutdown()

	confSrv := createConfFile(t, []byte(fmt.Sprintf(tmplSrv, o.Cluster.Port, "89386860ea1222698ea676fc97310bdf2bff6f7e2b0420fac3b3f8f5a08fede5")))
	srv, _ := RunServerWithConfig(confSrv)
	defer srv.Shutdown()

	checkClusterFormed(t, srvSeed, srv)

	// this change will result in the server being and remaining disconnected
	os.WriteFile(confSrv, []byte(fmt.Sprintf(tmplSrv, o.Cluster.Port, "aaaaaaaa09fde09451411ba3b42c0f74727d61a974c69fd3cf5257f39c75f0e9")), 0660)
	if err := srv.Reload(); err != nil {
		t.Fatalf("on Reload got %v", err)
	}

	checkNumRoutes(t, srvSeed, 0)
	checkNumRoutes(t, srv, 0)
}

func TestAllowNonTLSReload(t *testing.T) {
	tmpl := `
		listen: "127.0.0.1:-1"
		ping_interval: "%s"
		tls {
			ca_file: "configs/certs/ca.pem"
			cert_file: "configs/certs/server-cert.pem"
			key_file: "configs/certs/server-key.pem"
		}
		allow_non_tls: true
	`
	conf := createConfFile(t, []byte(fmt.Sprintf(tmpl, "10s")))
	s, o := RunServerWithConfig(conf)
	defer s.Shutdown()

	check := func() {
		t.Helper()
		nc := createClientConn(t, "127.0.0.1", o.Port)
		defer nc.Close()
		info := checkInfoMsg(t, nc)
		if !info.TLSAvailable {
			t.Fatal("TLSAvailable should be true, was false")
		}
		if info.TLSRequired {
			t.Fatal("TLSRequired should be false, was true")
		}
	}
	check()

	os.WriteFile(conf, []byte(fmt.Sprintf(tmpl, "20s")), 0660)
	if err := s.Reload(); err != nil {
		t.Fatalf("Error on reload: %v", err)
	}
	check()
}
