123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- // Copyright 2015 The etcd Authors
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package command
- import (
- "fmt"
- "os"
- "strings"
- "github.com/bgentry/speakeasy"
- "github.com/urfave/cli"
- "go.etcd.io/etcd/client"
- )
- func NewUserCommands() cli.Command {
- return cli.Command{
- Name: "user",
- Usage: "user add, grant and revoke subcommands",
- Subcommands: []cli.Command{
- {
- Name: "add",
- Usage: "add a new user for the etcd cluster",
- ArgsUsage: "<user>",
- Action: actionUserAdd,
- },
- {
- Name: "get",
- Usage: "get details for a user",
- ArgsUsage: "<user>",
- Action: actionUserGet,
- },
- {
- Name: "list",
- Usage: "list all current users",
- ArgsUsage: "<user>",
- Action: actionUserList,
- },
- {
- Name: "remove",
- Usage: "remove a user for the etcd cluster",
- ArgsUsage: "<user>",
- Action: actionUserRemove,
- },
- {
- Name: "grant",
- Usage: "grant roles to an etcd user",
- ArgsUsage: "<user>",
- Flags: []cli.Flag{cli.StringSliceFlag{Name: "roles", Value: new(cli.StringSlice), Usage: "List of roles to grant or revoke"}},
- Action: actionUserGrant,
- },
- {
- Name: "revoke",
- Usage: "revoke roles for an etcd user",
- ArgsUsage: "<user>",
- Flags: []cli.Flag{cli.StringSliceFlag{Name: "roles", Value: new(cli.StringSlice), Usage: "List of roles to grant or revoke"}},
- Action: actionUserRevoke,
- },
- {
- Name: "passwd",
- Usage: "change password for a user",
- ArgsUsage: "<user>",
- Action: actionUserPasswd,
- },
- },
- }
- }
- func mustNewAuthUserAPI(c *cli.Context) client.AuthUserAPI {
- hc := mustNewClient(c)
- if c.GlobalBool("debug") {
- fmt.Fprintf(os.Stderr, "Cluster-Endpoints: %s\n", strings.Join(hc.Endpoints(), ", "))
- }
- return client.NewAuthUserAPI(hc)
- }
- func actionUserList(c *cli.Context) error {
- if len(c.Args()) != 0 {
- fmt.Fprintln(os.Stderr, "No arguments accepted")
- os.Exit(1)
- }
- u := mustNewAuthUserAPI(c)
- ctx, cancel := contextWithTotalTimeout(c)
- users, err := u.ListUsers(ctx)
- cancel()
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(1)
- }
- for _, user := range users {
- fmt.Printf("%s\n", user)
- }
- return nil
- }
- func actionUserAdd(c *cli.Context) error {
- api, userarg := mustUserAPIAndName(c)
- ctx, cancel := contextWithTotalTimeout(c)
- defer cancel()
- user, _, _ := getUsernamePassword("", userarg+":")
- _, pass, err := getUsernamePassword("New password: ", userarg)
- if err != nil {
- fmt.Fprintln(os.Stderr, "Error reading password:", err)
- os.Exit(1)
- }
- err = api.AddUser(ctx, user, pass)
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(1)
- }
- fmt.Printf("User %s created\n", user)
- return nil
- }
- func actionUserRemove(c *cli.Context) error {
- api, user := mustUserAPIAndName(c)
- ctx, cancel := contextWithTotalTimeout(c)
- err := api.RemoveUser(ctx, user)
- cancel()
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(1)
- }
- fmt.Printf("User %s removed\n", user)
- return nil
- }
- func actionUserPasswd(c *cli.Context) error {
- api, user := mustUserAPIAndName(c)
- ctx, cancel := contextWithTotalTimeout(c)
- defer cancel()
- pass, err := speakeasy.Ask("New password: ")
- if err != nil {
- fmt.Fprintln(os.Stderr, "Error reading password:", err)
- os.Exit(1)
- }
- _, err = api.ChangePassword(ctx, user, pass)
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(1)
- }
- fmt.Printf("Password updated\n")
- return nil
- }
- func actionUserGrant(c *cli.Context) error {
- userGrantRevoke(c, true)
- return nil
- }
- func actionUserRevoke(c *cli.Context) error {
- userGrantRevoke(c, false)
- return nil
- }
- func userGrantRevoke(c *cli.Context, grant bool) {
- roles := c.StringSlice("roles")
- if len(roles) == 0 {
- fmt.Fprintln(os.Stderr, "No roles specified; please use `--roles`")
- os.Exit(1)
- }
- ctx, cancel := contextWithTotalTimeout(c)
- defer cancel()
- api, user := mustUserAPIAndName(c)
- var err error
- if grant {
- _, err = api.GrantUser(ctx, user, roles)
- } else {
- _, err = api.RevokeUser(ctx, user, roles)
- }
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(1)
- }
- fmt.Printf("User %s updated\n", user)
- }
- func actionUserGet(c *cli.Context) error {
- api, username := mustUserAPIAndName(c)
- ctx, cancel := contextWithTotalTimeout(c)
- user, err := api.GetUser(ctx, username)
- cancel()
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(1)
- }
- fmt.Printf("User: %s\n", user.User)
- fmt.Printf("Roles: %s\n", strings.Join(user.Roles, " "))
- return nil
- }
- func mustUserAPIAndName(c *cli.Context) (client.AuthUserAPI, string) {
- args := c.Args()
- if len(args) != 1 {
- fmt.Fprintln(os.Stderr, "Please provide a username")
- os.Exit(1)
- }
- api := mustNewAuthUserAPI(c)
- username := args[0]
- return api, username
- }
|