role_commands.go 6.2 KB

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