diff --git a/e2e/ctl_v3_auth_test.go b/e2e/ctl_v3_auth_test.go index 8acc9f3f0..3b5107f46 100644 --- a/e2e/ctl_v3_auth_test.go +++ b/e2e/ctl_v3_auth_test.go @@ -17,6 +17,8 @@ package e2e import ( "fmt" "testing" + + "github.com/coreos/etcd/clientv3" ) func TestCtlV3AuthEnable(t *testing.T) { testCtl(t, authEnableTest) } @@ -26,6 +28,7 @@ func TestCtlV3AuthRoleUpdate(t *testing.T) { testCtl(t, authRoleUpdateT func TestCtlV3AuthUserDeleteDuringOps(t *testing.T) { testCtl(t, authUserDeleteDuringOpsTest) } func TestCtlV3AuthRoleRevokeDuringOps(t *testing.T) { testCtl(t, authRoleRevokeDuringOpsTest) } func TestCtlV3AuthTxn(t *testing.T) { testCtl(t, authTestTxn) } +func TestCtlV3AuthPerfixPerm(t *testing.T) { testCtl(t, authTestPrefixPerm) } func authEnableTest(cx ctlCtx) { if err := authEnable(cx); err != nil { @@ -172,7 +175,7 @@ func authRoleUpdateTest(cx ctlCtx) { // grant a new key cx.user, cx.pass = "root", "root" - if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, "hoo", ""}); err != nil { + if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, "hoo", "", false}); err != nil { cx.t.Fatal(err) } @@ -268,7 +271,7 @@ func authRoleRevokeDuringOpsTest(cx ctlCtx) { cx.t.Fatal(err) } // grant a new key to the new role - if err := ctlV3RoleGrantPermission(cx, "test-role2", grantingPerm{true, true, "hoo", ""}); err != nil { + if err := ctlV3RoleGrantPermission(cx, "test-role2", grantingPerm{true, true, "hoo", "", false}); err != nil { cx.t.Fatal(err) } // grant the new role to the user @@ -370,7 +373,7 @@ func authTestTxn(cx ctlCtx) { // grant keys to test-user cx.user, cx.pass = "root", "root" for _, key := range grantedKeys { - if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, key, ""}); err != nil { + if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, key, "", false}); err != nil { cx.t.Fatal(err) } } @@ -422,3 +425,32 @@ func authTestTxn(cx ctlCtx) { cx.t.Fatal(err) } } + +func authTestPrefixPerm(cx ctlCtx) { + if err := authEnable(cx); err != nil { + cx.t.Fatal(err) + } + + cx.user, cx.pass = "root", "root" + authSetupTestUser(cx) + + prefix := "/prefix/" // directory like prefix + // grant keys to test-user + cx.user, cx.pass = "root", "root" + if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, prefix, "", true}); err != nil { + cx.t.Fatal(err) + } + + // try a prefix granted permission + cx.user, cx.pass = "test-user", "pass" + for i := 0; i < 10; i++ { + key := fmt.Sprintf("%s%d", prefix, i) + if err := ctlV3Put(cx, key, "val", ""); err != nil { + cx.t.Fatal(err) + } + } + + if err := ctlV3PutFailPerm(cx, clientv3.GetPrefixRangeEnd(prefix), "baz"); err != nil { + cx.t.Fatal(err) + } +} diff --git a/e2e/ctl_v3_role_test.go b/e2e/ctl_v3_role_test.go index 70192af60..5ef43b2a5 100644 --- a/e2e/ctl_v3_role_test.go +++ b/e2e/ctl_v3_role_test.go @@ -101,6 +101,9 @@ func ctlV3Role(cx ctlCtx, args []string, expStr string) error { func ctlV3RoleGrantPermission(cx ctlCtx, rolename string, perm grantingPerm) error { cmdArgs := append(cx.PrefixArgs(), "role", "grant-permission") + if perm.prefix { + cmdArgs = append(cmdArgs, "--prefix") + } cmdArgs = append(cmdArgs, rolename) cmdArgs = append(cmdArgs, grantingPermToArgs(perm)...) @@ -137,6 +140,7 @@ type grantingPerm struct { write bool key string rangeEnd string + prefix bool } func grantingPermToArgs(perm grantingPerm) []string { diff --git a/etcdctl/ctlv3/command/role_command.go b/etcdctl/ctlv3/command/role_command.go index 8e9326eab..5c33613a3 100644 --- a/etcdctl/ctlv3/command/role_command.go +++ b/etcdctl/ctlv3/command/role_command.go @@ -22,6 +22,10 @@ import ( "golang.org/x/net/context" ) +var ( + grantPermissionPrefix bool +) + // NewRoleCommand returns the cobra command for "role". func NewRoleCommand() *cobra.Command { ac := &cobra.Command{ @@ -72,11 +76,15 @@ func newRoleListCommand() *cobra.Command { } func newRoleGrantPermissionCommand() *cobra.Command { - return &cobra.Command{ + cmd := &cobra.Command{ Use: "grant-permission [endkey]", Short: "Grants a key to a role", Run: roleGrantPermissionCommandFunc, } + + cmd.Flags().BoolVar(&grantPermissionPrefix, "prefix", false, "grant a prefix permission") + + return cmd } func newRoleRevokePermissionCommand() *cobra.Command { @@ -183,7 +191,12 @@ func roleGrantPermissionCommandFunc(cmd *cobra.Command, args []string) { rangeEnd := "" if 4 <= len(args) { + if grantPermissionPrefix { + ExitWithError(ExitBadArgs, fmt.Errorf("don't pass both of --prefix option and range end to grant permission command")) + } rangeEnd = args[3] + } else if grantPermissionPrefix { + rangeEnd = clientv3.GetPrefixRangeEnd(args[2]) } _, err = mustClientFromCmd(cmd).Auth.RoleGrantPermission(context.TODO(), args[0], args[2], rangeEnd, perm)