Merge pull request #2552 from influxdata/fix/issue#2540
Only promote user to super admin in user org storepull/10616/head
commit
f6f3d24a70
|
@ -142,6 +142,22 @@ func (s *UsersStore) Add(ctx context.Context, u *chronograf.User) (*chronograf.U
|
|||
// and the user that was found in the underlying store
|
||||
usr.Roles = append(roles, u.Roles...)
|
||||
|
||||
// u.SuperAdmin == true is logically equivalent to u.SuperAdmin, however
|
||||
// it is more clear on a conceptual level to check equality
|
||||
//
|
||||
// TODO(desa): this should go away with https://github.com/influxdata/chronograf/issues/2207
|
||||
// I do not like checking super admin here. The organization users store should only be
|
||||
// concerned about organizations.
|
||||
//
|
||||
// This allows users to be promoted to SuperAdmin on Add if they had not previously had the status.
|
||||
// The reason for this is that we reuse Users and it is possible that one may try to "create" a user,
|
||||
// who has super admin status, without super admin status on the request. In this case, a user should
|
||||
// still retain their previous status and not be demoted. If we had done usr.SupderAdmin = u.SuperAdmin
|
||||
// we could possibly demote a users super admin status.
|
||||
if u.SuperAdmin == true {
|
||||
usr.SuperAdmin = true
|
||||
}
|
||||
|
||||
// Update the user in the underlying store
|
||||
if err := s.store.Update(ctx, usr); err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -313,11 +313,64 @@ func TestUsersStore_Add(t *testing.T) {
|
|||
Name: "docbrown",
|
||||
Provider: "github",
|
||||
Scheme: "oauth2",
|
||||
Roles: []chronograf.Role{
|
||||
{
|
||||
Organization: "1336",
|
||||
Name: "admin",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Add non-new user with Role. Stored user is not super admin. Provided user is super admin",
|
||||
fields: fields{
|
||||
UsersStore: &mocks.UsersStore{
|
||||
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
|
||||
},
|
||||
GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) {
|
||||
return &chronograf.User{
|
||||
ID: 1234,
|
||||
Name: "docbrown",
|
||||
Provider: "github",
|
||||
Scheme: "oauth2",
|
||||
SuperAdmin: false,
|
||||
Roles: []chronograf.Role{
|
||||
{
|
||||
Organization: "1337",
|
||||
Name: "editor",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
u: &chronograf.User{
|
||||
ID: 1234,
|
||||
Name: "docbrown",
|
||||
Provider: "github",
|
||||
Scheme: "oauth2",
|
||||
SuperAdmin: true,
|
||||
Roles: []chronograf.Role{
|
||||
{
|
||||
Organization: "1336",
|
||||
Name: "admin",
|
||||
},
|
||||
},
|
||||
},
|
||||
orgID: "1336",
|
||||
},
|
||||
want: &chronograf.User{
|
||||
Name: "docbrown",
|
||||
Provider: "github",
|
||||
Scheme: "oauth2",
|
||||
SuperAdmin: true,
|
||||
Roles: []chronograf.Role{
|
||||
{
|
||||
Organization: "1336",
|
||||
Name: "admin",
|
||||
|
@ -503,6 +556,9 @@ func TestUsersStore_Add(t *testing.T) {
|
|||
if got == nil && tt.want == nil {
|
||||
continue
|
||||
}
|
||||
if diff := cmp.Diff(got, tt.want, userCmpOptions...); diff != "" {
|
||||
t.Errorf("%q. UsersStore.Add():\n-got/+want\ndiff %s", tt.name, diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -157,6 +157,13 @@ func (s *Service) NewUser(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
ctx := r.Context()
|
||||
|
||||
cfg, err := s.Store.Config(ctx).Get(ctx)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, err.Error(), s.Logger)
|
||||
return
|
||||
}
|
||||
|
||||
user := &chronograf.User{
|
||||
Name: req.Name,
|
||||
Provider: req.Provider,
|
||||
|
@ -164,6 +171,10 @@ func (s *Service) NewUser(w http.ResponseWriter, r *http.Request) {
|
|||
Roles: req.Roles,
|
||||
}
|
||||
|
||||
if cfg.Auth.SuperAdminNewUsers {
|
||||
req.SuperAdmin = true
|
||||
}
|
||||
|
||||
if err := setSuperAdmin(ctx, req, user); err != nil {
|
||||
Error(w, http.StatusUnauthorized, err.Error(), s.Logger)
|
||||
return
|
||||
|
|
|
@ -113,6 +113,7 @@ func TestService_UserID(t *testing.T) {
|
|||
func TestService_NewUser(t *testing.T) {
|
||||
type fields struct {
|
||||
UsersStore chronograf.UsersStore
|
||||
ConfigStore chronograf.ConfigStore
|
||||
Logger chronograf.Logger
|
||||
}
|
||||
type args struct {
|
||||
|
@ -146,6 +147,13 @@ func TestService_NewUser(t *testing.T) {
|
|||
},
|
||||
fields: fields{
|
||||
Logger: log.New(log.DebugLevel),
|
||||
ConfigStore: &mocks.ConfigStore{
|
||||
Config: &chronograf.Config{
|
||||
Auth: chronograf.AuthConfig{
|
||||
SuperAdminNewUsers: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
UsersStore: &mocks.UsersStore{
|
||||
AddF: func(ctx context.Context, user *chronograf.User) (*chronograf.User, error) {
|
||||
return &chronograf.User{
|
||||
|
@ -189,6 +197,13 @@ func TestService_NewUser(t *testing.T) {
|
|||
},
|
||||
fields: fields{
|
||||
Logger: log.New(log.DebugLevel),
|
||||
ConfigStore: &mocks.ConfigStore{
|
||||
Config: &chronograf.Config{
|
||||
Auth: chronograf.AuthConfig{
|
||||
SuperAdminNewUsers: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
UsersStore: &mocks.UsersStore{
|
||||
AddF: func(ctx context.Context, user *chronograf.User) (*chronograf.User, error) {
|
||||
return &chronograf.User{
|
||||
|
@ -241,6 +256,13 @@ func TestService_NewUser(t *testing.T) {
|
|||
},
|
||||
fields: fields{
|
||||
Logger: log.New(log.DebugLevel),
|
||||
ConfigStore: &mocks.ConfigStore{
|
||||
Config: &chronograf.Config{
|
||||
Auth: chronograf.AuthConfig{
|
||||
SuperAdminNewUsers: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
UsersStore: &mocks.UsersStore{
|
||||
AddF: func(ctx context.Context, user *chronograf.User) (*chronograf.User, error) {
|
||||
return &chronograf.User{
|
||||
|
@ -291,6 +313,13 @@ func TestService_NewUser(t *testing.T) {
|
|||
},
|
||||
fields: fields{
|
||||
Logger: log.New(log.DebugLevel),
|
||||
ConfigStore: &mocks.ConfigStore{
|
||||
Config: &chronograf.Config{
|
||||
Auth: chronograf.AuthConfig{
|
||||
SuperAdminNewUsers: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
UsersStore: &mocks.UsersStore{
|
||||
AddF: func(ctx context.Context, user *chronograf.User) (*chronograf.User, error) {
|
||||
return &chronograf.User{
|
||||
|
@ -332,6 +361,13 @@ func TestService_NewUser(t *testing.T) {
|
|||
},
|
||||
fields: fields{
|
||||
Logger: log.New(log.DebugLevel),
|
||||
ConfigStore: &mocks.ConfigStore{
|
||||
Config: &chronograf.Config{
|
||||
Auth: chronograf.AuthConfig{
|
||||
SuperAdminNewUsers: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
UsersStore: &mocks.UsersStore{
|
||||
AddF: func(ctx context.Context, user *chronograf.User) (*chronograf.User, error) {
|
||||
return &chronograf.User{
|
||||
|
@ -349,6 +385,48 @@ func TestService_NewUser(t *testing.T) {
|
|||
wantContentType: "application/json",
|
||||
wantBody: `{"id":"1338","superAdmin":true,"name":"bob","provider":"github","scheme":"oauth2","roles":[],"links":{"self":"/chronograf/v1/users/1338"}}`,
|
||||
},
|
||||
{
|
||||
name: "Create a new User with SuperAdminNewUsers: true in ConfigStore",
|
||||
args: args{
|
||||
w: httptest.NewRecorder(),
|
||||
r: httptest.NewRequest(
|
||||
"POST",
|
||||
"http://any.url",
|
||||
nil,
|
||||
),
|
||||
user: &userRequest{
|
||||
Name: "bob",
|
||||
Provider: "github",
|
||||
Scheme: "oauth2",
|
||||
},
|
||||
userKeyUser: &chronograf.User{
|
||||
ID: 0,
|
||||
Name: "coolUser",
|
||||
Provider: "github",
|
||||
Scheme: "oauth2",
|
||||
SuperAdmin: true,
|
||||
},
|
||||
},
|
||||
fields: fields{
|
||||
Logger: log.New(log.DebugLevel),
|
||||
ConfigStore: &mocks.ConfigStore{
|
||||
Config: &chronograf.Config{
|
||||
Auth: chronograf.AuthConfig{
|
||||
SuperAdminNewUsers: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
UsersStore: &mocks.UsersStore{
|
||||
AddF: func(ctx context.Context, user *chronograf.User) (*chronograf.User, error) {
|
||||
user.ID = 1338
|
||||
return user, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
wantStatus: http.StatusCreated,
|
||||
wantContentType: "application/json",
|
||||
wantBody: `{"id":"1338","superAdmin":true,"name":"bob","provider":"github","scheme":"oauth2","roles":[],"links":{"self":"/chronograf/v1/users/1338"}}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
@ -356,6 +434,7 @@ func TestService_NewUser(t *testing.T) {
|
|||
s := &Service{
|
||||
Store: &mocks.Store{
|
||||
UsersStore: tt.fields.UsersStore,
|
||||
ConfigStore: tt.fields.ConfigStore,
|
||||
},
|
||||
Logger: tt.fields.Logger,
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ import React, {Component, PropTypes} from 'react'
|
|||
import Authorized, {SUPERADMIN_ROLE} from 'src/auth/Authorized'
|
||||
|
||||
import Dropdown from 'shared/components/Dropdown'
|
||||
import SlideToggle from 'shared/components/SlideToggle'
|
||||
|
||||
import {USERS_TABLE} from 'src/admin/constants/chronografTableSizing'
|
||||
import {USER_ROLES} from 'src/admin/constants/chronografAdmin'
|
||||
|
@ -17,7 +16,6 @@ class UsersTableRowNew extends Component {
|
|||
provider: '',
|
||||
scheme: 'oauth2',
|
||||
role: this.props.organization.defaultRole,
|
||||
superAdmin: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,10 +52,6 @@ class UsersTableRowNew extends Component {
|
|||
this.setState({role: newRole.text})
|
||||
}
|
||||
|
||||
handleSelectSuperAdmin = superAdmin => {
|
||||
this.setState({superAdmin})
|
||||
}
|
||||
|
||||
handleKeyDown = e => {
|
||||
const {name, provider} = this.state
|
||||
const preventCreate = !name || !provider
|
||||
|
@ -86,7 +80,7 @@ class UsersTableRowNew extends Component {
|
|||
colActions,
|
||||
} = USERS_TABLE
|
||||
const {onBlur} = this.props
|
||||
const {name, provider, scheme, role, superAdmin} = this.state
|
||||
const {name, provider, scheme, role} = this.state
|
||||
|
||||
const dropdownRolesItems = USER_ROLES.map(r => ({...r, text: r.name}))
|
||||
const preventCreate = !name || !provider
|
||||
|
@ -116,11 +110,7 @@ class UsersTableRowNew extends Component {
|
|||
</td>
|
||||
<Authorized requiredRole={SUPERADMIN_ROLE}>
|
||||
<td style={{width: colSuperAdmin}} className="text-center">
|
||||
<SlideToggle
|
||||
active={superAdmin}
|
||||
size="xs"
|
||||
onToggle={this.handleSelectSuperAdmin}
|
||||
/>
|
||||
—
|
||||
</td>
|
||||
</Authorized>
|
||||
<td style={{width: colProvider}}>
|
||||
|
|
|
@ -104,11 +104,6 @@ table.table.chronograf-admin-table tbody tr.chronograf-admin-table--user td div.
|
|||
/* Styles for new user row */
|
||||
table.table.chronograf-admin-table tbody tr.chronograf-admin-table--new-user {
|
||||
background-color: $g4-onyx;
|
||||
|
||||
> td {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Highlight "Me" in the users table */
|
||||
|
|
Loading…
Reference in New Issue