Merge pull request #2132 from influxdata/multitenancy_authorize_role
Role based authorizationpull/2160/head
commit
4b445b10d1
|
@ -2,7 +2,7 @@ package bolt
|
|||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"fmt"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/influxdata/chronograf"
|
||||
|
@ -38,17 +38,48 @@ func (s *UsersStore) get(ctx context.Context, id uint64) (*chronograf.User, erro
|
|||
return &u, nil
|
||||
}
|
||||
|
||||
func (s *UsersStore) each(ctx context.Context, fn func(*chronograf.User)) error {
|
||||
return s.client.db.View(func(tx *bolt.Tx) error {
|
||||
return tx.Bucket(UsersBucket).ForEach(func(k, v []byte) error {
|
||||
var user chronograf.User
|
||||
if err := internal.UnmarshalUser(v, &user); err != nil {
|
||||
return err
|
||||
}
|
||||
fn(&user)
|
||||
return nil
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Get searches the UsersStore for user with name
|
||||
func (s *UsersStore) Get(ctx context.Context, id string) (*chronograf.User, error) {
|
||||
uid, err := strconv.ParseUint(id, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func (s *UsersStore) Get(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) {
|
||||
if q.ID != nil {
|
||||
return s.get(ctx, *q.ID)
|
||||
}
|
||||
u, err := s.get(ctx, uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
if q.Name != nil && q.Provider != nil && q.Scheme != nil {
|
||||
var user *chronograf.User
|
||||
err := s.each(ctx, func(u *chronograf.User) {
|
||||
if user != nil {
|
||||
return
|
||||
}
|
||||
if u.Name == *q.Name && u.Provider == *q.Provider && u.Scheme == *q.Scheme {
|
||||
user = u
|
||||
}
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
return nil, chronograf.ErrUserNotFound
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
return u, nil
|
||||
|
||||
return nil, fmt.Errorf("must specify either ID, or Name, Provider, and Scheme in UserQuery")
|
||||
}
|
||||
|
||||
// Add a new Users in the UsersStore.
|
||||
|
|
|
@ -2,8 +2,6 @@ package bolt_test
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
@ -18,25 +16,45 @@ var cmpOptions = cmp.Options{
|
|||
cmpopts.EquateEmpty(),
|
||||
}
|
||||
|
||||
func TestUsersStore_Get(t *testing.T) {
|
||||
func TestUsersStore_GetWithID(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
ID string
|
||||
usr *chronograf.User
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *chronograf.User
|
||||
wantErr bool
|
||||
name string
|
||||
args args
|
||||
want *chronograf.User
|
||||
wantErr bool
|
||||
addFirst bool
|
||||
}{
|
||||
{
|
||||
name: "User not found",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
ID: "1337",
|
||||
usr: &chronograf.User{
|
||||
ID: 1337,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Get user",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
usr: &chronograf.User{
|
||||
Name: "billietta",
|
||||
Provider: "Google",
|
||||
Scheme: "OAuth2",
|
||||
},
|
||||
},
|
||||
want: &chronograf.User{
|
||||
Name: "billietta",
|
||||
Provider: "Google",
|
||||
Scheme: "OAuth2",
|
||||
},
|
||||
addFirst: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
client, err := NewTestClient()
|
||||
|
@ -49,17 +67,118 @@ func TestUsersStore_Get(t *testing.T) {
|
|||
defer client.Close()
|
||||
|
||||
s := client.UsersStore
|
||||
got, err := s.Get(tt.args.ctx, tt.args.ID)
|
||||
if tt.addFirst {
|
||||
tt.args.usr, err = s.Add(tt.args.ctx, tt.args.usr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
got, err := s.Get(tt.args.ctx, chronograf.UserQuery{ID: &tt.args.usr.ID})
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("%q. UsersStore.Get() error = %v, wantErr %v", tt.name, err, tt.wantErr)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("%q. UsersStore.Get() = %v, want %v", tt.name, got, tt.want)
|
||||
if diff := cmp.Diff(got, tt.want, cmpOptions...); diff != "" {
|
||||
t.Errorf("%q. UsersStore.Get():\n-got/+want\ndiff %s", tt.name, diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUsersStore_GetWithNameProviderScheme(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
name string
|
||||
provider string
|
||||
usr *chronograf.User
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *chronograf.User
|
||||
wantErr bool
|
||||
addFirst bool
|
||||
}{
|
||||
{
|
||||
name: "User not found",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
usr: &chronograf.User{
|
||||
Name: "billietta",
|
||||
Provider: "Google",
|
||||
Scheme: "OAuth2",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Get user",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
usr: &chronograf.User{
|
||||
Name: "billietta",
|
||||
Provider: "Google",
|
||||
Scheme: "OAuth2",
|
||||
},
|
||||
},
|
||||
want: &chronograf.User{
|
||||
Name: "billietta",
|
||||
Provider: "Google",
|
||||
Scheme: "OAuth2",
|
||||
},
|
||||
addFirst: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
client, err := NewTestClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := client.Open(context.TODO()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
s := client.UsersStore
|
||||
if tt.addFirst {
|
||||
tt.args.usr, err = s.Add(tt.args.ctx, tt.args.usr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
got, err := s.Get(tt.args.ctx, chronograf.UserQuery{
|
||||
Name: &tt.args.usr.Name,
|
||||
Provider: &tt.args.usr.Provider,
|
||||
Scheme: &tt.args.usr.Scheme,
|
||||
})
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("%q. UsersStore.Get() error = %v, wantErr %v", tt.name, err, tt.wantErr)
|
||||
continue
|
||||
}
|
||||
if diff := cmp.Diff(got, tt.want, cmpOptions...); diff != "" {
|
||||
t.Errorf("%q. UsersStore.Get():\n-got/+want\ndiff %s", tt.name, diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUsersStore_GetInvalid(t *testing.T) {
|
||||
client, err := NewTestClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := client.Open(context.TODO()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
s := client.UsersStore
|
||||
|
||||
_, err = s.Get(context.Background(), chronograf.UserQuery{})
|
||||
if err == nil {
|
||||
t.Errorf("Invalid Get. UsersStore.Get() error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUsersStore_Add(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
|
@ -114,7 +233,7 @@ func TestUsersStore_Add(t *testing.T) {
|
|||
continue
|
||||
}
|
||||
|
||||
got, err = s.Get(tt.args.ctx, fmt.Sprintf("%d", got.ID))
|
||||
got, err = s.Get(tt.args.ctx, chronograf.UserQuery{ID: &got.ID})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get user: %v", err)
|
||||
}
|
||||
|
@ -298,7 +417,7 @@ func TestUsersStore_Update(t *testing.T) {
|
|||
continue
|
||||
}
|
||||
|
||||
got, err := s.Get(tt.args.ctx, fmt.Sprintf("%d", tt.args.usr.ID))
|
||||
got, err := s.Get(tt.args.ctx, chronograf.UserQuery{ID: &tt.args.usr.ID})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get user: %v", err)
|
||||
}
|
||||
|
|
|
@ -606,6 +606,15 @@ type User struct {
|
|||
Scheme string `json:"scheme,omitempty"`
|
||||
}
|
||||
|
||||
// UserQuery represents the attributes that a user may be retrieved by.
|
||||
// It is predominantly used in the UsersStore.Get method.
|
||||
type UserQuery struct {
|
||||
ID *uint64
|
||||
Name *string
|
||||
Provider *string
|
||||
Scheme *string
|
||||
}
|
||||
|
||||
// UsersStore is the Storage and retrieval of authentication information
|
||||
type UsersStore interface {
|
||||
// All lists all users from the UsersStore
|
||||
|
@ -615,7 +624,7 @@ type UsersStore interface {
|
|||
// Delete the User from the UsersStore
|
||||
Delete(context.Context, *User) error
|
||||
// Get retrieves a user if name exists.
|
||||
Get(ctx context.Context, name string) (*User, error)
|
||||
Get(ctx context.Context, q UserQuery) (*User, error)
|
||||
// Update the user's permissions or roles
|
||||
Update(context.Context, *User) error
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package enterprise
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/influxdata/chronograf"
|
||||
)
|
||||
|
@ -28,7 +29,7 @@ func (c *UserStore) Add(ctx context.Context, u *chronograf.User) (*chronograf.Us
|
|||
}
|
||||
}
|
||||
|
||||
return c.Get(ctx, u.Name)
|
||||
return c.Get(ctx, chronograf.UserQuery{Name: &u.Name})
|
||||
}
|
||||
|
||||
// Delete the User from Influx Enterprise
|
||||
|
@ -37,8 +38,11 @@ func (c *UserStore) Delete(ctx context.Context, u *chronograf.User) error {
|
|||
}
|
||||
|
||||
// Get retrieves a user if name exists.
|
||||
func (c *UserStore) Get(ctx context.Context, name string) (*chronograf.User, error) {
|
||||
u, err := c.Ctrl.User(ctx, name)
|
||||
func (c *UserStore) Get(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) {
|
||||
if q.Name == nil {
|
||||
return nil, fmt.Errorf("query must specify name")
|
||||
}
|
||||
u, err := c.Ctrl.User(ctx, *q.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -48,7 +52,7 @@ func (c *UserStore) Get(ctx context.Context, name string) (*chronograf.User, err
|
|||
return nil, err
|
||||
}
|
||||
|
||||
role := ur[name]
|
||||
role := ur[*q.Name]
|
||||
cr := role.ToChronograf()
|
||||
// For now we are removing all users from a role being returned.
|
||||
for i, r := range cr {
|
||||
|
|
|
@ -375,7 +375,7 @@ func TestClient_Get(t *testing.T) {
|
|||
Ctrl: tt.fields.Ctrl,
|
||||
Logger: tt.fields.Logger,
|
||||
}
|
||||
got, err := c.Get(tt.args.ctx, tt.args.name)
|
||||
got, err := c.Get(tt.args.ctx, chronograf.UserQuery{Name: &tt.args.name})
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("%q. Client.Get() error = %v, wantErr %v", tt.name, err, tt.wantErr)
|
||||
continue
|
||||
|
|
|
@ -21,7 +21,7 @@ func (c *Client) Add(ctx context.Context, u *chronograf.User) (*chronograf.User,
|
|||
return nil, err
|
||||
}
|
||||
}
|
||||
return c.Get(ctx, u.Name)
|
||||
return c.Get(ctx, chronograf.UserQuery{Name: &u.Name})
|
||||
}
|
||||
|
||||
// Delete the User from InfluxDB
|
||||
|
@ -54,14 +54,18 @@ func (c *Client) Delete(ctx context.Context, u *chronograf.User) error {
|
|||
}
|
||||
|
||||
// Get retrieves a user if name exists.
|
||||
func (c *Client) Get(ctx context.Context, name string) (*chronograf.User, error) {
|
||||
func (c *Client) Get(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) {
|
||||
if q.Name == nil {
|
||||
return nil, fmt.Errorf("query must specify name")
|
||||
}
|
||||
|
||||
users, err := c.showUsers(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
if user.Name == name {
|
||||
if user.Name == *q.Name {
|
||||
perms, err := c.userPermissions(ctx, user.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -82,7 +86,7 @@ func (c *Client) Update(ctx context.Context, u *chronograf.User) error {
|
|||
return c.updatePassword(ctx, u.Name, u.Passwd)
|
||||
}
|
||||
|
||||
user, err := c.Get(ctx, u.Name)
|
||||
user, err := c.Get(ctx, chronograf.UserQuery{Name: &u.Name})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -392,7 +392,7 @@ func TestClient_Get(t *testing.T) {
|
|||
Logger: log.New(log.DebugLevel),
|
||||
}
|
||||
defer ts.Close()
|
||||
got, err := c.Get(tt.args.ctx, tt.args.name)
|
||||
got, err := c.Get(tt.args.ctx, chronograf.UserQuery{Name: &tt.args.name})
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("%q. Client.Get() error = %v, wantErr %v", tt.name, err, tt.wantErr)
|
||||
continue
|
||||
|
|
|
@ -13,7 +13,7 @@ type UsersStore struct {
|
|||
AllF func(context.Context) ([]chronograf.User, error)
|
||||
AddF func(context.Context, *chronograf.User) (*chronograf.User, error)
|
||||
DeleteF func(context.Context, *chronograf.User) error
|
||||
GetF func(ctx context.Context, name string) (*chronograf.User, error)
|
||||
GetF func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error)
|
||||
UpdateF func(context.Context, *chronograf.User) error
|
||||
}
|
||||
|
||||
|
@ -33,8 +33,8 @@ func (s *UsersStore) Delete(ctx context.Context, u *chronograf.User) error {
|
|||
}
|
||||
|
||||
// Get retrieves a user if name exists.
|
||||
func (s *UsersStore) Get(ctx context.Context, name string) (*chronograf.User, error) {
|
||||
return s.GetF(ctx, name)
|
||||
func (s *UsersStore) Get(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) {
|
||||
return s.GetF(ctx, q)
|
||||
}
|
||||
|
||||
// Update the user's permissions or roles
|
||||
|
|
100
server/auth.go
100
server/auth.go
|
@ -15,7 +15,7 @@ import (
|
|||
func AuthorizedToken(auth oauth2.Authenticator, logger chronograf.Logger, next http.Handler) http.HandlerFunc {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
log := logger.
|
||||
WithField("component", "auth").
|
||||
WithField("component", "token_auth").
|
||||
WithField("remote_addr", r.RemoteAddr).
|
||||
WithField("method", r.Method).
|
||||
WithField("url", r.URL)
|
||||
|
@ -45,3 +45,101 @@ func AuthorizedToken(auth oauth2.Authenticator, logger chronograf.Logger, next h
|
|||
return
|
||||
})
|
||||
}
|
||||
|
||||
// AuthorizedUser extracts the user name and provider from context. If the
|
||||
// user and provider can be found on the context, we look up the user by their
|
||||
// name and provider. If the user is found, we verify that the user has at at
|
||||
// least the role supplied.
|
||||
func AuthorizedUser(
|
||||
store chronograf.UsersStore,
|
||||
useAuth bool,
|
||||
role string,
|
||||
logger chronograf.Logger,
|
||||
next http.HandlerFunc,
|
||||
) http.HandlerFunc {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if !useAuth {
|
||||
next(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
log := logger.
|
||||
WithField("component", "role_auth").
|
||||
WithField("remote_addr", r.RemoteAddr).
|
||||
WithField("method", r.Method).
|
||||
WithField("url", r.URL)
|
||||
|
||||
ctx := r.Context()
|
||||
|
||||
username, err := getUsername(ctx)
|
||||
if err != nil {
|
||||
log.Error("Failed to retrieve username from context")
|
||||
Error(w, http.StatusUnauthorized, "User is not authorized", logger)
|
||||
return
|
||||
}
|
||||
provider, err := getProvider(ctx)
|
||||
if err != nil {
|
||||
log.Error("Failed to retrieve provider from context")
|
||||
Error(w, http.StatusUnauthorized, "User is not authorized", logger)
|
||||
return
|
||||
}
|
||||
scheme, err := getScheme(ctx)
|
||||
if err != nil {
|
||||
log.Error("Failed to retrieve scheme from context")
|
||||
Error(w, http.StatusUnauthorized, "User is not authorized", logger)
|
||||
return
|
||||
}
|
||||
|
||||
u, err := store.Get(ctx, chronograf.UserQuery{
|
||||
Name: &username,
|
||||
Provider: &provider,
|
||||
Scheme: &scheme,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("Failed to retrieve user")
|
||||
Error(w, http.StatusUnauthorized, "User is not authorized", logger)
|
||||
return
|
||||
}
|
||||
|
||||
if hasAuthorizedRole(u, role) {
|
||||
next(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
Error(w, http.StatusUnauthorized, "User is not authorized", logger)
|
||||
return
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func hasAuthorizedRole(u *chronograf.User, role string) bool {
|
||||
if u == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch role {
|
||||
case ViewerRoleName:
|
||||
for _, r := range u.Roles {
|
||||
switch r.Name {
|
||||
case ViewerRoleName, EditorRoleName, AdminRoleName:
|
||||
return true
|
||||
}
|
||||
}
|
||||
case EditorRoleName:
|
||||
for _, r := range u.Roles {
|
||||
switch r.Name {
|
||||
case EditorRoleName, AdminRoleName:
|
||||
return true
|
||||
}
|
||||
}
|
||||
case AdminRoleName:
|
||||
for _, r := range u.Roles {
|
||||
switch r.Name {
|
||||
case AdminRoleName:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -3,11 +3,14 @@ package server_test
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/influxdata/chronograf"
|
||||
clog "github.com/influxdata/chronograf/log"
|
||||
"github.com/influxdata/chronograf/mocks"
|
||||
"github.com/influxdata/chronograf/oauth2"
|
||||
"github.com/influxdata/chronograf/server"
|
||||
)
|
||||
|
@ -94,3 +97,496 @@ func TestAuthorizedToken(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthorizedUser(t *testing.T) {
|
||||
type fields struct {
|
||||
UsersStore chronograf.UsersStore
|
||||
Logger chronograf.Logger
|
||||
}
|
||||
type args struct {
|
||||
username string
|
||||
provider string
|
||||
useAuth bool
|
||||
role string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
authorized bool
|
||||
}{
|
||||
{
|
||||
name: "Not using auth",
|
||||
fields: fields{
|
||||
UsersStore: &mocks.UsersStore{},
|
||||
Logger: clog.New(clog.DebugLevel),
|
||||
},
|
||||
args: args{
|
||||
useAuth: false,
|
||||
},
|
||||
authorized: true,
|
||||
},
|
||||
{
|
||||
name: "User with viewer role is viewer authorized",
|
||||
fields: fields{
|
||||
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")
|
||||
}
|
||||
return &chronograf.User{
|
||||
ID: 1337,
|
||||
Name: "billysteve",
|
||||
Provider: "Google",
|
||||
Scheme: "OAuth2",
|
||||
Roles: []chronograf.Role{
|
||||
server.ViewerRole,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
Logger: clog.New(clog.DebugLevel),
|
||||
},
|
||||
args: args{
|
||||
username: "billysteve",
|
||||
provider: "Google",
|
||||
role: "viewer",
|
||||
useAuth: true,
|
||||
},
|
||||
authorized: true,
|
||||
},
|
||||
{
|
||||
name: "User with editor role is viewer authorized",
|
||||
fields: fields{
|
||||
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")
|
||||
}
|
||||
return &chronograf.User{
|
||||
ID: 1337,
|
||||
Name: "billysteve",
|
||||
Provider: "Google",
|
||||
Scheme: "OAuth2",
|
||||
Roles: []chronograf.Role{
|
||||
server.EditorRole,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
Logger: clog.New(clog.DebugLevel),
|
||||
},
|
||||
args: args{
|
||||
username: "billysteve",
|
||||
provider: "Google",
|
||||
role: "viewer",
|
||||
useAuth: true,
|
||||
},
|
||||
authorized: true,
|
||||
},
|
||||
{
|
||||
name: "User with admin role is viewer authorized",
|
||||
fields: fields{
|
||||
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")
|
||||
}
|
||||
return &chronograf.User{
|
||||
ID: 1337,
|
||||
Name: "billysteve",
|
||||
Provider: "Google",
|
||||
Scheme: "OAuth2",
|
||||
Roles: []chronograf.Role{
|
||||
server.AdminRole,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
Logger: clog.New(clog.DebugLevel),
|
||||
},
|
||||
args: args{
|
||||
username: "billysteve",
|
||||
provider: "Google",
|
||||
role: "viewer",
|
||||
useAuth: true,
|
||||
},
|
||||
authorized: true,
|
||||
},
|
||||
{
|
||||
name: "User with viewer role is editor unauthorized",
|
||||
fields: fields{
|
||||
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")
|
||||
}
|
||||
return &chronograf.User{
|
||||
ID: 1337,
|
||||
Name: "billysteve",
|
||||
Provider: "Google",
|
||||
Scheme: "OAuth2",
|
||||
Roles: []chronograf.Role{
|
||||
server.ViewerRole,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
Logger: clog.New(clog.DebugLevel),
|
||||
},
|
||||
args: args{
|
||||
username: "billysteve",
|
||||
provider: "Google",
|
||||
role: "editor",
|
||||
useAuth: true,
|
||||
},
|
||||
authorized: false,
|
||||
},
|
||||
{
|
||||
name: "User with editor role is editor authorized",
|
||||
fields: fields{
|
||||
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")
|
||||
}
|
||||
return &chronograf.User{
|
||||
ID: 1337,
|
||||
Name: "billysteve",
|
||||
Provider: "Google",
|
||||
Scheme: "OAuth2",
|
||||
Roles: []chronograf.Role{
|
||||
server.EditorRole,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
Logger: clog.New(clog.DebugLevel),
|
||||
},
|
||||
args: args{
|
||||
username: "billysteve",
|
||||
provider: "Google",
|
||||
role: "editor",
|
||||
useAuth: true,
|
||||
},
|
||||
authorized: true,
|
||||
},
|
||||
{
|
||||
name: "User with admin role is editor authorized",
|
||||
fields: fields{
|
||||
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")
|
||||
}
|
||||
return &chronograf.User{
|
||||
ID: 1337,
|
||||
Name: "billysteve",
|
||||
Provider: "Google",
|
||||
Scheme: "OAuth2",
|
||||
Roles: []chronograf.Role{
|
||||
server.AdminRole,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
Logger: clog.New(clog.DebugLevel),
|
||||
},
|
||||
args: args{
|
||||
username: "billysteve",
|
||||
provider: "Google",
|
||||
role: "editor",
|
||||
useAuth: true,
|
||||
},
|
||||
authorized: true,
|
||||
},
|
||||
{
|
||||
name: "User with viewer role is admin unauthorized",
|
||||
fields: fields{
|
||||
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")
|
||||
}
|
||||
return &chronograf.User{
|
||||
ID: 1337,
|
||||
Name: "billysteve",
|
||||
Provider: "Google",
|
||||
Scheme: "OAuth2",
|
||||
Roles: []chronograf.Role{
|
||||
server.ViewerRole,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
Logger: clog.New(clog.DebugLevel),
|
||||
},
|
||||
args: args{
|
||||
username: "billysteve",
|
||||
provider: "Google",
|
||||
role: "admin",
|
||||
useAuth: true,
|
||||
},
|
||||
authorized: false,
|
||||
},
|
||||
{
|
||||
name: "User with editor role is admin unauthorized",
|
||||
fields: fields{
|
||||
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")
|
||||
}
|
||||
return &chronograf.User{
|
||||
ID: 1337,
|
||||
Name: "billysteve",
|
||||
Provider: "Google",
|
||||
Scheme: "OAuth2",
|
||||
Roles: []chronograf.Role{
|
||||
server.EditorRole,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
Logger: clog.New(clog.DebugLevel),
|
||||
},
|
||||
args: args{
|
||||
username: "billysteve",
|
||||
provider: "Google",
|
||||
role: "admin",
|
||||
useAuth: true,
|
||||
},
|
||||
authorized: false,
|
||||
},
|
||||
{
|
||||
name: "User with admin role is admin authorized",
|
||||
fields: fields{
|
||||
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")
|
||||
}
|
||||
return &chronograf.User{
|
||||
ID: 1337,
|
||||
Name: "billysteve",
|
||||
Provider: "Google",
|
||||
Scheme: "OAuth2",
|
||||
Roles: []chronograf.Role{
|
||||
server.AdminRole,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
Logger: clog.New(clog.DebugLevel),
|
||||
},
|
||||
args: args{
|
||||
username: "billysteve",
|
||||
provider: "Google",
|
||||
role: "admin",
|
||||
useAuth: true,
|
||||
},
|
||||
authorized: true,
|
||||
},
|
||||
{
|
||||
name: "User with no role is viewer unauthorized",
|
||||
fields: fields{
|
||||
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")
|
||||
}
|
||||
return &chronograf.User{
|
||||
ID: 1337,
|
||||
Name: "billysteve",
|
||||
Provider: "Google",
|
||||
Scheme: "OAuth2",
|
||||
Roles: []chronograf.Role{},
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
Logger: clog.New(clog.DebugLevel),
|
||||
},
|
||||
args: args{
|
||||
username: "billysteve",
|
||||
provider: "Google",
|
||||
role: "view",
|
||||
useAuth: true,
|
||||
},
|
||||
authorized: false,
|
||||
},
|
||||
{
|
||||
name: "User with no role is editor unauthorized",
|
||||
fields: fields{
|
||||
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")
|
||||
}
|
||||
return &chronograf.User{
|
||||
ID: 1337,
|
||||
Name: "billysteve",
|
||||
Provider: "Google",
|
||||
Scheme: "OAuth2",
|
||||
Roles: []chronograf.Role{},
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
Logger: clog.New(clog.DebugLevel),
|
||||
},
|
||||
args: args{
|
||||
username: "billysteve",
|
||||
provider: "Google",
|
||||
role: "editor",
|
||||
useAuth: true,
|
||||
},
|
||||
authorized: false,
|
||||
},
|
||||
{
|
||||
name: "User with no role is admin unauthorized",
|
||||
fields: fields{
|
||||
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")
|
||||
}
|
||||
return &chronograf.User{
|
||||
ID: 1337,
|
||||
Name: "billysteve",
|
||||
Provider: "Google",
|
||||
Scheme: "OAuth2",
|
||||
Roles: []chronograf.Role{},
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
Logger: clog.New(clog.DebugLevel),
|
||||
},
|
||||
args: args{
|
||||
username: "billysteve",
|
||||
provider: "Google",
|
||||
role: "admin",
|
||||
useAuth: true,
|
||||
},
|
||||
authorized: false,
|
||||
},
|
||||
{
|
||||
name: "User with unknown role is viewer unauthorized",
|
||||
fields: fields{
|
||||
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")
|
||||
}
|
||||
return &chronograf.User{
|
||||
ID: 1337,
|
||||
Name: "billysteve",
|
||||
Provider: "Google",
|
||||
Scheme: "OAuth2",
|
||||
Roles: []chronograf.Role{
|
||||
{
|
||||
Name: "sweet_role",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
Logger: clog.New(clog.DebugLevel),
|
||||
},
|
||||
args: args{
|
||||
username: "billysteve",
|
||||
provider: "Google",
|
||||
role: "viewer",
|
||||
useAuth: true,
|
||||
},
|
||||
authorized: false,
|
||||
},
|
||||
{
|
||||
name: "User with unknown role is editor unauthorized",
|
||||
fields: fields{
|
||||
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")
|
||||
}
|
||||
return &chronograf.User{
|
||||
ID: 1337,
|
||||
Name: "billysteve",
|
||||
Provider: "Google",
|
||||
Scheme: "OAuth2",
|
||||
Roles: []chronograf.Role{
|
||||
{
|
||||
Name: "sweet_role",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
Logger: clog.New(clog.DebugLevel),
|
||||
},
|
||||
args: args{
|
||||
username: "billysteve",
|
||||
provider: "Google",
|
||||
role: "editor",
|
||||
useAuth: true,
|
||||
},
|
||||
authorized: false,
|
||||
},
|
||||
{
|
||||
name: "User with unknown role is admin unauthorized",
|
||||
fields: fields{
|
||||
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")
|
||||
}
|
||||
return &chronograf.User{
|
||||
ID: 1337,
|
||||
Name: "billysteve",
|
||||
Provider: "Google",
|
||||
Scheme: "OAuth2",
|
||||
Roles: []chronograf.Role{
|
||||
{
|
||||
Name: "sweet_role",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
Logger: clog.New(clog.DebugLevel),
|
||||
},
|
||||
args: args{
|
||||
username: "billysteve",
|
||||
provider: "Google",
|
||||
role: "admin",
|
||||
useAuth: true,
|
||||
},
|
||||
authorized: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var authorized bool
|
||||
next := func(w http.ResponseWriter, r *http.Request) {
|
||||
authorized = true
|
||||
}
|
||||
fn := server.AuthorizedUser(tt.fields.UsersStore, tt.args.useAuth, tt.args.role, tt.fields.Logger, next)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest(
|
||||
"GET",
|
||||
"http://any.url", // can be any valid URL as we are bypassing mux
|
||||
nil,
|
||||
)
|
||||
r = r.WithContext(context.WithValue(r.Context(), oauth2.PrincipalKey, oauth2.Principal{
|
||||
Subject: tt.args.username,
|
||||
Issuer: tt.args.provider,
|
||||
}))
|
||||
fn(w, r)
|
||||
|
||||
if authorized != tt.authorized {
|
||||
t.Errorf("%q. AuthorizedUser() = %v, expected %v", tt.name, authorized, tt.authorized)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
54
server/me.go
54
server/me.go
|
@ -36,7 +36,7 @@ func newMeResponse(usr *chronograf.User) meResponse {
|
|||
}
|
||||
}
|
||||
|
||||
func getEmail(ctx context.Context) (string, error) {
|
||||
func getUsername(ctx context.Context) (string, error) {
|
||||
principal, err := getPrincipal(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -47,6 +47,24 @@ func getEmail(ctx context.Context) (string, error) {
|
|||
return principal.Subject, nil
|
||||
}
|
||||
|
||||
func getProvider(ctx context.Context) (string, error) {
|
||||
principal, err := getPrincipal(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if principal.Issuer == "" {
|
||||
return "", fmt.Errorf("Token not found")
|
||||
}
|
||||
return principal.Issuer, nil
|
||||
}
|
||||
|
||||
// TODO: This Scheme value is hard-coded temporarily since we only currently
|
||||
// support OAuth2. This hard-coding should be removed whenever we add
|
||||
// support for other authentication schemes.
|
||||
func getScheme(ctx context.Context) (string, error) {
|
||||
return "OAuth2", nil
|
||||
}
|
||||
|
||||
func getPrincipal(ctx context.Context) (oauth2.Principal, error) {
|
||||
principal, ok := ctx.Value(oauth2.PrincipalKey).(oauth2.Principal)
|
||||
if !ok {
|
||||
|
@ -56,7 +74,7 @@ func getPrincipal(ctx context.Context) (oauth2.Principal, error) {
|
|||
return principal, nil
|
||||
}
|
||||
|
||||
// Me does a findOrCreate based on the email in the context
|
||||
// Me does a findOrCreate based on the username in the context
|
||||
func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
if !s.UseAuth {
|
||||
|
@ -66,14 +84,33 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
email, err := getEmail(ctx)
|
||||
username, err := getUsername(ctx)
|
||||
if err != nil {
|
||||
invalidData(w, err, s.Logger)
|
||||
return
|
||||
}
|
||||
provider, err := getProvider(ctx)
|
||||
if err != nil {
|
||||
invalidData(w, err, s.Logger)
|
||||
return
|
||||
}
|
||||
scheme, err := getScheme(ctx)
|
||||
if err != nil {
|
||||
invalidData(w, err, s.Logger)
|
||||
return
|
||||
}
|
||||
|
||||
usr, err := s.UsersStore.Get(ctx, email)
|
||||
if err == nil {
|
||||
usr, err := s.UsersStore.Get(ctx, chronograf.UserQuery{
|
||||
Name: &username,
|
||||
Provider: &provider,
|
||||
Scheme: &scheme,
|
||||
})
|
||||
if err != nil && err != chronograf.ErrUserNotFound {
|
||||
unknownErrorWithMessage(w, err, s.Logger)
|
||||
return
|
||||
}
|
||||
|
||||
if usr != nil {
|
||||
res := newMeResponse(usr)
|
||||
encodeJSON(w, http.StatusOK, res, s.Logger)
|
||||
return
|
||||
|
@ -81,7 +118,12 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// Because we didnt find a user, making a new one
|
||||
user := &chronograf.User{
|
||||
Name: email,
|
||||
Name: username,
|
||||
Provider: provider,
|
||||
// TODO: This Scheme value is hard-coded temporarily since we only currently
|
||||
// support OAuth2. This hard-coding should be removed whenever we add
|
||||
// support for other authentication schemes.
|
||||
Scheme: "OAuth2",
|
||||
}
|
||||
|
||||
newUser, err := s.UsersStore.Add(ctx, user)
|
||||
|
|
|
@ -43,21 +43,27 @@ func TestService_Me(t *testing.T) {
|
|||
},
|
||||
fields: fields{
|
||||
UseAuth: true,
|
||||
Logger: log.New(log.DebugLevel),
|
||||
UsersStore: &mocks.UsersStore{
|
||||
GetF: func(ctx context.Context, name string) (*chronograf.User, error) {
|
||||
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 &chronograf.User{
|
||||
Name: "me",
|
||||
Passwd: "hunter2",
|
||||
Name: "me",
|
||||
Provider: "GitHub",
|
||||
Scheme: "OAuth2",
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
principal: oauth2.Principal{
|
||||
Subject: "me",
|
||||
Issuer: "GitHub",
|
||||
},
|
||||
wantStatus: http.StatusOK,
|
||||
wantContentType: "application/json",
|
||||
wantBody: `{"name":"me","password":"hunter2","links":{"self":"/chronograf/v1/users/me"}}
|
||||
wantBody: `{"name":"me","provider":"GitHub","scheme":"OAuth2","links":{"self":"/chronograf/v1/users/me"}}
|
||||
`,
|
||||
},
|
||||
{
|
||||
|
@ -68,9 +74,13 @@ func TestService_Me(t *testing.T) {
|
|||
},
|
||||
fields: fields{
|
||||
UseAuth: true,
|
||||
Logger: log.New(log.DebugLevel),
|
||||
UsersStore: &mocks.UsersStore{
|
||||
GetF: func(ctx context.Context, name string) (*chronograf.User, error) {
|
||||
return nil, fmt.Errorf("Unknown User")
|
||||
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
|
||||
|
@ -79,10 +89,11 @@ func TestService_Me(t *testing.T) {
|
|||
},
|
||||
principal: oauth2.Principal{
|
||||
Subject: "secret",
|
||||
Issuer: "Auth0",
|
||||
},
|
||||
wantStatus: http.StatusOK,
|
||||
wantContentType: "application/json",
|
||||
wantBody: `{"name":"secret","links":{"self":"/chronograf/v1/users/secret"}}
|
||||
wantBody: `{"name":"secret","provider":"Auth0","scheme":"OAuth2","links":{"self":"/chronograf/v1/users/secret"}}
|
||||
`,
|
||||
},
|
||||
{
|
||||
|
@ -94,8 +105,8 @@ func TestService_Me(t *testing.T) {
|
|||
fields: fields{
|
||||
UseAuth: true,
|
||||
UsersStore: &mocks.UsersStore{
|
||||
GetF: func(ctx context.Context, name string) (*chronograf.User, error) {
|
||||
return nil, fmt.Errorf("Unknown User")
|
||||
GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) {
|
||||
return nil, chronograf.ErrUserNotFound
|
||||
},
|
||||
AddF: func(ctx context.Context, u *chronograf.User) (*chronograf.User, error) {
|
||||
return nil, fmt.Errorf("Why Heavy?")
|
||||
|
@ -105,6 +116,7 @@ func TestService_Me(t *testing.T) {
|
|||
},
|
||||
principal: oauth2.Principal{
|
||||
Subject: "secret",
|
||||
Issuer: "Heroku",
|
||||
},
|
||||
wantStatus: http.StatusInternalServerError,
|
||||
wantContentType: "application/json",
|
||||
|
@ -138,6 +150,7 @@ func TestService_Me(t *testing.T) {
|
|||
wantStatus: http.StatusUnprocessableEntity,
|
||||
principal: oauth2.Principal{
|
||||
Subject: "",
|
||||
Issuer: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
153
server/mux.go
153
server/mux.go
|
@ -67,127 +67,142 @@ func NewMux(opts MuxOpts, service Service) http.Handler {
|
|||
hr.NotFound = http.StripPrefix(opts.Basepath, hr.NotFound)
|
||||
}
|
||||
|
||||
EnsureViewer := func(next http.HandlerFunc) http.HandlerFunc {
|
||||
return AuthorizedUser(service.UsersStore, opts.UseAuth, ViewerRoleName, opts.Logger, next)
|
||||
}
|
||||
EnsureEditor := func(next http.HandlerFunc) http.HandlerFunc {
|
||||
return AuthorizedUser(service.UsersStore, opts.UseAuth, EditorRoleName, opts.Logger, next)
|
||||
}
|
||||
EnsureAdmin := func(next http.HandlerFunc) http.HandlerFunc {
|
||||
return AuthorizedUser(service.UsersStore, opts.UseAuth, AdminRoleName, opts.Logger, next)
|
||||
}
|
||||
|
||||
/* Documentation */
|
||||
router.GET("/swagger.json", Spec())
|
||||
router.GET("/docs", Redoc("/swagger.json"))
|
||||
|
||||
/* API */
|
||||
// Sources
|
||||
router.GET("/chronograf/v1/sources", service.Sources)
|
||||
router.POST("/chronograf/v1/sources", service.NewSource)
|
||||
router.GET("/chronograf/v1/sources", EnsureViewer(service.Sources))
|
||||
router.POST("/chronograf/v1/sources", EnsureEditor(service.NewSource))
|
||||
|
||||
router.GET("/chronograf/v1/sources/:id", service.SourcesID)
|
||||
router.PATCH("/chronograf/v1/sources/:id", service.UpdateSource)
|
||||
router.DELETE("/chronograf/v1/sources/:id", service.RemoveSource)
|
||||
router.GET("/chronograf/v1/sources/:id", EnsureViewer(service.SourcesID))
|
||||
router.PATCH("/chronograf/v1/sources/:id", EnsureEditor(service.UpdateSource))
|
||||
router.DELETE("/chronograf/v1/sources/:id", EnsureEditor(service.RemoveSource))
|
||||
|
||||
// Source Proxy to Influx; Has gzip compression around the handler
|
||||
influx := gziphandler.GzipHandler(http.HandlerFunc(service.Influx))
|
||||
influx := gziphandler.GzipHandler(http.HandlerFunc(EnsureViewer(service.Influx)))
|
||||
router.Handler("POST", "/chronograf/v1/sources/:id/proxy", influx)
|
||||
|
||||
// Write proxies line protocol write requests to InfluxDB
|
||||
router.POST("/chronograf/v1/sources/:id/write", service.Write)
|
||||
router.POST("/chronograf/v1/sources/:id/write", EnsureViewer(service.Write))
|
||||
|
||||
// Queries is used to analyze a specific queries
|
||||
router.POST("/chronograf/v1/sources/:id/queries", service.Queries)
|
||||
// Queries is used to analyze a specific queries and does not create any
|
||||
// resources. It's a POST because Queries are POSTed to InfluxDB, but this
|
||||
// only modifies InfluxDB resources with certain metaqueries, e.g. DROP DATABASE.
|
||||
//
|
||||
// Admins should ensure that the InfluxDB source as the proper permissions
|
||||
// intended for Chronograf Users with the Viewer Role type.
|
||||
router.POST("/chronograf/v1/sources/:id/queries", EnsureViewer(service.Queries))
|
||||
|
||||
// All possible permissions for users in this source
|
||||
router.GET("/chronograf/v1/sources/:id/permissions", service.Permissions)
|
||||
router.GET("/chronograf/v1/sources/:id/permissions", EnsureViewer(service.Permissions))
|
||||
|
||||
// Users associated with the data source
|
||||
router.GET("/chronograf/v1/sources/:id/users", service.SourceUsers)
|
||||
router.POST("/chronograf/v1/sources/:id/users", service.NewSourceUser)
|
||||
router.GET("/chronograf/v1/sources/:id/users", EnsureAdmin(service.SourceUsers))
|
||||
router.POST("/chronograf/v1/sources/:id/users", EnsureAdmin(service.NewSourceUser))
|
||||
|
||||
router.GET("/chronograf/v1/sources/:id/users/:uid", service.SourceUserID)
|
||||
router.DELETE("/chronograf/v1/sources/:id/users/:uid", service.RemoveSourceUser)
|
||||
router.PATCH("/chronograf/v1/sources/:id/users/:uid", service.UpdateSourceUser)
|
||||
router.GET("/chronograf/v1/sources/:id/users/:uid", EnsureAdmin(service.SourceUserID))
|
||||
router.DELETE("/chronograf/v1/sources/:id/users/:uid", EnsureAdmin(service.RemoveSourceUser))
|
||||
router.PATCH("/chronograf/v1/sources/:id/users/:uid", EnsureAdmin(service.UpdateSourceUser))
|
||||
|
||||
// Roles associated with the data source
|
||||
router.GET("/chronograf/v1/sources/:id/roles", service.SourceRoles)
|
||||
router.POST("/chronograf/v1/sources/:id/roles", service.NewSourceRole)
|
||||
router.GET("/chronograf/v1/sources/:id/roles", EnsureViewer(service.SourceRoles))
|
||||
router.POST("/chronograf/v1/sources/:id/roles", EnsureEditor(service.NewSourceRole))
|
||||
|
||||
router.GET("/chronograf/v1/sources/:id/roles/:rid", service.SourceRoleID)
|
||||
router.DELETE("/chronograf/v1/sources/:id/roles/:rid", service.RemoveSourceRole)
|
||||
router.PATCH("/chronograf/v1/sources/:id/roles/:rid", service.UpdateSourceRole)
|
||||
router.GET("/chronograf/v1/sources/:id/roles/:rid", EnsureViewer(service.SourceRoleID))
|
||||
router.DELETE("/chronograf/v1/sources/:id/roles/:rid", EnsureEditor(service.RemoveSourceRole))
|
||||
router.PATCH("/chronograf/v1/sources/:id/roles/:rid", EnsureEditor(service.UpdateSourceRole))
|
||||
|
||||
// Kapacitor
|
||||
router.GET("/chronograf/v1/sources/:id/kapacitors", service.Kapacitors)
|
||||
router.POST("/chronograf/v1/sources/:id/kapacitors", service.NewKapacitor)
|
||||
router.GET("/chronograf/v1/sources/:id/kapacitors", EnsureViewer(service.Kapacitors))
|
||||
router.POST("/chronograf/v1/sources/:id/kapacitors", EnsureEditor(service.NewKapacitor))
|
||||
|
||||
router.GET("/chronograf/v1/sources/:id/kapacitors/:kid", service.KapacitorsID)
|
||||
router.PATCH("/chronograf/v1/sources/:id/kapacitors/:kid", service.UpdateKapacitor)
|
||||
router.DELETE("/chronograf/v1/sources/:id/kapacitors/:kid", service.RemoveKapacitor)
|
||||
router.GET("/chronograf/v1/sources/:id/kapacitors/:kid", EnsureViewer(service.KapacitorsID))
|
||||
router.PATCH("/chronograf/v1/sources/:id/kapacitors/:kid", EnsureEditor(service.UpdateKapacitor))
|
||||
router.DELETE("/chronograf/v1/sources/:id/kapacitors/:kid", EnsureEditor(service.RemoveKapacitor))
|
||||
|
||||
// Kapacitor rules
|
||||
router.GET("/chronograf/v1/sources/:id/kapacitors/:kid/rules", service.KapacitorRulesGet)
|
||||
router.POST("/chronograf/v1/sources/:id/kapacitors/:kid/rules", service.KapacitorRulesPost)
|
||||
router.GET("/chronograf/v1/sources/:id/kapacitors/:kid/rules", EnsureViewer(service.KapacitorRulesGet))
|
||||
router.POST("/chronograf/v1/sources/:id/kapacitors/:kid/rules", EnsureEditor(service.KapacitorRulesPost))
|
||||
|
||||
router.GET("/chronograf/v1/sources/:id/kapacitors/:kid/rules/:tid", service.KapacitorRulesID)
|
||||
router.PUT("/chronograf/v1/sources/:id/kapacitors/:kid/rules/:tid", service.KapacitorRulesPut)
|
||||
router.PATCH("/chronograf/v1/sources/:id/kapacitors/:kid/rules/:tid", service.KapacitorRulesStatus)
|
||||
router.DELETE("/chronograf/v1/sources/:id/kapacitors/:kid/rules/:tid", service.KapacitorRulesDelete)
|
||||
router.GET("/chronograf/v1/sources/:id/kapacitors/:kid/rules/:tid", EnsureViewer(service.KapacitorRulesID))
|
||||
router.PUT("/chronograf/v1/sources/:id/kapacitors/:kid/rules/:tid", EnsureEditor(service.KapacitorRulesPut))
|
||||
router.PATCH("/chronograf/v1/sources/:id/kapacitors/:kid/rules/:tid", EnsureEditor(service.KapacitorRulesStatus))
|
||||
router.DELETE("/chronograf/v1/sources/:id/kapacitors/:kid/rules/:tid", EnsureEditor(service.KapacitorRulesDelete))
|
||||
|
||||
// Kapacitor Proxy
|
||||
router.GET("/chronograf/v1/sources/:id/kapacitors/:kid/proxy", service.KapacitorProxyGet)
|
||||
router.POST("/chronograf/v1/sources/:id/kapacitors/:kid/proxy", service.KapacitorProxyPost)
|
||||
router.PATCH("/chronograf/v1/sources/:id/kapacitors/:kid/proxy", service.KapacitorProxyPatch)
|
||||
router.DELETE("/chronograf/v1/sources/:id/kapacitors/:kid/proxy", service.KapacitorProxyDelete)
|
||||
router.GET("/chronograf/v1/sources/:id/kapacitors/:kid/proxy", EnsureViewer(service.KapacitorProxyGet))
|
||||
router.POST("/chronograf/v1/sources/:id/kapacitors/:kid/proxy", EnsureEditor(service.KapacitorProxyPost))
|
||||
router.PATCH("/chronograf/v1/sources/:id/kapacitors/:kid/proxy", EnsureEditor(service.KapacitorProxyPatch))
|
||||
router.DELETE("/chronograf/v1/sources/:id/kapacitors/:kid/proxy", EnsureEditor(service.KapacitorProxyDelete))
|
||||
|
||||
// Mappings
|
||||
router.GET("/chronograf/v1/mappings", service.GetMappings)
|
||||
router.GET("/chronograf/v1/mappings", EnsureViewer(service.GetMappings))
|
||||
|
||||
// Layouts
|
||||
router.GET("/chronograf/v1/layouts", service.Layouts)
|
||||
router.POST("/chronograf/v1/layouts", service.NewLayout)
|
||||
router.GET("/chronograf/v1/layouts", EnsureViewer(service.Layouts))
|
||||
router.POST("/chronograf/v1/layouts", EnsureEditor(service.NewLayout))
|
||||
|
||||
router.GET("/chronograf/v1/layouts/:id", service.LayoutsID)
|
||||
router.PUT("/chronograf/v1/layouts/:id", service.UpdateLayout)
|
||||
router.DELETE("/chronograf/v1/layouts/:id", service.RemoveLayout)
|
||||
router.GET("/chronograf/v1/layouts/:id", EnsureViewer(service.LayoutsID))
|
||||
router.PUT("/chronograf/v1/layouts/:id", EnsureEditor(service.UpdateLayout))
|
||||
router.DELETE("/chronograf/v1/layouts/:id", EnsureEditor(service.RemoveLayout))
|
||||
|
||||
// Users associated with Chronograf
|
||||
router.GET("/chronograf/v1/me", service.Me)
|
||||
|
||||
router.GET("/chronograf/v1/users", service.Users)
|
||||
router.POST("/chronograf/v1/users", service.NewUser)
|
||||
router.GET("/chronograf/v1/users", EnsureAdmin(service.Users))
|
||||
router.POST("/chronograf/v1/users", EnsureAdmin(service.NewUser))
|
||||
|
||||
router.GET("/chronograf/v1/users/:id", service.UserID)
|
||||
router.DELETE("/chronograf/v1/users/:id", service.RemoveUser)
|
||||
router.PATCH("/chronograf/v1/users/:id", service.UpdateUser)
|
||||
router.GET("/chronograf/v1/users/:id", EnsureAdmin(service.UserID))
|
||||
router.DELETE("/chronograf/v1/users/:id", EnsureAdmin(service.RemoveUser))
|
||||
router.PATCH("/chronograf/v1/users/:id", EnsureAdmin(service.UpdateUser))
|
||||
|
||||
// Dashboards
|
||||
router.GET("/chronograf/v1/dashboards", service.Dashboards)
|
||||
router.POST("/chronograf/v1/dashboards", service.NewDashboard)
|
||||
router.GET("/chronograf/v1/dashboards", EnsureViewer(service.Dashboards))
|
||||
router.POST("/chronograf/v1/dashboards", EnsureEditor(service.NewDashboard))
|
||||
|
||||
router.GET("/chronograf/v1/dashboards/:id", service.DashboardID)
|
||||
router.DELETE("/chronograf/v1/dashboards/:id", service.RemoveDashboard)
|
||||
router.PUT("/chronograf/v1/dashboards/:id", service.ReplaceDashboard)
|
||||
router.PATCH("/chronograf/v1/dashboards/:id", service.UpdateDashboard)
|
||||
router.GET("/chronograf/v1/dashboards/:id", EnsureViewer(service.DashboardID))
|
||||
router.DELETE("/chronograf/v1/dashboards/:id", EnsureEditor(service.RemoveDashboard))
|
||||
router.PUT("/chronograf/v1/dashboards/:id", EnsureEditor(service.ReplaceDashboard))
|
||||
router.PATCH("/chronograf/v1/dashboards/:id", EnsureEditor(service.UpdateDashboard))
|
||||
// Dashboard Cells
|
||||
router.GET("/chronograf/v1/dashboards/:id/cells", service.DashboardCells)
|
||||
router.POST("/chronograf/v1/dashboards/:id/cells", service.NewDashboardCell)
|
||||
router.GET("/chronograf/v1/dashboards/:id/cells", EnsureViewer(service.DashboardCells))
|
||||
router.POST("/chronograf/v1/dashboards/:id/cells", EnsureEditor(service.NewDashboardCell))
|
||||
|
||||
router.GET("/chronograf/v1/dashboards/:id/cells/:cid", service.DashboardCellID)
|
||||
router.DELETE("/chronograf/v1/dashboards/:id/cells/:cid", service.RemoveDashboardCell)
|
||||
router.PUT("/chronograf/v1/dashboards/:id/cells/:cid", service.ReplaceDashboardCell)
|
||||
router.GET("/chronograf/v1/dashboards/:id/cells/:cid", EnsureViewer(service.DashboardCellID))
|
||||
router.DELETE("/chronograf/v1/dashboards/:id/cells/:cid", EnsureEditor(service.RemoveDashboardCell))
|
||||
router.PUT("/chronograf/v1/dashboards/:id/cells/:cid", EnsureEditor(service.ReplaceDashboardCell))
|
||||
// Dashboard Templates
|
||||
router.GET("/chronograf/v1/dashboards/:id/templates", service.Templates)
|
||||
router.POST("/chronograf/v1/dashboards/:id/templates", service.NewTemplate)
|
||||
router.GET("/chronograf/v1/dashboards/:id/templates", EnsureViewer(service.Templates))
|
||||
router.POST("/chronograf/v1/dashboards/:id/templates", EnsureEditor(service.NewTemplate))
|
||||
|
||||
router.GET("/chronograf/v1/dashboards/:id/templates/:tid", service.TemplateID)
|
||||
router.DELETE("/chronograf/v1/dashboards/:id/templates/:tid", service.RemoveTemplate)
|
||||
router.PUT("/chronograf/v1/dashboards/:id/templates/:tid", service.ReplaceTemplate)
|
||||
router.GET("/chronograf/v1/dashboards/:id/templates/:tid", EnsureViewer(service.TemplateID))
|
||||
router.DELETE("/chronograf/v1/dashboards/:id/templates/:tid", EnsureEditor(service.RemoveTemplate))
|
||||
router.PUT("/chronograf/v1/dashboards/:id/templates/:tid", EnsureEditor(service.ReplaceTemplate))
|
||||
|
||||
// Databases
|
||||
router.GET("/chronograf/v1/sources/:id/dbs", service.GetDatabases)
|
||||
router.POST("/chronograf/v1/sources/:id/dbs", service.NewDatabase)
|
||||
router.GET("/chronograf/v1/sources/:id/dbs", EnsureViewer(service.GetDatabases))
|
||||
router.POST("/chronograf/v1/sources/:id/dbs", EnsureEditor(service.NewDatabase))
|
||||
|
||||
router.DELETE("/chronograf/v1/sources/:id/dbs/:dbid", service.DropDatabase)
|
||||
router.DELETE("/chronograf/v1/sources/:id/dbs/:dbid", EnsureEditor(service.DropDatabase))
|
||||
|
||||
// Retention Policies
|
||||
router.GET("/chronograf/v1/sources/:id/dbs/:dbid/rps", service.RetentionPolicies)
|
||||
router.POST("/chronograf/v1/sources/:id/dbs/:dbid/rps", service.NewRetentionPolicy)
|
||||
router.GET("/chronograf/v1/sources/:id/dbs/:dbid/rps", EnsureViewer(service.RetentionPolicies))
|
||||
router.POST("/chronograf/v1/sources/:id/dbs/:dbid/rps", EnsureEditor(service.NewRetentionPolicy))
|
||||
|
||||
router.PUT("/chronograf/v1/sources/:id/dbs/:dbid/rps/:rpid", service.UpdateRetentionPolicy)
|
||||
router.DELETE("/chronograf/v1/sources/:id/dbs/:dbid/rps/:rpid", service.DropRetentionPolicy)
|
||||
router.PUT("/chronograf/v1/sources/:id/dbs/:dbid/rps/:rpid", EnsureEditor(service.UpdateRetentionPolicy))
|
||||
router.DELETE("/chronograf/v1/sources/:id/dbs/:dbid/rps/:rpid", EnsureEditor(service.DropRetentionPolicy))
|
||||
|
||||
allRoutes := &AllRoutes{
|
||||
Logger: opts.Logger,
|
||||
|
|
|
@ -442,6 +442,7 @@ func (s *Service) SourceUsers(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
// SourceUserID retrieves a user with ID from store.
|
||||
// In InfluxDB, a User's Name is their UID, hence the semantic below.
|
||||
func (s *Service) SourceUserID(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
uid := httprouter.GetParamFromContext(ctx, "uid")
|
||||
|
@ -451,7 +452,7 @@ func (s *Service) SourceUserID(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
store := ts.Users(ctx)
|
||||
u, err := store.Get(ctx, uid)
|
||||
u, err := store.Get(ctx, chronograf.UserQuery{Name: &uid})
|
||||
if err != nil {
|
||||
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
|
||||
return
|
||||
|
@ -514,7 +515,7 @@ func (s *Service) UpdateSourceUser(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
u, err := store.Get(ctx, uid)
|
||||
u, err := store.Get(ctx, chronograf.UserQuery{Name: &uid})
|
||||
if err != nil {
|
||||
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
|
||||
return
|
||||
|
|
|
@ -778,7 +778,7 @@ func TestService_SourceUserID(t *testing.T) {
|
|||
},
|
||||
UsersF: func(ctx context.Context) chronograf.UsersStore {
|
||||
return &mocks.UsersStore{
|
||||
GetF: func(ctx context.Context, uid string) (*chronograf.User, error) {
|
||||
GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) {
|
||||
return &chronograf.User{
|
||||
Name: "strickland",
|
||||
Passwd: "discipline",
|
||||
|
@ -833,7 +833,7 @@ func TestService_SourceUserID(t *testing.T) {
|
|||
},
|
||||
UsersF: func(ctx context.Context) chronograf.UsersStore {
|
||||
return &mocks.UsersStore{
|
||||
GetF: func(ctx context.Context, uid string) (*chronograf.User, error) {
|
||||
GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) {
|
||||
return &chronograf.User{
|
||||
Name: "strickland",
|
||||
Passwd: "discipline",
|
||||
|
@ -1041,7 +1041,7 @@ func TestService_UpdateSourceUser(t *testing.T) {
|
|||
UpdateF: func(ctx context.Context, u *chronograf.User) error {
|
||||
return nil
|
||||
},
|
||||
GetF: func(ctx context.Context, name string) (*chronograf.User, error) {
|
||||
GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) {
|
||||
return &chronograf.User{
|
||||
Name: "marty",
|
||||
}, nil
|
||||
|
@ -1093,7 +1093,7 @@ func TestService_UpdateSourceUser(t *testing.T) {
|
|||
UpdateF: func(ctx context.Context, u *chronograf.User) error {
|
||||
return nil
|
||||
},
|
||||
GetF: func(ctx context.Context, name string) (*chronograf.User, error) {
|
||||
GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) {
|
||||
return &chronograf.User{
|
||||
Name: "marty",
|
||||
}, nil
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/bouk/httprouter"
|
||||
"github.com/influxdata/chronograf"
|
||||
|
@ -28,6 +29,11 @@ func (r *userRequest) ValidCreate() error {
|
|||
if r.Scheme == "" {
|
||||
return fmt.Errorf("Scheme required on Chronograf User request body")
|
||||
}
|
||||
|
||||
// TODO: This Scheme value is hard-coded temporarily since we only currently
|
||||
// support OAuth2. This hard-coding should be removed whenever we add
|
||||
// support for other authentication schemes.
|
||||
r.Scheme = "OAuth2"
|
||||
return r.ValidRoles()
|
||||
}
|
||||
|
||||
|
@ -132,8 +138,13 @@ var (
|
|||
func (s *Service) UserID(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
id := httprouter.GetParamFromContext(ctx, "id")
|
||||
user, err := s.UsersStore.Get(ctx, id)
|
||||
idStr := httprouter.GetParamFromContext(ctx, "id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
Error(w, http.StatusBadRequest, fmt.Sprintf("invalid user id: %s", err.Error()), s.Logger)
|
||||
return
|
||||
}
|
||||
user, err := s.UsersStore.Get(ctx, chronograf.UserQuery{ID: &id})
|
||||
if err != nil {
|
||||
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
|
||||
return
|
||||
|
@ -178,9 +189,14 @@ func (s *Service) NewUser(w http.ResponseWriter, r *http.Request) {
|
|||
// RemoveUser deletes a Chronograf user from store
|
||||
func (s *Service) RemoveUser(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
id := httprouter.GetParamFromContext(ctx, "id")
|
||||
idStr := httprouter.GetParamFromContext(ctx, "id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
Error(w, http.StatusBadRequest, fmt.Sprintf("invalid user id: %s", err.Error()), s.Logger)
|
||||
return
|
||||
}
|
||||
|
||||
u, err := s.UsersStore.Get(ctx, id)
|
||||
u, err := s.UsersStore.Get(ctx, chronograf.UserQuery{ID: &id})
|
||||
if err != nil {
|
||||
Error(w, http.StatusNotFound, err.Error(), s.Logger)
|
||||
}
|
||||
|
@ -205,9 +221,14 @@ func (s *Service) UpdateUser(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
ctx := r.Context()
|
||||
id := httprouter.GetParamFromContext(ctx, "id")
|
||||
idStr := httprouter.GetParamFromContext(ctx, "id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
Error(w, http.StatusBadRequest, fmt.Sprintf("invalid user id: %s", err.Error()), s.Logger)
|
||||
return
|
||||
}
|
||||
|
||||
u, err := s.UsersStore.Get(ctx, id)
|
||||
u, err := s.UsersStore.Get(ctx, chronograf.UserQuery{ID: &id})
|
||||
if err != nil {
|
||||
Error(w, http.StatusNotFound, err.Error(), s.Logger)
|
||||
}
|
||||
|
|
|
@ -47,9 +47,9 @@ func TestService_UserID(t *testing.T) {
|
|||
fields: fields{
|
||||
Logger: log.New(log.DebugLevel),
|
||||
UsersStore: &mocks.UsersStore{
|
||||
GetF: func(ctx context.Context, ID string) (*chronograf.User, error) {
|
||||
switch ID {
|
||||
case "1337":
|
||||
GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) {
|
||||
switch *q.ID {
|
||||
case 1337:
|
||||
return &chronograf.User{
|
||||
ID: 1337,
|
||||
Name: "billysteve",
|
||||
|
@ -60,7 +60,7 @@ func TestService_UserID(t *testing.T) {
|
|||
},
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("User with ID %s not found", ID)
|
||||
return nil, fmt.Errorf("User with ID %s not found", *q.ID)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -212,9 +212,9 @@ func TestService_RemoveUser(t *testing.T) {
|
|||
fields: fields{
|
||||
Logger: log.New(log.DebugLevel),
|
||||
UsersStore: &mocks.UsersStore{
|
||||
GetF: func(ctx context.Context, ID string) (*chronograf.User, error) {
|
||||
switch ID {
|
||||
case "1339":
|
||||
GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) {
|
||||
switch *q.ID {
|
||||
case 1339:
|
||||
return &chronograf.User{
|
||||
ID: 1339,
|
||||
Name: "helena",
|
||||
|
@ -222,7 +222,7 @@ func TestService_RemoveUser(t *testing.T) {
|
|||
Scheme: "LDAP",
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("User with ID %s not found", ID)
|
||||
return nil, fmt.Errorf("User with ID %s not found", *q.ID)
|
||||
}
|
||||
},
|
||||
DeleteF: func(ctx context.Context, user *chronograf.User) error {
|
||||
|
@ -303,9 +303,9 @@ func TestService_UpdateUser(t *testing.T) {
|
|||
UpdateF: func(ctx context.Context, user *chronograf.User) error {
|
||||
return nil
|
||||
},
|
||||
GetF: func(ctx context.Context, ID string) (*chronograf.User, error) {
|
||||
switch ID {
|
||||
case "1336":
|
||||
GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) {
|
||||
switch *q.ID {
|
||||
case 1336:
|
||||
return &chronograf.User{
|
||||
ID: 1336,
|
||||
Name: "bobbetta2",
|
||||
|
@ -316,7 +316,7 @@ func TestService_UpdateUser(t *testing.T) {
|
|||
},
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("User with ID %s not found", ID)
|
||||
return nil, fmt.Errorf("User with ID %s not found", *q.ID)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -351,9 +351,9 @@ func TestService_UpdateUser(t *testing.T) {
|
|||
UpdateF: func(ctx context.Context, user *chronograf.User) error {
|
||||
return nil
|
||||
},
|
||||
GetF: func(ctx context.Context, ID string) (*chronograf.User, error) {
|
||||
switch ID {
|
||||
case "1336":
|
||||
GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) {
|
||||
switch *q.ID {
|
||||
case 1336:
|
||||
return &chronograf.User{
|
||||
ID: 1336,
|
||||
Name: "bobbetta2",
|
||||
|
@ -361,7 +361,7 @@ func TestService_UpdateUser(t *testing.T) {
|
|||
Scheme: "OAuth2",
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("User with ID %s not found", ID)
|
||||
return nil, fmt.Errorf("User with ID %s not found", *q.ID)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue