From 6c564febb967ece1c7ae8b3859ed6dfb6416db7b Mon Sep 17 00:00:00 2001 From: niksis02 Date: Thu, 22 Jan 2026 19:47:39 +0400 Subject: [PATCH] feat: makes root creds usable for admin subcommand with lower precendence Closes #1760 The admin subcommand accepts the `--access` and `--secret` flags as admin user credentials. This change makes these flags optional and allows the root user credentials to be used as admin credentials with lower precedence when the admin flags are omitted. Both invocation styles are now valid: ```bash versitygw admin --access user --secret pass -er http://127.0.0.1:7070 list-buckets ``` and ```bash versitygw --access user --secret pass admin -er http://127.0.0.1:7070 list-buckets ``` Additionally, if `ROOT_ACCESS_KEY` and `ROOT_SECRET_KEY` are already set, the following also works: ```bash versitygw admin -er http://127.0.0.1:7070 list-buckets ``` --- cmd/versitygw/admin.go | 61 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/cmd/versitygw/admin.go b/cmd/versitygw/admin.go index 01eb9d51..f88082bd 100644 --- a/cmd/versitygw/admin.go +++ b/cmd/versitygw/admin.go @@ -241,7 +241,6 @@ func adminCommand() *cli.Command { Usage: "admin access key id", EnvVars: []string{"ADMIN_ACCESS_KEY_ID", "ADMIN_ACCESS_KEY"}, Aliases: []string{"a"}, - Required: true, Destination: &adminAccess, }, &cli.StringFlag{ @@ -249,7 +248,6 @@ func adminCommand() *cli.Command { Usage: "admin secret access key", EnvVars: []string{"ADMIN_SECRET_ACCESS_KEY", "ADMIN_SECRET_KEY"}, Aliases: []string{"s"}, - Required: true, Destination: &adminSecret, }, &cli.StringFlag{ @@ -279,6 +277,32 @@ func adminCommand() *cli.Command { } } +// getAdminCreds returns the effective admin access key ID and secret key. +// If admin-specific credentials are not provided, it falls back to the +// root user credentials. Both resulting values must be non-empty; +// otherwise, an error is returned. +func getAdminCreds() (string, string, error) { + access := adminAccess + secret := adminSecret + + // Fallbacks to root user credentials + if access == "" { + access = rootUserAccess + } + if secret == "" { + secret = rootUserSecret + } + + if access == "" { + return "", "", errors.New("subcommand admin access key id is not set") + } + if secret == "" { + return "", "", errors.New("subcommand admin secret access key is not set") + } + + return access, secret, nil +} + func initHTTPClient() *http.Client { tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: allowInsecure}, @@ -287,6 +311,10 @@ func initHTTPClient() *http.Client { } func createUser(ctx *cli.Context) error { + adminAccess, adminSecret, err := getAdminCreds() + if err != nil { + return err + } access, secret, role := ctx.String("access"), ctx.String("secret"), ctx.String("role") userID, groupID, projectID := ctx.Int("user-id"), ctx.Int("group-id"), ctx.Int("project-id") if access == "" || secret == "" { @@ -348,6 +376,10 @@ func createUser(ctx *cli.Context) error { } func deleteUser(ctx *cli.Context) error { + adminAccess, adminSecret, err := getAdminCreds() + if err != nil { + return err + } access := ctx.String("access") if access == "" { return fmt.Errorf("invalid input parameter for the user access key") @@ -391,6 +423,11 @@ func deleteUser(ctx *cli.Context) error { } func updateUser(ctx *cli.Context) error { + adminAccess, adminSecret, err := getAdminCreds() + if err != nil { + return err + } + access, secret, userId, groupId, projectID, role := ctx.String("access"), ctx.String("secret"), @@ -462,6 +499,11 @@ func updateUser(ctx *cli.Context) error { } func listUsers(ctx *cli.Context) error { + adminAccess, adminSecret, err := getAdminCreds() + if err != nil { + return err + } + req, err := http.NewRequest(http.MethodPatch, fmt.Sprintf("%v/list-users", adminEndpoint), nil) if err != nil { return fmt.Errorf("failed to send the request: %w", err) @@ -680,6 +722,11 @@ func parseTag(input string) (types.Tag, error) { } func createBucket(ctx *cli.Context) error { + adminAccess, adminSecret, err := getAdminCreds() + if err != nil { + return err + } + bucket, owner := ctx.String("bucket"), ctx.String("owner") payload, err := parseCreateBucketPayload(ctx.String("create-bucket-configuration")) @@ -768,6 +815,11 @@ func printAcctTable(accs []auth.Account) { } func changeBucketOwner(ctx *cli.Context) error { + adminAccess, adminSecret, err := getAdminCreds() + if err != nil { + return err + } + bucket, owner := ctx.String("bucket"), ctx.String("owner") req, err := http.NewRequest(http.MethodPatch, fmt.Sprintf("%v/change-bucket-owner/?bucket=%v&owner=%v", adminEndpoint, bucket, owner), nil) if err != nil { @@ -819,6 +871,11 @@ func printBuckets(buckets []s3response.Bucket) { } func listBuckets(ctx *cli.Context) error { + adminAccess, adminSecret, err := getAdminCreds() + if err != nil { + return err + } + req, err := http.NewRequest(http.MethodPatch, fmt.Sprintf("%v/list-buckets", adminEndpoint), nil) if err != nil { return fmt.Errorf("failed to send the request: %w", err)