etcd/auth/range_perm_cache.go

155 lines
3.8 KiB
Go
Raw Normal View History

// 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"
)
2016-06-11 02:36:51 +03:00
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 {
2016-06-10 20:59:34 +03:00
role := getRole(tx, roleName)
if role == nil {
continue
}
for _, perm := range role.KeyPermission {
var ivl adt.Interval
if len(perm.RangeEnd) != 0 {
ivl = adt.NewStringInterval(string(perm.Key), string(perm.RangeEnd))
} else {
ivl = adt.NewStringPoint(string(perm.Key))
}
2016-06-11 02:36:51 +03:00
switch perm.PermType {
case authpb.READWRITE:
readPerms.Insert(ivl, struct{}{})
writePerms.Insert(ivl, struct{}{})
2016-06-11 02:36:51 +03:00
case authpb.READ:
readPerms.Insert(ivl, struct{}{})
2016-06-11 02:36:51 +03:00
case authpb.WRITE:
writePerms.Insert(ivl, struct{}{})
}
}
}
2016-06-11 02:36:51 +03:00
return &unifiedRangePermissions{
readPerms: readPerms,
writePerms: writePerms,
2016-06-11 02:36:51 +03:00
}
}
func checkKeyInterval(cachedPerms *unifiedRangePermissions, key, rangeEnd string, permtyp authpb.Permission_Type) bool {
var tocheck *adt.IntervalTree
2016-06-11 05:18:07 +03:00
switch permtyp {
case authpb.READ:
tocheck = cachedPerms.readPerms
case authpb.WRITE:
tocheck = cachedPerms.writePerms
default:
plog.Panicf("unknown auth type: %v", permtyp)
}
ivl := adt.NewStringInterval(key, rangeEnd)
isContiguous := true
var maxEnd, minBegin adt.Comparable
2016-06-11 05:18:07 +03:00
tocheck.Visit(ivl, func(n *adt.IntervalValue) bool {
if minBegin == nil {
minBegin = n.Ivl.Begin
maxEnd = n.Ivl.End
2016-06-11 05:18:07 +03:00
return true
}
if maxEnd.Compare(n.Ivl.Begin) < 0 {
isContiguous = false
return false
}
if n.Ivl.End.Compare(maxEnd) > 0 {
maxEnd = n.Ivl.End
}
return true
})
return isContiguous && maxEnd.Compare(ivl.End) >= 0 && minBegin.Compare(ivl.Begin) <= 0
}
func checkKeyPoint(cachedPerms *unifiedRangePermissions, key string, permtyp authpb.Permission_Type) bool {
var tocheck *adt.IntervalTree
switch permtyp {
case authpb.READ:
tocheck = cachedPerms.readPerms
case authpb.WRITE:
tocheck = cachedPerms.writePerms
default:
plog.Panicf("unknown auth type: %v", permtyp)
}
pt := adt.NewStringPoint(key)
return tocheck.Contains(pt)
}
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]
2016-06-11 05:18:07 +03:00
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
}