diff --git a/admin.go b/admin.go index 9e3a4dd..f36c3a3 100644 --- a/admin.go +++ b/admin.go @@ -38,6 +38,26 @@ func (c *Client) DeleteUser(id int64) error { return c.request("DELETE", fmt.Sprintf("/api/admin/users/%d", id), nil, nil, nil) } +// UpdateUserPassword updates a user password. +func (c *Client) UpdateUserPassword(id int64, password string) error { + body := map[string]string{"password": password} + data, err := json.Marshal(body) + if err != nil { + return err + } + return c.request("PUT", fmt.Sprintf("/api/admin/users/%d/password", id), nil, bytes.NewBuffer(data), nil) +} + +// UpdateUserPermissions sets a user's admin status. +func (c *Client) UpdateUserPermissions(id int64, isAdmin bool) error { + body := map[string]bool{"isGrafanaAdmin": isAdmin} + data, err := json.Marshal(body) + if err != nil { + return err + } + return c.request("PUT", fmt.Sprintf("/api/admin/users/%d/permissions", id), nil, bytes.NewBuffer(data), nil) +} + // PauseAllAlerts pauses all Grafana alerts. func (c *Client) PauseAllAlerts() (PauseAllAlertsResponse, error) { result := PauseAllAlertsResponse{} diff --git a/admin_test.go b/admin_test.go index 97ce5a6..b361a0b 100644 --- a/admin_test.go +++ b/admin_test.go @@ -8,8 +8,10 @@ import ( ) const ( - createUserJSON = `{"id":1,"message":"User created"}` - deleteUserJSON = `{"message":"User deleted"}` + createUserJSON = `{"id":1,"message":"User created"}` + deleteUserJSON = `{"message":"User deleted"}` + updateUserPasswordJSON = `{"message":"User password updated"}` + updateUserPermissionsJSON = `{"message":"User permissions updated"}` pauseAllAlertsJSON = `{ "alertsAffected": 1, @@ -47,6 +49,26 @@ func TestDeleteUser(t *testing.T) { } } +func TestUpdateUserPassword(t *testing.T) { + server, client := gapiTestTools(200, updateUserPasswordJSON) + defer server.Close() + + err := client.UpdateUserPassword(int64(1), "new-password") + if err != nil { + t.Error(err) + } +} + +func TestUpdateUserPermissions(t *testing.T) { + server, client := gapiTestTools(200, updateUserPermissionsJSON) + defer server.Close() + + err := client.UpdateUserPermissions(int64(1), false) + if err != nil { + t.Error(err) + } +} + func TestPauseAllAlerts(t *testing.T) { server, client := gapiTestTools(200, pauseAllAlertsJSON) defer server.Close() diff --git a/user.go b/user.go index 83a6620..1c88a09 100644 --- a/user.go +++ b/user.go @@ -1,50 +1,74 @@ package gapi import ( + "bytes" + "encoding/json" + "fmt" "net/url" + "time" ) -// User represents a Grafana user. +// User represents a Grafana user. It is structured after the UserProfileDTO +// struct in the Grafana codebase. type User struct { - Id int64 `json:"id,omitempty"` - Email string `json:"email,omitempty"` - Name string `json:"name,omitempty"` - Login string `json:"login,omitempty"` - Password string `json:"password,omitempty"` - IsAdmin bool `json:"isAdmin,omitempty"` + Id int64 `json:"id,omitempty"` + Email string `json:"email,omitempty"` + Name string `json:"name,omitempty"` + Login string `json:"login,omitempty"` + Theme string `json:"theme,omitempty"` + OrgId int64 `json:"orgId,omitempty"` + IsAdmin bool `json:"isGrafanaAdmin,omitempty"` + IsDisabled bool `json:"isDisabled,omitempty"` + IsExternal bool `json:"isExternal,omitempty"` + UpdatedAt time.Time `json:"updatedAt,omitempty"` + CreatedAt time.Time `json:"createdAt,omitempty"` + AuthLabels []string `json:"authLabels,omitempty"` + AvatarUrl string `json:"avatarUrl,omitempty"` + Password string `json:"password,omitempty"` +} + +// UserSearch represents a Grafana user as returned by API endpoints that +// return a collection of Grafana users. This representation of user has +// reduced and differing fields. It is structured after the UserSearchHitDTO +// struct in the Grafana codebase. +type UserSearch struct { + Id int64 `json:"id,omitempty"` + Email string `json:"email,omitempty"` + Name string `json:"name,omitempty"` + Login string `json:"login,omitempty"` + IsAdmin bool `json:"isAdmin,omitempty"` + IsDisabled bool `json:"isDisabled,omitempty"` + LastSeenAt time.Time `json:"lastSeenAt,omitempty"` + LastSeenAtAge string `json:"lastSeenAtAge,omitempty"` + AuthLabels []string `json:"authLabels,omitempty"` + AvatarUrl string `json:"avatarUrl,omitempty"` } // Users fetches and returns Grafana users. -func (c *Client) Users() ([]User, error) { - users := make([]User, 0) - err := c.request("GET", "/api/users", nil, nil, &users) - if err != nil { - return users, err - } +func (c *Client) Users() (users []UserSearch, err error) { + err = c.request("GET", "/api/users", nil, nil, &users) + return +} - return users, err +// User fetches a user by ID. +func (c *Client) User(id int64) (user User, err error) { + err = c.request("GET", fmt.Sprintf("/api/users/%d", id), nil, nil, &user) + return } -// UserByEmail fetches and returns the user whose email matches that passed. -func (c *Client) UserByEmail(email string) (User, error) { - user := User{} +// UserByEmail fetches a user by email address. +func (c *Client) UserByEmail(email string) (user User, err error) { query := url.Values{} query.Add("loginOrEmail", email) - tmp := struct { - Id int64 `json:"id,omitempty"` - Email string `json:"email,omitempty"` - Name string `json:"name,omitempty"` - Login string `json:"login,omitempty"` - Password string `json:"password,omitempty"` - IsAdmin bool `json:"isGrafanaAdmin,omitempty"` - }{} + err = c.request("GET", "/api/users/lookup", query, nil, &user) + return +} - err := c.request("GET", "/api/users/lookup", query, nil, &tmp) +// UserUpdate updates a user by ID. +func (c *Client) UserUpdate(u User) error { + data, err := json.Marshal(u) if err != nil { - return user, err + return err } - - user = User(tmp) - - return user, err + return c.request("PUT", fmt.Sprintf("/api/users/%d", u.Id), nil, bytes.NewBuffer(data), nil) } diff --git a/user_test.go b/user_test.go index be64dcf..f4303ef 100644 --- a/user_test.go +++ b/user_test.go @@ -7,8 +7,10 @@ import ( ) const ( - getUsersJSON = `[{"id":1,"name":"","login":"admin","email":"admin@localhost","avatarUrl":"/avatar/46d229b033af06a191ff2267bca9ae56","isAdmin":true,"lastSeenAt":"2018-06-28T14:42:24Z","lastSeenAtAge":"\u003c 1m"}]` - getUserByEmailJSON = `{"id":1,"email":"admin@localhost","name":"","login":"admin","theme":"","orgId":1,"isGrafanaAdmin":true}` + getUsersJSON = `[{"id":1,"email":"users@localhost","isAdmin":true}]` + getUserJSON = `{"id":2,"email":"user@localhost","isGrafanaAdmin":false}` + getUserByEmailJSON = `{"id":3,"email":"userByEmail@localhost","isGrafanaAdmin":true}` + getUserUpdateJSON = `{"id":4,"email":"userUpdate@localhost","isGrafanaAdmin":false}` ) func TestUsers(t *testing.T) { @@ -22,38 +24,66 @@ func TestUsers(t *testing.T) { t.Log(pretty.PrettyFormat(resp)) - user := User{ - Id: 1, - Email: "admin@localhost", - Name: "", - Login: "admin", - IsAdmin: true, + if len(resp) != 1 { + t.Fatal("No users were returned.") } - if len(resp) != 1 || resp[0] != user { + user := resp[0] + + if user.Email != "users@localhost" || + user.Id != 1 || + user.IsAdmin != true { t.Error("Not correctly parsing returned users.") } } +func TestUser(t *testing.T) { + server, client := gapiTestTools(200, getUserJSON) + defer server.Close() + + user, err := client.User(1) + if err != nil { + t.Error(err) + } + + t.Log(pretty.PrettyFormat(user)) + + if user.Email != "user@localhost" || + user.Id != 2 || + user.IsAdmin != false { + t.Error("Not correctly parsing returned user.") + } +} + func TestUserByEmail(t *testing.T) { server, client := gapiTestTools(200, getUserByEmailJSON) defer server.Close() - resp, err := client.UserByEmail("admin@localhost") + user, err := client.UserByEmail("admin@localhost") if err != nil { t.Error(err) } - t.Log(pretty.PrettyFormat(resp)) + t.Log(pretty.PrettyFormat(user)) - user := User{ - Id: 1, - Email: "admin@localhost", - Name: "", - Login: "admin", - IsAdmin: true, - } - if resp != user { + if user.Email != "userByEmail@localhost" || + user.Id != 3 || + user.IsAdmin != true { t.Error("Not correctly parsing returned user.") } } + +func TestUserUpdate(t *testing.T) { + server, client := gapiTestTools(200, getUserUpdateJSON) + defer server.Close() + + user, err := client.User(4) + if err != nil { + t.Error(err) + } + user.IsAdmin = true + err = client.UserUpdate(user) + if err != nil { + t.Error(err) + } +}