commands/token/revoke/revoke.go (183 lines of code) (raw):
package revoke
import (
"encoding/json"
"errors"
"fmt"
"strconv"
"gitlab.com/gitlab-org/cli/commands/token/filter"
"gitlab.com/gitlab-org/cli/commands/flag"
"gitlab.com/gitlab-org/cli/pkg/iostreams"
"github.com/MakeNowJust/heredoc/v2"
"github.com/spf13/cobra"
gitlab "gitlab.com/gitlab-org/api/client-go"
"gitlab.com/gitlab-org/cli/api"
"gitlab.com/gitlab-org/cli/commands/cmdutils"
"gitlab.com/gitlab-org/cli/internal/glrepo"
)
type RevokeOptions struct {
HTTPClient func() (*gitlab.Client, error)
IO *iostreams.IOStreams
BaseRepo func() (glrepo.Interface, error)
User string
Group string
Name string
TokenId int
OutputFormat string
}
func NewCmdRevoke(f *cmdutils.Factory, runE func(opts *RevokeOptions) error) *cobra.Command {
opts := &RevokeOptions{
IO: f.IO,
}
cmd := &cobra.Command{
Use: "revoke <token-name|token-id>",
Short: "Revoke user, group or project access tokens",
Aliases: []string{"revoke", "rm"},
Args: cobra.RangeArgs(1, 1),
Long: heredoc.Doc(`
Revoke an user, group or project access token. If multiple tokens with the same name exist, you can specify
the ID of the token.
The output format can be either "JSON" or "text". The JSON output will show the meta information of the
revoked token. The normal text output is a description of the revoked token name and ID.
Administrators can revoke personal access tokens belonging to other users.
`),
Example: heredoc.Doc(`
Revoke a project access token of current project
- glab token revoke my-project-token
Revoke a project access token of a specific project
- glab token revoke --repo user/my-repo my-project-token
Revoke a group access token
- glab token revoke --group group/sub-group my-group-token
Revoke my personal access token
- glab token revoke --user @me my-personal-token
Revoke a personal access token of another user (administrator only)
- glab token revoke --user johndoe johns-personal-token
`),
RunE: func(cmd *cobra.Command, args []string) (err error) {
// Supports repo override
opts.HTTPClient = f.HttpClient
opts.BaseRepo = f.BaseRepo
if opts.TokenId, err = strconv.Atoi(args[0]); err != nil {
opts.Name = args[0]
}
if opts.Group, err = flag.GroupOverride(cmd); err != nil {
return
}
if opts.Group != "" && opts.User != "" {
return cmdutils.FlagError{Err: errors.New("'--group' and '--user' are mutually exclusive.")}
}
if runE != nil {
return runE(opts)
}
return revokeTokenRun(opts)
},
}
cmdutils.EnableRepoOverride(cmd, f)
cmd.Flags().StringVarP(&opts.Group, "group", "g", "", "Revoke group access token. Ignored if a user or repository argument is set.")
cmd.Flags().StringVarP(&opts.User, "user", "U", "", "Revoke personal access token. Use @me for the current user.")
cmd.Flags().StringVarP(&opts.OutputFormat, "output", "F", "text", "Format output as: text, json. 'text' provides the name and ID of the revoked token; 'json' outputs the token with metadata.")
return cmd
}
func revokeTokenRun(opts *RevokeOptions) error {
httpClient, err := opts.HTTPClient()
if err != nil {
return err
}
var outputToken any
var outputTokenValue string
if opts.User != "" {
user, err := api.UserByName(httpClient, opts.User)
if err != nil {
return cmdutils.FlagError{Err: err}
}
options := &gitlab.ListPersonalAccessTokensOptions{
ListOptions: gitlab.ListOptions{PerPage: 100},
UserID: &user.ID,
}
tokens, err := api.ListPersonalAccessTokens(httpClient, options)
if err != nil {
return err
}
var token *gitlab.PersonalAccessToken
tokens = filter.Filter(tokens, func(t *gitlab.PersonalAccessToken) bool {
return t.Active && (t.Name == opts.Name || t.ID == opts.TokenId)
})
switch len(tokens) {
case 1:
token = tokens[0]
case 0:
return cmdutils.FlagError{Err: fmt.Errorf("no token found with the name '%v'.", opts.Name)}
default:
return cmdutils.FlagError{Err: fmt.Errorf("multiple tokens found with the name '%v'. Use the ID instead.", opts.Name)}
}
if err = api.RevokePersonalAccessToken(httpClient, token.ID); err != nil {
return err
}
token.Revoked = true
outputToken = token
outputTokenValue = fmt.Sprintf("revoked %s %s %d", opts.User, token.Name, token.ID)
} else {
if opts.Group != "" {
options := &gitlab.ListGroupAccessTokensOptions{PerPage: 100}
tokens, err := api.ListGroupAccessTokens(httpClient, opts.Group, options)
if err != nil {
return err
}
var token *gitlab.GroupAccessToken
tokens = filter.Filter(tokens, func(t *gitlab.GroupAccessToken) bool {
return t.Active && (t.Name == opts.Name || t.ID == opts.TokenId)
})
switch len(tokens) {
case 1:
token = tokens[0]
case 0:
return cmdutils.FlagError{Err: fmt.Errorf("no token found with the name '%v'.", opts.Name)}
default:
return cmdutils.FlagError{Err: fmt.Errorf("multiple tokens found with the name '%v'. Use the ID instead.", opts.Name)}
}
if err = api.RevokeGroupAccessToken(httpClient, opts.Group, token.ID); err != nil {
return err
}
token.Revoked = true
outputToken = token
outputTokenValue = fmt.Sprintf("revoked %s %d", token.Name, token.ID)
} else {
repo, err := opts.BaseRepo()
if err != nil {
return err
}
options := &gitlab.ListProjectAccessTokensOptions{ListOptions: gitlab.ListOptions{PerPage: 100}}
tokens, err := api.ListProjectAccessTokens(httpClient, repo.FullName(), options)
if err != nil {
return err
}
tokens = filter.Filter(tokens, func(t *gitlab.ProjectAccessToken) bool {
return t.Active && (t.Name == opts.Name || t.ID == opts.TokenId)
})
var token *gitlab.ProjectAccessToken
switch len(tokens) {
case 1:
token = tokens[0]
case 0:
return cmdutils.FlagError{Err: fmt.Errorf("no token found with the name '%v'.", opts.Name)}
default:
return cmdutils.FlagError{Err: fmt.Errorf("multiple tokens found with the name '%v'. Use the ID instead.", opts.Name)}
}
if err = api.RevokeProjectAccessToken(httpClient, repo.FullName(), token.ID); err != nil {
return err
}
token.Revoked = true
outputToken = token
outputTokenValue = fmt.Sprintf("revoked %s %d", token.Name, token.ID)
}
}
if opts.OutputFormat == "json" {
encoder := json.NewEncoder(opts.IO.StdOut)
if err := encoder.Encode(outputToken); err != nil {
return err
}
} else {
if _, err := opts.IO.StdOut.Write([]byte(outputTokenValue)); err != nil {
return err
}
}
return nil
}