From 0d1d51a3bb642878c6a4c262a89ca2826163b54a Mon Sep 17 00:00:00 2001 From: Derek Collison Date: Thu, 4 Oct 2018 12:03:22 -0700 Subject: [PATCH 1/2] Support for global/reserved accounts Signed-off-by: Derek Collison --- server/accounts_test.go | 29 +++++++++++++++++++++++++++-- server/auth.go | 4 ++++ server/errors.go | 3 +++ server/opts.go | 12 ++++++++++++ server/server.go | 10 +++++++++- 5 files changed, 55 insertions(+), 3 deletions(-) diff --git a/server/accounts_test.go b/server/accounts_test.go index 95746b59..d7350086 100644 --- a/server/accounts_test.go +++ b/server/accounts_test.go @@ -108,8 +108,9 @@ func TestAccountFromOptions(t *testing.T) { } s := New(&opts) - if la := len(s.accounts); la != 2 { - t.Fatalf("Expected to have a server with two accounts, got %v", la) + ta := s.numReservedAccounts() + 2 + if la := len(s.accounts); la != ta { + t.Fatalf("Expected to have a server with %d total accounts, got %v", ta, la) } // Check that sl is filled in. fooAcc := s.LookupAccount("foo") @@ -1003,6 +1004,30 @@ func TestAccountMapsUsers(t *testing.T) { } } +func TestAccountGlobalDefault(t *testing.T) { + opts := defaultServerOptions + s := New(&opts) + + acc := s.LookupAccount(globalAccountName) + if acc == nil { + t.Fatalf("Expected a global default account on a new server, got none.") + } + // Make sure we can not create one with same name.. + _, err := s.RegisterAccount(globalAccountName) + if err == nil { + t.Fatalf("Expected error trying to create a new reserved account") + } + + // Make sure we can not define one in a config file either. + confFileName := createConfFile(t, []byte(`accounts { $G {} }`)) + defer os.Remove(confFileName) + + _, err = ProcessConfigFile(confFileName) + if err == nil { + t.Fatalf("Expected an error parsing config file with reserved account") + } +} + func BenchmarkNewRouteReply(b *testing.B) { opts := defaultServerOptions s := New(&opts) diff --git a/server/auth.go b/server/auth.go index 506ecbab..20a607c9 100644 --- a/server/auth.go +++ b/server/auth.go @@ -41,6 +41,10 @@ type ClientAuthentication interface { RegisterUser(*User) } +// For backwards compatibility, users who are not explicitly defined into an +// account will be grouped in the default global account. +const globalAccountName = "$G" + // Accounts type Account struct { Name string diff --git a/server/errors.go b/server/errors.go index 003b9880..f80add2f 100644 --- a/server/errors.go +++ b/server/errors.go @@ -56,6 +56,9 @@ var ( // ErrBadAccount represents a malformed or incorrect account. ErrBadAccount = errors.New("Bad Account") + // ErrReservedAccount represents a reserved account that can not be created. + ErrReservedAccount = errors.New("Reserved Account") + // ErrMissingAccount is returned when an account does not exist. ErrMissingAccount = errors.New("Account Missing") diff --git a/server/opts.go b/server/opts.go index aee21198..79cd8b4b 100644 --- a/server/opts.go +++ b/server/opts.go @@ -627,6 +627,11 @@ type importService struct { to string } +// Checks if an account name is reserved. +func isReservedAccount(name string) bool { + return strings.Compare(name, globalAccountName) == 0 +} + // parseAccounts will parse the different accounts syntax. func parseAccounts(v interface{}, opts *Options) error { var ( @@ -643,6 +648,10 @@ func parseAccounts(v interface{}, opts *Options) error { m := make(map[string]struct{}, len(v.([]interface{}))) for _, name := range v.([]interface{}) { ns := name.(string) + // Check for reserved names. + if isReservedAccount(ns) { + return fmt.Errorf("%q is a Reserved Account", ns) + } if _, ok := m[ns]; ok { return fmt.Errorf("Duplicate Account Entry: %s", ns) } @@ -661,6 +670,9 @@ func parseAccounts(v interface{}, opts *Options) error { if !ok { return fmt.Errorf("Expected map entries for accounts") } + if isReservedAccount(aname) { + return fmt.Errorf("%q is a Reserved Account", aname) + } acc := &Account{Name: aname} opts.Accounts = append(opts.Accounts, acc) diff --git a/server/server.go b/server/server.go index 1d3db247..0b30b05b 100644 --- a/server/server.go +++ b/server/server.go @@ -196,6 +196,9 @@ func New(opts *Options) *Server { // For tracking accounts s.accounts = make(map[string]*Account) + // Create global account. + s.registerAccount(&Account{Name: globalAccountName, sl: s.gsl}) + // For tracking clients s.clients = make(map[uint64]*client) @@ -302,6 +305,12 @@ func (s *Server) newAccountsAllowed() bool { return s.opts.AllowNewAccounts } +// numReservedAccounts will return the number of reserved accounts configured in the server. +// Currently this is 1 for the global default service. +func (s *Server) numReservedAccounts() int { + return 1 +} + // LookupOrRegisterAccount will return the given account if known or create a new entry. func (s *Server) LookupOrRegisterAccount(name string) (account *Account, isNew bool) { s.mu.Lock() @@ -1166,7 +1175,6 @@ func (s *Server) NumSubscriptions() uint32 { subs += acc.sl.Count() } } - subs += s.gsl.Count() s.mu.Unlock() return subs } From b014db85023ffe6f55840e20f4437c45e946bded Mon Sep 17 00:00:00 2001 From: Derek Collison Date: Thu, 4 Oct 2018 12:50:12 -0700 Subject: [PATCH 2/2] Updates based on comments Signed-off-by: Derek Collison --- server/accounts_test.go | 9 +++------ server/opts.go | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/server/accounts_test.go b/server/accounts_test.go index d7350086..68c77896 100644 --- a/server/accounts_test.go +++ b/server/accounts_test.go @@ -1008,13 +1008,11 @@ func TestAccountGlobalDefault(t *testing.T) { opts := defaultServerOptions s := New(&opts) - acc := s.LookupAccount(globalAccountName) - if acc == nil { + if acc := s.LookupAccount(globalAccountName); acc == nil { t.Fatalf("Expected a global default account on a new server, got none.") } // Make sure we can not create one with same name.. - _, err := s.RegisterAccount(globalAccountName) - if err == nil { + if _, err := s.RegisterAccount(globalAccountName); err == nil { t.Fatalf("Expected error trying to create a new reserved account") } @@ -1022,8 +1020,7 @@ func TestAccountGlobalDefault(t *testing.T) { confFileName := createConfFile(t, []byte(`accounts { $G {} }`)) defer os.Remove(confFileName) - _, err = ProcessConfigFile(confFileName) - if err == nil { + if _, err := ProcessConfigFile(confFileName); err == nil { t.Fatalf("Expected an error parsing config file with reserved account") } } diff --git a/server/opts.go b/server/opts.go index 79cd8b4b..5aef8338 100644 --- a/server/opts.go +++ b/server/opts.go @@ -629,7 +629,7 @@ type importService struct { // Checks if an account name is reserved. func isReservedAccount(name string) bool { - return strings.Compare(name, globalAccountName) == 0 + return name == globalAccountName } // parseAccounts will parse the different accounts syntax.