user_commands.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  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/codegangsta/cli"
  21. "github.com/coreos/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) {
  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. }
  99. func actionUserAdd(c *cli.Context) {
  100. api, userarg := mustUserAPIAndName(c)
  101. ctx, cancel := contextWithTotalTimeout(c)
  102. defer cancel()
  103. user, _, _ := getUsernamePassword("", userarg+":")
  104. currentUser, err := api.GetUser(ctx, user)
  105. if currentUser != nil {
  106. fmt.Fprintf(os.Stderr, "User %s already exists\n", user)
  107. os.Exit(1)
  108. }
  109. _, pass, err := getUsernamePassword("New password: ", userarg)
  110. if err != nil {
  111. fmt.Fprintln(os.Stderr, "Error reading password:", err)
  112. os.Exit(1)
  113. }
  114. err = api.AddUser(ctx, user, pass)
  115. if err != nil {
  116. fmt.Fprintln(os.Stderr, err.Error())
  117. os.Exit(1)
  118. }
  119. fmt.Printf("User %s created\n", user)
  120. }
  121. func actionUserRemove(c *cli.Context) {
  122. api, user := mustUserAPIAndName(c)
  123. ctx, cancel := contextWithTotalTimeout(c)
  124. err := api.RemoveUser(ctx, user)
  125. cancel()
  126. if err != nil {
  127. fmt.Fprintln(os.Stderr, err.Error())
  128. os.Exit(1)
  129. }
  130. fmt.Printf("User %s removed\n", user)
  131. }
  132. func actionUserPasswd(c *cli.Context) {
  133. api, user := mustUserAPIAndName(c)
  134. ctx, cancel := contextWithTotalTimeout(c)
  135. defer cancel()
  136. currentUser, err := api.GetUser(ctx, user)
  137. if currentUser == nil {
  138. fmt.Fprintln(os.Stderr, err.Error())
  139. os.Exit(1)
  140. }
  141. pass, err := speakeasy.Ask("New password: ")
  142. if err != nil {
  143. fmt.Fprintln(os.Stderr, "Error reading password:", err)
  144. os.Exit(1)
  145. }
  146. _, err = api.ChangePassword(ctx, user, pass)
  147. if err != nil {
  148. fmt.Fprintln(os.Stderr, err.Error())
  149. os.Exit(1)
  150. }
  151. fmt.Printf("Password updated\n")
  152. }
  153. func actionUserGrant(c *cli.Context) {
  154. userGrantRevoke(c, true)
  155. }
  156. func actionUserRevoke(c *cli.Context) {
  157. userGrantRevoke(c, false)
  158. }
  159. func userGrantRevoke(c *cli.Context, grant bool) {
  160. roles := c.StringSlice("roles")
  161. if len(roles) == 0 {
  162. fmt.Fprintln(os.Stderr, "No roles specified; please use `--roles`")
  163. os.Exit(1)
  164. }
  165. ctx, cancel := contextWithTotalTimeout(c)
  166. defer cancel()
  167. api, user := mustUserAPIAndName(c)
  168. currentUser, err := api.GetUser(ctx, user)
  169. if currentUser == nil {
  170. fmt.Fprintln(os.Stderr, err.Error())
  171. os.Exit(1)
  172. }
  173. if grant {
  174. _, err = api.GrantUser(ctx, user, roles)
  175. } else {
  176. _, err = api.RevokeUser(ctx, user, roles)
  177. }
  178. if err != nil {
  179. fmt.Fprintln(os.Stderr, err.Error())
  180. os.Exit(1)
  181. }
  182. fmt.Printf("User %s updated\n", user)
  183. }
  184. func actionUserGet(c *cli.Context) {
  185. api, username := mustUserAPIAndName(c)
  186. ctx, cancel := contextWithTotalTimeout(c)
  187. user, err := api.GetUser(ctx, username)
  188. cancel()
  189. if err != nil {
  190. fmt.Fprintln(os.Stderr, err.Error())
  191. os.Exit(1)
  192. }
  193. fmt.Printf("User: %s\n", user.User)
  194. fmt.Printf("Roles: %s\n", strings.Join(user.Roles, " "))
  195. }
  196. func mustUserAPIAndName(c *cli.Context) (client.AuthUserAPI, string) {
  197. args := c.Args()
  198. if len(args) != 1 {
  199. fmt.Fprintln(os.Stderr, "Please provide a username")
  200. os.Exit(1)
  201. }
  202. api := mustNewAuthUserAPI(c)
  203. username := args[0]
  204. return api, username
  205. }