influxdb/chronograf/enterprise/users.go

198 lines
4.7 KiB
Go

package enterprise
import (
"context"
"fmt"
"github.com/influxdata/influxdb/v2/chronograf"
)
// UserStore uses a control client operate on Influx Enterprise users
type UserStore struct {
Ctrl
Logger chronograf.Logger
}
// Add creates a new User in Influx Enterprise
func (c *UserStore) Add(ctx context.Context, u *chronograf.User) (*chronograf.User, error) {
if err := c.Ctrl.CreateUser(ctx, u.Name, u.Passwd); err != nil {
return nil, err
}
perms := ToEnterprise(u.Permissions)
if err := c.Ctrl.SetUserPerms(ctx, u.Name, perms); err != nil {
return nil, err
}
for _, role := range u.Roles {
if err := c.Ctrl.AddRoleUsers(ctx, role.Name, []string{u.Name}); err != nil {
return nil, err
}
}
return c.Get(ctx, chronograf.UserQuery{Name: &u.Name})
}
// Delete the User from Influx Enterprise
func (c *UserStore) Delete(ctx context.Context, u *chronograf.User) error {
return c.Ctrl.DeleteUser(ctx, u.Name)
}
// Num of users in Influx
func (c *UserStore) Num(ctx context.Context) (int, error) {
all, err := c.All(ctx)
if err != nil {
return 0, err
}
return len(all), nil
}
// Get retrieves a user if name exists.
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
}
ur, err := c.Ctrl.UserRoles(ctx)
if err != nil {
return nil, err
}
role := ur[*q.Name]
cr := role.ToChronograf()
// For now we are removing all users from a role being returned.
for i, r := range cr {
r.Users = []chronograf.User{}
cr[i] = r
}
return &chronograf.User{
Name: u.Name,
Permissions: ToChronograf(u.Permissions),
Roles: cr,
}, nil
}
// Update the user's permissions or roles
func (c *UserStore) 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.Ctrl.ChangePassword(ctx, u.Name, u.Passwd)
}
if u.Roles != nil {
// Make a list of the roles we want this user to have:
want := make([]string, len(u.Roles))
for i, r := range u.Roles {
want[i] = r.Name
}
// Find the list of all roles this user is currently in
userRoles, err := c.UserRoles(ctx)
if err != nil {
return nil
}
// Make a list of the roles the user currently has
roles := userRoles[u.Name]
have := make([]string, len(roles.Roles))
for i, r := range roles.Roles {
have[i] = r.Name
}
// Calculate the roles the user will be removed from and the roles the user
// will be added to.
revoke, add := Difference(want, have)
// First, add the user to the new roles
for _, role := range add {
if err := c.Ctrl.AddRoleUsers(ctx, role, []string{u.Name}); err != nil {
return err
}
}
// ... and now remove the user from an extra roles
for _, role := range revoke {
if err := c.Ctrl.RemoveRoleUsers(ctx, role, []string{u.Name}); err != nil {
return err
}
}
}
if u.Permissions != nil {
perms := ToEnterprise(u.Permissions)
return c.Ctrl.SetUserPerms(ctx, u.Name, perms)
}
return nil
}
// All is all users in influx
func (c *UserStore) All(ctx context.Context) ([]chronograf.User, error) {
all, err := c.Ctrl.Users(ctx, nil)
if err != nil {
return nil, err
}
ur, err := c.Ctrl.UserRoles(ctx)
if err != nil {
return nil, err
}
res := make([]chronograf.User, len(all.Users))
for i, user := range all.Users {
role := ur[user.Name]
cr := role.ToChronograf()
// For now we are removing all users from a role being returned.
for i, r := range cr {
r.Users = []chronograf.User{}
cr[i] = r
}
res[i] = chronograf.User{
Name: user.Name,
Permissions: ToChronograf(user.Permissions),
Roles: cr,
}
}
return res, nil
}
// ToEnterprise converts chronograf permission shape to enterprise
func ToEnterprise(perms chronograf.Permissions) Permissions {
res := Permissions{}
for _, perm := range perms {
if perm.Scope == chronograf.AllScope {
// Enterprise uses empty string as the key for all databases
res[""] = perm.Allowed
} else {
res[perm.Name] = perm.Allowed
}
}
return res
}
// ToChronograf converts enterprise permissions shape to chronograf shape
func ToChronograf(perms Permissions) chronograf.Permissions {
res := chronograf.Permissions{}
for db, perm := range perms {
// Enterprise uses empty string as the key for all databases
if db == "" {
res = append(res, chronograf.Permission{
Scope: chronograf.AllScope,
Allowed: perm,
})
} else {
res = append(res, chronograf.Permission{
Scope: chronograf.DBScope,
Name: db,
Allowed: perm,
})
}
}
return res
}