1// Copyright 2016 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 15package command 16 17import ( 18 "context" 19 "fmt" 20 21 "github.com/coreos/etcd/clientv3" 22 "github.com/spf13/cobra" 23) 24 25var ( 26 rolePermPrefix bool 27 rolePermFromKey bool 28) 29 30// NewRoleCommand returns the cobra command for "role". 31func NewRoleCommand() *cobra.Command { 32 ac := &cobra.Command{ 33 Use: "role <subcommand>", 34 Short: "Role related commands", 35 } 36 37 ac.AddCommand(newRoleAddCommand()) 38 ac.AddCommand(newRoleDeleteCommand()) 39 ac.AddCommand(newRoleGetCommand()) 40 ac.AddCommand(newRoleListCommand()) 41 ac.AddCommand(newRoleGrantPermissionCommand()) 42 ac.AddCommand(newRoleRevokePermissionCommand()) 43 44 return ac 45} 46 47func newRoleAddCommand() *cobra.Command { 48 return &cobra.Command{ 49 Use: "add <role name>", 50 Short: "Adds a new role", 51 Run: roleAddCommandFunc, 52 } 53} 54 55func newRoleDeleteCommand() *cobra.Command { 56 return &cobra.Command{ 57 Use: "delete <role name>", 58 Short: "Deletes a role", 59 Run: roleDeleteCommandFunc, 60 } 61} 62 63func newRoleGetCommand() *cobra.Command { 64 return &cobra.Command{ 65 Use: "get <role name>", 66 Short: "Gets detailed information of a role", 67 Run: roleGetCommandFunc, 68 } 69} 70 71func newRoleListCommand() *cobra.Command { 72 return &cobra.Command{ 73 Use: "list", 74 Short: "Lists all roles", 75 Run: roleListCommandFunc, 76 } 77} 78 79func newRoleGrantPermissionCommand() *cobra.Command { 80 cmd := &cobra.Command{ 81 Use: "grant-permission [options] <role name> <permission type> <key> [endkey]", 82 Short: "Grants a key to a role", 83 Run: roleGrantPermissionCommandFunc, 84 } 85 86 cmd.Flags().BoolVar(&rolePermPrefix, "prefix", false, "grant a prefix permission") 87 cmd.Flags().BoolVar(&rolePermFromKey, "from-key", false, "grant a permission of keys that are greater than or equal to the given key using byte compare") 88 89 return cmd 90} 91 92func newRoleRevokePermissionCommand() *cobra.Command { 93 cmd := &cobra.Command{ 94 Use: "revoke-permission <role name> <key> [endkey]", 95 Short: "Revokes a key from a role", 96 Run: roleRevokePermissionCommandFunc, 97 } 98 99 cmd.Flags().BoolVar(&rolePermPrefix, "prefix", false, "revoke a prefix permission") 100 cmd.Flags().BoolVar(&rolePermFromKey, "from-key", false, "revoke a permission of keys that are greater than or equal to the given key using byte compare") 101 102 return cmd 103} 104 105// roleAddCommandFunc executes the "role add" command. 106func roleAddCommandFunc(cmd *cobra.Command, args []string) { 107 if len(args) != 1 { 108 ExitWithError(ExitBadArgs, fmt.Errorf("role add command requires role name as its argument.")) 109 } 110 111 resp, err := mustClientFromCmd(cmd).Auth.RoleAdd(context.TODO(), args[0]) 112 if err != nil { 113 ExitWithError(ExitError, err) 114 } 115 116 display.RoleAdd(args[0], *resp) 117} 118 119// roleDeleteCommandFunc executes the "role delete" command. 120func roleDeleteCommandFunc(cmd *cobra.Command, args []string) { 121 if len(args) != 1 { 122 ExitWithError(ExitBadArgs, fmt.Errorf("role delete command requires role name as its argument.")) 123 } 124 125 resp, err := mustClientFromCmd(cmd).Auth.RoleDelete(context.TODO(), args[0]) 126 if err != nil { 127 ExitWithError(ExitError, err) 128 } 129 130 display.RoleDelete(args[0], *resp) 131} 132 133// roleGetCommandFunc executes the "role get" command. 134func roleGetCommandFunc(cmd *cobra.Command, args []string) { 135 if len(args) != 1 { 136 ExitWithError(ExitBadArgs, fmt.Errorf("role get command requires role name as its argument.")) 137 } 138 139 name := args[0] 140 resp, err := mustClientFromCmd(cmd).Auth.RoleGet(context.TODO(), name) 141 if err != nil { 142 ExitWithError(ExitError, err) 143 } 144 145 display.RoleGet(name, *resp) 146} 147 148// roleListCommandFunc executes the "role list" command. 149func roleListCommandFunc(cmd *cobra.Command, args []string) { 150 if len(args) != 0 { 151 ExitWithError(ExitBadArgs, fmt.Errorf("role list command requires no arguments.")) 152 } 153 154 resp, err := mustClientFromCmd(cmd).Auth.RoleList(context.TODO()) 155 if err != nil { 156 ExitWithError(ExitError, err) 157 } 158 159 display.RoleList(*resp) 160} 161 162// roleGrantPermissionCommandFunc executes the "role grant-permission" command. 163func roleGrantPermissionCommandFunc(cmd *cobra.Command, args []string) { 164 if len(args) < 3 { 165 ExitWithError(ExitBadArgs, fmt.Errorf("role grant command requires role name, permission type, and key [endkey] as its argument.")) 166 } 167 168 perm, err := clientv3.StrToPermissionType(args[1]) 169 if err != nil { 170 ExitWithError(ExitBadArgs, err) 171 } 172 173 key, rangeEnd := permRange(args[2:]) 174 resp, err := mustClientFromCmd(cmd).Auth.RoleGrantPermission(context.TODO(), args[0], key, rangeEnd, perm) 175 if err != nil { 176 ExitWithError(ExitError, err) 177 } 178 179 display.RoleGrantPermission(args[0], *resp) 180} 181 182// roleRevokePermissionCommandFunc executes the "role revoke-permission" command. 183func roleRevokePermissionCommandFunc(cmd *cobra.Command, args []string) { 184 if len(args) < 2 { 185 ExitWithError(ExitBadArgs, fmt.Errorf("role revoke-permission command requires role name and key [endkey] as its argument.")) 186 } 187 188 key, rangeEnd := permRange(args[1:]) 189 resp, err := mustClientFromCmd(cmd).Auth.RoleRevokePermission(context.TODO(), args[0], key, rangeEnd) 190 if err != nil { 191 ExitWithError(ExitError, err) 192 } 193 display.RoleRevokePermission(args[0], args[1], rangeEnd, *resp) 194} 195 196func permRange(args []string) (string, string) { 197 key := args[0] 198 var rangeEnd string 199 if len(key) == 0 { 200 if rolePermPrefix && rolePermFromKey { 201 ExitWithError(ExitBadArgs, fmt.Errorf("--from-key and --prefix flags are mutually exclusive")) 202 } 203 204 // Range permission is expressed as adt.BytesAffineInterval, 205 // so the empty prefix which should be matched with every key must be like this ["\x00", <end>). 206 key = "\x00" 207 if rolePermPrefix || rolePermFromKey { 208 // For the both cases of prefix and from-key, a permission with an empty key 209 // should allow access to the entire key space. 210 // 0x00 will be treated as open ended in server side. 211 rangeEnd = "\x00" 212 } 213 } else { 214 var err error 215 rangeEnd, err = rangeEndFromPermFlags(args[0:]) 216 if err != nil { 217 ExitWithError(ExitBadArgs, err) 218 } 219 } 220 return key, rangeEnd 221} 222 223func rangeEndFromPermFlags(args []string) (string, error) { 224 if len(args) == 1 { 225 if rolePermPrefix { 226 if rolePermFromKey { 227 return "", fmt.Errorf("--from-key and --prefix flags are mutually exclusive") 228 } 229 return clientv3.GetPrefixRangeEnd(args[0]), nil 230 } 231 if rolePermFromKey { 232 return "\x00", nil 233 } 234 // single key case 235 return "", nil 236 } 237 if rolePermPrefix { 238 return "", fmt.Errorf("unexpected endkey argument with --prefix flag") 239 } 240 if rolePermFromKey { 241 return "", fmt.Errorf("unexpected endkey argument with --from-key flag") 242 } 243 return args[1], nil 244} 245