role_commands.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. // Copyright 2015 CoreOS, Inc.
  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. "reflect"
  19. "strings"
  20. "github.com/coreos/etcd/Godeps/_workspace/src/github.com/codegangsta/cli"
  21. "github.com/coreos/etcd/client"
  22. "github.com/coreos/etcd/pkg/pathutil"
  23. )
  24. func NewRoleCommands() cli.Command {
  25. return cli.Command{
  26. Name: "role",
  27. Usage: "role add, grant and revoke subcommands",
  28. Subcommands: []cli.Command{
  29. {
  30. Name: "add",
  31. Usage: "add a new role for the etcd cluster",
  32. ArgsUsage: "<role> ",
  33. Action: actionRoleAdd,
  34. },
  35. {
  36. Name: "get",
  37. Usage: "get details for a role",
  38. ArgsUsage: "<role>",
  39. Action: actionRoleGet,
  40. },
  41. {
  42. Name: "list",
  43. Usage: "list all roles",
  44. ArgsUsage: " ",
  45. Action: actionRoleList,
  46. },
  47. {
  48. Name: "remove",
  49. Usage: "remove a role from the etcd cluster",
  50. ArgsUsage: "<role>",
  51. Action: actionRoleRemove,
  52. },
  53. {
  54. Name: "grant",
  55. Usage: "grant path matches to an etcd role",
  56. ArgsUsage: "<role>",
  57. Flags: []cli.Flag{
  58. cli.StringFlag{Name: "path", Value: "", Usage: "Path granted for the role to access"},
  59. cli.BoolFlag{Name: "read", Usage: "Grant read-only access"},
  60. cli.BoolFlag{Name: "write", Usage: "Grant write-only access"},
  61. cli.BoolFlag{Name: "readwrite", Usage: "Grant read-write access"},
  62. },
  63. Action: actionRoleGrant,
  64. },
  65. {
  66. Name: "revoke",
  67. Usage: "revoke path matches for an etcd role",
  68. ArgsUsage: "<role>",
  69. Flags: []cli.Flag{
  70. cli.StringFlag{Name: "path", Value: "", Usage: "Path revoked for the role to access"},
  71. cli.BoolFlag{Name: "read", Usage: "Revoke read access"},
  72. cli.BoolFlag{Name: "write", Usage: "Revoke write access"},
  73. cli.BoolFlag{Name: "readwrite", Usage: "Revoke read-write access"},
  74. },
  75. Action: actionRoleRevoke,
  76. },
  77. },
  78. }
  79. }
  80. func mustNewAuthRoleAPI(c *cli.Context) client.AuthRoleAPI {
  81. hc := mustNewClient(c)
  82. if c.GlobalBool("debug") {
  83. fmt.Fprintf(os.Stderr, "Cluster-Endpoints: %s\n", strings.Join(hc.Endpoints(), ", "))
  84. }
  85. return client.NewAuthRoleAPI(hc)
  86. }
  87. func actionRoleList(c *cli.Context) {
  88. if len(c.Args()) != 0 {
  89. fmt.Fprintln(os.Stderr, "No arguments accepted")
  90. os.Exit(1)
  91. }
  92. r := mustNewAuthRoleAPI(c)
  93. ctx, cancel := contextWithTotalTimeout(c)
  94. roles, err := r.ListRoles(ctx)
  95. cancel()
  96. if err != nil {
  97. fmt.Fprintln(os.Stderr, err.Error())
  98. os.Exit(1)
  99. }
  100. for _, role := range roles {
  101. fmt.Printf("%s\n", role)
  102. }
  103. }
  104. func actionRoleAdd(c *cli.Context) {
  105. api, role := mustRoleAPIAndName(c)
  106. ctx, cancel := contextWithTotalTimeout(c)
  107. defer cancel()
  108. currentRole, err := api.GetRole(ctx, role)
  109. if currentRole != nil {
  110. fmt.Fprintf(os.Stderr, "Role %s already exists\n", role)
  111. os.Exit(1)
  112. }
  113. err = api.AddRole(ctx, role)
  114. if err != nil {
  115. fmt.Fprintln(os.Stderr, err.Error())
  116. os.Exit(1)
  117. }
  118. fmt.Printf("Role %s created\n", role)
  119. }
  120. func actionRoleRemove(c *cli.Context) {
  121. api, role := mustRoleAPIAndName(c)
  122. ctx, cancel := contextWithTotalTimeout(c)
  123. err := api.RemoveRole(ctx, role)
  124. cancel()
  125. if err != nil {
  126. fmt.Fprintln(os.Stderr, err.Error())
  127. os.Exit(1)
  128. }
  129. fmt.Printf("Role %s removed\n", role)
  130. }
  131. func actionRoleGrant(c *cli.Context) {
  132. roleGrantRevoke(c, true)
  133. }
  134. func actionRoleRevoke(c *cli.Context) {
  135. roleGrantRevoke(c, false)
  136. }
  137. func roleGrantRevoke(c *cli.Context, grant bool) {
  138. path := c.String("path")
  139. if path == "" {
  140. fmt.Fprintln(os.Stderr, "No path specified; please use `-path`")
  141. os.Exit(1)
  142. }
  143. if pathutil.CanonicalURLPath(path) != path {
  144. fmt.Fprintf(os.Stderr, "Not canonical path; please use `-path=%s`\n", pathutil.CanonicalURLPath(path))
  145. os.Exit(1)
  146. }
  147. read := c.Bool("read")
  148. write := c.Bool("write")
  149. rw := c.Bool("readwrite")
  150. permcount := 0
  151. for _, v := range []bool{read, write, rw} {
  152. if v {
  153. permcount++
  154. }
  155. }
  156. if permcount != 1 {
  157. fmt.Fprintln(os.Stderr, "Please specify exactly one of -read, -write or -readwrite")
  158. os.Exit(1)
  159. }
  160. var permType client.PermissionType
  161. switch {
  162. case read:
  163. permType = client.ReadPermission
  164. case write:
  165. permType = client.WritePermission
  166. case rw:
  167. permType = client.ReadWritePermission
  168. }
  169. api, role := mustRoleAPIAndName(c)
  170. ctx, cancel := contextWithTotalTimeout(c)
  171. defer cancel()
  172. currentRole, err := api.GetRole(ctx, role)
  173. if err != nil {
  174. fmt.Fprintln(os.Stderr, err.Error())
  175. os.Exit(1)
  176. }
  177. var newRole *client.Role
  178. if grant {
  179. newRole, err = api.GrantRoleKV(ctx, role, []string{path}, permType)
  180. } else {
  181. newRole, err = api.RevokeRoleKV(ctx, role, []string{path}, permType)
  182. }
  183. if err != nil {
  184. fmt.Fprintln(os.Stderr, err.Error())
  185. os.Exit(1)
  186. }
  187. if reflect.DeepEqual(newRole, currentRole) {
  188. if grant {
  189. fmt.Printf("Role unchanged; already granted")
  190. } else {
  191. fmt.Printf("Role unchanged; already revoked")
  192. }
  193. }
  194. fmt.Printf("Role %s updated\n", role)
  195. }
  196. func actionRoleGet(c *cli.Context) {
  197. api, rolename := mustRoleAPIAndName(c)
  198. ctx, cancel := contextWithTotalTimeout(c)
  199. role, err := api.GetRole(ctx, rolename)
  200. cancel()
  201. if err != nil {
  202. fmt.Fprintln(os.Stderr, err.Error())
  203. os.Exit(1)
  204. }
  205. fmt.Printf("Role: %s\n", role.Role)
  206. fmt.Printf("KV Read:\n")
  207. for _, v := range role.Permissions.KV.Read {
  208. fmt.Printf("\t%s\n", v)
  209. }
  210. fmt.Printf("KV Write:\n")
  211. for _, v := range role.Permissions.KV.Write {
  212. fmt.Printf("\t%s\n", v)
  213. }
  214. }
  215. func mustRoleAPIAndName(c *cli.Context) (client.AuthRoleAPI, string) {
  216. args := c.Args()
  217. if len(args) != 1 {
  218. fmt.Fprintln(os.Stderr, "Please provide a role name")
  219. os.Exit(1)
  220. }
  221. name := args[0]
  222. api := mustNewAuthRoleAPI(c)
  223. return api, name
  224. }