mirror of
https://github.com/hanzoai/forge.git
synced 2026-10-30 15:00:39 +00:00
Generating a token was a command; seeing what existed and revoking one were not, so a token could only be created and never audited or withdrawn. Both gaps are the same shape and both are closed here: list prints id, name, scope and dates for one named user, and delete takes exactly one of --token-name or --id and refuses an id that belongs to somebody else. Scope stays narrow by construction: ListAccessTokensOptions requires a UserID, so one user's tokens are the widest set either command can reach. generate-access-token gains the initDB guard its siblings already had. Without it the test harness — which supplies its own database — re-enters MustInstalled and log.Fatal kills the process, so the new tests could not run against it. Co-authored-by: Hanzo Dev <[email protected]>
70 lines
1.8 KiB
Go
70 lines
1.8 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"text/tabwriter"
|
|
|
|
auth_model "github.com/hanzoai/git/models/auth"
|
|
"github.com/hanzoai/git/models/db"
|
|
user_model "github.com/hanzoai/git/models/user"
|
|
"github.com/hanzoai/git/modules/setting"
|
|
|
|
"github.com/urfave/cli/v3"
|
|
)
|
|
|
|
func newUserListAccessTokensCommand() *cli.Command {
|
|
return &cli.Command{
|
|
Name: "list-access-tokens",
|
|
Usage: "List the access tokens of a specific user",
|
|
Flags: []cli.Flag{
|
|
&cli.StringFlag{
|
|
Name: "username",
|
|
Aliases: []string{"u"},
|
|
Usage: "Username",
|
|
},
|
|
},
|
|
Action: runListAccessTokens,
|
|
}
|
|
}
|
|
|
|
func runListAccessTokens(ctx context.Context, c *cli.Command) error {
|
|
if !c.IsSet("username") {
|
|
return errors.New("you must provide a username to list the tokens of")
|
|
}
|
|
|
|
// The test harness builds its own database, so initDB there re-enters
|
|
// MustInstalled and kills the process before the command runs. Every other
|
|
// admin command guards it the same way.
|
|
if !setting.IsInTesting {
|
|
if err := initDB(ctx); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
user, err := user_model.GetUserByName(ctx, c.String("username"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// UserID is required by ListAccessTokensOptions.ToConds, so one user's
|
|
// tokens are the widest set this can ever return.
|
|
tokens, err := db.Find[auth_model.AccessToken](ctx, auth_model.ListAccessTokensOptions{UserID: user.ID})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
w := tabwriter.NewWriter(os.Stdout, 5, 0, 1, ' ', 0)
|
|
fmt.Fprint(w, "ID\tName\tScope\tCreated\tLastUsed\n")
|
|
for _, t := range tokens {
|
|
fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\n", t.ID, t.Name, t.Scope,
|
|
t.CreatedUnix.FormatDate(), t.UpdatedUnix.FormatDate())
|
|
}
|
|
return w.Flush()
|
|
}
|