1 /* 2 Copyright 2021 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 authentication 18 19 import ( 20 "context" 21 "errors" 22 "net/http" 23 "sync" 24 25 "github.com/go-logr/logr" 26 authenticationv1 "k8s.io/api/authentication/v1" 27 "k8s.io/klog/v2" 28 29 logf "sigs.k8s.io/controller-runtime/pkg/log" 30 ) 31 32 var ( 33 errUnableToEncodeResponse = errors.New("unable to encode response") 34 ) 35 36 // Request defines the input for an authentication handler. 37 // It contains information to identify the object in 38 // question (group, version, kind, resource, subresource, 39 // name, namespace), as well as the operation in question 40 // (e.g. Get, Create, etc), and the object itself. 41 type Request struct { 42 authenticationv1.TokenReview 43 } 44 45 // Response is the output of an authentication handler. 46 // It contains a response indicating if a given 47 // operation is allowed. 48 type Response struct { 49 authenticationv1.TokenReview 50 } 51 52 // Complete populates any fields that are yet to be set in 53 // the underlying TokenResponse, It mutates the response. 54 func (r *Response) Complete(req Request) error { 55 r.UID = req.UID 56 57 return nil 58 } 59 60 // Handler can handle an TokenReview. 61 type Handler interface { 62 // Handle yields a response to an TokenReview. 63 // 64 // The supplied context is extracted from the received http.Request, allowing wrapping 65 // http.Handlers to inject values into and control cancelation of downstream request processing. 66 Handle(context.Context, Request) Response 67 } 68 69 // HandlerFunc implements Handler interface using a single function. 70 type HandlerFunc func(context.Context, Request) Response 71 72 var _ Handler = HandlerFunc(nil) 73 74 // Handle process the TokenReview by invoking the underlying function. 75 func (f HandlerFunc) Handle(ctx context.Context, req Request) Response { 76 return f(ctx, req) 77 } 78 79 // Webhook represents each individual webhook. 80 type Webhook struct { 81 // Handler actually processes an authentication request returning whether it was authenticated or unauthenticated, 82 // and potentially patches to apply to the handler. 83 Handler Handler 84 85 // WithContextFunc will allow you to take the http.Request.Context() and 86 // add any additional information such as passing the request path or 87 // headers thus allowing you to read them from within the handler 88 WithContextFunc func(context.Context, *http.Request) context.Context 89 90 setupLogOnce sync.Once 91 log logr.Logger 92 } 93 94 // Handle processes TokenReview. 95 func (wh *Webhook) Handle(ctx context.Context, req Request) Response { 96 resp := wh.Handler.Handle(ctx, req) 97 if err := resp.Complete(req); err != nil { 98 wh.getLogger(&req).Error(err, "unable to encode response") 99 return Errored(errUnableToEncodeResponse) 100 } 101 102 return resp 103 } 104 105 // getLogger constructs a logger from the injected log and LogConstructor. 106 func (wh *Webhook) getLogger(req *Request) logr.Logger { 107 wh.setupLogOnce.Do(func() { 108 if wh.log.GetSink() == nil { 109 wh.log = logf.Log.WithName("authentication") 110 } 111 }) 112 113 return logConstructor(wh.log, req) 114 } 115 116 // logConstructor adds some commonly interesting fields to the given logger. 117 func logConstructor(base logr.Logger, req *Request) logr.Logger { 118 if req != nil { 119 return base.WithValues("object", klog.KRef(req.Namespace, req.Name), 120 "namespace", req.Namespace, "name", req.Name, 121 "user", req.Status.User.Username, 122 "requestID", req.UID, 123 ) 124 } 125 return base 126 } 127