// 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 ( "go.etcd.io/etcd/auth/authpb" "go.etcd.io/etcd/mvcc/backend" "go.etcd.io/etcd/pkg/adt" "go.uber.org/zap" ) func getMergedPerms(lg *zap.Logger, tx backend.BatchTx, userName string) *unifiedRangePermissions { user := getUser(lg, tx, userName) if user == nil { return nil } readPerms := adt.NewIntervalTree() writePerms := adt.NewIntervalTree() for _, roleName := range user.Roles { role := getRole(tx, roleName) if role == nil { continue } for _, perm := range role.KeyPermission { var ivl adt.Interval var rangeEnd []byte if len(perm.RangeEnd) != 1 || perm.RangeEnd[0] != 0 { rangeEnd = perm.RangeEnd } if len(perm.RangeEnd) != 0 { ivl = adt.NewBytesAffineInterval(perm.Key, rangeEnd) } else { ivl = adt.NewBytesAffinePoint(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( lg *zap.Logger, cachedPerms *unifiedRangePermissions, key, rangeEnd []byte, permtyp authpb.Permission_Type) bool { if len(rangeEnd) == 1 && rangeEnd[0] == 0 { rangeEnd = nil } ivl := adt.NewBytesAffineInterval(key, rangeEnd) switch permtyp { case authpb.READ: return cachedPerms.readPerms.Contains(ivl) case authpb.WRITE: return cachedPerms.writePerms.Contains(ivl) default: if lg != nil { lg.Panic("unknown auth type", zap.String("auth-type", permtyp.String())) } else { plog.Panicf("unknown auth type: %v", permtyp) } } return false } func checkKeyPoint(lg *zap.Logger, cachedPerms *unifiedRangePermissions, key []byte, permtyp authpb.Permission_Type) bool { pt := adt.NewBytesAffinePoint(key) switch permtyp { case authpb.READ: return cachedPerms.readPerms.Intersects(pt) case authpb.WRITE: return cachedPerms.writePerms.Intersects(pt) default: if lg != nil { lg.Panic("unknown auth type", zap.String("auth-type", permtyp.String())) } else { plog.Panicf("unknown auth type: %v", permtyp) } } return false } func (as *authStore) isRangeOpPermitted(tx backend.BatchTx, userName string, key, rangeEnd []byte, permtyp authpb.Permission_Type) bool { // assumption: tx is Lock()ed _, ok := as.rangePermCache[userName] if !ok { perms := getMergedPerms(as.lg, tx, userName) if perms == nil { if as.lg != nil { as.lg.Warn( "failed to create a merged permission", zap.String("user-name", userName), ) } else { 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.lg, as.rangePermCache[userName], key, permtyp) } return checkKeyInterval(as.lg, 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 }