package v1_auth import ( "context" "fmt" "github.com/influxdata/influx-cli/v2/api" "github.com/influxdata/influx-cli/v2/clients" "github.com/influxdata/influx-cli/v2/pkg/influxid" ) type Client struct { clients.CLI api.LegacyAuthorizationsApi api.UsersApi api.OrganizationsApi } type AuthLookupParams struct { ID string Username string } func (p AuthLookupParams) Validate() (err error) { if p.Username == "" && p.ID == "" { err = fmt.Errorf("id or username required") } else if p.Username != "" && p.ID != "" { err = fmt.Errorf("specify id or username, not both") } return } func (p AuthLookupParams) IsSet() bool { return p.ID != "" || p.Username != "" } type v1PrintOpts struct { deleted bool token *v1Token tokens []v1Token } type v1Token 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 CreateParams struct { clients.OrgParams Desc string Username string Password string NoPassword bool ReadBucket []string WriteBucket []string } func (c Client) Create(ctx context.Context, params *CreateParams) error { if params.Password != "" && params.NoPassword { return fmt.Errorf("only one of --password and --no-password may be specified") } orgID, err := params.GetOrgID(ctx, c.ActiveConfig, c.OrganizationsApi) if err != nil { return err } // verify an existing token with the same username doesn't already exist auths, err := c.LegacyAuthorizationsApi.GetLegacyAuthorizations(ctx).Token(params.Username).Execute() if apiError, ok := err.(api.ApiError); ok { if apiError.ErrorCode() != api.ERRORCODE_NOT_FOUND { return fmt.Errorf("failed to verify username %q has no auth: %w", params.Username, err) } else if len(auths.GetAuthorizations()) != 0 { return fmt.Errorf("authorization with username %q already exists", params.Username) } } password := params.Password if password == "" && !params.NoPassword { pass, err := c.StdIO.GetPassword("Please type your password") if err != nil { return err } password = pass } bucketPerms := []struct { action string perms []string }{ {action: "read", perms: params.ReadBucket}, {action: "write", perms: params.WriteBucket}, } var permissions []api.Permission for _, bp := range bucketPerms { for _, p := range bp.perms { p := p // 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: api.PermissionResource{ Type: "buckets", Id: &p, OrgID: &orgID, }, } permissions = append(permissions, newPerm) } } authReq := api.LegacyAuthorizationPostRequest{ Description: ¶ms.Desc, OrgID: orgID, Permissions: permissions, Token: ¶ms.Username, } newAuth, err := c.LegacyAuthorizationsApi.PostLegacyAuthorizations(ctx).LegacyAuthorizationPostRequest(authReq).Execute() if err != nil { return fmt.Errorf("failed to create new authorization: %w", err) } if password != "" { err := c.LegacyAuthorizationsApi.PostLegacyAuthorizationsIDPassword(ctx, newAuth.GetId()). PasswordResetBody(api.PasswordResetBody{Password: password}). Execute() if err != nil { _ = c.LegacyAuthorizationsApi.DeleteLegacyAuthorizationsID(ctx, newAuth.GetId()).Execute() return fmt.Errorf("failed to set password for %q: %w", params.Username, err) } } usr, err := c.UsersApi.GetUsersID(ctx, newAuth.GetUserID()).Execute() if err != nil { return err } ps := make([]string, 0, len(newAuth.Permissions)) for _, p := range newAuth.Permissions { ps = append(ps, p.String()) } return c.printV1Tokens(&v1PrintOpts{ token: &v1Token{ ID: newAuth.GetId(), Description: newAuth.GetDescription(), Token: newAuth.GetToken(), Status: newAuth.GetStatus(), UserName: usr.GetName(), UserID: usr.GetId(), Permissions: ps, }, }) } type RemoveParams struct { AuthLookupParams } func (c Client) Remove(ctx context.Context, params *RemoveParams) error { id, err := c.getAuthReqID(ctx, ¶ms.AuthLookupParams) if err != nil { return err } auth, err := c.LegacyAuthorizationsApi.GetLegacyAuthorizationsID(ctx, id).Execute() if err != nil { return fmt.Errorf("could not find Authorization from ID %q: %w", id, err) } err = c.LegacyAuthorizationsApi.DeleteLegacyAuthorizationsID(ctx, id).Execute() if err != nil { return fmt.Errorf("could not remove Authorization with ID %q: %w", id, err) } usr, err := c.UsersApi.GetUsersID(ctx, auth.GetUserID()).Execute() if err != nil { return fmt.Errorf("could not find user from user ID %q: %w", auth.GetUserID(), err) } ps := make([]string, 0, len(auth.GetPermissions())) for _, p := range auth.GetPermissions() { ps = append(ps, p.String()) } return c.printV1Tokens(&v1PrintOpts{ deleted: true, token: &v1Token{ ID: auth.GetId(), Description: auth.GetDescription(), Token: auth.GetToken(), Status: auth.GetStatus(), UserName: usr.GetName(), UserID: usr.GetId(), Permissions: ps, }, }) } type ListParams struct { clients.OrgParams AuthLookupParams User string UserID string } func (c Client) List(ctx context.Context, params *ListParams) error { req := c.LegacyAuthorizationsApi.GetLegacyAuthorizations(ctx) if params.User != "" { req = req.User(params.User) } if params.UserID != "" { req = req.UserID(params.UserID) } if params.OrgID != "" { req = req.OrgID(params.OrgID) } if params.OrgName != "" { req = req.Org(params.OrgName) } if params.Username != "" { req = req.Token(params.Username) } if params.ID != "" { req = req.AuthID(params.ID) } auths, err := req.Execute() if err != nil { return fmt.Errorf("could not find Authorizations for specified arguments: %w", err) } var tokens []v1Token for _, a := range auths.GetAuthorizations() { var permissions []string for _, p := range a.GetPermissions() { permissions = append(permissions, p.String()) } usr, err := c.UsersApi.GetUsersID(ctx, a.GetUserID()).Execute() if err != nil { return fmt.Errorf("could not find user with ID %q: %w", a.GetUserID(), err) } tokens = append(tokens, v1Token{ ID: a.GetId(), Description: a.GetDescription(), Token: a.GetToken(), Status: a.GetStatus(), UserName: usr.GetName(), UserID: usr.GetId(), Permissions: permissions, }) } return c.printV1Tokens(&v1PrintOpts{tokens: tokens}) } type ActiveParams struct { AuthLookupParams } func (c Client) SetActive(ctx context.Context, params *ActiveParams, active bool) error { id, err := c.getAuthReqID(ctx, ¶ms.AuthLookupParams) if err != nil { return err } req := c.LegacyAuthorizationsApi.PatchLegacyAuthorizationsID(ctx, id) var status string if active { status = "active" } else { status = "inactive" } req = req.AuthorizationUpdateRequest(api.AuthorizationUpdateRequest{ Status: &status, }) auth, err := req.Execute() if err != nil { return fmt.Errorf("failed to update status of authorization with ID %q: %w", id, err) } usr, err := c.UsersApi.GetUsersID(ctx, auth.GetUserID()).Execute() if err != nil { return fmt.Errorf("could not find user from User ID %q: %w", auth.GetUserID(), err) } ps := make([]string, 0, len(auth.GetPermissions())) for _, p := range auth.GetPermissions() { ps = append(ps, p.String()) } return c.printV1Tokens(&v1PrintOpts{ token: &v1Token{ ID: auth.GetId(), Description: auth.GetDescription(), Token: auth.GetToken(), Status: auth.GetStatus(), UserName: usr.GetName(), UserID: usr.GetId(), Permissions: ps, }, }) } type SetPasswordParams struct { AuthLookupParams Password string } func (c Client) SetPassword(ctx context.Context, params *SetPasswordParams) error { id, err := c.getAuthReqID(ctx, ¶ms.AuthLookupParams) if err != nil { return err } password := params.Password if password == "" { pass, err := c.StdIO.GetPassword("Please type new password") if err != nil { return err } password = pass } err = c.LegacyAuthorizationsApi.PostLegacyAuthorizationsIDPassword(ctx, id). PasswordResetBody(api.PasswordResetBody{Password: password}). Execute() if err != nil { return fmt.Errorf("error setting password: %w", err) } return nil } func (c Client) printV1Tokens(params *v1PrintOpts) error { if c.PrintAsJSON { var v interface{} if params.token != nil { v = params.token } else { v = params.tokens } return c.PrintJSON(v) } headers := []string{ "ID", "Description", "Username", "v2 User Name", "v2 User ID", "Permissions", } if params.deleted { headers = append(headers, "Deleted") } if params.token != nil { params.tokens = append(params.tokens, *params.token) } var rows []map[string]interface{} for _, u := range params.tokens { row := map[string]interface{}{ "ID": u.ID, "Description": u.Description, "Username": u.Token, "v2 User Name": u.UserName, "v2 User ID": u.UserID, "Permissions": u.Permissions, } if params.deleted { row["Deleted"] = true } rows = append(rows, row) } return c.PrintTable(headers, rows...) } func (c Client) getAuthReqID(ctx context.Context, params *AuthLookupParams) (id string, err error) { if params.ID != "" { id = params.ID } else { var auths api.Authorizations auths, err = c.LegacyAuthorizationsApi.GetLegacyAuthorizations(ctx).Token(params.Username).Execute() if err != nil || len(auths.GetAuthorizations()) == 0 { err = fmt.Errorf("could not find v1 auth with token (username) %q: %w", params.Username, err) } else { id = auths.GetAuthorizations()[0].GetId() } } return }