diff --git a/cmd/admin-accesskey-create.go b/cmd/admin-accesskey-create.go new file mode 100644 index 0000000000..42629c841d --- /dev/null +++ b/cmd/admin-accesskey-create.go @@ -0,0 +1,94 @@ +// 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 adminAccesskeyCreateFlags = []cli.Flag{ + cli.StringFlag{ + Name: "access-key", + Usage: "set an access key for the account", + }, + 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 adminAccesskeyCreateCmd = cli.Command{ + Name: "create", + Usage: "create access key pairs for users", + Action: mainAdminAccesskeyCreate, + Before: setGlobalsFromContext, + Flags: append(adminAccesskeyCreateFlags, globalFlags...), + OnUsageError: onUsageError, + CustomHelpTemplate: `NAME: + {{.HelpName}} - {{.Usage}} + +USAGE: + {{.HelpName}} [FLAGS] [TARGET] + +FLAGS: + {{range .VisibleFlags}}{{.}} + {{end}} +EXAMPLES: + 1. Create a new access key pair with the same policy as the authenticated user + {{.Prompt}} {{.HelpName}} myminio/ + + 2. Create a new access key pair with custom access key and secret key + {{.Prompt}} {{.HelpName}} myminio/ --access-key myaccesskey --secret-key mysecretkey + + 3. Create a new access key pair for user 'tester' that expires in 1 day + {{.Prompt}} {{.HelpName}} myminio/ tester --expiry-duration 24h + + 4. Create a new access key pair for authenticated user that expires on 2025-01-01 + {{.Prompt}} {{.HelpName}} --expiry 2025-01-01 + + 5. Create a new access key pair for user 'tester' with a custom policy + {{.Prompt}} {{.HelpName}} myminio/ tester --policy /path/to/policy.json + + 6. Create a new access key pair for user 'tester' with a custom name and description + {{.Prompt}} {{.HelpName}} myminio/ tester --name "Tester's Access Key" --description "Access key for tester" +`, +} + +func mainAdminAccesskeyCreate(ctx *cli.Context) error { + return commonAccesskeyCreate(ctx, false) +} diff --git a/cmd/admin-accesskey-disable.go b/cmd/admin-accesskey-disable.go new file mode 100644 index 0000000000..715f09e827 --- /dev/null +++ b/cmd/admin-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 adminAccesskeyDisableCmd = cli.Command{ + Name: "disable", + Usage: "disable an access key", + Action: mainAdminAccesskeyDisable, + Before: setGlobalsFromContext, + Flags: globalFlags, + OnUsageError: onUsageError, + CustomHelpTemplate: `NAME: + {{.HelpName}} - {{.Usage}} + +USAGE: + {{.HelpName}} [FLAGS] [TARGET] + +FLAGS: + {{range .VisibleFlags}}{{.}} + {{end}} +EXAMPLES: + 1. Disable access key + {{.Prompt}} {{.HelpName}} myminio myaccesskey +`, +} + +func mainAdminAccesskeyDisable(ctx *cli.Context) error { + return enableDisableAccesskey(ctx, false) +} diff --git a/cmd/admin-accesskey-edit.go b/cmd/admin-accesskey-edit.go new file mode 100644 index 0000000000..e36df3c186 --- /dev/null +++ b/cmd/admin-accesskey-edit.go @@ -0,0 +1,77 @@ +// 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 adminAccesskeyEditFlags = []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 adminAccesskeyEditCmd = cli.Command{ + Name: "edit", + Usage: "edit existing access keys", + Action: mainAdminAccesskeyEdit, + Before: setGlobalsFromContext, + Flags: append(adminAccesskeyEditFlags, 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 mainAdminAccesskeyEdit(ctx *cli.Context) error { + return commonAccesskeyEdit(ctx) +} diff --git a/cmd/admin-accesskey-enable.go b/cmd/admin-accesskey-enable.go new file mode 100644 index 0000000000..6a1eb2b281 --- /dev/null +++ b/cmd/admin-accesskey-enable.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 adminAccesskeyEnableCmd = cli.Command{ + Name: "enable", + Usage: "enable an access key", + Action: mainAdminAccesskeyEnable, + Before: setGlobalsFromContext, + Flags: globalFlags, + OnUsageError: onUsageError, + CustomHelpTemplate: `NAME: + {{.HelpName}} - {{.Usage}} + +USAGE: + {{.HelpName}} [FLAGS] [TARGET] + +FLAGS: + {{range .VisibleFlags}}{{.}} + {{end}} +EXAMPLES: + 1. Enable access key + {{.Prompt}} {{.HelpName}} myminio myaccesskey +`, +} + +func mainAdminAccesskeyEnable(ctx *cli.Context) error { + return enableDisableAccesskey(ctx, true) +} diff --git a/cmd/admin-accesskey-info.go b/cmd/admin-accesskey-info.go new file mode 100644 index 0000000000..d0eb8d195c --- /dev/null +++ b/cmd/admin-accesskey-info.go @@ -0,0 +1,50 @@ +// Copyright (c) 2015-2023 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 adminAccesskeyInfoCmd = cli.Command{ + Name: "info", + Usage: "info about given access key pairs", + Action: mainAdminAccesskeyInfo, + Before: setGlobalsFromContext, + Flags: globalFlags, + OnUsageError: onUsageError, + CustomHelpTemplate: `NAME: + {{.HelpName}} - {{.Usage}} + +USAGE: + {{.HelpName}} [FLAGS] TARGET ACCESSKEY [ACCESSKEY...] + +FLAGS: + {{range .VisibleFlags}}{{.}} + {{end}} +EXAMPLES: + 1. Get info for the access key "testkey" + {{.Prompt}} {{.HelpName}} local/ testkey + 2. Get info for the access keys "testkey" and "testkey2" + {{.Prompt}} {{.HelpName}} local/ testkey testkey2 + `, +} + +func mainAdminAccesskeyInfo(ctx *cli.Context) error { + return commonAccesskeyInfo(ctx) +} diff --git a/cmd/admin-accesskey-list.go b/cmd/admin-accesskey-list.go new file mode 100644 index 0000000000..4bcc7efdcb --- /dev/null +++ b/cmd/admin-accesskey-list.go @@ -0,0 +1,168 @@ +// 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 ( + "strings" + + "github.com/charmbracelet/lipgloss" + humanize "github.com/dustin/go-humanize" + "github.com/minio/cli" + json "github.com/minio/colorjson" + "github.com/minio/madmin-go/v3" + "github.com/minio/mc/pkg/probe" +) + +var adminAccesskeyListFlags = []cli.Flag{ + cli.BoolFlag{ + Name: "users-only", + Usage: "only list user DNs", + }, + cli.BoolFlag{ + Name: "temp-only", + Usage: "only list temporary access keys", + }, + cli.BoolFlag{ + Name: "svcacc-only", + Usage: "only list service account access keys", + }, + cli.BoolFlag{ + Name: "self", + Usage: "list access keys for the authenticated user", + }, + cli.BoolFlag{ + Name: "all", + Usage: "list all access keys for all builtin users", + }, +} + +var adminAccesskeyListCmd = cli.Command{ + Name: "list", + ShortName: "ls", + Usage: "list access key pairs for builtin users", + Action: mainAdminAccesskeyList, + Before: setGlobalsFromContext, + Flags: append(adminAccesskeyListFlags, globalFlags...), + OnUsageError: onUsageError, + CustomHelpTemplate: `NAME: + {{.HelpName}} - {{.Usage}} + +USAGE: + {{.HelpName}} [FLAGS] TARGET [DN...] + +FLAGS: + {{range .VisibleFlags}}{{.}} + {{end}} +EXAMPLES: + 1. Get list of all builtin users and associated access keys in local server + {{.Prompt}} {{.HelpName}} local/ --all + + 2. Get list of access keys for the authenticated user in local server + {{.Prompt}} {{.HelpName}} local/ --self + + 3. Get list of builtin users in local server + {{.Prompt}} {{.HelpName}} local/ --all --users-only + + 4. Get list of all builtin users and associated temporary access keys in play server (if admin) + {{.Prompt}} {{.HelpName}} play/ --temp-only + + 5. Get list of access keys associated with user 'foobar' + {{.Prompt}} {{.HelpName}} play/ foobar + + 6. Get list of access keys associated with users 'foobar' and 'tester' + {{.Prompt}} {{.HelpName}} play/ foobar tester + + 7. Get all users and access keys if admin, else get authenticated user and associated access keys + {{.Prompt}} {{.HelpName}} local/ +`, +} + +type userAccesskeyList struct { + Status string `json:"status"` + User string `json:"user"` + STSKeys []madmin.ServiceAccountInfo `json:"stsKeys"` + ServiceAccounts []madmin.ServiceAccountInfo `json:"svcaccs"` + LDAP bool `json:"ldap,omitempty"` +} + +func (m userAccesskeyList) String() string { + labelStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#04B575")) + o := strings.Builder{} + + userStr := "User" + if m.LDAP { + userStr = "DN" + } + o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render(userStr+":"), m.User)) + if len(m.STSKeys) > 0 || len(m.ServiceAccounts) > 0 { + o.WriteString(iFmt(2, "%s\n", labelStyle.Render("Access Keys:"))) + } + for _, k := range m.STSKeys { + expiration := "never" + if nilExpiry(k.Expiration) != nil { + expiration = humanize.Time(*k.Expiration) + } + o.WriteString(iFmt(4, "%s, expires: %s, sts: true\n", k.AccessKey, expiration)) + } + for _, k := range m.ServiceAccounts { + expiration := "never" + if nilExpiry(k.Expiration) != nil { + expiration = humanize.Time(*k.Expiration) + } + o.WriteString(iFmt(4, "%s, expires: %s, sts: false\n", k.AccessKey, expiration)) + } + + return o.String() +} + +func (m userAccesskeyList) JSON() string { + jsonMessageBytes, e := json.MarshalIndent(m, "", " ") + fatalIf(probe.NewError(e), "Unable to marshal into JSON.") + + return string(jsonMessageBytes) +} + +func mainAdminAccesskeyList(ctx *cli.Context) error { + aliasedURL, tentativeAll, users, opts := commonAccesskeyList(ctx) + + // Create a new MinIO Admin Client + client, err := newAdminClient(aliasedURL) + fatalIf(err, "Unable to initialize admin connection.") + + accessKeysMap, e := client.ListAccessKeysBulk(globalContext, users, opts) + if e != nil { + if e.Error() == "Access Denied." && tentativeAll { + // retry with self + opts.All = false + accessKeysMap, e = client.ListAccessKeysBulk(globalContext, users, opts) + } + fatalIf(probe.NewError(e), "Unable to list access keys.") + } + + for user, accessKeys := range accessKeysMap { + m := userAccesskeyList{ + Status: "success", + User: user, + ServiceAccounts: accessKeys.ServiceAccounts, + STSKeys: accessKeys.STSKeys, + LDAP: false, + } + printMsg(m) + } + return nil +} diff --git a/cmd/admin-accesskey-remove.go b/cmd/admin-accesskey-remove.go new file mode 100644 index 0000000000..38ca1323bb --- /dev/null +++ b/cmd/admin-accesskey-remove.go @@ -0,0 +1,49 @@ +// 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 adminAccesskeyRemoveCmd = cli.Command{ + Name: "remove", + ShortName: "rm", + Usage: "delete access key pairs for builtin users", + Action: mainAdminAccesskeyRemove, + Before: setGlobalsFromContext, + Flags: globalFlags, + OnUsageError: onUsageError, + CustomHelpTemplate: `NAME: + {{.HelpName}} - {{.Usage}} + +USAGE: + {{.HelpName}} [FLAGS] TARGET ACCESSKEY + +FLAGS: + {{range .VisibleFlags}}{{.}} + {{end}} +EXAMPLES: + 1. Remove the access key "testkey" from local server + {{.Prompt}} {{.HelpName}} local/ testkey + `, +} + +func mainAdminAccesskeyRemove(ctx *cli.Context) error { + return commonAccesskeyRemove(ctx) +} diff --git a/cmd/admin-accesskey.go b/cmd/admin-accesskey.go new file mode 100644 index 0000000000..b5f0a0e189 --- /dev/null +++ b/cmd/admin-accesskey.go @@ -0,0 +1,45 @@ +// 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 adminAccesskeySubcommands = []cli.Command{ + adminAccesskeyListCmd, + adminAccesskeyRemoveCmd, + adminAccesskeyInfoCmd, + adminAccesskeyCreateCmd, + adminAccesskeyEditCmd, + adminAccesskeyEnableCmd, + adminAccesskeyDisableCmd, +} + +var adminAccesskeyCmd = cli.Command{ + Name: "accesskey", + Usage: "manage access keys defined in the MinIO server", + Action: mainAdminAccesskey, + Before: setGlobalsFromContext, + Flags: globalFlags, + Subcommands: adminAccesskeySubcommands, + HideHelpCommand: true, +} + +func mainAdminAccesskey(ctx *cli.Context) error { + commandNotFound(ctx, adminAccesskeySubcommands) + return nil +} diff --git a/cmd/admin-main.go b/cmd/admin-main.go index ecb98f6fca..af237b5374 100644 --- a/cmd/admin-main.go +++ b/cmd/admin-main.go @@ -56,6 +56,7 @@ var adminCmdSubcommands = []cli.Command{ adminClusterCmd, adminRebalanceCmd, adminLogsCmd, + adminAccesskeyCmd, } var adminCmd = cli.Command{ diff --git a/cmd/auto-complete.go b/cmd/auto-complete.go index 3d54a01f73..5c633fb975 100644 --- a/cmd/auto-complete.go +++ b/cmd/auto-complete.go @@ -392,6 +392,16 @@ var completeCmds = map[string]complete.Predictor{ "/idp/ldap/accesskey/enable": aliasCompleter, "/idp/ldap/accesskey/disable": aliasCompleter, + "/admin/accesskey/create": aliasCompleter, + "/admin/accesskey/list": aliasCompleter, + "/admin/accesskey/ls": aliasCompleter, + "/admin/accesskey/remove": aliasCompleter, + "/admin/accesskey/rm": aliasCompleter, + "/admin/accesskey/info": aliasCompleter, + "/admin/accesskey/edit": aliasCompleter, + "/admin/accesskey/enable": aliasCompleter, + "/admin/accesskey/disable": aliasCompleter, + "/admin/policy/info": aliasCompleter, "/admin/policy/update": aliasCompleter, "/admin/policy/add": aliasCompleter, diff --git a/cmd/idp-ldap-accesskey-create.go b/cmd/idp-ldap-accesskey-create.go index dcb690fe94..f86724de32 100644 --- a/cmd/idp-ldap-accesskey-create.go +++ b/cmd/idp-ldap-accesskey-create.go @@ -98,6 +98,10 @@ EXAMPLES: } func mainIDPLdapAccesskeyCreate(ctx *cli.Context) error { + return commonAccesskeyCreate(ctx, true) +} + +func commonAccesskeyCreate(ctx *cli.Context, ldap bool) error { if len(ctx.Args()) == 0 || len(ctx.Args()) > 2 { showCommandHelpAndExit(ctx, 1) // last argument is exit code } @@ -114,7 +118,13 @@ func mainIDPLdapAccesskeyCreate(ctx *cli.Context) error { client, err := newAdminClient(aliasedURL) fatalIf(err, "Unable to initialize admin connection.") - res, e := client.AddServiceAccountLDAP(globalContext, opts) + var res madmin.Credentials + var e error + if ldap { + res, e = client.AddServiceAccountLDAP(globalContext, opts) + } else { + res, e = client.AddServiceAccount(globalContext, opts) + } fatalIf(probe.NewError(e), "Unable to add service account.") m := ldapAccesskeyMessage{ diff --git a/cmd/idp-ldap-accesskey-edit.go b/cmd/idp-ldap-accesskey-edit.go index 2a8680a3e5..9b3fb3785b 100644 --- a/cmd/idp-ldap-accesskey-edit.go +++ b/cmd/idp-ldap-accesskey-edit.go @@ -82,6 +82,10 @@ EXAMPLES: } func mainIDPLdapAccesskeyEdit(ctx *cli.Context) error { + return commonAccesskeyEdit(ctx) +} + +func commonAccesskeyEdit(ctx *cli.Context) error { if len(ctx.Args()) == 0 || len(ctx.Args()) > 2 { showCommandHelpAndExit(ctx, 1) // last argument is exit code } @@ -115,6 +119,10 @@ func accessKeyEditOpts(ctx *cli.Context) madmin.UpdateServiceAccountReq { description := ctx.String("description") expDurVal := ctx.Duration("expiry-duration") + if name == "" && expVal == "" && expDurVal == 0 && policyPath == "" && secretKey == "" && description == "" { + fatalIf(probe.NewError(errors.New("At least one property must be edited")), "invalid flags") + } + if expVal != "" && expDurVal != 0 { fatalIf(probe.NewError(errors.New("Only one of --expiry or --expiry-duration can be specified")), "invalid flags") } diff --git a/cmd/idp-ldap-accesskey-info.go b/cmd/idp-ldap-accesskey-info.go index 67f42e873a..02f99721eb 100644 --- a/cmd/idp-ldap-accesskey-info.go +++ b/cmd/idp-ldap-accesskey-info.go @@ -120,6 +120,11 @@ func (m ldapAccesskeyMessage) JSON() string { } func mainIDPLdapAccesskeyInfo(ctx *cli.Context) error { + return commonAccesskeyInfo(ctx) +} + +// currently no difference between ldap and builtin accesskey info +func commonAccesskeyInfo(ctx *cli.Context) error { if len(ctx.Args()) < 2 { showCommandHelpAndExit(ctx, 1) // last argument is exit code } @@ -151,9 +156,8 @@ func mainIDPLdapAccesskeyInfo(ctx *cli.Context) error { Policy: json.RawMessage(tempRes.Policy), Name: tempRes.Name, Description: tempRes.Description, - Expiration: tempRes.Expiration, + Expiration: nilExpiry(tempRes.Expiration), } - printMsg(m) } } else { @@ -167,12 +171,18 @@ func mainIDPLdapAccesskeyInfo(ctx *cli.Context) error { Policy: json.RawMessage(res.Policy), Name: res.Name, Description: res.Description, - Expiration: res.Expiration, + Expiration: nilExpiry(res.Expiration), } - printMsg(m) } } return nil } + +func nilExpiry(expiry *time.Time) *time.Time { + if expiry != nil && expiry.Equal(timeSentinel) { + return nil + } + return expiry +} diff --git a/cmd/idp-ldap-accesskey-list.go b/cmd/idp-ldap-accesskey-list.go index 0a6224216d..aa6b20af21 100644 --- a/cmd/idp-ldap-accesskey-list.go +++ b/cmd/idp-ldap-accesskey-list.go @@ -19,12 +19,8 @@ package cmd import ( "errors" - "strings" - "github.com/charmbracelet/lipgloss" - "github.com/dustin/go-humanize" "github.com/minio/cli" - json "github.com/minio/colorjson" "github.com/minio/madmin-go/v3" "github.com/minio/mc/pkg/probe" ) @@ -70,10 +66,10 @@ FLAGS: {{range .VisibleFlags}}{{.}} {{end}} EXAMPLES: - 1. Get list of all users and associated access keys in local server (if admin) + 1. Get list of all LDAP users and associated access keys in local server (if admin) {{.Prompt}} {{.HelpName}} local/ - 2. Get list of users in local server (if admin) + 2. Get list of LDAP users in local server (if admin) {{.Prompt}} {{.HelpName}} local/ --users-only 3. Get list of all users and associated temporary access keys in play server (if admin) @@ -93,47 +89,37 @@ EXAMPLES: `, } -type ldapUsersList struct { - Status string `json:"status"` - DN string `json:"dn"` - STSKeys []madmin.ServiceAccountInfo `json:"stsKeys"` - ServiceAccounts []madmin.ServiceAccountInfo `json:"svcaccs"` -} +func mainIDPLdapAccesskeyList(ctx *cli.Context) error { + aliasedURL, tentativeAll, users, opts := commonAccesskeyList(ctx) -func (m ldapUsersList) String() string { - labelStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#04B575")) - o := strings.Builder{} + // Create a new MinIO Admin Client + client, err := newAdminClient(aliasedURL) + fatalIf(err, "Unable to initialize admin connection.") - o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("DN:"), m.DN)) - if len(m.STSKeys) > 0 || len(m.ServiceAccounts) > 0 { - o.WriteString(iFmt(2, "%s\n", labelStyle.Render("Access Keys:"))) - } - for _, k := range m.STSKeys { - expiration := "never" - if k.Expiration != nil { - expiration = humanize.Time(*k.Expiration) + accessKeysMap, e := client.ListAccessKeysLDAPBulkWithOpts(globalContext, users, opts) + if e != nil { + if e.Error() == "Access Denied." && tentativeAll { + // retry with self + opts.All = false + accessKeysMap, e = client.ListAccessKeysLDAPBulkWithOpts(globalContext, users, opts) } - o.WriteString(iFmt(4, "%s, expires: %s, sts: true\n", k.AccessKey, expiration)) + fatalIf(probe.NewError(e), "Unable to list access keys.") } - for _, k := range m.ServiceAccounts { - expiration := "never" - if k.Expiration != nil { - expiration = humanize.Time(*k.Expiration) + + for dn, accessKeys := range accessKeysMap { + m := userAccesskeyList{ + Status: "success", + User: dn, + ServiceAccounts: accessKeys.ServiceAccounts, + STSKeys: accessKeys.STSKeys, + LDAP: true, } - o.WriteString(iFmt(4, "%s, expires: %s, sts: false\n", k.AccessKey, expiration)) + printMsg(m) } - - return o.String() -} - -func (m ldapUsersList) JSON() string { - jsonMessageBytes, e := json.MarshalIndent(m, "", " ") - fatalIf(probe.NewError(e), "Unable to marshal into JSON.") - - return string(jsonMessageBytes) + return nil } -func mainIDPLdapAccesskeyList(ctx *cli.Context) error { +func commonAccesskeyList(ctx *cli.Context) (aliasedURL string, tentativeAll bool, users []string, opts madmin.ListAccessKeysOpts) { if len(ctx.Args()) == 0 { showCommandHelpAndExit(ctx, 1) // last argument is exit code } @@ -142,18 +128,18 @@ func mainIDPLdapAccesskeyList(ctx *cli.Context) error { stsOnly := ctx.Bool("temp-only") svcaccOnly := ctx.Bool("svcacc-only") selfFlag := ctx.Bool("self") - allFlag := ctx.Bool("all") + opts.All = ctx.Bool("all") args := ctx.Args() - aliasedURL := args.Get(0) - users := args.Tail() + aliasedURL = args.Get(0) + users = args.Tail() var e error if (usersOnly && svcaccOnly) || (usersOnly && stsOnly) || (svcaccOnly && stsOnly) { e = errors.New("only one of --users-only, --temp-only, or --permanent-only can be specified") - } else if selfFlag && allFlag { + } else if selfFlag && opts.All { e = errors.New("only one of --self or --all can be specified") - } else if (selfFlag || allFlag) && len(users) > 0 { + } else if (selfFlag || opts.All) && len(users) > 0 { e = errors.New("user DNs cannot be specified with --self or --all") } fatalIf(probe.NewError(e), "Invalid flags.") @@ -161,45 +147,21 @@ func mainIDPLdapAccesskeyList(ctx *cli.Context) error { // If no users/self/all flags are specified, tentatively assume --all // If access is denied on tentativeAll, retry with self // This is to maintain compatibility with the previous behavior - tentativeAll := false - if !selfFlag && !allFlag && len(users) == 0 { + if !selfFlag && !opts.All && len(users) == 0 { tentativeAll = true - allFlag = true + opts.All = true } - var listType string switch { case usersOnly: - listType = madmin.AccessKeyListUsersOnly + opts.ListType = madmin.AccessKeyListUsersOnly case stsOnly: - listType = madmin.AccessKeyListSTSOnly + opts.ListType = madmin.AccessKeyListSTSOnly case svcaccOnly: - listType = madmin.AccessKeyListSvcaccOnly + opts.ListType = madmin.AccessKeyListSvcaccOnly default: - listType = madmin.AccessKeyListAll - } - - // Create a new MinIO Admin Client - client, err := newAdminClient(aliasedURL) - fatalIf(err, "Unable to initialize admin connection.") - - accessKeysMap, e := client.ListAccessKeysLDAPBulk(globalContext, users, listType, allFlag) - if e != nil { - if e.Error() == "Access Denied." && tentativeAll { - // retry with self - accessKeysMap, e = client.ListAccessKeysLDAPBulk(globalContext, users, listType, false) - } - fatalIf(probe.NewError(e), "Unable to list access keys.") + opts.ListType = madmin.AccessKeyListAll } - for dn, accessKeys := range accessKeysMap { - m := ldapUsersList{ - Status: "success", - DN: dn, - ServiceAccounts: accessKeys.ServiceAccounts, - STSKeys: accessKeys.STSKeys, - } - printMsg(m) - } - return nil + return aliasedURL, tentativeAll, users, opts } diff --git a/cmd/idp-ldap-accesskey-remove.go b/cmd/idp-ldap-accesskey-remove.go index 8f4d57b667..73f39e2310 100644 --- a/cmd/idp-ldap-accesskey-remove.go +++ b/cmd/idp-ldap-accesskey-remove.go @@ -46,6 +46,11 @@ EXAMPLES: } func mainIDPLdapAccesskeyRemove(ctx *cli.Context) error { + return commonAccesskeyRemove(ctx) +} + +// No difference between ldap and builtin accesskey remove for now +func commonAccesskeyRemove(ctx *cli.Context) error { if len(ctx.Args()) != 2 { showCommandHelpAndExit(ctx, 1) // last argument is exit code }