github.com/minio/mc@v0.0.0-20240503112107-b471de8d1882/cmd/idp-ldap-accesskey-create.go (about)

     1  // Copyright (c) 2015-2023 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package cmd
    19  
    20  import (
    21  	"bytes"
    22  	"fmt"
    23  	"os"
    24  	"time"
    25  
    26  	"github.com/minio/cli"
    27  	"github.com/minio/madmin-go/v3"
    28  	"github.com/minio/mc/pkg/probe"
    29  	"github.com/minio/pkg/v2/policy"
    30  )
    31  
    32  var idpLdapAccesskeyCreateFlags = []cli.Flag{
    33  	cli.StringFlag{
    34  		Name:  "access-key",
    35  		Usage: "set an access key for the account",
    36  	},
    37  	cli.StringFlag{
    38  		Name:  "secret-key",
    39  		Usage: "set a secret key for the  account",
    40  	},
    41  	cli.StringFlag{
    42  		Name:  "policy",
    43  		Usage: "path to a JSON policy file",
    44  	},
    45  	cli.StringFlag{
    46  		Name:  "name",
    47  		Usage: "friendly name for the account",
    48  	},
    49  	cli.StringFlag{
    50  		Name:  "description",
    51  		Usage: "description for the account",
    52  	},
    53  	cli.StringFlag{
    54  		Name:  "expiry-duration",
    55  		Usage: "duration before the access key expires",
    56  	},
    57  	cli.StringFlag{
    58  		Name:  "expiry",
    59  		Usage: "expiry date for the access key",
    60  	},
    61  	cli.BoolFlag{
    62  		Name:   "login",
    63  		Usage:  "log in using ldap credentials to generate access key pair for future use",
    64  		Hidden: true,
    65  	},
    66  }
    67  
    68  var idpLdapAccesskeyCreateCmd = cli.Command{
    69  	Name:         "create",
    70  	Usage:        "create access key pairs for LDAP",
    71  	Action:       mainIDPLdapAccesskeyCreate,
    72  	Before:       setGlobalsFromContext,
    73  	Flags:        append(idpLdapAccesskeyCreateFlags, globalFlags...),
    74  	OnUsageError: onUsageError,
    75  	CustomHelpTemplate: `NAME:
    76    {{.HelpName}} - {{.Usage}}
    77  
    78  USAGE:
    79    {{.HelpName}} [FLAGS] [TARGET]
    80  
    81  FLAGS:
    82    {{range .VisibleFlags}}{{.}}
    83    {{end}}
    84  EXAMPLES:
    85    1. Create a new access key pair with the same policy as the authenticated user
    86       {{.Prompt}} {{.HelpName}} local/
    87    2. Create a new access key pair with custom access key and secret key
    88       {{.Prompt}} {{.HelpName}} local/ --access-key myaccesskey --secret-key mysecretkey
    89    4. Create a new access key pair for user with username "james" that expires in 1 day
    90       {{.Prompt}} {{.HelpName}} local/ james --expiry-duration 24h
    91    5. Create a new access key pair for authenticated user that expires on 2021-01-01
    92       {{.Prompt}} {{.HelpName}} --expiry 2021-01-01
    93  	`,
    94  }
    95  
    96  func mainIDPLdapAccesskeyCreate(ctx *cli.Context) error {
    97  	if len(ctx.Args()) == 0 || len(ctx.Args()) > 2 {
    98  		showCommandHelpAndExit(ctx, 1) // last argument is exit code
    99  	}
   100  
   101  	args := ctx.Args()
   102  	aliasedURL := args.Get(0)
   103  	targetUser := args.Get(1)
   104  
   105  	if ctx.Bool("login") {
   106  		deprecatedError("mc idp ldap accesskey create-with-login")
   107  	}
   108  
   109  	opts := accessKeyCreateOpts(ctx, targetUser)
   110  	client, err := newAdminClient(aliasedURL)
   111  	fatalIf(err, "Unable to initialize admin connection.")
   112  
   113  	res, e := client.AddServiceAccountLDAP(globalContext, opts)
   114  	fatalIf(probe.NewError(e), "Unable to add service account.")
   115  
   116  	m := ldapAccesskeyMessage{
   117  		op:          "create",
   118  		Status:      "success",
   119  		AccessKey:   res.AccessKey,
   120  		SecretKey:   res.SecretKey,
   121  		Expiration:  &res.Expiration,
   122  		Name:        opts.Name,
   123  		Description: opts.Description,
   124  	}
   125  	printMsg(m)
   126  
   127  	return nil
   128  }
   129  
   130  func accessKeyCreateOpts(ctx *cli.Context, targetUser string) madmin.AddServiceAccountReq {
   131  	accessVal := ctx.String("access-key")
   132  	secretVal := ctx.String("secret-key")
   133  	name := ctx.String("name")
   134  	description := ctx.String("description")
   135  	policyPath := ctx.String("policy")
   136  
   137  	expDurVal := ctx.Duration("expiry-duration")
   138  	expVal := ctx.String("expiry")
   139  
   140  	if expVal != "" && expDurVal != 0 {
   141  		e := fmt.Errorf("Only one of --expiry or --expiry-duration can be specified")
   142  		fatalIf(probe.NewError(e), "Invalid flags.")
   143  	}
   144  
   145  	var exp time.Time
   146  	if expVal != "" {
   147  		location, e := time.LoadLocation("Local")
   148  		if e != nil {
   149  			fatalIf(probe.NewError(e), "Unable to parse the expiry argument.")
   150  		}
   151  
   152  		patternMatched := false
   153  		for _, format := range supportedTimeFormats {
   154  			t, e := time.ParseInLocation(format, expVal, location)
   155  			if e == nil {
   156  				patternMatched = true
   157  				exp = t
   158  				break
   159  			}
   160  		}
   161  
   162  		if !patternMatched {
   163  			e := fmt.Errorf("invalid expiry date format '%s'", expVal)
   164  			fatalIf(probe.NewError(e), "unable to parse the expiry argument.")
   165  		}
   166  	} else if expDurVal != 0 {
   167  		exp = time.Now().Add(expDurVal)
   168  	} else {
   169  		exp = time.Unix(0, 0)
   170  	}
   171  
   172  	var policyBytes []byte
   173  	if policyPath != "" {
   174  		// Validate the policy document and ensure it has at least when statement
   175  		var e error
   176  		policyBytes, e = os.ReadFile(policyPath)
   177  		fatalIf(probe.NewError(e), "Unable to open the policy document.")
   178  		p, e := policy.ParseConfig(bytes.NewReader(policyBytes))
   179  		fatalIf(probe.NewError(e), "Unable to parse the policy document.")
   180  		if p.IsEmpty() {
   181  			fatalIf(errInvalidArgument(), "Empty policy documents are not allowed.")
   182  		}
   183  	}
   184  
   185  	accessKey, secretKey, e := generateCredentials()
   186  	fatalIf(probe.NewError(e), "Unable to generate credentials.")
   187  
   188  	// If access key and secret key are provided, use them instead
   189  	if accessVal != "" {
   190  		accessKey = accessVal
   191  	}
   192  	if secretVal != "" {
   193  		secretKey = secretVal
   194  	}
   195  	return madmin.AddServiceAccountReq{
   196  		Policy:      policyBytes,
   197  		TargetUser:  targetUser,
   198  		AccessKey:   accessKey,
   199  		SecretKey:   secretKey,
   200  		Name:        name,
   201  		Description: description,
   202  		Expiration:  &exp,
   203  	}
   204  }