user_commands.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. // Copyright 2015 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. package command
  15. import (
  16. "fmt"
  17. "os"
  18. "strings"
  19. "github.com/bgentry/speakeasy"
  20. "github.com/urfave/cli"
  21. "go.etcd.io/etcd/client"
  22. )
  23. func NewUserCommands() cli.Command {
  24. return cli.Command{
  25. Name: "user",
  26. Usage: "user add, grant and revoke subcommands",
  27. Subcommands: []cli.Command{
  28. {
  29. Name: "add",
  30. Usage: "add a new user for the etcd cluster",
  31. ArgsUsage: "<user>",
  32. Action: actionUserAdd,
  33. },
  34. {
  35. Name: "get",
  36. Usage: "get details for a user",
  37. ArgsUsage: "<user>",
  38. Action: actionUserGet,
  39. },
  40. {
  41. Name: "list",
  42. Usage: "list all current users",
  43. ArgsUsage: "<user>",
  44. Action: actionUserList,
  45. },
  46. {
  47. Name: "remove",
  48. Usage: "remove a user for the etcd cluster",
  49. ArgsUsage: "<user>",
  50. Action: actionUserRemove,
  51. },
  52. {
  53. Name: "grant",
  54. Usage: "grant roles to an etcd user",
  55. ArgsUsage: "<user>",
  56. Flags: []cli.Flag{cli.StringSliceFlag{Name: "roles", Value: new(cli.StringSlice), Usage: "List of roles to grant or revoke"}},
  57. Action: actionUserGrant,
  58. },
  59. {
  60. Name: "revoke",
  61. Usage: "revoke roles for an etcd user",
  62. ArgsUsage: "<user>",
  63. Flags: []cli.Flag{cli.StringSliceFlag{Name: "roles", Value: new(cli.StringSlice), Usage: "List of roles to grant or revoke"}},
  64. Action: actionUserRevoke,
  65. },
  66. {
  67. Name: "passwd",
  68. Usage: "change password for a user",
  69. ArgsUsage: "<user>",
  70. Action: actionUserPasswd,
  71. },
  72. },
  73. }
  74. }
  75. func mustNewAuthUserAPI(c *cli.Context) client.AuthUserAPI {
  76. hc := mustNewClient(c)
  77. if c.GlobalBool("debug") {
  78. fmt.Fprintf(os.Stderr, "Cluster-Endpoints: %s\n", strings.Join(hc.Endpoints(), ", "))
  79. }
  80. return client.NewAuthUserAPI(hc)
  81. }
  82. func actionUserList(c *cli.Context) error {
  83. if len(c.Args()) != 0 {
  84. fmt.Fprintln(os.Stderr, "No arguments accepted")
  85. os.Exit(1)
  86. }
  87. u := mustNewAuthUserAPI(c)
  88. ctx, cancel := contextWithTotalTimeout(c)
  89. users, err := u.ListUsers(ctx)
  90. cancel()
  91. if err != nil {
  92. fmt.Fprintln(os.Stderr, err.Error())
  93. os.Exit(1)
  94. }
  95. for _, user := range users {
  96. fmt.Printf("%s\n", user)
  97. }
  98. return nil
  99. }
  100. func actionUserAdd(c *cli.Context) error {
  101. api, userarg := mustUserAPIAndName(c)
  102. ctx, cancel := contextWithTotalTimeout(c)
  103. defer cancel()
  104. user, _, _ := getUsernamePassword("", userarg+":")
  105. _, pass, err := getUsernamePassword("New password: ", userarg)
  106. if err != nil {
  107. fmt.Fprintln(os.Stderr, "Error reading password:", err)
  108. os.Exit(1)
  109. }
  110. err = api.AddUser(ctx, user, pass)
  111. if err != nil {
  112. fmt.Fprintln(os.Stderr, err.Error())
  113. os.Exit(1)
  114. }
  115. fmt.Printf("User %s created\n", user)
  116. return nil
  117. }
  118. func actionUserRemove(c *cli.Context) error {
  119. api, user := mustUserAPIAndName(c)
  120. ctx, cancel := contextWithTotalTimeout(c)
  121. err := api.RemoveUser(ctx, user)
  122. cancel()
  123. if err != nil {
  124. fmt.Fprintln(os.Stderr, err.Error())
  125. os.Exit(1)
  126. }
  127. fmt.Printf("User %s removed\n", user)
  128. return nil
  129. }
  130. func actionUserPasswd(c *cli.Context) error {
  131. api, user := mustUserAPIAndName(c)
  132. ctx, cancel := contextWithTotalTimeout(c)
  133. defer cancel()
  134. pass, err := speakeasy.Ask("New password: ")
  135. if err != nil {
  136. fmt.Fprintln(os.Stderr, "Error reading password:", err)
  137. os.Exit(1)
  138. }
  139. _, err = api.ChangePassword(ctx, user, pass)
  140. if err != nil {
  141. fmt.Fprintln(os.Stderr, err.Error())
  142. os.Exit(1)
  143. }
  144. fmt.Printf("Password updated\n")
  145. return nil
  146. }
  147. func actionUserGrant(c *cli.Context) error {
  148. userGrantRevoke(c, true)
  149. return nil
  150. }
  151. func actionUserRevoke(c *cli.Context) error {
  152. userGrantRevoke(c, false)
  153. return nil
  154. }
  155. func userGrantRevoke(c *cli.Context, grant bool) {
  156. roles := c.StringSlice("roles")
  157. if len(roles) == 0 {
  158. fmt.Fprintln(os.Stderr, "No roles specified; please use `--roles`")
  159. os.Exit(1)
  160. }
  161. ctx, cancel := contextWithTotalTimeout(c)
  162. defer cancel()
  163. api, user := mustUserAPIAndName(c)
  164. var err error
  165. if grant {
  166. _, err = api.GrantUser(ctx, user, roles)
  167. } else {
  168. _, err = api.RevokeUser(ctx, user, roles)
  169. }
  170. if err != nil {
  171. fmt.Fprintln(os.Stderr, err.Error())
  172. os.Exit(1)
  173. }
  174. fmt.Printf("User %s updated\n", user)
  175. }
  176. func actionUserGet(c *cli.Context) error {
  177. api, username := mustUserAPIAndName(c)
  178. ctx, cancel := contextWithTotalTimeout(c)
  179. user, err := api.GetUser(ctx, username)
  180. cancel()
  181. if err != nil {
  182. fmt.Fprintln(os.Stderr, err.Error())
  183. os.Exit(1)
  184. }
  185. fmt.Printf("User: %s\n", user.User)
  186. fmt.Printf("Roles: %s\n", strings.Join(user.Roles, " "))
  187. return nil
  188. }
  189. func mustUserAPIAndName(c *cli.Context) (client.AuthUserAPI, string) {
  190. args := c.Args()
  191. if len(args) != 1 {
  192. fmt.Fprintln(os.Stderr, "Please provide a username")
  193. os.Exit(1)
  194. }
  195. api := mustNewAuthUserAPI(c)
  196. username := args[0]
  197. return api, username
  198. }