blob: 691b65ba38e7439859656ee7cdaca5820cc558b3 [file] [log] [blame]
khenaidooffe076b2019-01-15 16:08:08 -05001// Copyright 2016 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package auth
16
17import (
18 "github.com/coreos/etcd/auth/authpb"
19 "github.com/coreos/etcd/mvcc/backend"
20 "github.com/coreos/etcd/pkg/adt"
21)
22
23func getMergedPerms(tx backend.BatchTx, userName string) *unifiedRangePermissions {
24 user := getUser(tx, userName)
25 if user == nil {
26 plog.Errorf("invalid user name %s", userName)
27 return nil
28 }
29
30 readPerms := &adt.IntervalTree{}
31 writePerms := &adt.IntervalTree{}
32
33 for _, roleName := range user.Roles {
34 role := getRole(tx, roleName)
35 if role == nil {
36 continue
37 }
38
39 for _, perm := range role.KeyPermission {
40 var ivl adt.Interval
41 var rangeEnd []byte
42
43 if len(perm.RangeEnd) != 1 || perm.RangeEnd[0] != 0 {
44 rangeEnd = perm.RangeEnd
45 }
46
47 if len(perm.RangeEnd) != 0 {
48 ivl = adt.NewBytesAffineInterval(perm.Key, rangeEnd)
49 } else {
50 ivl = adt.NewBytesAffinePoint(perm.Key)
51 }
52
53 switch perm.PermType {
54 case authpb.READWRITE:
55 readPerms.Insert(ivl, struct{}{})
56 writePerms.Insert(ivl, struct{}{})
57
58 case authpb.READ:
59 readPerms.Insert(ivl, struct{}{})
60
61 case authpb.WRITE:
62 writePerms.Insert(ivl, struct{}{})
63 }
64 }
65 }
66
67 return &unifiedRangePermissions{
68 readPerms: readPerms,
69 writePerms: writePerms,
70 }
71}
72
73func checkKeyInterval(cachedPerms *unifiedRangePermissions, key, rangeEnd []byte, permtyp authpb.Permission_Type) bool {
74 if len(rangeEnd) == 1 && rangeEnd[0] == 0 {
75 rangeEnd = nil
76 }
77
78 ivl := adt.NewBytesAffineInterval(key, rangeEnd)
79 switch permtyp {
80 case authpb.READ:
81 return cachedPerms.readPerms.Contains(ivl)
82 case authpb.WRITE:
83 return cachedPerms.writePerms.Contains(ivl)
84 default:
85 plog.Panicf("unknown auth type: %v", permtyp)
86 }
87 return false
88}
89
90func checkKeyPoint(cachedPerms *unifiedRangePermissions, key []byte, permtyp authpb.Permission_Type) bool {
91 pt := adt.NewBytesAffinePoint(key)
92 switch permtyp {
93 case authpb.READ:
94 return cachedPerms.readPerms.Intersects(pt)
95 case authpb.WRITE:
96 return cachedPerms.writePerms.Intersects(pt)
97 default:
98 plog.Panicf("unknown auth type: %v", permtyp)
99 }
100 return false
101}
102
103func (as *authStore) isRangeOpPermitted(tx backend.BatchTx, userName string, key, rangeEnd []byte, permtyp authpb.Permission_Type) bool {
104 // assumption: tx is Lock()ed
105 _, ok := as.rangePermCache[userName]
106 if !ok {
107 perms := getMergedPerms(tx, userName)
108 if perms == nil {
109 plog.Errorf("failed to create a unified permission of user %s", userName)
110 return false
111 }
112 as.rangePermCache[userName] = perms
113 }
114
115 if len(rangeEnd) == 0 {
116 return checkKeyPoint(as.rangePermCache[userName], key, permtyp)
117 }
118
119 return checkKeyInterval(as.rangePermCache[userName], key, rangeEnd, permtyp)
120}
121
122func (as *authStore) clearCachedPerm() {
123 as.rangePermCache = make(map[string]*unifiedRangePermissions)
124}
125
126func (as *authStore) invalidateCachedPerm(userName string) {
127 delete(as.rangePermCache, userName)
128}
129
130type unifiedRangePermissions struct {
131 readPerms *adt.IntervalTree
132 writePerms *adt.IntervalTree
133}