role_commands.go 6.4 KB

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