Skip to content

Commit

Permalink
Move mc idp ldap accesskey create --login to `mc idp ldap accesskey…
Browse files Browse the repository at this point in the history
… create-with-login` (#4894)
  • Loading branch information
taran-p authored Apr 12, 2024
1 parent 76c3674 commit e701bce
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 107 deletions.
13 changes: 7 additions & 6 deletions cmd/auto-complete.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,12 +381,13 @@ var completeCmds = map[string]complete.Predictor{
"/idp/ldap/policy/attach": aliasCompleter,
"/idp/ldap/policy/detach": aliasCompleter,

"/idp/ldap/accesskey/create": aliasCompleter,
"/idp/ldap/accesskey/list": aliasCompleter,
"/idp/ldap/accesskey/ls": aliasCompleter,
"/idp/ldap/accesskey/remove": aliasCompleter,
"/idp/ldap/accesskey/rm": aliasCompleter,
"/idp/ldap/accesskey/info": aliasCompleter,
"/idp/ldap/accesskey/create": aliasCompleter,
"/idp/ldap/accesskey/create-with-login": aliasCompleter,
"/idp/ldap/accesskey/list": aliasCompleter,
"/idp/ldap/accesskey/ls": aliasCompleter,
"/idp/ldap/accesskey/remove": aliasCompleter,
"/idp/ldap/accesskey/rm": aliasCompleter,
"/idp/ldap/accesskey/info": aliasCompleter,

"/admin/policy/info": aliasCompleter,
"/admin/policy/update": aliasCompleter,
Expand Down
122 changes: 122 additions & 0 deletions cmd/idp-ldap-accesskey-create-with-login.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// 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 <http://www.gnu.org/licenses/>.

package cmd

import (
"bufio"
"fmt"
"net/url"
"os"

"github.com/fatih/color"
"github.com/minio/cli"
"github.com/minio/madmin-go/v3"
"github.com/minio/mc/pkg/probe"
"github.com/minio/minio-go/v7/pkg/credentials"
"github.com/minio/pkg/v2/console"
"golang.org/x/term"
)

var idpLdapAccesskeyCreateWithLoginCmd = cli.Command{
Name: "create-with-login",
Usage: "log in using LDAP credentials to generate access key pair",
Action: mainIDPLdapAccesskeyCreateWithLogin,
Before: setGlobalsFromContext,
Flags: append(idpLdapAccesskeyCreateFlags, globalFlags...),
OnUsageError: onUsageError,
CustomHelpTemplate: `NAME:
{{.HelpName}} - {{.Usage}}
USAGE:
{{.HelpName}} [FLAGS] URL
FLAGS:
{{range .VisibleFlags}}{{.}}
{{end}}
EXAMPLES:
1. Create a new access key pair for https://minio.example.com by logging in with LDAP credentials
{{.Prompt}} {{.HelpName}} https://minio.example.com
2. Create a new access key pair for http://localhost:9000 via login with custom access key and secret key
{{.Prompt}} {{.HelpName}} http://localhost:9000 --access-key myaccesskey --secret-key mysecretkey
`,
}

func mainIDPLdapAccesskeyCreateWithLogin(ctx *cli.Context) error {
if len(ctx.Args()) != 1 {
showCommandHelpAndExit(ctx, 1) // last argument is exit code
}

args := ctx.Args()
url := args.Get(0)

opts := accessKeyCreateOpts(ctx, "")

isTerminal := term.IsTerminal(int(os.Stdin.Fd()))
if !isTerminal {
e := fmt.Errorf("login flag cannot be used with non-interactive terminal")
fatalIf(probe.NewError(e), "Invalid flags.")
}

client := loginLDAPAccesskey(url)

res, e := client.AddServiceAccountLDAP(globalContext, opts)
fatalIf(probe.NewError(e), "Unable to add service account.")

m := ldapAccesskeyMessage{
op: "create",
Status: "success",
AccessKey: res.AccessKey,
SecretKey: res.SecretKey,
Expiration: &res.Expiration,
Name: opts.Name,
Description: opts.Description,
}
printMsg(m)

return nil
}

func loginLDAPAccesskey(URL string) *madmin.AdminClient {
console.SetColor(cred, color.New(color.FgYellow, color.Italic))
reader := bufio.NewReader(os.Stdin)

fmt.Printf("%s", console.Colorize(cred, "Enter LDAP Username: "))
value, _, e := reader.ReadLine()
fatalIf(probe.NewError(e), "Unable to read username")
username := string(value)

fmt.Printf("%s", console.Colorize(cred, "Enter Password: "))
bytePassword, e := term.ReadPassword(int(os.Stdin.Fd()))
fatalIf(probe.NewError(e), "Unable to read password")
fmt.Printf("\n")
password := string(bytePassword)

ldapID, e := credentials.NewLDAPIdentity(URL, username, password)
fatalIf(probe.NewError(e), "Unable to initialize LDAP identity.")

u, e := url.Parse(URL)
fatalIf(probe.NewError(e), "Unable to parse server URL.")

client, e := madmin.NewWithOptions(u.Host, &madmin.Options{
Creds: ldapID,
Secure: u.Scheme == "https",
})
fatalIf(probe.NewError(e), "Unable to initialize admin connection.")

return client
}
129 changes: 41 additions & 88 deletions cmd/idp-ldap-accesskey-create.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,15 @@
package cmd

import (
"bufio"
"bytes"
"fmt"
"net/url"
"os"
"time"

"github.com/fatih/color"
"github.com/minio/cli"
"github.com/minio/madmin-go/v3"
"github.com/minio/mc/pkg/probe"
"github.com/minio/minio-go/v7/pkg/credentials"
"github.com/minio/pkg/v2/console"
"github.com/minio/pkg/v2/policy"
"golang.org/x/term"
)

var idpLdapAccesskeyCreateFlags = []cli.Flag{
Expand Down Expand Up @@ -65,8 +59,9 @@ var idpLdapAccesskeyCreateFlags = []cli.Flag{
Usage: "expiry date for the access key",
},
cli.BoolFlag{
Name: "login",
Usage: "log in using ldap credentials to generate access key par for future use",
Name: "login",
Usage: "log in using ldap credentials to generate access key pair for future use",
Hidden: true,
},
}

Expand All @@ -90,13 +85,11 @@ EXAMPLES:
1. Create a new access key pair with the same policy as the authenticated user
{{.Prompt}} {{.HelpName}} local/
2. Create a new access key pair with custom access key and secret key
{{.Prompt}} {{.HelpName}} local/ --access-key myaccesskey --secret-key mysecretkey
{{.Prompt}} {{.HelpName}} local/ --access-key myaccesskey --secret-key mysecretkey
4. Create a new access key pair for user with username "james" that expires in 1 day
{{.Prompt}} {{.HelpName}} james --expiry-duration 24h
{{.Prompt}} {{.HelpName}} local/ james --expiry-duration 24h
5. Create a new access key pair for authenticated user that expires on 2021-01-01
{{.Prompt}} {{.HelpName}} --expiry 2021-01-01
6. Create a new access key pair for minio.example.com by logging in with LDAP credentials
{{.Prompt}} {{.HelpName}} --login minio.example.com
{{.Prompt}} {{.HelpName}} --expiry 2021-01-01
`,
}

Expand All @@ -109,7 +102,32 @@ func mainIDPLdapAccesskeyCreate(ctx *cli.Context) error {
aliasedURL := args.Get(0)
targetUser := args.Get(1)

login := ctx.Bool("login")
if ctx.Bool("login") {
deprecatedError("mc idp ldap accesskey create-with-login")
}

opts := accessKeyCreateOpts(ctx, targetUser)
client, err := newAdminClient(aliasedURL)
fatalIf(err, "Unable to initialize admin connection.")

res, e := client.AddServiceAccountLDAP(globalContext, opts)
fatalIf(probe.NewError(e), "Unable to add service account.")

m := ldapAccesskeyMessage{
op: "create",
Status: "success",
AccessKey: res.AccessKey,
SecretKey: res.SecretKey,
Expiration: &res.Expiration,
Name: opts.Name,
Description: opts.Description,
}
printMsg(m)

return nil
}

func accessKeyCreateOpts(ctx *cli.Context, targetUser string) madmin.AddServiceAccountReq {
accessVal := ctx.String("access-key")
secretVal := ctx.String("secret-key")
name := ctx.String("name")
Expand All @@ -118,6 +136,7 @@ func mainIDPLdapAccesskeyCreate(ctx *cli.Context) error {

expDurVal := ctx.Duration("expiry-duration")
expVal := ctx.String("expiry")

if expVal != "" && expDurVal != 0 {
e := fmt.Errorf("Only one of --expiry or --expiry-duration can be specified")
fatalIf(probe.NewError(e), "Invalid flags.")
Expand Down Expand Up @@ -163,28 +182,6 @@ func mainIDPLdapAccesskeyCreate(ctx *cli.Context) error {
}
}

var client *madmin.AdminClient

// If login flag is set, use LDAP credentials to generate access key pair
if login {
if targetUser != "" {
fatalIf(errInvalidArgument().Trace(targetUser), "login flag cannot be used with a target user")
}
isTerminal := term.IsTerminal(int(os.Stdin.Fd()))
if !isTerminal {
e := fmt.Errorf("login flag cannot be used with non-interactive terminal")
fatalIf(probe.NewError(e), "Invalid flags.")
}

// For login, aliasedURL is not aliased, but the actual server URL
client = loginLDAPAccesskey(aliasedURL)
} else {
var err *probe.Error
// If login flag is not set, continue normally
client, err = newAdminClient(aliasedURL)
fatalIf(err, "Unable to initialize admin connection.")
}

accessKey, secretKey, e := generateCredentials()
fatalIf(probe.NewError(e), "Unable to generate credentials.")

Expand All @@ -195,57 +192,13 @@ func mainIDPLdapAccesskeyCreate(ctx *cli.Context) error {
if secretVal != "" {
secretKey = secretVal
}

res, e := client.AddServiceAccountLDAP(globalContext,
madmin.AddServiceAccountReq{
Policy: policyBytes,
TargetUser: targetUser,
AccessKey: accessKey,
SecretKey: secretKey,
Name: name,
Description: description,
Expiration: &exp,
})
fatalIf(probe.NewError(e), "Unable to add service account.")

m := ldapAccesskeyMessage{
op: "create",
Status: "success",
AccessKey: res.AccessKey,
SecretKey: res.SecretKey,
Expiration: &res.Expiration,
return madmin.AddServiceAccountReq{
Policy: policyBytes,
TargetUser: targetUser,
AccessKey: accessKey,
SecretKey: secretKey,
Name: name,
Description: description,
Expiration: &exp,
}
printMsg(m)

return nil
}

func loginLDAPAccesskey(URL string) *madmin.AdminClient {
console.SetColor(cred, color.New(color.FgYellow, color.Italic))
reader := bufio.NewReader(os.Stdin)

fmt.Printf("%s", console.Colorize(cred, "Enter LDAP Username: "))
value, _, e := reader.ReadLine()
fatalIf(probe.NewError(e), "Unable to read username")
username := string(value)

fmt.Printf("%s", console.Colorize(cred, "Enter Password: "))
bytePassword, e := term.ReadPassword(int(os.Stdin.Fd()))
fatalIf(probe.NewError(e), "Unable to read password")
fmt.Printf("\n")
password := string(bytePassword)

ldapID, e := credentials.NewLDAPIdentity(URL, username, password)
fatalIf(probe.NewError(e), "Unable to initialize LDAP identity.")

u, e := url.Parse(URL)
fatalIf(probe.NewError(e), "Unable to parse server URL.")

client, e := madmin.NewWithOptions(u.Host, &madmin.Options{
Creds: ldapID,
Secure: u.Scheme == "https",
})
fatalIf(probe.NewError(e), "Unable to initialize admin connection.")

return client
}
18 changes: 5 additions & 13 deletions cmd/idp-ldap-accesskey-info.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,8 @@ func (m ldapAccesskeyMessage) String() string {
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("Policy:"), policyStr))
if m.Name != "" {
o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Name:"), m.Name))
}
if m.Description != "" {
o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Description:"), m.Description))
}
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()
Expand All @@ -106,13 +102,9 @@ func (m ldapAccesskeyMessage) String() string {

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\n", labelStyle.Render("Expiration:"), expirationStr))
if m.Name != "" {
o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Name:"), m.Name))
}
if m.Description != "" {
o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Description:"), m.Description))
}
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()
case "remove":
Expand Down
1 change: 1 addition & 0 deletions cmd/idp-ldap-accesskey.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var idpLdapAccesskeySubcommands = []cli.Command{
idpLdapAccesskeyRemoveCmd,
idpLdapAccesskeyInfoCmd,
idpLdapAccesskeyCreateCmd,
idpLdapAccesskeyCreateWithLoginCmd,
}

var idpLdapAccesskeyCmd = cli.Command{
Expand Down

0 comments on commit e701bce

Please sign in to comment.