diff --git a/server/me.go b/server/me.go index 0ddefa17b2..469b025d71 100644 --- a/server/me.go +++ b/server/me.go @@ -268,7 +268,8 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) { Organization: fmt.Sprintf("%d", defaultOrg.ID), }, }, - SuperAdmin: s.firstUser(), + // TODO(desa): this needs a better name + SuperAdmin: s.newUsersAreSuperAdmin(), } newUser, err := s.Store.Users(serverCtx).Add(serverCtx, user) @@ -299,7 +300,6 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) { encodeJSON(w, http.StatusOK, res, s.Logger) } -// TODO(desa): very slow func (s *Service) firstUser() bool { serverCtx := serverContext(context.Background()) users, err := s.Store.Users(serverCtx).All(serverCtx) @@ -309,6 +309,9 @@ func (s *Service) firstUser() bool { return len(users) == 0 } +func (s *Service) newUsersAreSuperAdmin() bool { + return !s.NewUsersNotSuperAdmin +} func (s *Service) usersOrganizations(ctx context.Context, u *chronograf.User) ([]chronograf.Organization, error) { if u == nil { diff --git a/server/me_test.go b/server/me_test.go index d88fcdb085..361eeca0ae 100644 --- a/server/me_test.go +++ b/server/me_test.go @@ -21,10 +21,11 @@ type MockUsers struct{} func TestService_Me(t *testing.T) { type fields struct { - UsersStore chronograf.UsersStore - OrganizationsStore chronograf.OrganizationsStore - Logger chronograf.Logger - UseAuth bool + UsersStore chronograf.UsersStore + OrganizationsStore chronograf.OrganizationsStore + Logger chronograf.Logger + UseAuth bool + NewUsersNotSuperAdmin bool } type args struct { w *httptest.ResponseRecorder @@ -46,8 +47,9 @@ func TestService_Me(t *testing.T) { r: httptest.NewRequest("GET", "http://example.com/foo", nil), }, fields: fields{ - UseAuth: true, - Logger: log.New(log.DebugLevel), + UseAuth: true, + NewUsersNotSuperAdmin: true, + Logger: log.New(log.DebugLevel), OrganizationsStore: &mocks.OrganizationsStore{ DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) { return &chronograf.Organization{ @@ -142,10 +144,6 @@ func TestService_Me(t *testing.T) { }, }, UsersStore: &mocks.UsersStore{ - AllF: func(ctx context.Context) ([]chronograf.User, error) { - // This function gets to verify that there is at least one first user - return []chronograf.User{{}}, nil - }, GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) { if q.Name == nil || q.Provider == nil || q.Scheme == nil { return nil, fmt.Errorf("Invalid user query: missing Name, Provider, and/or Scheme") @@ -177,8 +175,9 @@ func TestService_Me(t *testing.T) { r: httptest.NewRequest("GET", "http://example.com/foo", nil), }, fields: fields{ - UseAuth: true, - Logger: log.New(log.DebugLevel), + UseAuth: true, + NewUsersNotSuperAdmin: false, + Logger: log.New(log.DebugLevel), OrganizationsStore: &mocks.OrganizationsStore{ DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) { return &chronograf.Organization{ @@ -198,10 +197,53 @@ func TestService_Me(t *testing.T) { }, }, UsersStore: &mocks.UsersStore{ - AllF: func(ctx context.Context) ([]chronograf.User, error) { - // This function gets to verify that there is at least one first user - return []chronograf.User{{}}, nil + GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) { + if q.Name == nil || q.Provider == nil || q.Scheme == nil { + return nil, fmt.Errorf("Invalid user query: missing Name, Provider, and/or Scheme") + } + return nil, chronograf.ErrUserNotFound }, + AddF: func(ctx context.Context, u *chronograf.User) (*chronograf.User, error) { + return u, nil + }, + UpdateF: func(ctx context.Context, u *chronograf.User) error { + return nil + }, + }, + }, + principal: oauth2.Principal{ + Subject: "secret", + Issuer: "auth0", + }, + wantStatus: http.StatusOK, + wantContentType: "application/json", + wantBody: `{"name":"secret","superAdmin":true,"roles":[{"name":"member","organization":"0"}],"provider":"auth0","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"0","name":"The Bad Place"}],"currentOrganization":{"id":"0","name":"The Bad Place"}} +`, + }, + { + name: "New user - New users not super admin", + args: args{ + w: httptest.NewRecorder(), + r: httptest.NewRequest("GET", "http://example.com/foo", nil), + }, + fields: fields{ + UseAuth: true, + NewUsersNotSuperAdmin: true, + Logger: log.New(log.DebugLevel), + OrganizationsStore: &mocks.OrganizationsStore{ + DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) { + return &chronograf.Organization{ + ID: 0, + }, nil + }, + GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) { + return &chronograf.Organization{ + ID: 0, + Name: "The Bad Place", + }, nil + }, + }, + UsersStore: &mocks.UsersStore{ GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) { if q.Name == nil || q.Provider == nil || q.Scheme == nil { return nil, fmt.Errorf("Invalid user query: missing Name, Provider, and/or Scheme") @@ -232,7 +274,8 @@ func TestService_Me(t *testing.T) { r: httptest.NewRequest("GET", "http://example.com/foo", nil), }, fields: fields{ - UseAuth: true, + UseAuth: true, + NewUsersNotSuperAdmin: true, OrganizationsStore: &mocks.OrganizationsStore{ DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) { return &chronograf.Organization{ @@ -249,10 +292,6 @@ func TestService_Me(t *testing.T) { }, }, UsersStore: &mocks.UsersStore{ - AllF: func(ctx context.Context) ([]chronograf.User, error) { - // This function gets to verify that there is at least one first user - return []chronograf.User{{}}, nil - }, GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) { return nil, chronograf.ErrUserNotFound }, @@ -280,8 +319,9 @@ func TestService_Me(t *testing.T) { r: httptest.NewRequest("GET", "http://example.com/foo", nil), }, fields: fields{ - UseAuth: false, - Logger: log.New(log.DebugLevel), + UseAuth: false, + NewUsersNotSuperAdmin: true, + Logger: log.New(log.DebugLevel), }, wantStatus: http.StatusOK, wantContentType: "application/json", @@ -295,8 +335,9 @@ func TestService_Me(t *testing.T) { r: httptest.NewRequest("GET", "http://example.com/foo", nil), }, fields: fields{ - UseAuth: true, - Logger: log.New(log.DebugLevel), + UseAuth: true, + NewUsersNotSuperAdmin: true, + Logger: log.New(log.DebugLevel), }, wantStatus: http.StatusUnprocessableEntity, principal: oauth2.Principal{ @@ -358,8 +399,9 @@ func TestService_Me(t *testing.T) { UsersStore: tt.fields.UsersStore, OrganizationsStore: tt.fields.OrganizationsStore, }, - Logger: tt.fields.Logger, - UseAuth: tt.fields.UseAuth, + Logger: tt.fields.Logger, + UseAuth: tt.fields.UseAuth, + NewUsersNotSuperAdmin: tt.fields.NewUsersNotSuperAdmin, } s.Me(tt.args.w, tt.args.r) diff --git a/server/server.go b/server/server.go index 1ec1a2ca67..e24cda33ad 100644 --- a/server/server.go +++ b/server/server.go @@ -57,6 +57,8 @@ type Server struct { CannedPath string `short:"c" long:"canned-path" description:"Path to directory of pre-canned application layouts (/usr/share/chronograf/canned)" env:"CANNED_PATH" default:"canned"` TokenSecret string `short:"t" long:"token-secret" description:"Secret to sign tokens" env:"TOKEN_SECRET"` AuthDuration time.Duration `long:"auth-duration" default:"720h" description:"Total duration of cookie life for authentication (in hours). 0 means authentication expires on browser close." env:"AUTH_DURATION"` + // TODO(desa): think of a better name + NewUsersNotSuperAdmin bool `long:"new-users-not-super-admin" description:"All new users will not be given the Super Admin privilege" env:"NEW_USRES_NOT_SUPER_ADMIN"` GithubClientID string `short:"i" long:"github-client-id" description:"Github Client ID for OAuth 2 support" env:"GH_CLIENT_ID"` GithubClientSecret string `short:"s" long:"github-client-secret" description:"Github Client Secret for OAuth 2 support" env:"GH_CLIENT_SECRET"` @@ -300,6 +302,8 @@ func (s *Server) Serve(ctx context.Context) error { return err } service := openService(ctx, s.BoltPath, layoutBuilder, sourcesBuilder, kapacitorBuilder, logger, s.useAuth()) + // TODO(desa): better name + service.NewUsersNotSuperAdmin = s.NewUsersNotSuperAdmin if err := service.HandleNewSources(ctx, s.NewSources); err != nil { logger. WithField("component", "server"). diff --git a/server/service.go b/server/service.go index 13c6d7a02b..ca74c64e7f 100644 --- a/server/service.go +++ b/server/service.go @@ -15,7 +15,9 @@ type Service struct { TimeSeriesClient TimeSeriesClient Logger chronograf.Logger UseAuth bool - Databases chronograf.Databases + // TODO(desa): better name + NewUsersNotSuperAdmin bool + Databases chronograf.Databases } // TimeSeriesClient returns the correct client for a time series database.