From 54268c4808cb17d2fa4c1fb5bc8bcc201a58bb64 Mon Sep 17 00:00:00 2001 From: Taran Pelkey Date: Thu, 5 Sep 2024 07:03:52 -0400 Subject: [PATCH] Add `edit`, `enable`, and `disable` to `mc idp ldap accesskey` (#5033) --- cmd/auto-complete.go | 3 + cmd/idp-ldap-accesskey-disable.go | 48 +++++++++ cmd/idp-ldap-accesskey-edit.go | 167 ++++++++++++++++++++++++++++++ cmd/idp-ldap-accesskey-enable.go | 84 +++++++++++++++ cmd/idp-ldap-accesskey-info.go | 35 +++---- cmd/idp-ldap-accesskey-remove.go | 4 - cmd/idp-ldap-accesskey.go | 3 + 7 files changed, 322 insertions(+), 22 deletions(-) create mode 100644 cmd/idp-ldap-accesskey-disable.go create mode 100644 cmd/idp-ldap-accesskey-edit.go create mode 100644 cmd/idp-ldap-accesskey-enable.go diff --git a/cmd/auto-complete.go b/cmd/auto-complete.go index b05a6c585e..3d54a01f73 100644 --- a/cmd/auto-complete.go +++ b/cmd/auto-complete.go @@ -388,6 +388,9 @@ var completeCmds = map[string]complete.Predictor{ "/idp/ldap/accesskey/remove": aliasCompleter, "/idp/ldap/accesskey/rm": aliasCompleter, "/idp/ldap/accesskey/info": aliasCompleter, + "/idp/ldap/accesskey/edit": aliasCompleter, + "/idp/ldap/accesskey/enable": aliasCompleter, + "/idp/ldap/accesskey/disable": aliasCompleter, "/admin/policy/info": aliasCompleter, "/admin/policy/update": aliasCompleter, diff --git a/cmd/idp-ldap-accesskey-disable.go b/cmd/idp-ldap-accesskey-disable.go new file mode 100644 index 0000000000..511da3afaf --- /dev/null +++ b/cmd/idp-ldap-accesskey-disable.go @@ -0,0 +1,48 @@ +// Copyright (c) 2015-2024 MinIO, Inc. +// +// This file is part of MinIO Object Storage stack +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package cmd + +import ( + "github.com/minio/cli" +) + +var idpLdapAccesskeyDisableCmd = cli.Command{ + Name: "disable", + Usage: "disable an access key", + Action: mainIDPLdapAccesskeyDisable, + Before: setGlobalsFromContext, + Flags: globalFlags, + OnUsageError: onUsageError, + CustomHelpTemplate: `NAME: + {{.HelpName}} - {{.Usage}} + +USAGE: + {{.HelpName}} [FLAGS] [TARGET] + +FLAGS: + {{range .VisibleFlags}}{{.}} + {{end}} +EXAMPLES: + 1. Disable LDAP access key + {{.Prompt}} {{.HelpName}} myminio myaccesskey +`, +} + +func mainIDPLdapAccesskeyDisable(ctx *cli.Context) error { + return enableDisableAccesskey(ctx, false) +} diff --git a/cmd/idp-ldap-accesskey-edit.go b/cmd/idp-ldap-accesskey-edit.go new file mode 100644 index 0000000000..2a8680a3e5 --- /dev/null +++ b/cmd/idp-ldap-accesskey-edit.go @@ -0,0 +1,167 @@ +// Copyright (c) 2015-2024 MinIO, Inc. +// +// This file is part of MinIO Object Storage stack +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package cmd + +import ( + "bytes" + "errors" + "fmt" + "os" + "time" + + "github.com/minio/cli" + "github.com/minio/madmin-go/v3" + "github.com/minio/mc/pkg/probe" + "github.com/minio/pkg/v3/policy" +) + +var idpLdapAccesskeyEditFlags = []cli.Flag{ + cli.StringFlag{ + Name: "secret-key", + Usage: "set a secret key for the account", + }, + cli.StringFlag{ + Name: "policy", + Usage: "path to a JSON policy file", + }, + cli.StringFlag{ + Name: "name", + Usage: "friendly name for the account", + }, + cli.StringFlag{ + Name: "description", + Usage: "description for the account", + }, + cli.StringFlag{ + Name: "expiry-duration", + Usage: "duration before the access key expires", + }, + cli.StringFlag{ + Name: "expiry", + Usage: "expiry date for the access key", + }, +} + +var idpLdapAccesskeyEditCmd = cli.Command{ + Name: "edit", + Usage: "edit existing access keys for LDAP", + Action: mainIDPLdapAccesskeyEdit, + Before: setGlobalsFromContext, + Flags: append(idpLdapAccesskeyEditFlags, globalFlags...), + OnUsageError: onUsageError, + CustomHelpTemplate: `NAME: + {{.HelpName}} - {{.Usage}} + +USAGE: + {{.HelpName}} [FLAGS] [TARGET] + +FLAGS: + {{range .VisibleFlags}}{{.}} + {{end}} +EXAMPLES: + 1. Change the secret key for the access key "testkey" + {{.Prompt}} {{.HelpName}} myminio/ testkey --secret-key 'xxxxxxx' + 2. Change the expiry duration for the access key "testkey" + {{.Prompt}} {{.HelpName}} myminio/ testkey ---expiry-duration 24h +`, +} + +func mainIDPLdapAccesskeyEdit(ctx *cli.Context) error { + if len(ctx.Args()) == 0 || len(ctx.Args()) > 2 { + showCommandHelpAndExit(ctx, 1) // last argument is exit code + } + + args := ctx.Args() + aliasedURL := args.Get(0) + accessKey := args.Get(1) + + opts := accessKeyEditOpts(ctx) + client, err := newAdminClient(aliasedURL) + fatalIf(err, "Unable to initialize admin connection.") + + e := client.UpdateServiceAccount(globalContext, accessKey, opts) + fatalIf(probe.NewError(e), "Unable to edit service account.") + + m := ldapAccesskeyMessage{ + op: "edit", + Status: "success", + AccessKey: accessKey, + } + printMsg(m) + + return nil +} + +func accessKeyEditOpts(ctx *cli.Context) madmin.UpdateServiceAccountReq { + name := ctx.String("name") + expVal := ctx.String("expiry") + policyPath := ctx.String("policy") + secretKey := ctx.String("secret-key") + description := ctx.String("description") + expDurVal := ctx.Duration("expiry-duration") + + if expVal != "" && expDurVal != 0 { + fatalIf(probe.NewError(errors.New("Only one of --expiry or --expiry-duration can be specified")), "invalid flags") + } + + opts := madmin.UpdateServiceAccountReq{ + NewName: name, + NewSecretKey: secretKey, + NewDescription: description, + } + + if policyPath != "" { + // Validate the policy document and ensure it has at least one statement + policyBytes, e := os.ReadFile(policyPath) + fatalIf(probe.NewError(e), "unable to read the policy document") + + p, e := policy.ParseConfig(bytes.NewReader(policyBytes)) + fatalIf(probe.NewError(e), "unable to parse the policy document") + + if p.IsEmpty() { + fatalIf(errInvalidArgument(), "empty policies are not allowed") + } + + opts.NewPolicy = policyBytes + } + + switch { + case expVal != "": + location, e := time.LoadLocation("Local") + fatalIf(probe.NewError(e), "unable to load local location. verify your local TZ= settings") + + var found bool + for _, format := range supportedTimeFormats { + t, e := time.ParseInLocation(format, expVal, location) + if e == nil { + found = true + opts.NewExpiration = &t + break + } + } + + if !found { + fatalIf(probe.NewError(fmt.Errorf("invalid expiry date format '%s'", expVal)), "unable to parse the expiry argument") + } + case expDurVal != 0: + t := time.Now().Add(expDurVal) + opts.NewExpiration = &t + } + + return opts +} diff --git a/cmd/idp-ldap-accesskey-enable.go b/cmd/idp-ldap-accesskey-enable.go new file mode 100644 index 0000000000..b32016f639 --- /dev/null +++ b/cmd/idp-ldap-accesskey-enable.go @@ -0,0 +1,84 @@ +// Copyright (c) 2015-2024 MinIO, Inc. +// +// This file is part of MinIO Object Storage stack +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package cmd + +import ( + "github.com/minio/cli" + "github.com/minio/madmin-go/v3" + "github.com/minio/mc/pkg/probe" +) + +var idpLdapAccesskeyEnableCmd = cli.Command{ + Name: "enable", + Usage: "enable an access key", + Action: mainIDPLdapAccesskeyEnable, + Before: setGlobalsFromContext, + Flags: globalFlags, + OnUsageError: onUsageError, + CustomHelpTemplate: `NAME: + {{.HelpName}} - {{.Usage}} + +USAGE: + {{.HelpName}} [FLAGS] [TARGET] + +FLAGS: + {{range .VisibleFlags}}{{.}} + {{end}} +EXAMPLES: + 1. Enable LDAP access key + {{.Prompt}} {{.HelpName}} myminio myaccesskey +`, +} + +func mainIDPLdapAccesskeyEnable(ctx *cli.Context) error { + return enableDisableAccesskey(ctx, true) +} + +func enableDisableAccesskey(ctx *cli.Context, enable bool) error { + if len(ctx.Args()) == 0 || len(ctx.Args()) > 2 { + showCommandHelpAndExit(ctx, 1) // last argument is exit code + } + + args := ctx.Args() + aliasedURL := args.Get(0) + accessKey := args.Get(1) + + client, err := newAdminClient(aliasedURL) + fatalIf(err, "Unable to initialize admin connection.") + + op := "disable" + status := "off" + if enable { + op = "enable" + status = "on" + } + + e := client.UpdateServiceAccount(globalContext, accessKey, madmin.UpdateServiceAccountReq{ + NewStatus: status, + }) + fatalIf(probe.NewError(e), "Unable to add service account.") + + m := ldapAccesskeyMessage{ + op: op, + Status: "success", + AccessKey: accessKey, + } + printMsg(m) + + return nil +} diff --git a/cmd/idp-ldap-accesskey-info.go b/cmd/idp-ldap-accesskey-info.go index ae0b3146fe..67f42e873a 100644 --- a/cmd/idp-ldap-accesskey-info.go +++ b/cmd/idp-ldap-accesskey-info.go @@ -26,7 +26,6 @@ import ( "github.com/minio/cli" json "github.com/minio/colorjson" "github.com/minio/mc/pkg/probe" - "github.com/minio/pkg/v3/console" ) var idpLdapAccesskeyInfoCmd = cli.Command{ @@ -68,6 +67,8 @@ type ldapAccesskeyMessage struct { } func (m ldapAccesskeyMessage) String() string { + labelStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#04B575")) // green + o := strings.Builder{} switch m.op { case "info": expirationStr := "NONE" @@ -78,39 +79,37 @@ func (m ldapAccesskeyMessage) String() string { if m.ImpliedPolicy { policyStr = "implied" } - - labelStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#04B575")) // green - o := strings.Builder{} - + statusStr := "enabled" + if m.AccountStatus == "off" { + statusStr = "disabled" + } o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Access Key:"), m.AccessKey)) o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Parent User:"), m.ParentUser)) + o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Status:"), statusStr)) o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Policy:"), policyStr)) o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Name:"), m.Name)) o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Description:"), m.Description)) - o.WriteString(iFmt(0, "%s %s\n\n", labelStyle.Render("Expiration:"), expirationStr)) - - return o.String() - + o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Expiration:"), expirationStr)) case "create": expirationStr := "NONE" if m.Expiration != nil && !m.Expiration.IsZero() && !m.Expiration.Equal(timeSentinel) { expirationStr = m.Expiration.String() } - - labelStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#04B575")) // green - o := strings.Builder{} - o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Access Key:"), m.AccessKey)) o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Secret Key:"), m.SecretKey)) o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Expiration:"), expirationStr)) o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Name:"), m.Name)) - o.WriteString(iFmt(0, "%s %s\n\n", labelStyle.Render("Description:"), m.Description)) - - return o.String() + o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Description:"), m.Description)) case "remove": - return console.Colorize("RemoveAccessKey", "Successfully removed access key `"+m.AccessKey+"`.") + o.WriteString(labelStyle.Render(iFmt(0, "Successfully removed access key `%s`.", m.AccessKey))) + case "edit": + o.WriteString(labelStyle.Render(iFmt(0, "Successfully edited access key `%s`.", m.AccessKey))) + case "enable": + o.WriteString(labelStyle.Render(iFmt(0, "Successfully enabled access key `%s`.", m.AccessKey))) + case "disable": + o.WriteString(labelStyle.Render(iFmt(0, "Successfully disabled access key `%s`.", m.AccessKey))) } - return "" + return o.String() } func (m ldapAccesskeyMessage) JSON() string { diff --git a/cmd/idp-ldap-accesskey-remove.go b/cmd/idp-ldap-accesskey-remove.go index d2069625a1..8f4d57b667 100644 --- a/cmd/idp-ldap-accesskey-remove.go +++ b/cmd/idp-ldap-accesskey-remove.go @@ -18,10 +18,8 @@ package cmd import ( - "github.com/fatih/color" "github.com/minio/cli" "github.com/minio/mc/pkg/probe" - "github.com/minio/pkg/v3/console" ) var idpLdapAccesskeyRemoveCmd = cli.Command{ @@ -52,8 +50,6 @@ func mainIDPLdapAccesskeyRemove(ctx *cli.Context) error { showCommandHelpAndExit(ctx, 1) // last argument is exit code } - console.SetColor("RemoveAccessKey", color.New(color.FgGreen)) - args := ctx.Args() aliasedURL := args.Get(0) accessKey := args.Get(1) diff --git a/cmd/idp-ldap-accesskey.go b/cmd/idp-ldap-accesskey.go index 392be7c731..a0f1751670 100644 --- a/cmd/idp-ldap-accesskey.go +++ b/cmd/idp-ldap-accesskey.go @@ -25,6 +25,9 @@ var idpLdapAccesskeySubcommands = []cli.Command{ idpLdapAccesskeyInfoCmd, idpLdapAccesskeyCreateCmd, idpLdapAccesskeyCreateWithLoginCmd, + idpLdapAccesskeyEditCmd, + idpLdapAccesskeyEnableCmd, + idpLdapAccesskeyDisableCmd, } var idpLdapAccesskeyCmd = cli.Command{