mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00

This commit adds a new option --from-key to the command etcdctl role grant-permission. If the option is passed, an open ended permission will be granted to a role e.g. from start-key to any keys those are larger than start-key. Example: $ ETCDCTL_API=3 bin/etcdctl --user root:p role grant r1 readwrite a b $ ETCDCTL_API=3 bin/etcdctl --user root:p role grant --from-key r1 readwrite c $ ETCDCTL_API=3 bin/etcdctl --user root:p role get r1 Role r1 KV Read: [a, b) (prefix a) [c, <open ended> KV Write: [a, b) (prefix a) [c, <open ended> Note that a closed parenthesis doesn't follow the above <open ended> for indicating that the role has an open ended permission ("<open ended>" is a valid range end). Fixes https://github.com/coreos/etcd/issues/7468
136 lines
3.5 KiB
Go
136 lines
3.5 KiB
Go
// Copyright 2016 The etcd Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package auth
|
|
|
|
import (
|
|
"github.com/coreos/etcd/auth/authpb"
|
|
"github.com/coreos/etcd/mvcc/backend"
|
|
"github.com/coreos/etcd/pkg/adt"
|
|
)
|
|
|
|
func getMergedPerms(tx backend.BatchTx, userName string) *unifiedRangePermissions {
|
|
user := getUser(tx, userName)
|
|
if user == nil {
|
|
plog.Errorf("invalid user name %s", userName)
|
|
return nil
|
|
}
|
|
|
|
readPerms := &adt.IntervalTree{}
|
|
writePerms := &adt.IntervalTree{}
|
|
|
|
for _, roleName := range user.Roles {
|
|
role := getRole(tx, roleName)
|
|
if role == nil {
|
|
continue
|
|
}
|
|
|
|
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.NewStringAffineInterval(string(perm.Key), string(rangeEnd))
|
|
} else {
|
|
ivl = adt.NewStringAffinePoint(string(perm.Key))
|
|
}
|
|
|
|
switch perm.PermType {
|
|
case authpb.READWRITE:
|
|
readPerms.Insert(ivl, struct{}{})
|
|
writePerms.Insert(ivl, struct{}{})
|
|
|
|
case authpb.READ:
|
|
readPerms.Insert(ivl, struct{}{})
|
|
|
|
case authpb.WRITE:
|
|
writePerms.Insert(ivl, struct{}{})
|
|
}
|
|
}
|
|
}
|
|
|
|
return &unifiedRangePermissions{
|
|
readPerms: readPerms,
|
|
writePerms: writePerms,
|
|
}
|
|
}
|
|
|
|
func checkKeyInterval(cachedPerms *unifiedRangePermissions, key, rangeEnd string, permtyp authpb.Permission_Type) bool {
|
|
if len(rangeEnd) == 1 && rangeEnd[0] == '\x00' {
|
|
rangeEnd = ""
|
|
}
|
|
|
|
ivl := adt.NewStringAffineInterval(key, rangeEnd)
|
|
switch permtyp {
|
|
case authpb.READ:
|
|
return cachedPerms.readPerms.Contains(ivl)
|
|
case authpb.WRITE:
|
|
return cachedPerms.writePerms.Contains(ivl)
|
|
default:
|
|
plog.Panicf("unknown auth type: %v", permtyp)
|
|
}
|
|
return false
|
|
}
|
|
|
|
func checkKeyPoint(cachedPerms *unifiedRangePermissions, key string, permtyp authpb.Permission_Type) bool {
|
|
pt := adt.NewStringAffinePoint(key)
|
|
switch permtyp {
|
|
case authpb.READ:
|
|
return cachedPerms.readPerms.Intersects(pt)
|
|
case authpb.WRITE:
|
|
return cachedPerms.writePerms.Intersects(pt)
|
|
default:
|
|
plog.Panicf("unknown auth type: %v", permtyp)
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (as *authStore) isRangeOpPermitted(tx backend.BatchTx, userName string, key, rangeEnd string, permtyp authpb.Permission_Type) bool {
|
|
// assumption: tx is Lock()ed
|
|
_, ok := as.rangePermCache[userName]
|
|
if !ok {
|
|
perms := getMergedPerms(tx, userName)
|
|
if perms == nil {
|
|
plog.Errorf("failed to create a unified permission of user %s", userName)
|
|
return false
|
|
}
|
|
as.rangePermCache[userName] = perms
|
|
}
|
|
|
|
if len(rangeEnd) == 0 {
|
|
return checkKeyPoint(as.rangePermCache[userName], key, permtyp)
|
|
}
|
|
|
|
return checkKeyInterval(as.rangePermCache[userName], key, rangeEnd, permtyp)
|
|
}
|
|
|
|
func (as *authStore) clearCachedPerm() {
|
|
as.rangePermCache = make(map[string]*unifiedRangePermissions)
|
|
}
|
|
|
|
func (as *authStore) invalidateCachedPerm(userName string) {
|
|
delete(as.rangePermCache, userName)
|
|
}
|
|
|
|
type unifiedRangePermissions struct {
|
|
readPerms *adt.IntervalTree
|
|
writePerms *adt.IntervalTree
|
|
}
|