blob: 7b6c182409c77766f2c85831e54fb37e18e83a05 [file] [log] [blame]
khenaidoo59ce9dd2019-11-11 13:05:32 -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 "go.etcd.io/etcd/auth/authpb"
19 "go.etcd.io/etcd/mvcc/backend"
20 "go.etcd.io/etcd/pkg/adt"
21
22 "go.uber.org/zap"
23)
24
25func getMergedPerms(lg *zap.Logger, tx backend.BatchTx, userName string) *unifiedRangePermissions {
26 user := getUser(lg, tx, userName)
27 if user == nil {
28 return nil
29 }
30
31 readPerms := adt.NewIntervalTree()
32 writePerms := adt.NewIntervalTree()
33
34 for _, roleName := range user.Roles {
35 role := getRole(tx, roleName)
36 if role == nil {
37 continue
38 }
39
40 for _, perm := range role.KeyPermission {
41 var ivl adt.Interval
42 var rangeEnd []byte
43
44 if len(perm.RangeEnd) != 1 || perm.RangeEnd[0] != 0 {
45 rangeEnd = perm.RangeEnd
46 }
47
48 if len(perm.RangeEnd) != 0 {
49 ivl = adt.NewBytesAffineInterval(perm.Key, rangeEnd)
50 } else {
51 ivl = adt.NewBytesAffinePoint(perm.Key)
52 }
53
54 switch perm.PermType {
55 case authpb.READWRITE:
56 readPerms.Insert(ivl, struct{}{})
57 writePerms.Insert(ivl, struct{}{})
58
59 case authpb.READ:
60 readPerms.Insert(ivl, struct{}{})
61
62 case authpb.WRITE:
63 writePerms.Insert(ivl, struct{}{})
64 }
65 }
66 }
67
68 return &unifiedRangePermissions{
69 readPerms: readPerms,
70 writePerms: writePerms,
71 }
72}
73
74func checkKeyInterval(
75 lg *zap.Logger,
76 cachedPerms *unifiedRangePermissions,
77 key, rangeEnd []byte,
78 permtyp authpb.Permission_Type) bool {
79 if len(rangeEnd) == 1 && rangeEnd[0] == 0 {
80 rangeEnd = nil
81 }
82
83 ivl := adt.NewBytesAffineInterval(key, rangeEnd)
84 switch permtyp {
85 case authpb.READ:
86 return cachedPerms.readPerms.Contains(ivl)
87 case authpb.WRITE:
88 return cachedPerms.writePerms.Contains(ivl)
89 default:
90 if lg != nil {
91 lg.Panic("unknown auth type", zap.String("auth-type", permtyp.String()))
92 } else {
93 plog.Panicf("unknown auth type: %v", permtyp)
94 }
95 }
96 return false
97}
98
99func checkKeyPoint(lg *zap.Logger, cachedPerms *unifiedRangePermissions, key []byte, permtyp authpb.Permission_Type) bool {
100 pt := adt.NewBytesAffinePoint(key)
101 switch permtyp {
102 case authpb.READ:
103 return cachedPerms.readPerms.Intersects(pt)
104 case authpb.WRITE:
105 return cachedPerms.writePerms.Intersects(pt)
106 default:
107 if lg != nil {
108 lg.Panic("unknown auth type", zap.String("auth-type", permtyp.String()))
109 } else {
110 plog.Panicf("unknown auth type: %v", permtyp)
111 }
112 }
113 return false
114}
115
116func (as *authStore) isRangeOpPermitted(tx backend.BatchTx, userName string, key, rangeEnd []byte, permtyp authpb.Permission_Type) bool {
117 // assumption: tx is Lock()ed
118 _, ok := as.rangePermCache[userName]
119 if !ok {
120 perms := getMergedPerms(as.lg, tx, userName)
121 if perms == nil {
122 if as.lg != nil {
123 as.lg.Warn(
124 "failed to create a merged permission",
125 zap.String("user-name", userName),
126 )
127 } else {
128 plog.Errorf("failed to create a unified permission of user %s", userName)
129 }
130 return false
131 }
132 as.rangePermCache[userName] = perms
133 }
134
135 if len(rangeEnd) == 0 {
136 return checkKeyPoint(as.lg, as.rangePermCache[userName], key, permtyp)
137 }
138
139 return checkKeyInterval(as.lg, as.rangePermCache[userName], key, rangeEnd, permtyp)
140}
141
142func (as *authStore) clearCachedPerm() {
143 as.rangePermCache = make(map[string]*unifiedRangePermissions)
144}
145
146func (as *authStore) invalidateCachedPerm(userName string) {
147 delete(as.rangePermCache, userName)
148}
149
150type unifiedRangePermissions struct {
151 readPerms adt.IntervalTree
152 writePerms adt.IntervalTree
153}