go.etcd.io/etcd@v3.3.27+incompatible/etcdctl/ctlv3/command/user_command.go (about)

     1  // Copyright 2016 The etcd Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package command
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"strings"
    21  
    22  	"github.com/bgentry/speakeasy"
    23  	"github.com/spf13/cobra"
    24  )
    25  
    26  var (
    27  	userShowDetail bool
    28  )
    29  
    30  // NewUserCommand returns the cobra command for "user".
    31  func NewUserCommand() *cobra.Command {
    32  	ac := &cobra.Command{
    33  		Use:   "user <subcommand>",
    34  		Short: "User related commands",
    35  	}
    36  
    37  	ac.AddCommand(newUserAddCommand())
    38  	ac.AddCommand(newUserDeleteCommand())
    39  	ac.AddCommand(newUserGetCommand())
    40  	ac.AddCommand(newUserListCommand())
    41  	ac.AddCommand(newUserChangePasswordCommand())
    42  	ac.AddCommand(newUserGrantRoleCommand())
    43  	ac.AddCommand(newUserRevokeRoleCommand())
    44  
    45  	return ac
    46  }
    47  
    48  var (
    49  	passwordInteractive bool
    50  )
    51  
    52  func newUserAddCommand() *cobra.Command {
    53  	cmd := cobra.Command{
    54  		Use:   "add <user name or user:password> [options]",
    55  		Short: "Adds a new user",
    56  		Run:   userAddCommandFunc,
    57  	}
    58  
    59  	cmd.Flags().BoolVar(&passwordInteractive, "interactive", true, "Read password from stdin instead of interactive terminal")
    60  
    61  	return &cmd
    62  }
    63  
    64  func newUserDeleteCommand() *cobra.Command {
    65  	return &cobra.Command{
    66  		Use:   "delete <user name>",
    67  		Short: "Deletes a user",
    68  		Run:   userDeleteCommandFunc,
    69  	}
    70  }
    71  
    72  func newUserGetCommand() *cobra.Command {
    73  	cmd := cobra.Command{
    74  		Use:   "get <user name> [options]",
    75  		Short: "Gets detailed information of a user",
    76  		Run:   userGetCommandFunc,
    77  	}
    78  
    79  	cmd.Flags().BoolVar(&userShowDetail, "detail", false, "Show permissions of roles granted to the user")
    80  
    81  	return &cmd
    82  }
    83  
    84  func newUserListCommand() *cobra.Command {
    85  	return &cobra.Command{
    86  		Use:   "list",
    87  		Short: "Lists all users",
    88  		Run:   userListCommandFunc,
    89  	}
    90  }
    91  
    92  func newUserChangePasswordCommand() *cobra.Command {
    93  	cmd := cobra.Command{
    94  		Use:   "passwd <user name> [options]",
    95  		Short: "Changes password of user",
    96  		Run:   userChangePasswordCommandFunc,
    97  	}
    98  
    99  	cmd.Flags().BoolVar(&passwordInteractive, "interactive", true, "If true, read password from stdin instead of interactive terminal")
   100  
   101  	return &cmd
   102  }
   103  
   104  func newUserGrantRoleCommand() *cobra.Command {
   105  	return &cobra.Command{
   106  		Use:   "grant-role <user name> <role name>",
   107  		Short: "Grants a role to a user",
   108  		Run:   userGrantRoleCommandFunc,
   109  	}
   110  }
   111  
   112  func newUserRevokeRoleCommand() *cobra.Command {
   113  	return &cobra.Command{
   114  		Use:   "revoke-role <user name> <role name>",
   115  		Short: "Revokes a role from a user",
   116  		Run:   userRevokeRoleCommandFunc,
   117  	}
   118  }
   119  
   120  // userAddCommandFunc executes the "user add" command.
   121  func userAddCommandFunc(cmd *cobra.Command, args []string) {
   122  	if len(args) != 1 {
   123  		ExitWithError(ExitBadArgs, fmt.Errorf("user add command requires user name as its argument."))
   124  	}
   125  
   126  	var password string
   127  	var user string
   128  
   129  	splitted := strings.SplitN(args[0], ":", 2)
   130  	if len(splitted) < 2 {
   131  		user = args[0]
   132  		if !passwordInteractive {
   133  			fmt.Scanf("%s", &password)
   134  		} else {
   135  			password = readPasswordInteractive(args[0])
   136  		}
   137  	} else {
   138  		user = splitted[0]
   139  		password = splitted[1]
   140  		if len(user) == 0 {
   141  			ExitWithError(ExitBadArgs, fmt.Errorf("empty user name is not allowed."))
   142  		}
   143  	}
   144  
   145  	resp, err := mustClientFromCmd(cmd).Auth.UserAdd(context.TODO(), user, password)
   146  	if err != nil {
   147  		ExitWithError(ExitError, err)
   148  	}
   149  
   150  	display.UserAdd(user, *resp)
   151  }
   152  
   153  // userDeleteCommandFunc executes the "user delete" command.
   154  func userDeleteCommandFunc(cmd *cobra.Command, args []string) {
   155  	if len(args) != 1 {
   156  		ExitWithError(ExitBadArgs, fmt.Errorf("user delete command requires user name as its argument."))
   157  	}
   158  
   159  	resp, err := mustClientFromCmd(cmd).Auth.UserDelete(context.TODO(), args[0])
   160  	if err != nil {
   161  		ExitWithError(ExitError, err)
   162  	}
   163  	display.UserDelete(args[0], *resp)
   164  }
   165  
   166  // userGetCommandFunc executes the "user get" command.
   167  func userGetCommandFunc(cmd *cobra.Command, args []string) {
   168  	if len(args) != 1 {
   169  		ExitWithError(ExitBadArgs, fmt.Errorf("user get command requires user name as its argument."))
   170  	}
   171  
   172  	name := args[0]
   173  	client := mustClientFromCmd(cmd)
   174  	resp, err := client.Auth.UserGet(context.TODO(), name)
   175  	if err != nil {
   176  		ExitWithError(ExitError, err)
   177  	}
   178  
   179  	if userShowDetail {
   180  		fmt.Printf("User: %s\n", name)
   181  		for _, role := range resp.Roles {
   182  			fmt.Printf("\n")
   183  			roleResp, err := client.Auth.RoleGet(context.TODO(), role)
   184  			if err != nil {
   185  				ExitWithError(ExitError, err)
   186  			}
   187  			display.RoleGet(role, *roleResp)
   188  		}
   189  	} else {
   190  		display.UserGet(name, *resp)
   191  	}
   192  }
   193  
   194  // userListCommandFunc executes the "user list" command.
   195  func userListCommandFunc(cmd *cobra.Command, args []string) {
   196  	if len(args) != 0 {
   197  		ExitWithError(ExitBadArgs, fmt.Errorf("user list command requires no arguments."))
   198  	}
   199  
   200  	resp, err := mustClientFromCmd(cmd).Auth.UserList(context.TODO())
   201  	if err != nil {
   202  		ExitWithError(ExitError, err)
   203  	}
   204  
   205  	display.UserList(*resp)
   206  }
   207  
   208  // userChangePasswordCommandFunc executes the "user passwd" command.
   209  func userChangePasswordCommandFunc(cmd *cobra.Command, args []string) {
   210  	if len(args) != 1 {
   211  		ExitWithError(ExitBadArgs, fmt.Errorf("user passwd command requires user name as its argument."))
   212  	}
   213  
   214  	var password string
   215  
   216  	if !passwordInteractive {
   217  		fmt.Scanf("%s", &password)
   218  	} else {
   219  		password = readPasswordInteractive(args[0])
   220  	}
   221  
   222  	resp, err := mustClientFromCmd(cmd).Auth.UserChangePassword(context.TODO(), args[0], password)
   223  	if err != nil {
   224  		ExitWithError(ExitError, err)
   225  	}
   226  
   227  	display.UserChangePassword(*resp)
   228  }
   229  
   230  // userGrantRoleCommandFunc executes the "user grant-role" command.
   231  func userGrantRoleCommandFunc(cmd *cobra.Command, args []string) {
   232  	if len(args) != 2 {
   233  		ExitWithError(ExitBadArgs, fmt.Errorf("user grant command requires user name and role name as its argument."))
   234  	}
   235  
   236  	resp, err := mustClientFromCmd(cmd).Auth.UserGrantRole(context.TODO(), args[0], args[1])
   237  	if err != nil {
   238  		ExitWithError(ExitError, err)
   239  	}
   240  
   241  	display.UserGrantRole(args[0], args[1], *resp)
   242  }
   243  
   244  // userRevokeRoleCommandFunc executes the "user revoke-role" command.
   245  func userRevokeRoleCommandFunc(cmd *cobra.Command, args []string) {
   246  	if len(args) != 2 {
   247  		ExitWithError(ExitBadArgs, fmt.Errorf("user revoke-role requires user name and role name as its argument."))
   248  	}
   249  
   250  	resp, err := mustClientFromCmd(cmd).Auth.UserRevokeRole(context.TODO(), args[0], args[1])
   251  	if err != nil {
   252  		ExitWithError(ExitError, err)
   253  	}
   254  
   255  	display.UserRevokeRole(args[0], args[1], *resp)
   256  }
   257  
   258  func readPasswordInteractive(name string) string {
   259  	prompt1 := fmt.Sprintf("Password of %s: ", name)
   260  	password1, err1 := speakeasy.Ask(prompt1)
   261  	if err1 != nil {
   262  		ExitWithError(ExitBadArgs, fmt.Errorf("failed to ask password: %s.", err1))
   263  	}
   264  
   265  	if len(password1) == 0 {
   266  		ExitWithError(ExitBadArgs, fmt.Errorf("empty password"))
   267  	}
   268  
   269  	prompt2 := fmt.Sprintf("Type password of %s again for confirmation: ", name)
   270  	password2, err2 := speakeasy.Ask(prompt2)
   271  	if err2 != nil {
   272  		ExitWithError(ExitBadArgs, fmt.Errorf("failed to ask password: %s.", err2))
   273  	}
   274  
   275  	if strings.Compare(password1, password2) != 0 {
   276  		ExitWithError(ExitBadArgs, fmt.Errorf("given passwords are different."))
   277  	}
   278  
   279  	return password1
   280  }