commands/token/list/list.go (220 lines of code) (raw):
package list
import (
"encoding/json"
"errors"
"strconv"
"strings"
"time"
"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/commands/flag"
"gitlab.com/gitlab-org/cli/commands/token/accesslevel"
"gitlab.com/gitlab-org/cli/internal/glrepo"
"gitlab.com/gitlab-org/cli/pkg/iostreams"
)
type ListOpts struct {
HTTPClient func() (*gitlab.Client, error)
IO *iostreams.IOStreams
BaseRepo func() (glrepo.Interface, error)
User string
Group string
OutputFormat string
ListActive bool
}
func NewCmdList(f *cmdutils.Factory, runE func(opts *ListOpts) error) *cobra.Command {
opts := &ListOpts{
IO: f.IO,
}
cmd := &cobra.Command{
Use: "list",
Short: "List user, group, or project access tokens.",
Aliases: []string{"ls"},
Args: cobra.ExactArgs(0),
Long: heredoc.Doc(`
List all tokens of a user, group, or project.
The output contains the token's meta information, not the actual token value. The output format
can be "JSON" or "text". The access level property is printed in human-readable form in the text
output, but displays the integer value in JSON.
Administrators can list tokens of other users.
`),
Example: heredoc.Doc(
`
List the current project's access tokens
- glab token list
- glab token list --output json
List the project access tokens of a specific project
- glab token list --repo user/my-repo
List group access tokens
- glab token list --group group/sub-group
List my personal access tokens
- glab token list --user @me
Administrators only: list the personal access tokens of another user
- glab token list --user johndoe
`,
),
RunE: func(cmd *cobra.Command, args []string) (err error) {
opts.HTTPClient = f.HttpClient
opts.BaseRepo = f.BaseRepo
opts.Group, err = flag.GroupOverride(cmd)
if err != nil {
return err
}
if opts.Group != "" && opts.User != "" {
return cmdutils.FlagError{Err: errors.New("--user and --group are mutually exclusive.")}
}
if runE != nil {
return runE(opts)
}
return listRun(opts)
},
}
cmdutils.EnableRepoOverride(cmd, f)
cmd.Flags().StringVarP(&opts.Group, "group", "g", "", "List group access tokens. Ignored if a user or repository argument is set.")
cmd.Flags().StringVarP(&opts.User, "user", "U", "", "List personal access tokens. Use @me for the current user.")
cmd.Flags().StringVarP(&opts.OutputFormat, "output", "F", "text", "Format output as: text, json. text provides a readable table, json outputs the tokens with metadata.")
cmd.Flags().BoolVarP(&opts.ListActive, "active", "a", false, "List only the active tokens.")
return cmd
}
type Token struct {
ID string
Name string
Description string
AccessLevel string
Active string
Revoked string
CreatedAt string
ExpiresAt string
LastUsedAt string
Scopes string
}
type Tokens []Token
func formatLastUsedAt(lastUsedAt *time.Time) string {
if lastUsedAt == nil {
return "-"
}
return lastUsedAt.Format(time.RFC3339)
}
func formatDescription(description string) string {
if description == "" {
return "-"
}
return description
}
func formatAccessLevel(accessLevel gitlab.AccessLevelValue) string {
level := accesslevel.AccessLevel{Value: accessLevel}
levelStr := level.String()
if levelStr == "no" {
return "-"
}
return levelStr
}
func formatExpiresAt(expiresAt *gitlab.ISOTime) string {
if expiresAt == nil {
return "-"
}
return expiresAt.String()
}
func listRun(opts *ListOpts) error {
httpClient, err := opts.HTTPClient()
if err != nil {
return err
}
var apiTokens any
var outputTokens Tokens
switch {
case opts.User != "":
user, err := api.UserByName(httpClient, opts.User)
if err != nil {
return err
}
options := &gitlab.ListPersonalAccessTokensOptions{
UserID: &user.ID,
}
tokens, err := api.ListPersonalAccessTokens(httpClient, options)
if err != nil {
return err
}
apiTokens = tokens
outputTokens = make([]Token, 0, len(tokens))
for _, token := range tokens {
if !opts.ListActive || token.Active {
outputTokens = append(outputTokens, Token{
ID: strconv.FormatInt(int64(token.ID), 10),
Name: token.Name,
Description: formatDescription(token.Description),
AccessLevel: "-",
Active: strconv.FormatBool(token.Active),
Revoked: strconv.FormatBool(token.Revoked),
CreatedAt: token.CreatedAt.Format(time.RFC3339),
ExpiresAt: formatExpiresAt(token.ExpiresAt),
LastUsedAt: formatLastUsedAt(token.LastUsedAt),
Scopes: strings.Join(token.Scopes, ","),
})
}
}
case opts.Group != "":
options := &gitlab.ListGroupAccessTokensOptions{}
tokens, err := api.ListGroupAccessTokens(httpClient, opts.Group, options)
if err != nil {
return err
}
apiTokens = tokens
outputTokens = make([]Token, 0, len(tokens))
for _, token := range tokens {
if !opts.ListActive || token.Active {
outputTokens = append(outputTokens, Token{
ID: strconv.FormatInt(int64(token.ID), 10),
Name: token.Name,
Description: formatDescription(token.Description),
AccessLevel: formatAccessLevel(token.AccessLevel),
Active: strconv.FormatBool(token.Active),
Revoked: strconv.FormatBool(token.Revoked),
CreatedAt: token.CreatedAt.Format(time.RFC3339),
ExpiresAt: formatExpiresAt(token.ExpiresAt),
LastUsedAt: formatLastUsedAt(token.LastUsedAt),
Scopes: strings.Join(token.Scopes, ","),
})
}
}
default:
repo, err := opts.BaseRepo()
if err != nil {
return err
}
tokens, err := api.ListProjectAccessTokens(httpClient, repo.FullName(), &gitlab.ListProjectAccessTokensOptions{})
if err != nil {
return err
}
apiTokens = tokens
outputTokens = make([]Token, 0, len(tokens))
for _, token := range tokens {
if !opts.ListActive || token.Active {
outputTokens = append(outputTokens, Token{
ID: strconv.FormatInt(int64(token.ID), 10),
Name: token.Name,
Description: formatDescription(token.Description),
AccessLevel: formatAccessLevel(token.AccessLevel),
Active: strconv.FormatBool(token.Active),
Revoked: strconv.FormatBool(token.Revoked),
CreatedAt: token.CreatedAt.Format(time.RFC3339),
ExpiresAt: formatExpiresAt(token.ExpiresAt),
LastUsedAt: formatLastUsedAt(token.LastUsedAt),
Scopes: strings.Join(token.Scopes, ","),
})
}
}
}
if opts.OutputFormat == "json" {
encoder := json.NewEncoder(opts.IO.StdOut)
if err := encoder.Encode(apiTokens); err != nil {
return err
}
} else {
table := createTablePrinter(outputTokens)
opts.IO.LogInfof("%s", table.String())
}
return nil
}