
* chore: update openapi * feat: update replication field names to match influx changes * feat: add new field to show replication queue sync progress * chore: update to master openapi
467 lines
11 KiB
Go
467 lines
11 KiB
Go
package auth
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/influxdata/influx-cli/v2/api"
|
|
"github.com/influxdata/influx-cli/v2/api/extras"
|
|
"github.com/influxdata/influx-cli/v2/clients"
|
|
"github.com/influxdata/influx-cli/v2/pkg/influxid"
|
|
)
|
|
|
|
type Client struct {
|
|
clients.CLI
|
|
api.AuthorizationsAPITokensApi
|
|
api.UsersApi
|
|
api.OrganizationsApi
|
|
api.ResourcesApi
|
|
}
|
|
|
|
const (
|
|
ReadAction = "read"
|
|
WriteAction = "write"
|
|
)
|
|
|
|
type token struct {
|
|
ID string `json:"id"`
|
|
Description string `json:"description"`
|
|
Token string `json:"token"`
|
|
Status string `json:"status"`
|
|
UserName string `json:"userName"`
|
|
UserID string `json:"userID"`
|
|
Permissions []string `json:"permissions"`
|
|
}
|
|
|
|
type printParams struct {
|
|
deleted bool
|
|
token *token
|
|
tokens []token
|
|
}
|
|
|
|
type ResourcePermission struct {
|
|
Name string
|
|
Read bool
|
|
Write bool
|
|
IsCloud bool
|
|
IsOss bool
|
|
}
|
|
|
|
type CreateParams struct {
|
|
clients.OrgParams
|
|
User string
|
|
Description string
|
|
|
|
ResourcePermissions []*ResourcePermission
|
|
|
|
WriteBucketIds []string
|
|
ReadBucketIds []string
|
|
|
|
OperatorPermission bool
|
|
AllAccess bool
|
|
}
|
|
|
|
func BuildResourcePermissions() []*ResourcePermission {
|
|
cloudResources := make(map[string]struct{})
|
|
ossResources := make(map[string]struct{})
|
|
for _, val := range extras.ResourceEnumCloudValues() {
|
|
cloudResources[string(val)] = struct{}{}
|
|
}
|
|
for _, val := range extras.ResourceEnumOSSValues() {
|
|
ossResources[string(val)] = struct{}{}
|
|
}
|
|
|
|
perms := make([]*ResourcePermission, 0, len(ossResources))
|
|
// First the common permissions in order
|
|
for _, val := range extras.ResourceEnumOSSValues() {
|
|
if _, ok := cloudResources[string(val)]; ok {
|
|
perms = append(perms, &ResourcePermission{
|
|
Name: string(val),
|
|
IsCloud: true,
|
|
IsOss: true,
|
|
})
|
|
}
|
|
}
|
|
|
|
// Then oss-only in order
|
|
for _, val := range extras.ResourceEnumOSSValues() {
|
|
if _, ok := cloudResources[string(val)]; !ok {
|
|
perms = append(perms, &ResourcePermission{
|
|
Name: string(val),
|
|
IsOss: true,
|
|
})
|
|
}
|
|
}
|
|
|
|
// Then cloud-only in order
|
|
for _, val := range extras.ResourceEnumCloudValues() {
|
|
if _, ok := ossResources[string(val)]; !ok {
|
|
perms = append(perms, &ResourcePermission{
|
|
Name: string(val),
|
|
IsCloud: true,
|
|
})
|
|
}
|
|
}
|
|
|
|
return perms
|
|
}
|
|
|
|
func (c Client) Create(ctx context.Context, params *CreateParams) error {
|
|
orgID, err := params.GetOrgID(ctx, c.ActiveConfig, c.OrganizationsApi)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
bucketPerms := []struct {
|
|
action string
|
|
perms []string
|
|
}{
|
|
{action: ReadAction, perms: params.ReadBucketIds},
|
|
{action: WriteAction, perms: params.WriteBucketIds},
|
|
}
|
|
|
|
// Get the user ID because the command only takes a username, not ID
|
|
users, err := c.UsersApi.GetUsers(ctx).Name(params.User).Execute()
|
|
if err != nil || len(users.GetUsers()) == 0 {
|
|
return fmt.Errorf("could not find user with name %q: %w", params.User, err)
|
|
}
|
|
userID := users.GetUsers()[0].GetId()
|
|
|
|
var permissions []api.Permission
|
|
for _, bp := range bucketPerms {
|
|
for _, p := range bp.perms {
|
|
// verify the input ID
|
|
if err := influxid.Validate(p); err != nil {
|
|
return fmt.Errorf("invalid bucket ID '%s': %w (did you pass a bucket name instead of an ID?)", p, err)
|
|
}
|
|
|
|
newPerm := api.Permission{
|
|
Action: bp.action,
|
|
Resource: makePermResource("buckets", p, orgID),
|
|
}
|
|
permissions = append(permissions, newPerm)
|
|
}
|
|
}
|
|
|
|
for _, resourcePermission := range params.ResourcePermissions {
|
|
var actions []string
|
|
if resourcePermission.Read {
|
|
actions = append(actions, ReadAction)
|
|
}
|
|
if resourcePermission.Write {
|
|
actions = append(actions, WriteAction)
|
|
}
|
|
|
|
for _, action := range actions {
|
|
p := api.Permission{
|
|
Action: action,
|
|
Resource: makePermResource(resourcePermission.Name, "", orgID),
|
|
}
|
|
permissions = append(permissions, p)
|
|
}
|
|
}
|
|
|
|
if params.OperatorPermission {
|
|
if len(permissions) > 0 {
|
|
return fmt.Errorf("cannot compose other permissions with operator permissions")
|
|
}
|
|
resources, err := c.GetResources(ctx).Execute()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, r := range resources {
|
|
|
|
if r == string(extras.RESOURCEENUMOSS_INSTANCE) {
|
|
// We skip the instance type since it is only set on setup
|
|
continue
|
|
}
|
|
for _, action := range []string{ReadAction, WriteAction} {
|
|
permissions = append(permissions, api.Permission{
|
|
Action: action,
|
|
Resource: makePermResource(r, "", ""),
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
if params.AllAccess {
|
|
if len(permissions) > 0 {
|
|
return fmt.Errorf("cannot compose other permissions with all access permissions")
|
|
}
|
|
resources, err := c.GetResources(ctx).Execute()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, r := range resources {
|
|
if r == string(extras.RESOURCEENUMCLOUD_ORGS) {
|
|
// orgs are handled specifically - all access is for a single org, not global access to all orgs
|
|
permissions = append(permissions, api.Permission{
|
|
Action: ReadAction,
|
|
Resource: api.PermissionResource{
|
|
Type: r,
|
|
Id: &orgID,
|
|
},
|
|
})
|
|
} else if r == string(extras.RESOURCEENUMCLOUD_USERS) {
|
|
// users are handled specifically - all access is for a single user, not global to all users
|
|
for _, action := range []string{ReadAction, WriteAction} {
|
|
permissions = append(permissions, api.Permission{
|
|
Action: action,
|
|
Resource: api.PermissionResource{
|
|
Type: r,
|
|
Id: &userID,
|
|
},
|
|
})
|
|
}
|
|
} else if r == string(extras.RESOURCEENUMOSS_INSTANCE) {
|
|
// We skip the instance type since it is only set on setup
|
|
continue
|
|
} else {
|
|
for _, action := range []string{ReadAction, WriteAction} {
|
|
permissions = append(permissions, api.Permission{
|
|
Action: action,
|
|
Resource: makePermResource(r, "", orgID),
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
authReq := api.AuthorizationPostRequest{
|
|
OrgID: orgID,
|
|
UserID: &userID,
|
|
Permissions: permissions,
|
|
}
|
|
if params.Description != "" {
|
|
authReq.SetDescription(params.Description)
|
|
}
|
|
|
|
auth, err := c.PostAuthorizations(ctx).AuthorizationPostRequest(authReq).Execute()
|
|
if err != nil {
|
|
return fmt.Errorf("could not write auth with provided arguments: %w", err)
|
|
}
|
|
|
|
ps := make([]string, 0, len(auth.GetPermissions()))
|
|
for _, p := range auth.GetPermissions() {
|
|
ps = append(ps, p.String())
|
|
}
|
|
|
|
return c.printAuth(printParams{
|
|
token: &token{
|
|
ID: auth.GetId(),
|
|
Description: auth.GetDescription(),
|
|
Token: auth.GetToken(),
|
|
Status: auth.GetStatus(),
|
|
UserName: auth.GetUser(),
|
|
UserID: auth.GetUserID(),
|
|
Permissions: ps,
|
|
},
|
|
})
|
|
}
|
|
|
|
func (c Client) Remove(ctx context.Context, authID string) error {
|
|
// check if auth exists first for better error logging, and to
|
|
// acquire the auth that was deleted since the delete
|
|
// request does not return the Authorization object.
|
|
a, err := c.GetAuthorizationsID(ctx, authID).Execute()
|
|
if err != nil {
|
|
return fmt.Errorf("could not find auth with ID %q: %w", authID, err)
|
|
}
|
|
|
|
if err := c.DeleteAuthorizationsID(ctx, authID).Execute(); err != nil {
|
|
return fmt.Errorf("could not remove auth with ID %q: %w", authID, err)
|
|
}
|
|
|
|
ps := make([]string, 0, len(a.Permissions))
|
|
for _, p := range a.Permissions {
|
|
ps = append(ps, p.String())
|
|
}
|
|
|
|
return c.printAuth(printParams{
|
|
deleted: true,
|
|
token: &token{
|
|
ID: a.GetId(),
|
|
Description: a.GetDescription(),
|
|
Token: a.GetToken(),
|
|
Status: a.GetStatus(),
|
|
UserName: a.GetUser(),
|
|
UserID: a.GetUserID(),
|
|
Permissions: ps,
|
|
},
|
|
})
|
|
}
|
|
|
|
type ListParams struct {
|
|
clients.OrgParams
|
|
Id string
|
|
User string
|
|
UserID string
|
|
}
|
|
|
|
func (c Client) List(ctx context.Context, params *ListParams) error {
|
|
|
|
// If ID parameter is set, search by that over other filters
|
|
if params.Id != "" {
|
|
return c.findAuthorization(ctx, params.Id)
|
|
}
|
|
|
|
req := c.GetAuthorizations(ctx)
|
|
if params.User != "" {
|
|
req.User(params.User)
|
|
}
|
|
if params.UserID != "" {
|
|
req.UserID(params.UserID)
|
|
}
|
|
if params.OrgName != "" {
|
|
req.Org(params.OrgName)
|
|
}
|
|
if params.OrgID != "" {
|
|
req.OrgID(params.OrgID)
|
|
}
|
|
|
|
auths, err := req.Execute()
|
|
if err != nil {
|
|
return fmt.Errorf("could not find authorization with given parameters: %w", err)
|
|
}
|
|
|
|
var tokens []token
|
|
for _, a := range auths.GetAuthorizations() {
|
|
var ps []string
|
|
for _, p := range a.GetPermissions() {
|
|
ps = append(ps, p.String())
|
|
}
|
|
|
|
tokens = append(tokens, token{
|
|
ID: a.GetId(),
|
|
Description: a.GetDescription(),
|
|
Token: a.GetToken(),
|
|
Status: a.GetStatus(),
|
|
UserName: a.GetUser(),
|
|
UserID: a.GetUserID(),
|
|
Permissions: ps,
|
|
})
|
|
}
|
|
|
|
return c.printAuth(printParams{tokens: tokens})
|
|
}
|
|
|
|
func (c Client) findAuthorization(ctx context.Context, authID string) error {
|
|
a, err := c.GetAuthorizationsID(ctx, authID).Execute()
|
|
if err != nil {
|
|
return fmt.Errorf("could not find authorization with ID %q: %w", authID, err)
|
|
}
|
|
|
|
ps := make([]string, 0, len(a.GetPermissions()))
|
|
for _, p := range a.GetPermissions() {
|
|
ps = append(ps, p.String())
|
|
}
|
|
|
|
return c.printAuth(printParams{
|
|
token: &token{
|
|
ID: a.GetId(),
|
|
Description: a.GetDescription(),
|
|
Token: a.GetToken(),
|
|
Status: a.GetStatus(),
|
|
UserName: a.GetUser(),
|
|
UserID: a.GetUserID(),
|
|
Permissions: ps,
|
|
},
|
|
})
|
|
}
|
|
|
|
func (c Client) SetActive(ctx context.Context, authID string, active bool) error {
|
|
|
|
// check if auth exists first for better error logging
|
|
if _, err := c.GetAuthorizationsID(ctx, authID).Execute(); err != nil {
|
|
return fmt.Errorf("could not find auth with ID %q: %w", authID, err)
|
|
}
|
|
|
|
var status string
|
|
if active {
|
|
status = "active"
|
|
} else {
|
|
status = "inactive"
|
|
}
|
|
a, err := c.PatchAuthorizationsID(ctx, authID).
|
|
AuthorizationUpdateRequest(api.AuthorizationUpdateRequest{Status: &status}).
|
|
Execute()
|
|
if err != nil {
|
|
return fmt.Errorf("could not update status of auth with ID %q: %w", authID, err)
|
|
}
|
|
|
|
ps := make([]string, 0, len(a.GetPermissions()))
|
|
for _, p := range a.GetPermissions() {
|
|
ps = append(ps, p.String())
|
|
}
|
|
|
|
return c.printAuth(printParams{
|
|
token: &token{
|
|
ID: a.GetId(),
|
|
Description: a.GetDescription(),
|
|
Token: a.GetToken(),
|
|
Status: a.GetStatus(),
|
|
UserName: a.GetUser(),
|
|
UserID: a.GetUserID(),
|
|
Permissions: ps,
|
|
},
|
|
})
|
|
}
|
|
|
|
func (c Client) printAuth(opts printParams) error {
|
|
if c.PrintAsJSON {
|
|
var v interface{}
|
|
if opts.token != nil {
|
|
v = opts.token
|
|
} else {
|
|
v = opts.tokens
|
|
}
|
|
return c.PrintJSON(v)
|
|
}
|
|
|
|
headers := []string{
|
|
"ID",
|
|
"Description",
|
|
"Token",
|
|
"User Name",
|
|
"User ID",
|
|
"Permissions",
|
|
}
|
|
if opts.deleted {
|
|
headers = append(headers, "Deleted")
|
|
}
|
|
|
|
if opts.token != nil {
|
|
opts.tokens = append(opts.tokens, *opts.token)
|
|
}
|
|
|
|
var rows []map[string]interface{}
|
|
for _, t := range opts.tokens {
|
|
row := map[string]interface{}{
|
|
"ID": t.ID,
|
|
"Description": t.Description,
|
|
"Token": t.Token,
|
|
"User Name": t.UserName,
|
|
"User ID": t.UserID,
|
|
"Permissions": t.Permissions,
|
|
}
|
|
if opts.deleted {
|
|
row["Deleted"] = true
|
|
}
|
|
rows = append(rows, row)
|
|
}
|
|
return c.PrintTable(headers, rows...)
|
|
}
|
|
|
|
func makePermResource(permType string, id string, orgId string) api.PermissionResource {
|
|
if permType == "orgs" || permType == "users" {
|
|
orgId = ""
|
|
}
|
|
pr := api.PermissionResource{Type: permType}
|
|
if id != "" {
|
|
pr.Id = &id
|
|
}
|
|
if orgId != "" {
|
|
pr.OrgID = &orgId
|
|
}
|
|
return pr
|
|
}
|