Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

etcdctl: add a new option --open-ended for unlimited range permission #7649

Merged
merged 2 commits into from
Apr 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions auth/range_perm_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,18 @@ func getMergedPerms(tx backend.BatchTx, userName string) *unifiedRangePermission

for _, perm := range role.KeyPermission {
var ivl adt.Interval
var rangeEnd string

if len(perm.RangeEnd) == 1 && perm.RangeEnd[0] == 0 {
rangeEnd = ""
} else {
rangeEnd = string(perm.RangeEnd)
}

if len(perm.RangeEnd) != 0 {
ivl = adt.NewStringInterval(string(perm.Key), string(perm.RangeEnd))
ivl = adt.NewStringAffineInterval(string(perm.Key), string(rangeEnd))
} else {
ivl = adt.NewStringPoint(string(perm.Key))
ivl = adt.NewStringAffinePoint(string(perm.Key))
}

switch perm.PermType {
Expand All @@ -66,7 +73,11 @@ func getMergedPerms(tx backend.BatchTx, userName string) *unifiedRangePermission
}

func checkKeyInterval(cachedPerms *unifiedRangePermissions, key, rangeEnd string, permtyp authpb.Permission_Type) bool {
ivl := adt.NewStringInterval(key, rangeEnd)
if len(rangeEnd) == 1 && rangeEnd[0] == '\x00' {
rangeEnd = ""
}

ivl := adt.NewStringAffineInterval(key, rangeEnd)
switch permtyp {
case authpb.READ:
return cachedPerms.readPerms.Contains(ivl)
Expand All @@ -79,7 +90,7 @@ func checkKeyInterval(cachedPerms *unifiedRangePermissions, key, rangeEnd string
}

func checkKeyPoint(cachedPerms *unifiedRangePermissions, key string, permtyp authpb.Permission_Type) bool {
pt := adt.NewStringPoint(key)
pt := adt.NewStringAffinePoint(key)
switch permtyp {
case authpb.READ:
return cachedPerms.readPerms.Intersects(pt)
Expand Down
6 changes: 3 additions & 3 deletions auth/range_perm_cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ func TestRangePermission(t *testing.T) {
want bool
}{
{
[]adt.Interval{adt.NewStringInterval("a", "c"), adt.NewStringInterval("x", "z")},
[]adt.Interval{adt.NewStringAffineInterval("a", "c"), adt.NewStringAffineInterval("x", "z")},
"a", "z",
false,
},
{
[]adt.Interval{adt.NewStringInterval("a", "f"), adt.NewStringInterval("c", "d"), adt.NewStringInterval("f", "z")},
[]adt.Interval{adt.NewStringAffineInterval("a", "f"), adt.NewStringAffineInterval("c", "d"), adt.NewStringAffineInterval("f", "z")},
"a", "z",
true,
},
{
[]adt.Interval{adt.NewStringInterval("a", "d"), adt.NewStringInterval("a", "b"), adt.NewStringInterval("c", "f")},
[]adt.Interval{adt.NewStringAffineInterval("a", "d"), adt.NewStringAffineInterval("a", "b"), adt.NewStringAffineInterval("c", "f")},
"a", "f",
true,
},
Expand Down
54 changes: 53 additions & 1 deletion e2e/ctl_v3_auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func TestCtlV3AuthMemberUpdate(t *testing.T) { testCtl(t, authTestMemberUpda
func TestCtlV3AuthCertCN(t *testing.T) { testCtl(t, authTestCertCN, withCfg(configClientTLSCertAuth)) }
func TestCtlV3AuthRevokeWithDelete(t *testing.T) { testCtl(t, authTestRevokeWithDelete) }
func TestCtlV3AuthInvalidMgmt(t *testing.T) { testCtl(t, authTestInvalidMgmt) }
func TestCtlV3AuthFromKeyPerm(t *testing.T) { testCtl(t, authTestFromKeyPerm) }

func authEnableTest(cx ctlCtx) {
if err := authEnable(cx); err != nil {
Expand Down Expand Up @@ -199,7 +200,7 @@ func authRoleUpdateTest(cx ctlCtx) {

// revoke the newly granted key
cx.user, cx.pass = "root", "root"
if err := ctlV3RoleRevokePermission(cx, "test-role", "hoo", ""); err != nil {
if err := ctlV3RoleRevokePermission(cx, "test-role", "hoo", "", false); err != nil {
cx.t.Fatal(err)
}

Expand Down Expand Up @@ -613,3 +614,54 @@ func authTestInvalidMgmt(cx ctlCtx) {
cx.t.Fatal("revoking the role root from the user root must not be allowed")
}
}

func authTestFromKeyPerm(cx ctlCtx) {
if err := authEnable(cx); err != nil {
cx.t.Fatal(err)
}

cx.user, cx.pass = "root", "root"
authSetupTestUser(cx)

// grant keys after z to test-user
cx.user, cx.pass = "root", "root"
if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, "z", "\x00", false}); err != nil {
cx.t.Fatal(err)
}

// try the granted open ended permission
cx.user, cx.pass = "test-user", "pass"
for i := 0; i < 10; i++ {
key := fmt.Sprintf("z%d", i)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can stress the >= range a bit more with \xff's:

key := "\xff"
for j := 0; j < i; j++ {
    key += "\xff"
}

if err := ctlV3Put(cx, key, "val", ""); err != nil {
cx.t.Fatal(err)
}
}
largeKey := ""
for i := 0; i < 10; i++ {
largeKey += "\xff"
if err := ctlV3Put(cx, largeKey, "val", ""); err != nil {
cx.t.Fatal(err)
}
}

// try a non granted key
if err := ctlV3PutFailPerm(cx, "x", "baz"); err != nil {
cx.t.Fatal(err)
}

// revoke the open ended permission
cx.user, cx.pass = "root", "root"
if err := ctlV3RoleRevokePermission(cx, "test-role", "z", "", true); err != nil {
cx.t.Fatal(err)
}

// try the revoked open ended permission
cx.user, cx.pass = "test-user", "pass"
for i := 0; i < 10; i++ {
key := fmt.Sprintf("z%d", i)
if err := ctlV3PutFailPerm(cx, key, "val"); err != nil {
cx.t.Fatal(err)
}
}
}
18 changes: 16 additions & 2 deletions e2e/ctl_v3_role_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,10 @@ func ctlV3RoleGrantPermission(cx ctlCtx, rolename string, perm grantingPerm) err
cmdArgs := append(cx.PrefixArgs(), "role", "grant-permission")
if perm.prefix {
cmdArgs = append(cmdArgs, "--prefix")
} else if len(perm.rangeEnd) == 1 && perm.rangeEnd[0] == '\x00' {
cmdArgs = append(cmdArgs, "--from-key")
}

cmdArgs = append(cmdArgs, rolename)
cmdArgs = append(cmdArgs, grantingPermToArgs(perm)...)

Expand All @@ -117,20 +120,26 @@ func ctlV3RoleGrantPermission(cx ctlCtx, rolename string, perm grantingPerm) err
return err
}

func ctlV3RoleRevokePermission(cx ctlCtx, rolename string, key, rangeEnd string) error {
func ctlV3RoleRevokePermission(cx ctlCtx, rolename string, key, rangeEnd string, fromKey bool) error {
cmdArgs := append(cx.PrefixArgs(), "role", "revoke-permission")
cmdArgs = append(cmdArgs, rolename)
cmdArgs = append(cmdArgs, key)
expStr := ""
if len(rangeEnd) != 0 {
cmdArgs = append(cmdArgs, rangeEnd)
expStr = fmt.Sprintf("Permission of range [%s, %s) is revoked from role %s", key, rangeEnd, rolename)
} else if fromKey {
cmdArgs = append(cmdArgs, "--from-key")
expStr = fmt.Sprintf("Permission of range [%s, <open ended> is revoked from role %s", key, rolename)
} else {
expStr = fmt.Sprintf("Permission of key %s is revoked from role %s", key, rolename)
}

proc, err := spawnCmd(cmdArgs)
if err != nil {
return err
}

expStr := fmt.Sprintf("Permission of key %s is revoked from role %s", key, rolename)
_, err = proc.Expect(expStr)
return err
}
Expand Down Expand Up @@ -161,5 +170,10 @@ func grantingPermToArgs(perm grantingPerm) []string {
if len(perm.rangeEnd) == 0 {
return []string{permstr, perm.key}
}

if len(perm.rangeEnd) == 1 && perm.rangeEnd[0] == '\x00' {
return []string{permstr, perm.key}
}

return []string{permstr, perm.key, perm.rangeEnd}
}
12 changes: 10 additions & 2 deletions etcdctl/ctlv3/command/printer_simple.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,11 @@ func (s *simplePrinter) RoleGet(role string, r v3.AuthRoleGetResponse) {
printRange := func(perm *v3.Permission) {
sKey := string(perm.Key)
sRangeEnd := string(perm.RangeEnd)
fmt.Printf("\t[%s, %s)", sKey, sRangeEnd)
if strings.Compare(sRangeEnd, "\x00") != 0 {
fmt.Printf("\t[%s, %s)", sKey, sRangeEnd)
} else {
fmt.Printf("\t[%s, <open ended>", sKey)
}
if strings.Compare(v3.GetPrefixRangeEnd(sKey), sRangeEnd) == 0 {
fmt.Printf(" (prefix %s)", sKey)
}
Expand Down Expand Up @@ -188,7 +192,11 @@ func (s *simplePrinter) RoleRevokePermission(role string, key string, end string
fmt.Printf("Permission of key %s is revoked from role %s\n", key, role)
return
}
fmt.Printf("Permission of range [%s, %s) is revoked from role %s\n", key, end, role)
if strings.Compare(end, "\x00") != 0 {
fmt.Printf("Permission of range [%s, %s) is revoked from role %s\n", key, end, role)
} else {
fmt.Printf("Permission of range [%s, <open ended> is revoked from role %s\n", key, role)
}
}

func (s *simplePrinter) UserAdd(name string, r v3.AuthUserAddResponse) {
Expand Down
21 changes: 20 additions & 1 deletion etcdctl/ctlv3/command/role_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

var (
grantPermissionPrefix bool
permFromKey bool
)

// NewRoleCommand returns the cobra command for "role".
Expand Down Expand Up @@ -83,16 +84,21 @@ func newRoleGrantPermissionCommand() *cobra.Command {
}

cmd.Flags().BoolVar(&grantPermissionPrefix, "prefix", false, "grant a prefix permission")
cmd.Flags().BoolVar(&permFromKey, "from-key", false, "grant a permission of keys that are greater than or equal to the given key using byte compare")

return cmd
}

func newRoleRevokePermissionCommand() *cobra.Command {
return &cobra.Command{
cmd := &cobra.Command{
Use: "revoke-permission <role name> <key> [endkey]",
Short: "Revokes a key from a role",
Run: roleRevokePermissionCommandFunc,
}

cmd.Flags().BoolVar(&permFromKey, "from-key", false, "grant a permission of keys that are greater than or equal to the given key using byte compare")

return cmd
}

// roleAddCommandFunc executes the "role add" command.
Expand Down Expand Up @@ -168,9 +174,20 @@ func roleGrantPermissionCommandFunc(cmd *cobra.Command, args []string) {
if grantPermissionPrefix {
ExitWithError(ExitBadArgs, fmt.Errorf("don't pass both of --prefix option and range end to grant permission command"))
}

if permFromKey {
ExitWithError(ExitBadArgs, fmt.Errorf("don't pass both of --from-key option and range end to grant permission command"))
}

rangeEnd = args[3]
} else if grantPermissionPrefix {
if permFromKey {
ExitWithError(ExitBadArgs, fmt.Errorf("don't pass both of --from-key option and --prefix option to grant permission command"))
}

rangeEnd = clientv3.GetPrefixRangeEnd(args[2])
} else if permFromKey {
rangeEnd = "\x00"
}

resp, err := mustClientFromCmd(cmd).Auth.RoleGrantPermission(context.TODO(), args[0], args[2], rangeEnd, perm)
Expand All @@ -190,6 +207,8 @@ func roleRevokePermissionCommandFunc(cmd *cobra.Command, args []string) {
rangeEnd := ""
if 3 <= len(args) {
rangeEnd = args[2]
} else if permFromKey {
rangeEnd = "\x00"
}

resp, err := mustClientFromCmd(cmd).Auth.RoleRevokePermission(context.TODO(), args[0], args[1], rangeEnd)
Expand Down