155 lines
3.8 KiB
Go
155 lines
3.8 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
|
|
|
|
if len(perm.RangeEnd) != 0 {
|
|
ivl = adt.NewStringInterval(string(perm.Key), string(perm.RangeEnd))
|
|
} else {
|
|
ivl = adt.NewStringPoint(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 {
|
|
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)
|
|
}
|
|
|
|
ivl := adt.NewStringInterval(key, rangeEnd)
|
|
|
|
isContiguous := true
|
|
var maxEnd, minBegin adt.Comparable
|
|
|
|
tocheck.Visit(ivl, func(n *adt.IntervalValue) bool {
|
|
if minBegin == nil {
|
|
minBegin = n.Ivl.Begin
|
|
maxEnd = n.Ivl.End
|
|
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]
|
|
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
|
|
}
|