From b304ac256ecdc703b2450b98622befe94a832d88 Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Fri, 17 Feb 2017 15:13:51 -0600 Subject: [PATCH] Add get of all users for a data source --- bolt/users.go | 22 ++++++++ chronograf.go | 2 + enterprise/enterprise.go | 1 + enterprise/mocks_test.go | 7 +++ enterprise/users.go | 16 ++++++ influx/users.go | 7 ++- server/admin.go | 108 ++++++++++++++++++++++++++++----------- 7 files changed, 132 insertions(+), 31 deletions(-) diff --git a/bolt/users.go b/bolt/users.go index e01f49e1b8..6f2c2c98a5 100644 --- a/bolt/users.go +++ b/bolt/users.go @@ -120,3 +120,25 @@ func (s *UsersStore) Update(ctx context.Context, usr *chronograf.User) error { return nil } + +// All returns all users +func (s *UsersStore) All(ctx context.Context) ([]chronograf.User, error) { + var users []chronograf.User + if err := s.client.db.View(func(tx *bolt.Tx) error { + if err := tx.Bucket(UsersBucket).ForEach(func(k, v []byte) error { + var user chronograf.User + if err := internal.UnmarshalUser(v, &user); err != nil { + return err + } + users = append(users, user) + return nil + }); err != nil { + return err + } + return nil + }); err != nil { + return nil, err + } + + return users, nil +} diff --git a/chronograf.go b/chronograf.go index b8582487a7..5f7976db1e 100644 --- a/chronograf.go +++ b/chronograf.go @@ -229,6 +229,8 @@ type User struct { // UsersStore is the Storage and retrieval of authentication information type UsersStore interface { + // All lists all users from the UsersStore + All(context.Context) ([]User, error) // Create a new User in the UsersStore Add(context.Context, *User) (*User, error) // Delete the User from the UsersStore diff --git a/enterprise/enterprise.go b/enterprise/enterprise.go index 7f4e1bedda..3a1cda3b3c 100644 --- a/enterprise/enterprise.go +++ b/enterprise/enterprise.go @@ -24,6 +24,7 @@ type Client struct { CreateUser(ctx context.Context, name, passwd string) error DeleteUser(ctx context.Context, name string) error ChangePassword(ctx context.Context, name, passwd string) error + Users(ctx context.Context, name *string) (*Users, error) } Logger chronograf.Logger diff --git a/enterprise/mocks_test.go b/enterprise/mocks_test.go index 5915b4164c..21c11274a3 100644 --- a/enterprise/mocks_test.go +++ b/enterprise/mocks_test.go @@ -39,16 +39,23 @@ func (cc *ControlClient) ShowCluster(context.Context) (*enterprise.Cluster, erro func (cc *ControlClient) User(ctx context.Context, name string) (*enterprise.User, error) { return nil, nil } + func (cc *ControlClient) CreateUser(ctx context.Context, name, passwd string) error { return nil } + func (cc *ControlClient) DeleteUser(ctx context.Context, name string) error { return nil } + func (cc *ControlClient) ChangePassword(ctx context.Context, name, passwd string) error { return nil } +func (cc *ControlClient) Users(ctx context.Context, name *string) (*enterprise.Users, error) { + return nil, nil +} + type TimeSeries struct { URLs []string Response Response diff --git a/enterprise/users.go b/enterprise/users.go index 038d71a9d6..887c16ae6e 100644 --- a/enterprise/users.go +++ b/enterprise/users.go @@ -35,3 +35,19 @@ func (c *Client) Update(ctx context.Context, u *chronograf.User) error { // TODO: Update permissions return c.Ctrl.ChangePassword(ctx, u.Name, u.Passwd) } + +// All is all users in influx +func (c *Client) All(ctx context.Context) ([]chronograf.User, error) { + all, err := c.Ctrl.Users(ctx, nil) + if err != nil { + return nil, err + } + + res := make([]chronograf.User, len(all.Users)) + for i, user := range all.Users { + res[i] = chronograf.User{ + Name: user.Name, + } + } + return res, nil +} diff --git a/influx/users.go b/influx/users.go index 794b3fbbcb..ca86cb756e 100644 --- a/influx/users.go +++ b/influx/users.go @@ -6,10 +6,10 @@ import ( "github.com/influxdata/chronograf" ) -// Create a new User in Influx Enterprise +// Create a new User in InfluxDB func (c *Client) Add(context.Context, *chronograf.User) (*chronograf.User, error) { return nil, nil } -// Delete the User from Influx Enterprise +// Delete the User from InfluxDB func (c *Client) Delete(context.Context, *chronograf.User) error { return nil } // Get retrieves a user if name exists. @@ -19,3 +19,6 @@ func (c *Client) Get(ctx context.Context, name string) (*chronograf.User, error) // Update the user's permissions or roles func (c *Client) Update(context.Context, *chronograf.User) error { return nil } + +// All is all users in influx +func (c *Client) All(context.Context) ([]chronograf.User, error) { return nil, nil } diff --git a/server/admin.go b/server/admin.go index 4462417788..57b1edceb0 100644 --- a/server/admin.go +++ b/server/admin.go @@ -1,6 +1,7 @@ package server import ( + "context" "encoding/json" "fmt" "net/http" @@ -48,21 +49,8 @@ type sourceUserLinks struct { // NewSourceUser adds user to source func (h *Service) NewSourceUser(w http.ResponseWriter, r *http.Request) { - srcID, err := paramID("id", r) - if err != nil { - Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) - return - } - - ctx := r.Context() - src, err := h.SourcesStore.Get(ctx, srcID) - if err != nil { - notFound(w, srcID, h.Logger) - return - } - var req newSourceUserRequest - if err = json.NewDecoder(r.Body).Decode(&req); err != nil { + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { invalidJSON(w, h.Logger) return } @@ -71,13 +59,13 @@ func (h *Service) NewSourceUser(w http.ResponseWriter, r *http.Request) { return } - if err = h.TimeSeries.Connect(ctx, &src); err != nil { - msg := fmt.Sprintf("Unable to connect to source %d", srcID) - Error(w, http.StatusBadRequest, msg, h.Logger) + ctx := r.Context() + + srcID, store, err := h.sourceUsersStore(ctx, w, r) + if err != nil { return } - store := h.TimeSeries.Users(ctx) user := &chronograf.User{ Name: req.Username, Passwd: req.Password, @@ -93,31 +81,93 @@ func (h *Service) NewSourceUser(w http.ResponseWriter, r *http.Request) { encodeJSON(w, http.StatusCreated, su, h.Logger) } -// SourceUserID retrieves a user with ID from store. -func (h *Service) SourceUserID(w http.ResponseWriter, r *http.Request) { - srcID, err := paramID("id", r) +type sourceUsers struct { + Users []sourceUser `json:"users"` +} + +// SourceUsers retrieves all users from source. +func (h *Service) SourceUsers(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + srcID, store, err := h.sourceUsersStore(ctx, w, r) if err != nil { - Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) return } + users, err := store.All(ctx) + if err != nil { + Error(w, http.StatusBadRequest, err.Error(), h.Logger) + return + } + + su := []sourceUser{} + for _, u := range users { + su = append(su, NewSourceUser(srcID, u.Name)) + } + + res := sourceUsers{ + Users: su, + } + + encodeJSON(w, http.StatusOK, res, h.Logger) +} + +// SourceUserID retrieves a user with ID from store. +func (h *Service) SourceUserID(w http.ResponseWriter, r *http.Request) { ctx := r.Context() + uid := httprouter.GetParamFromContext(ctx, "uid") + + srcID, store, err := h.sourceUsersStore(ctx, w, r) + if err != nil { + return + } + + u, err := store.Get(ctx, uid) + if err != nil { + Error(w, http.StatusBadRequest, err.Error(), h.Logger) + return + } + + res := NewSourceUser(srcID, u.Name) + encodeJSON(w, http.StatusOK, res, h.Logger) +} + +func (h *Service) RemoveSourceUser(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + uid := httprouter.GetParamFromContext(ctx, "uid") + + _, store, err := h.sourceUsersStore(ctx, w, r) + if err != nil { + return + } + + if err := store.Delete(ctx, &chronograf.User{Name: uid}); err != nil { + Error(w, http.StatusBadRequest, err.Error(), h.Logger) + return + } + + w.WriteHeader(http.StatusNoContent) +} + +func (h *Service) sourceUsersStore(ctx context.Context, w http.ResponseWriter, r *http.Request) (int, chronograf.UsersStore, error) { + srcID, err := paramID("id", r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) + return 0, nil, err + } + src, err := h.SourcesStore.Get(ctx, srcID) if err != nil { notFound(w, srcID, h.Logger) - return + return 0, nil, err } if err = h.TimeSeries.Connect(ctx, &src); err != nil { msg := fmt.Sprintf("Unable to connect to source %d", srcID) Error(w, http.StatusBadRequest, msg, h.Logger) - return + return 0, nil, err } - uid := httprouter.GetParamFromContext(ctx, "uid") store := h.TimeSeries.Users(ctx) - u, err := store.Get(ctx, uid) - - res := NewSourceUser(srcID, u.Name) - encodeJSON(w, http.StatusOK, res, h.Logger) + return srcID, store, nil }