k8s.io/apiserver@v0.31.1/pkg/admission/plugin/policy/validating/caching_authorizer.go (about) 1 /* 2 Copyright 2023 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package validating 18 19 import ( 20 "context" 21 "encoding/json" 22 "sort" 23 "strings" 24 25 "k8s.io/apimachinery/pkg/fields" 26 "k8s.io/apimachinery/pkg/labels" 27 "k8s.io/apiserver/pkg/authentication/user" 28 "k8s.io/apiserver/pkg/authorization/authorizer" 29 ) 30 31 type authzResult struct { 32 authorized authorizer.Decision 33 reason string 34 err error 35 } 36 37 type cachingAuthorizer struct { 38 authorizer authorizer.Authorizer 39 decisions map[string]authzResult 40 } 41 42 func newCachingAuthorizer(in authorizer.Authorizer) authorizer.Authorizer { 43 return &cachingAuthorizer{ 44 authorizer: in, 45 decisions: make(map[string]authzResult), 46 } 47 } 48 49 // The attribute accessors known to cache key construction. If this fails to compile, the cache 50 // implementation may need to be updated. 51 var _ authorizer.Attributes = (interface { 52 GetUser() user.Info 53 GetVerb() string 54 IsReadOnly() bool 55 GetNamespace() string 56 GetResource() string 57 GetSubresource() string 58 GetName() string 59 GetAPIGroup() string 60 GetAPIVersion() string 61 IsResourceRequest() bool 62 GetPath() string 63 GetFieldSelector() (fields.Requirements, error) 64 GetLabelSelector() (labels.Requirements, error) 65 })(nil) 66 67 // The user info accessors known to cache key construction. If this fails to compile, the cache 68 // implementation may need to be updated. 69 var _ user.Info = (interface { 70 GetName() string 71 GetUID() string 72 GetGroups() []string 73 GetExtra() map[string][]string 74 })(nil) 75 76 // Authorize returns an authorization decision by delegating to another Authorizer. If an equivalent 77 // check has already been performed, a cached result is returned. Not safe for concurrent use. 78 func (ca *cachingAuthorizer) Authorize(ctx context.Context, a authorizer.Attributes) (authorizer.Decision, string, error) { 79 type SerializableAttributes struct { 80 authorizer.AttributesRecord 81 LabelSelector string 82 } 83 84 serializableAttributes := SerializableAttributes{ 85 AttributesRecord: authorizer.AttributesRecord{ 86 Verb: a.GetVerb(), 87 Namespace: a.GetNamespace(), 88 APIGroup: a.GetAPIGroup(), 89 APIVersion: a.GetAPIVersion(), 90 Resource: a.GetResource(), 91 Subresource: a.GetSubresource(), 92 Name: a.GetName(), 93 ResourceRequest: a.IsResourceRequest(), 94 Path: a.GetPath(), 95 }, 96 } 97 // in the error case, we won't honor this field selector, so the cache doesn't need it. 98 if fieldSelector, err := a.GetFieldSelector(); len(fieldSelector) > 0 { 99 serializableAttributes.FieldSelectorRequirements, serializableAttributes.FieldSelectorParsingErr = fieldSelector, err 100 } 101 if labelSelector, _ := a.GetLabelSelector(); len(labelSelector) > 0 { 102 // the labels requirements have private elements so those don't help us serialize to a unique key 103 serializableAttributes.LabelSelector = labelSelector.String() 104 } 105 106 if u := a.GetUser(); u != nil { 107 di := &user.DefaultInfo{ 108 Name: u.GetName(), 109 UID: u.GetUID(), 110 } 111 112 // Differently-ordered groups or extras could cause otherwise-equivalent checks to 113 // have distinct cache keys. 114 if groups := u.GetGroups(); len(groups) > 0 { 115 di.Groups = make([]string, len(groups)) 116 copy(di.Groups, groups) 117 sort.Strings(di.Groups) 118 } 119 120 if extra := u.GetExtra(); len(extra) > 0 { 121 di.Extra = make(map[string][]string, len(extra)) 122 for k, vs := range extra { 123 vdupe := make([]string, len(vs)) 124 copy(vdupe, vs) 125 sort.Strings(vdupe) 126 di.Extra[k] = vdupe 127 } 128 } 129 130 serializableAttributes.User = di 131 } 132 133 var b strings.Builder 134 if err := json.NewEncoder(&b).Encode(serializableAttributes); err != nil { 135 return authorizer.DecisionNoOpinion, "", err 136 } 137 key := b.String() 138 139 if cached, ok := ca.decisions[key]; ok { 140 return cached.authorized, cached.reason, cached.err 141 } 142 143 authorized, reason, err := ca.authorizer.Authorize(ctx, a) 144 145 ca.decisions[key] = authzResult{ 146 authorized: authorized, 147 reason: reason, 148 err: err, 149 } 150 151 return authorized, reason, err 152 }