influxdb/chronograf/influx/users.go

231 lines
5.2 KiB
Go

package influx
import (
"context"
"encoding/json"
"fmt"
"github.com/influxdata/platform/chronograf"
)
// Add a new User in InfluxDB
func (c *Client) Add(ctx context.Context, u *chronograf.User) (*chronograf.User, error) {
_, err := c.Query(ctx, chronograf.Query{
Command: fmt.Sprintf(`CREATE USER "%s" WITH PASSWORD '%s'`, u.Name, u.Passwd),
})
if err != nil {
return nil, err
}
for _, p := range u.Permissions {
if err := c.grantPermission(ctx, u.Name, p); err != nil {
return nil, err
}
}
return c.Get(ctx, chronograf.UserQuery{Name: &u.Name})
}
// Delete the User from InfluxDB
func (c *Client) Delete(ctx context.Context, u *chronograf.User) error {
res, err := c.Query(ctx, chronograf.Query{
Command: fmt.Sprintf(`DROP USER "%s"`, u.Name),
})
if err != nil {
return err
}
// The DROP USER statement puts the error within the results itself
// So, we have to crack open the results to see what happens
octets, err := res.MarshalJSON()
if err != nil {
return err
}
results := make([]struct{ Error string }, 0)
if err := json.Unmarshal(octets, &results); err != nil {
return err
}
// At last, we can check if there are any error strings
for _, r := range results {
if r.Error != "" {
return fmt.Errorf(r.Error)
}
}
return nil
}
// Get retrieves a user if name exists.
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 == *q.Name {
perms, err := c.userPermissions(ctx, user.Name)
if err != nil {
return nil, err
}
user.Permissions = append(user.Permissions, perms...)
return &user, nil
}
}
return nil, fmt.Errorf("user not found")
}
// Update the user's permissions or roles
func (c *Client) Update(ctx context.Context, u *chronograf.User) error {
// Only allow one type of change at a time. If it is a password
// change then do it and return without any changes to permissions
if u.Passwd != "" {
return c.updatePassword(ctx, u.Name, u.Passwd)
}
user, err := c.Get(ctx, chronograf.UserQuery{Name: &u.Name})
if err != nil {
return err
}
revoke, add := Difference(u.Permissions, user.Permissions)
for _, a := range add {
if err := c.grantPermission(ctx, u.Name, a); err != nil {
return err
}
}
for _, r := range revoke {
if err := c.revokePermission(ctx, u.Name, r); err != nil {
return err
}
}
return nil
}
// All users in influx
func (c *Client) All(ctx context.Context) ([]chronograf.User, error) {
users, err := c.showUsers(ctx)
if err != nil {
return nil, err
}
// For all users we need to look up permissions to add to the user.
for i, user := range users {
perms, err := c.userPermissions(ctx, user.Name)
if err != nil {
return nil, err
}
user.Permissions = append(user.Permissions, perms...)
users[i] = user
}
return users, nil
}
// Num is the number of users in DB
func (c *Client) Num(ctx context.Context) (int, error) {
all, err := c.All(ctx)
if err != nil {
return 0, err
}
return len(all), nil
}
// showUsers runs SHOW USERS InfluxQL command and returns chronograf users.
func (c *Client) showUsers(ctx context.Context) ([]chronograf.User, error) {
res, err := c.Query(ctx, chronograf.Query{
Command: `SHOW USERS`,
})
if err != nil {
return nil, err
}
octets, err := res.MarshalJSON()
if err != nil {
return nil, err
}
results := showResults{}
if err := json.Unmarshal(octets, &results); err != nil {
return nil, err
}
return results.Users(), nil
}
func (c *Client) grantPermission(ctx context.Context, username string, perm chronograf.Permission) error {
query := ToGrant(username, perm)
if query == "" {
return nil
}
_, err := c.Query(ctx, chronograf.Query{
Command: query,
})
return err
}
func (c *Client) revokePermission(ctx context.Context, username string, perm chronograf.Permission) error {
query := ToRevoke(username, perm)
if query == "" {
return nil
}
_, err := c.Query(ctx, chronograf.Query{
Command: query,
})
return err
}
func (c *Client) userPermissions(ctx context.Context, name string) (chronograf.Permissions, error) {
res, err := c.Query(ctx, chronograf.Query{
Command: fmt.Sprintf(`SHOW GRANTS FOR "%s"`, name),
})
if err != nil {
return nil, err
}
octets, err := res.MarshalJSON()
if err != nil {
return nil, err
}
results := showResults{}
if err := json.Unmarshal(octets, &results); err != nil {
return nil, err
}
return results.Permissions(), nil
}
func (c *Client) updatePassword(ctx context.Context, name, passwd string) error {
res, err := c.Query(ctx, chronograf.Query{
Command: fmt.Sprintf(`SET PASSWORD for "%s" = '%s'`, name, passwd),
})
if err != nil {
return err
}
// The SET PASSWORD statements puts the error within the results itself
// So, we have to crack open the results to see what happens
octets, err := res.MarshalJSON()
if err != nil {
return err
}
results := make([]struct{ Error string }, 0)
if err := json.Unmarshal(octets, &results); err != nil {
return err
}
// At last, we can check if there are any error strings
for _, r := range results {
if r.Error != "" {
return fmt.Errorf(r.Error)
}
}
return nil
}