diff --git a/server/accounts_test.go b/server/accounts_test.go index 95746b59..68c77896 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,27 @@ func TestAccountMapsUsers(t *testing.T) { } } +func TestAccountGlobalDefault(t *testing.T) { + opts := defaultServerOptions + s := New(&opts) + + 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.. + if _, err := s.RegisterAccount(globalAccountName); 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) + + if _, err := ProcessConfigFile(confFileName); 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..5aef8338 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 name == globalAccountName +} + // 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 }