mirror of
https://github.com/hanzomsg/nats-server.git
synced 2026-10-30 20:27:34 +00:00
763 lines
19 KiB
Go
763 lines
19 KiB
Go
// Copyright 2012-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 server
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/url"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/nats-io/jwt/v2"
|
|
"github.com/nats-io/nats.go"
|
|
"github.com/nats-io/nkeys"
|
|
)
|
|
|
|
func TestUserCloneNilPermissions(t *testing.T) {
|
|
user := &User{
|
|
Username: "foo",
|
|
Password: "bar",
|
|
}
|
|
|
|
clone := user.clone()
|
|
|
|
if !reflect.DeepEqual(user, clone) {
|
|
t.Fatalf("Cloned Users are incorrect.\nexpected: %+v\ngot: %+v",
|
|
user, clone)
|
|
}
|
|
|
|
clone.Password = "baz"
|
|
if reflect.DeepEqual(user, clone) {
|
|
t.Fatal("Expected Users to be different")
|
|
}
|
|
}
|
|
|
|
func TestUserClone(t *testing.T) {
|
|
user := &User{
|
|
Username: "foo",
|
|
Password: "bar",
|
|
Permissions: &Permissions{
|
|
Publish: &SubjectPermission{
|
|
Allow: []string{"foo"},
|
|
},
|
|
Subscribe: &SubjectPermission{
|
|
Allow: []string{"bar"},
|
|
},
|
|
},
|
|
}
|
|
|
|
clone := user.clone()
|
|
|
|
if !reflect.DeepEqual(user, clone) {
|
|
t.Fatalf("Cloned Users are incorrect.\nexpected: %+v\ngot: %+v",
|
|
user, clone)
|
|
}
|
|
|
|
clone.Permissions.Subscribe.Allow = []string{"baz"}
|
|
if reflect.DeepEqual(user, clone) {
|
|
t.Fatal("Expected Users to be different")
|
|
}
|
|
}
|
|
|
|
func TestUserClonePermissionsNoLists(t *testing.T) {
|
|
user := &User{
|
|
Username: "foo",
|
|
Password: "bar",
|
|
Permissions: &Permissions{},
|
|
}
|
|
|
|
clone := user.clone()
|
|
|
|
if clone.Permissions.Publish != nil {
|
|
t.Fatalf("Expected Publish to be nil, got: %v", clone.Permissions.Publish)
|
|
}
|
|
if clone.Permissions.Subscribe != nil {
|
|
t.Fatalf("Expected Subscribe to be nil, got: %v", clone.Permissions.Subscribe)
|
|
}
|
|
}
|
|
|
|
func TestUserCloneNoPermissions(t *testing.T) {
|
|
user := &User{
|
|
Username: "foo",
|
|
Password: "bar",
|
|
}
|
|
|
|
clone := user.clone()
|
|
|
|
if clone.Permissions != nil {
|
|
t.Fatalf("Expected Permissions to be nil, got: %v", clone.Permissions)
|
|
}
|
|
}
|
|
|
|
func TestUserCloneNil(t *testing.T) {
|
|
user := (*User)(nil)
|
|
clone := user.clone()
|
|
if clone != nil {
|
|
t.Fatalf("Expected nil, got: %+v", clone)
|
|
}
|
|
}
|
|
|
|
func TestUserUnknownAllowedConnectionType(t *testing.T) {
|
|
o := DefaultOptions()
|
|
o.Users = []*User{{
|
|
Username: "user",
|
|
Password: "pwd",
|
|
AllowedConnectionTypes: testCreateAllowedConnectionTypes([]string{jwt.ConnectionTypeStandard, "someNewType"}),
|
|
}}
|
|
_, err := NewServer(o)
|
|
if err == nil || !strings.Contains(err.Error(), "connection type") {
|
|
t.Fatalf("Expected error about unknown connection type, got %v", err)
|
|
}
|
|
|
|
o.Users[0].AllowedConnectionTypes = testCreateAllowedConnectionTypes([]string{"websocket"})
|
|
s, err := NewServer(o)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
s.mu.Lock()
|
|
user := s.opts.Users[0]
|
|
s.mu.Unlock()
|
|
for act := range user.AllowedConnectionTypes {
|
|
if act != jwt.ConnectionTypeWebsocket {
|
|
t.Fatalf("Expected map to have been updated with proper case, got %v", act)
|
|
}
|
|
}
|
|
// Same with NKey user now.
|
|
o.Users = nil
|
|
o.Nkeys = []*NkeyUser{{
|
|
Nkey: "somekey",
|
|
AllowedConnectionTypes: testCreateAllowedConnectionTypes([]string{jwt.ConnectionTypeStandard, "someNewType"}),
|
|
}}
|
|
_, err = NewServer(o)
|
|
if err == nil || !strings.Contains(err.Error(), "connection type") {
|
|
t.Fatalf("Expected error about unknown connection type, got %v", err)
|
|
}
|
|
o.Nkeys[0].AllowedConnectionTypes = testCreateAllowedConnectionTypes([]string{"websocket"})
|
|
s, err = NewServer(o)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
s.mu.Lock()
|
|
nkey := s.opts.Nkeys[0]
|
|
s.mu.Unlock()
|
|
for act := range nkey.AllowedConnectionTypes {
|
|
if act != jwt.ConnectionTypeWebsocket {
|
|
t.Fatalf("Expected map to have been updated with proper case, got %v", act)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDNSAltNameMatching(t *testing.T) {
|
|
for idx, test := range []struct {
|
|
altName string
|
|
urls []string
|
|
match bool
|
|
}{
|
|
{"foo", []string{"FOO"}, true},
|
|
{"foo", []string{".."}, false},
|
|
{"foo", []string{"."}, false},
|
|
{"Foo", []string{"foO"}, true},
|
|
{"FOO", []string{"foo"}, true},
|
|
{"foo1", []string{"bar"}, false},
|
|
{"multi", []string{"m", "mu", "mul", "multi"}, true},
|
|
{"multi", []string{"multi", "m", "mu", "mul"}, true},
|
|
{"foo.bar", []string{"foo", "foo.bar.bar", "foo.baz"}, false},
|
|
{"foo.Bar", []string{"foo", "bar.foo", "Foo.Bar"}, true},
|
|
{"foo.*", []string{"foo", "bar.foo", "Foo.Bar"}, false}, // only match left most
|
|
{"f*.bar", []string{"foo", "bar.foo", "Foo.Bar"}, false},
|
|
{"*.bar", []string{"foo.bar"}, true},
|
|
{"*", []string{"baz.bar", "bar", "z.y"}, true},
|
|
{"*", []string{"bar"}, true},
|
|
{"*", []string{"."}, false},
|
|
{"*", []string{""}, true},
|
|
{"*", []string{"*"}, true},
|
|
{"bar.*", []string{"bar.*"}, true},
|
|
{"*.Y-X-red-mgmt.default.svc", []string{"A.Y-X-red-mgmt.default.svc"}, true},
|
|
{"*.Y-X-green-mgmt.default.svc", []string{"A.Y-X-green-mgmt.default.svc"}, true},
|
|
{"*.Y-X-blue-mgmt.default.svc", []string{"A.Y-X-blue-mgmt.default.svc"}, true},
|
|
{"Y-X-red-mgmt", []string{"Y-X-red-mgmt"}, true},
|
|
{"Y-X-red-mgmt", []string{"X-X-red-mgmt"}, false},
|
|
{"Y-X-red-mgmt", []string{"Y-X-green-mgmt"}, false},
|
|
{"Y-X-red-mgmt", []string{"Y"}, false},
|
|
{"Y-X-red-mgmt", []string{"Y-X"}, false},
|
|
{"Y-X-red-mgmt", []string{"Y-X-red"}, false},
|
|
{"Y-X-red-mgmt", []string{"X-red-mgmt"}, false},
|
|
{"Y-X-green-mgmt", []string{"Y-X-green-mgmt"}, true},
|
|
{"Y-X-blue-mgmt", []string{"Y-X-blue-mgmt"}, true},
|
|
{"connect.Y.local", []string{"connect.Y.local"}, true},
|
|
{"connect.Y.local", []string{".Y.local"}, false},
|
|
{"connect.Y.local", []string{"..local"}, false},
|
|
{"gcp.Y.local", []string{"gcp.Y.local"}, true},
|
|
{"uswest1.gcp.Y.local", []string{"uswest1.gcp.Y.local"}, true},
|
|
} {
|
|
urlSet := make([]*url.URL, len(test.urls))
|
|
for i, u := range test.urls {
|
|
var err error
|
|
urlSet[i], err = url.Parse("nats://" + u)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if dnsAltNameMatches(dnsAltNameLabels(test.altName), urlSet) != test.match {
|
|
t.Fatal("Test", idx, "Match miss match, expected:", test.match)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNoAuthUser(t *testing.T) {
|
|
conf := createConfFile(t, []byte(`
|
|
listen: "127.0.0.1:-1"
|
|
accounts {
|
|
FOO { users [{user: "foo", password: "pwd1"}] }
|
|
BAR { users [{user: "bar", password: "pwd2"}] }
|
|
}
|
|
no_auth_user: "foo"
|
|
`))
|
|
defer os.Remove(conf)
|
|
s, o := RunServerWithConfig(conf)
|
|
defer s.Shutdown()
|
|
|
|
for _, test := range []struct {
|
|
name string
|
|
usrInfo string
|
|
ok bool
|
|
account string
|
|
}{
|
|
{"valid user/pwd", "bar:pwd2@", true, "BAR"},
|
|
{"invalid pwd", "bar:wrong@", false, _EMPTY_},
|
|
{"some token", "sometoken@", false, _EMPTY_},
|
|
{"user used without pwd", "bar@", false, _EMPTY_}, // will be treated as a token
|
|
{"user with empty password", "bar:@", false, _EMPTY_},
|
|
{"no user", _EMPTY_, true, "FOO"},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
url := fmt.Sprintf("nats://%s127.0.0.1:%d", test.usrInfo, o.Port)
|
|
nc, err := nats.Connect(url)
|
|
if err != nil {
|
|
if test.ok {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
return
|
|
} else if !test.ok {
|
|
nc.Close()
|
|
t.Fatalf("Should have failed, did not")
|
|
}
|
|
var accName string
|
|
s.mu.Lock()
|
|
for _, c := range s.clients {
|
|
c.mu.Lock()
|
|
if c.acc != nil {
|
|
accName = c.acc.Name
|
|
}
|
|
c.mu.Unlock()
|
|
break
|
|
}
|
|
s.mu.Unlock()
|
|
nc.Close()
|
|
checkClientsCount(t, s, 0)
|
|
if accName != test.account {
|
|
t.Fatalf("The account should have been %q, got %q", test.account, accName)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNoAuthUserNkey(t *testing.T) {
|
|
conf := createConfFile(t, []byte(`
|
|
listen: "127.0.0.1:-1"
|
|
accounts {
|
|
FOO { users [{user: "foo", password: "pwd1"}] }
|
|
BAR { users [{nkey: "UBO2MQV67TQTVIRV3XFTEZOACM4WLOCMCDMAWN5QVN5PI2N6JHTVDRON"}] }
|
|
}
|
|
no_auth_user: "UBO2MQV67TQTVIRV3XFTEZOACM4WLOCMCDMAWN5QVN5PI2N6JHTVDRON"
|
|
`))
|
|
s, _ := RunServerWithConfig(conf)
|
|
defer s.Shutdown()
|
|
|
|
// Make sure we connect ok and to the correct account.
|
|
nc := natsConnect(t, s.ClientURL())
|
|
defer nc.Close()
|
|
resp, err := nc.Request(userDirectInfoSubj, nil, time.Second)
|
|
require_NoError(t, err)
|
|
response := ServerAPIResponse{Data: &UserInfo{}}
|
|
err = json.Unmarshal(resp.Data, &response)
|
|
require_NoError(t, err)
|
|
userInfo := response.Data.(*UserInfo)
|
|
require_Equal(t, userInfo.UserID, "UBO2MQV67TQTVIRV3XFTEZOACM4WLOCMCDMAWN5QVN5PI2N6JHTVDRON")
|
|
require_Equal(t, userInfo.Account, "BAR")
|
|
}
|
|
|
|
func TestUserConnectionDeadline(t *testing.T) {
|
|
clientAuth := &DummyAuth{
|
|
t: t,
|
|
register: true,
|
|
deadline: time.Now().Add(50 * time.Millisecond),
|
|
}
|
|
|
|
opts := DefaultOptions()
|
|
opts.CustomClientAuthentication = clientAuth
|
|
|
|
s := RunServer(opts)
|
|
defer s.Shutdown()
|
|
|
|
var dcerr error
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
|
|
nc, err := nats.Connect(
|
|
s.ClientURL(),
|
|
nats.UserInfo("valid", _EMPTY_),
|
|
nats.NoReconnect(),
|
|
nats.ErrorHandler(func(nc *nats.Conn, _ *nats.Subscription, err error) {
|
|
dcerr = err
|
|
cancel()
|
|
}))
|
|
if err != nil {
|
|
t.Fatalf("Expected client to connect, got: %s", err)
|
|
}
|
|
|
|
<-ctx.Done()
|
|
|
|
checkFor(t, 2*time.Second, 100*time.Millisecond, func() error {
|
|
if nc.IsConnected() {
|
|
return fmt.Errorf("Expected to be disconnected")
|
|
}
|
|
return nil
|
|
})
|
|
|
|
if dcerr == nil || dcerr.Error() != "nats: authentication expired" {
|
|
t.Fatalf("Expected a auth expired error: got: %v", dcerr)
|
|
}
|
|
}
|
|
|
|
func TestNoAuthUserNoConnectProto(t *testing.T) {
|
|
conf := createConfFile(t, []byte(`
|
|
listen: "127.0.0.1:-1"
|
|
accounts {
|
|
A { users [{user: "foo", password: "pwd"}] }
|
|
}
|
|
authorization { timeout: 1 }
|
|
no_auth_user: "foo"
|
|
`))
|
|
defer os.Remove(conf)
|
|
s, o := RunServerWithConfig(conf)
|
|
defer s.Shutdown()
|
|
|
|
checkClients := func(n int) {
|
|
t.Helper()
|
|
time.Sleep(100 * time.Millisecond)
|
|
if nc := s.NumClients(); nc != n {
|
|
t.Fatalf("Expected %d clients, got %d", n, nc)
|
|
}
|
|
}
|
|
|
|
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", o.Host, o.Port))
|
|
require_NoError(t, err)
|
|
defer conn.Close()
|
|
checkClientsCount(t, s, 1)
|
|
|
|
// With no auth user we should not require a CONNECT.
|
|
// Make sure we are good on not sending CONN first.
|
|
_, err = conn.Write([]byte("PUB foo 2\r\nok\r\n"))
|
|
require_NoError(t, err)
|
|
checkClients(1)
|
|
conn.Close()
|
|
|
|
// Now make sure we still do get timed out though.
|
|
conn, err = net.Dial("tcp", fmt.Sprintf("%s:%d", o.Host, o.Port))
|
|
require_NoError(t, err)
|
|
defer conn.Close()
|
|
checkClientsCount(t, s, 1)
|
|
|
|
time.Sleep(1200 * time.Millisecond)
|
|
checkClientsCount(t, s, 0)
|
|
}
|
|
|
|
type captureProxyRequiredLogger struct {
|
|
DummyLogger
|
|
ch chan string
|
|
}
|
|
|
|
func (l *captureProxyRequiredLogger) Debugf(format string, args ...any) {
|
|
msg := fmt.Sprintf(format, args...)
|
|
if strings.Contains(msg, ErrAuthProxyRequired.Error()) {
|
|
select {
|
|
case l.ch <- msg:
|
|
default:
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAuthProxyRequired(t *testing.T) {
|
|
conf := createConfFile(t, []byte(`
|
|
port: -1
|
|
authorization {
|
|
user: user
|
|
password: pwd
|
|
proxy_required: true
|
|
}
|
|
`))
|
|
s, _ := RunServerWithConfig(conf)
|
|
defer s.Shutdown()
|
|
|
|
l := &captureProxyRequiredLogger{ch: make(chan string, 1)}
|
|
s.SetLogger(l, true, false)
|
|
|
|
_, err := nats.Connect(s.ClientURL(), nats.UserInfo("user", "pwd"))
|
|
require_True(t, errors.Is(err, nats.ErrAuthorization))
|
|
|
|
checkLog := func() {
|
|
t.Helper()
|
|
select {
|
|
case <-l.ch:
|
|
return
|
|
case <-time.After(time.Second):
|
|
t.Fatal("Did not get log statement")
|
|
}
|
|
}
|
|
checkLog()
|
|
|
|
drainLog := func() {
|
|
for {
|
|
select {
|
|
case <-l.ch:
|
|
default:
|
|
return
|
|
}
|
|
}
|
|
}
|
|
s.Shutdown()
|
|
drainLog()
|
|
|
|
nkUsr1, err := nkeys.CreateUser()
|
|
require_NoError(t, err)
|
|
nkPub1, err := nkUsr1.PublicKey()
|
|
require_NoError(t, err)
|
|
|
|
nkUsr2, err := nkeys.CreateUser()
|
|
require_NoError(t, err)
|
|
nkPub2, err := nkUsr2.PublicKey()
|
|
require_NoError(t, err)
|
|
|
|
conf = createConfFile(t, fmt.Appendf(nil, `
|
|
port: -1
|
|
authorization {
|
|
users: [
|
|
{user: user1, password: pwd1}
|
|
{user: user2, password: pwd2, proxy_required: true}
|
|
{user: user3, password: pwd3, proxy_required: false}
|
|
{nkey: "%s", proxy_required: true}
|
|
{nkey: "%s", proxy_required: false}
|
|
]
|
|
}
|
|
`, nkPub1, nkPub2))
|
|
s, _ = RunServerWithConfig(conf)
|
|
defer s.Shutdown()
|
|
|
|
s.SetLogger(l, true, false)
|
|
|
|
checkClients := func() {
|
|
t.Helper()
|
|
// Should connect ok.
|
|
nc := natsConnect(t, s.ClientURL(), nats.UserInfo("user1", "pwd1"))
|
|
nc.Close()
|
|
|
|
// Should not, since it requires going through proxy.
|
|
_, err = nats.Connect(s.ClientURL(), nats.UserInfo("user2", "pwd2"))
|
|
require_True(t, errors.Is(err, nats.ErrAuthorization))
|
|
checkLog()
|
|
|
|
// Should connect ok.
|
|
nc = natsConnect(t, s.ClientURL(), nats.UserInfo("user3", "pwd3"))
|
|
nc.Close()
|
|
|
|
// Should not, since it requires going through proxy.
|
|
_, err = nats.Connect(s.ClientURL(), nats.Nkey(nkPub1, func(nonce []byte) ([]byte, error) {
|
|
return nkUsr1.Sign(nonce)
|
|
}))
|
|
require_True(t, errors.Is(err, nats.ErrAuthorization))
|
|
checkLog()
|
|
|
|
// Should connect ok.
|
|
nc = natsConnect(t, s.ClientURL(), nats.Nkey(nkPub2, func(nonce []byte) ([]byte, error) {
|
|
return nkUsr2.Sign(nonce)
|
|
}))
|
|
nc.Close()
|
|
}
|
|
checkClients()
|
|
s.Shutdown()
|
|
drainLog()
|
|
|
|
conf = createConfFile(t, fmt.Appendf(nil, `
|
|
port: -1
|
|
accounts {
|
|
A {
|
|
users: [
|
|
{user: user1, password: pwd1}
|
|
{user: user2, password: pwd2, proxy_required: true}
|
|
{user: user3, password: pwd3, proxy_required: false}
|
|
{nkey: "%s", proxy_required: true}
|
|
{nkey: "%s", proxy_required: false}
|
|
]
|
|
}
|
|
SYS { users: [ {user:sys, password: pwd} ] }
|
|
}
|
|
system_account: SYS
|
|
`, nkPub1, nkPub2))
|
|
s, _ = RunServerWithConfig(conf)
|
|
defer s.Shutdown()
|
|
|
|
s.SetLogger(l, true, false)
|
|
|
|
snc := natsConnect(t, s.ClientURL(), nats.UserInfo("sys", "pwd"))
|
|
defer snc.Close()
|
|
sub := natsSubSync(t, snc, "$SYS.SERVER.*.CLIENT.AUTH.ERR")
|
|
sub2 := natsSubSync(t, snc, "$SYS.ACCOUNT.A.DISCONNECT")
|
|
natsFlush(t, snc)
|
|
|
|
checkClients()
|
|
|
|
// We should get 2 authentication error messages, saying that
|
|
// the reason is "ProxyRequired".
|
|
for range 2 {
|
|
var de DisconnectEventMsg
|
|
msg := natsNexMsg(t, sub, time.Second)
|
|
err = json.Unmarshal(msg.Data, &de)
|
|
require_NoError(t, err)
|
|
require_Equal(t, de.Reason, ProxyRequired.String())
|
|
}
|
|
|
|
// We should get 3 disconnect events since only 3 have connected
|
|
// ok and then just closed.
|
|
for range 3 {
|
|
var de DisconnectEventMsg
|
|
msg := natsNexMsg(t, sub2, time.Second)
|
|
err = json.Unmarshal(msg.Data, &de)
|
|
require_NoError(t, err)
|
|
require_Equal(t, de.Reason, ClientClosed.String())
|
|
}
|
|
// Make sure there is no more.
|
|
if msg, err := sub2.NextMsg(100 * time.Millisecond); err != nats.ErrTimeout {
|
|
t.Fatalf("Should have received only 3 disconnect messages, got another: %s", msg.Data)
|
|
}
|
|
|
|
s.Shutdown()
|
|
drainLog()
|
|
|
|
conf = createConfFile(t, []byte(`
|
|
port: -1
|
|
leafnodes: {
|
|
port: -1
|
|
authorization {
|
|
user: user
|
|
password: pwd
|
|
proxy_required: true
|
|
}
|
|
}
|
|
`))
|
|
s, o := RunServerWithConfig(conf)
|
|
defer s.Shutdown()
|
|
|
|
s.SetLogger(l, true, false)
|
|
|
|
lconf := createConfFile(t, fmt.Appendf(nil, `
|
|
port: -1
|
|
leafnodes {
|
|
reconnect_interval: "50ms"
|
|
remotes [ {url: "nats://user:[email protected]:%d"} ]
|
|
}
|
|
`, o.LeafNode.Port))
|
|
leaf, _ := RunServerWithConfig(lconf)
|
|
defer leaf.Shutdown()
|
|
|
|
time.Sleep(125 * time.Millisecond)
|
|
checkLeafNodeConnectedCount(t, leaf, 0)
|
|
checkLog()
|
|
leaf.Shutdown()
|
|
s.Shutdown()
|
|
drainLog()
|
|
|
|
conf = createConfFile(t, []byte(`
|
|
port: -1
|
|
leafnodes: {
|
|
port: -1
|
|
reconnect_interval: "50ms"
|
|
authorization {
|
|
users: [
|
|
{user: user1, password: pwd1}
|
|
{user: user2, password: pwd2, proxy_required: true}
|
|
{user: user3, password: pwd3, proxy_required: false}
|
|
]
|
|
}
|
|
}
|
|
`))
|
|
s, o = RunServerWithConfig(conf)
|
|
defer s.Shutdown()
|
|
|
|
s.SetLogger(l, true, false)
|
|
|
|
lconf = createConfFile(t, fmt.Appendf(nil, `
|
|
port: -1
|
|
leafnodes {
|
|
reconnect_interval: "50ms"
|
|
remotes [ {url: "nats://user2:[email protected]:%d"} ]
|
|
}
|
|
`, o.LeafNode.Port))
|
|
leaf, _ = RunServerWithConfig(lconf)
|
|
defer leaf.Shutdown()
|
|
|
|
time.Sleep(125 * time.Millisecond)
|
|
checkLeafNodeConnectedCount(t, leaf, 0)
|
|
checkLog()
|
|
leaf.Shutdown()
|
|
s.Shutdown()
|
|
drainLog()
|
|
|
|
conf = createConfFile(t, fmt.Appendf(nil, `
|
|
port: -1
|
|
leafnodes: {
|
|
port: -1
|
|
authorization {
|
|
nkey: %s
|
|
proxy_required: true
|
|
}
|
|
}
|
|
`, nkPub1))
|
|
s, o = RunServerWithConfig(conf)
|
|
defer s.Shutdown()
|
|
|
|
s.SetLogger(l, true, false)
|
|
|
|
nkSeed1, err := nkUsr1.Seed()
|
|
require_NoError(t, err)
|
|
|
|
lconf = createConfFile(t, fmt.Appendf(nil, `
|
|
port: -1
|
|
leafnodes {
|
|
reconnect_interval: "50ms"
|
|
remotes [
|
|
{
|
|
url: "nats://127.0.0.1:%d"
|
|
nkey: "%s"
|
|
}
|
|
]
|
|
}
|
|
`, o.LeafNode.Port, nkSeed1))
|
|
leaf, _ = RunServerWithConfig(lconf)
|
|
defer leaf.Shutdown()
|
|
|
|
time.Sleep(125 * time.Millisecond)
|
|
checkLeafNodeConnectedCount(t, leaf, 0)
|
|
checkLog()
|
|
leaf.Shutdown()
|
|
s.Shutdown()
|
|
drainLog()
|
|
|
|
// Check with operator mode.
|
|
conf = createConfFile(t, []byte(`
|
|
port: -1
|
|
server_name: OP
|
|
operator = "../test/configs/nkeys/op.jwt"
|
|
resolver = MEMORY
|
|
listen: "127.0.0.1:-1"
|
|
leafnodes {
|
|
listen: "127.0.0.1:-1"
|
|
}
|
|
`))
|
|
s, o = RunServerWithConfig(conf)
|
|
defer s.Shutdown()
|
|
|
|
s.SetLogger(l, true, false)
|
|
|
|
_, akp := createAccount(s)
|
|
kp, err := nkeys.CreateUser()
|
|
require_NoError(t, err)
|
|
pub, err := kp.PublicKey()
|
|
require_NoError(t, err)
|
|
nuc := jwt.NewUserClaims(pub)
|
|
nuc.ProxyRequired = true
|
|
ujwt, err := nuc.Encode(akp)
|
|
require_NoError(t, err)
|
|
|
|
lopts := &DefaultTestOptions
|
|
u, err := url.Parse(fmt.Sprintf("nats://%s:%d", o.LeafNode.Host, o.LeafNode.Port))
|
|
require_NoError(t, err)
|
|
remote := &RemoteLeafOpts{URLs: []*url.URL{u}}
|
|
remote.SignatureCB = func(nonce []byte) (string, []byte, error) {
|
|
sig, err := kp.Sign(nonce)
|
|
return ujwt, sig, err
|
|
}
|
|
lopts.LeafNode.Remotes = []*RemoteLeafOpts{remote}
|
|
lopts.LeafNode.ReconnectInterval = 100 * time.Millisecond
|
|
leaf = RunServer(lopts)
|
|
defer leaf.Shutdown()
|
|
|
|
time.Sleep(125 * time.Millisecond)
|
|
checkLeafNodeConnectedCount(t, leaf, 0)
|
|
checkLog()
|
|
leaf.Shutdown()
|
|
|
|
// Try with an user.
|
|
drainLog()
|
|
|
|
_, err = nats.Connect(s.ClientURL(), createUserCredsEx(t, nuc, akp))
|
|
require_True(t, errors.Is(err, nats.ErrAuthorization))
|
|
checkLog()
|
|
|
|
drainLog()
|
|
|
|
// Try with creds file.
|
|
kp, err = nkeys.CreateUser()
|
|
require_NoError(t, err)
|
|
useed, err := kp.Seed()
|
|
require_NoError(t, err)
|
|
pub, err = kp.PublicKey()
|
|
require_NoError(t, err)
|
|
nuc = jwt.NewUserClaims(pub)
|
|
nuc.ProxyRequired = true
|
|
ujwt, err = nuc.Encode(akp)
|
|
require_NoError(t, err)
|
|
credsFile := genCredsFile(t, ujwt, useed)
|
|
|
|
lconf = createConfFile(t, fmt.Appendf(nil, `
|
|
port: -1
|
|
leafnodes {
|
|
reconnect_interval: "50ms"
|
|
remotes [
|
|
{
|
|
url: "nats://127.0.0.1:%d"
|
|
credentials: '%s'
|
|
}
|
|
]
|
|
}
|
|
`, o.LeafNode.Port, credsFile))
|
|
leaf, _ = RunServerWithConfig(lconf)
|
|
defer leaf.Shutdown()
|
|
|
|
time.Sleep(125 * time.Millisecond)
|
|
checkLeafNodeConnectedCount(t, leaf, 0)
|
|
checkLog()
|
|
leaf.Shutdown()
|
|
|
|
s.Shutdown()
|
|
drainLog()
|
|
}
|