github.com/minio/mc@v0.0.0-20240503112107-b471de8d1882/cmd/admin-user-add.go (about)

     1  // Copyright (c) 2015-2022 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  	"bufio"
    22  	"fmt"
    23  	"os"
    24  	"strings"
    25  
    26  	"github.com/fatih/color"
    27  	"github.com/minio/cli"
    28  	json "github.com/minio/colorjson"
    29  	"github.com/minio/mc/pkg/probe"
    30  	"github.com/minio/pkg/v2/console"
    31  	"golang.org/x/term"
    32  )
    33  
    34  var adminUserAddCmd = cli.Command{
    35  	Name:         "add",
    36  	Usage:        "add a new user",
    37  	Action:       mainAdminUserAdd,
    38  	OnUsageError: onUsageError,
    39  	Before:       setGlobalsFromContext,
    40  	Flags:        globalFlags,
    41  	CustomHelpTemplate: `NAME:
    42    {{.HelpName}} - {{.Usage}}
    43  
    44  USAGE:
    45    {{.HelpName}} TARGET ACCESSKEY SECRETKEY
    46  
    47  ACCESSKEY:
    48    Also called as username.
    49  
    50  SECRETKEY:
    51    Also called as password.
    52  
    53  FLAGS:
    54    {{range .VisibleFlags}}{{.}}
    55    {{end}}
    56  EXAMPLES:
    57    1. Add a new user 'foobar' to MinIO server.
    58       {{.DisableHistory}}
    59       {{.Prompt}} {{.HelpName}} myminio foobar foo12345
    60       {{.EnableHistory}}
    61  
    62    2. Add a new user 'foobar' to MinIO server, prompting for keys.
    63       {{.Prompt}} {{.HelpName}} myminio
    64       Enter Access Key: foobar
    65       Enter Secret Key: foobar12345
    66  
    67    3. Add a new user 'foobar' to MinIO server using piped keys.
    68       {{.DisableHistory}}
    69       {{.Prompt}} echo -e "foobar\nfoobar12345" | {{.HelpName}} myminio
    70       {{.EnableHistory}}
    71  
    72    4. Add a new user 'foobar' to MinIO server, then attach IAM policy "writeonly".
    73       {{.Prompt}} {{.HelpName}} myminio foobar foo12345 
    74       {{.Prompt}} mc admin policy attach myminio writeonly --user foobar
    75  `,
    76  }
    77  
    78  // checkAdminUserAddSyntax - validate all the passed arguments
    79  func checkAdminUserAddSyntax(ctx *cli.Context) {
    80  	argsNr := len(ctx.Args())
    81  	if argsNr > 3 || argsNr < 1 {
    82  		showCommandHelpAndExit(ctx, 1)
    83  	}
    84  }
    85  
    86  // userGroup container for content message structure
    87  type userGroup struct {
    88  	Name     string   `json:"name,omitempty"`
    89  	Policies []string `json:"policies,omitempty"`
    90  }
    91  
    92  // userMessage container for content message structure
    93  type userMessage struct {
    94  	op             string
    95  	Status         string      `json:"status"` // TODO: remove this?
    96  	AccessKey      string      `json:"accessKey,omitempty"`
    97  	SecretKey      string      `json:"secretKey,omitempty"`
    98  	PolicyName     string      `json:"policyName,omitempty"`
    99  	UserStatus     string      `json:"userStatus,omitempty"`
   100  	MemberOf       []userGroup `json:"memberOf,omitempty"`
   101  	Authentication string      `json:"authentication,omitempty"`
   102  }
   103  
   104  func (u userMessage) String() string {
   105  	switch u.op {
   106  	case "list":
   107  		userFieldMaxLen := 9
   108  		accessFieldMaxLen := 20
   109  		policyFieldMaxLen := 20
   110  
   111  		// Create a new pretty table with cols configuration
   112  		return newPrettyTable("  ",
   113  			Field{"UserStatus", userFieldMaxLen},
   114  			Field{"AccessKey", accessFieldMaxLen},
   115  			Field{"PolicyName", policyFieldMaxLen},
   116  		).buildRow(u.UserStatus, u.AccessKey, u.PolicyName)
   117  	case "info":
   118  		memberOf := []string{}
   119  		for _, group := range u.MemberOf {
   120  			memberOf = append(memberOf, group.Name)
   121  		}
   122  		lines := []string{
   123  			fmt.Sprintf("AccessKey: %s", u.AccessKey),
   124  			fmt.Sprintf("Status: %s", u.UserStatus),
   125  			fmt.Sprintf("PolicyName: %s", u.PolicyName),
   126  			fmt.Sprintf("MemberOf: %s", memberOf),
   127  		}
   128  		if u.Authentication != "" {
   129  			lines = append(lines, fmt.Sprintf("Authentication: %s", u.Authentication))
   130  		}
   131  		return console.Colorize("UserMessage", strings.Join(lines, "\n"))
   132  	case "remove":
   133  		return console.Colorize("UserMessage", "Removed user `"+u.AccessKey+"` successfully.")
   134  	case "disable":
   135  		return console.Colorize("UserMessage", "Disabled user `"+u.AccessKey+"` successfully.")
   136  	case "enable":
   137  		return console.Colorize("UserMessage", "Enabled user `"+u.AccessKey+"` successfully.")
   138  	case "add":
   139  		return console.Colorize("UserMessage", "Added user `"+u.AccessKey+"` successfully.")
   140  	}
   141  	return ""
   142  }
   143  
   144  func (u userMessage) JSON() string {
   145  	u.Status = "success"
   146  	jsonMessageBytes, e := json.MarshalIndent(u, "", " ")
   147  	fatalIf(probe.NewError(e), "Unable to marshal into JSON.")
   148  
   149  	return string(jsonMessageBytes)
   150  }
   151  
   152  // fetchUserKeys - returns the access and secret key
   153  func fetchUserKeys(args cli.Args) (string, string) {
   154  	accessKey := ""
   155  	secretKey := ""
   156  	console.SetColor(cred, color.New(color.FgYellow, color.Italic))
   157  	isTerminal := term.IsTerminal(int(os.Stdin.Fd()))
   158  	reader := bufio.NewReader(os.Stdin)
   159  
   160  	argCount := len(args)
   161  
   162  	if argCount == 1 {
   163  		if isTerminal {
   164  			fmt.Printf("%s", console.Colorize(cred, "Enter Access Key: "))
   165  		}
   166  		value, _, _ := reader.ReadLine()
   167  		accessKey = string(value)
   168  	} else {
   169  		accessKey = args.Get(1)
   170  	}
   171  
   172  	if argCount == 1 || argCount == 2 {
   173  		if isTerminal {
   174  			fmt.Printf("%s", console.Colorize(cred, "Enter Secret Key: "))
   175  			bytePassword, _ := term.ReadPassword(int(os.Stdin.Fd()))
   176  			fmt.Printf("\n")
   177  			secretKey = string(bytePassword)
   178  		} else {
   179  			value, _, _ := reader.ReadLine()
   180  			secretKey = string(value)
   181  		}
   182  	} else {
   183  		secretKey = args.Get(2)
   184  	}
   185  
   186  	return accessKey, secretKey
   187  }
   188  
   189  // mainAdminUserAdd is the handle for "mc admin user add" command.
   190  func mainAdminUserAdd(ctx *cli.Context) error {
   191  	checkAdminUserAddSyntax(ctx)
   192  
   193  	console.SetColor("UserMessage", color.New(color.FgGreen))
   194  
   195  	// Get the alias parameter from cli
   196  	args := ctx.Args()
   197  	aliasedURL := args.Get(0)
   198  	accessKey, secretKey := fetchUserKeys(args)
   199  
   200  	// Create a new MinIO Admin Client
   201  	client, err := newAdminClient(aliasedURL)
   202  	fatalIf(err, "Unable to initialize admin connection.")
   203  
   204  	fatalIf(probe.NewError(client.AddUser(globalContext, accessKey, secretKey)).Trace(args...), "Unable to add new user")
   205  
   206  	printMsg(userMessage{
   207  		op:         ctx.Command.Name,
   208  		AccessKey:  accessKey,
   209  		SecretKey:  secretKey,
   210  		UserStatus: "enabled",
   211  	})
   212  
   213  	return nil
   214  }