Ensure users name, provider, and scheme are unique
parent
7ef6078c98
commit
9920f5e90e
|
@ -82,8 +82,35 @@ func (s *UsersStore) Get(ctx context.Context, q chronograf.UserQuery) (*chronogr
|
|||
return nil, fmt.Errorf("must specify either ID, or Name, Provider, and Scheme in UserQuery")
|
||||
}
|
||||
|
||||
func (s *UsersStore) userExists(ctx context.Context, u *chronograf.User) (bool, error) {
|
||||
_, err := s.Get(ctx, chronograf.UserQuery{
|
||||
Name: &u.Name,
|
||||
Provider: &u.Provider,
|
||||
Scheme: &u.Scheme,
|
||||
})
|
||||
if err == chronograf.ErrUserNotFound {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Add a new User to the UsersStore.
|
||||
func (s *UsersStore) Add(ctx context.Context, u *chronograf.User) (*chronograf.User, error) {
|
||||
if u == nil {
|
||||
return nil, fmt.Errorf("user provided is nil")
|
||||
}
|
||||
userExists, err := s.userExists(ctx, u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if userExists {
|
||||
return nil, chronograf.ErrUserAlreadyExists
|
||||
}
|
||||
if err := s.client.db.Update(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket(UsersBucket)
|
||||
seq, err := b.NextSequence()
|
||||
|
|
|
@ -2,6 +2,7 @@ package bolt_test
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
@ -181,8 +182,9 @@ func TestUsersStore_GetInvalid(t *testing.T) {
|
|||
|
||||
func TestUsersStore_Add(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
u *chronograf.User
|
||||
ctx context.Context
|
||||
u *chronograf.User
|
||||
addFirst bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
|
@ -216,6 +218,24 @@ func TestUsersStore_Add(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "User already exists",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
addFirst: true,
|
||||
u: &chronograf.User{
|
||||
Name: "docbrown",
|
||||
Provider: "github",
|
||||
Scheme: "oauth2",
|
||||
Roles: []chronograf.Role{
|
||||
{
|
||||
Name: "editor",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
client, err := NewTestClient()
|
||||
|
@ -227,12 +247,19 @@ func TestUsersStore_Add(t *testing.T) {
|
|||
}
|
||||
defer client.Close()
|
||||
s := client.UsersStore
|
||||
if tt.args.addFirst {
|
||||
_, _ = s.Add(tt.args.ctx, tt.args.u)
|
||||
}
|
||||
got, err := s.Add(tt.args.ctx, tt.args.u)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("%q. UsersStore.Add() error = %v, wantErr %v", tt.name, err, tt.wantErr)
|
||||
continue
|
||||
}
|
||||
|
||||
if tt.wantErr {
|
||||
continue
|
||||
}
|
||||
|
||||
got, err = s.Get(tt.args.ctx, chronograf.UserQuery{ID: &got.ID})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get user: %v", err)
|
||||
|
@ -287,7 +314,9 @@ func TestUsersStore_Delete(t *testing.T) {
|
|||
s := client.UsersStore
|
||||
|
||||
if tt.addFirst {
|
||||
tt.args.user, _ = s.Add(tt.args.ctx, tt.args.user)
|
||||
var err error
|
||||
tt.args.user, err = s.Add(tt.args.ctx, tt.args.user)
|
||||
fmt.Println(err)
|
||||
}
|
||||
if err := s.Delete(tt.args.ctx, tt.args.user); (err != nil) != tt.wantErr {
|
||||
t.Errorf("%q. UsersStore.Delete() error = %v, wantErr %v", tt.name, err, tt.wantErr)
|
||||
|
|
|
@ -26,6 +26,7 @@ const (
|
|||
ErrLayoutNotFound = Error("layout not found")
|
||||
ErrDashboardNotFound = Error("dashboard not found")
|
||||
ErrUserNotFound = Error("user not found")
|
||||
ErrUserAlreadyExists = Error("user already exists")
|
||||
ErrOrganizationNotFound = Error("organization not found")
|
||||
ErrLayoutInvalid = Error("layout is invalid")
|
||||
ErrAlertNotFound = Error("alert not found")
|
||||
|
@ -633,6 +634,9 @@ type User struct {
|
|||
|
||||
// UserQuery represents the attributes that a user may be retrieved by.
|
||||
// It is predominantly used in the UsersStore.Get method.
|
||||
//
|
||||
// It is expected that only one of ID or Name, Provider, and Scheme will be
|
||||
// specified, but all are provided UserStores should prefer ID.
|
||||
type UserQuery struct {
|
||||
ID *uint64
|
||||
Name *string
|
||||
|
@ -641,6 +645,11 @@ type UserQuery struct {
|
|||
}
|
||||
|
||||
// UsersStore is the Storage and retrieval of authentication information
|
||||
//
|
||||
// While not necessary for the app to function correctly, it is
|
||||
// expected that Implementors of the UsersStore will take
|
||||
// care to guarantee that the combinartion of a users Name, Provider,
|
||||
// and Scheme are unique.
|
||||
type UsersStore interface {
|
||||
// All lists all users from the UsersStore
|
||||
All(context.Context) ([]User, error)
|
||||
|
|
Loading…
Reference in New Issue