k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/plugin/pkg/admission/certificates/approval/admission_test.go (about) 1 /* 2 Copyright 2020 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 approval 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "testing" 24 25 "k8s.io/apimachinery/pkg/runtime" 26 "k8s.io/apimachinery/pkg/runtime/schema" 27 "k8s.io/apiserver/pkg/admission" 28 "k8s.io/apiserver/pkg/authentication/user" 29 "k8s.io/apiserver/pkg/authorization/authorizer" 30 31 certificatesapi "k8s.io/kubernetes/pkg/apis/certificates" 32 ) 33 34 func TestPlugin_Validate(t *testing.T) { 35 tests := map[string]struct { 36 attributes admission.Attributes 37 allowedName string 38 allowed bool 39 authzErr error 40 }{ 41 "wrong type": { 42 attributes: &testAttributes{ 43 resource: certificatesapi.Resource("certificatesigningrequests"), 44 subresource: "approval", 45 oldObj: &certificatesapi.CertificateSigningRequestList{}, 46 operation: admission.Update, 47 }, 48 allowed: false, 49 }, 50 "reject requests if looking up permissions fails": { 51 attributes: &testAttributes{ 52 resource: certificatesapi.Resource("certificatesigningrequests"), 53 subresource: "approval", 54 oldObj: &certificatesapi.CertificateSigningRequest{Spec: certificatesapi.CertificateSigningRequestSpec{ 55 SignerName: "abc.com/xyz", 56 }}, 57 operation: admission.Update, 58 }, 59 authzErr: errors.New("forced error"), 60 allowed: false, 61 }, 62 "should allow request if user is authorized for specific signerName": { 63 allowedName: "abc.com/xyz", 64 attributes: &testAttributes{ 65 resource: certificatesapi.Resource("certificatesigningrequests"), 66 subresource: "approval", 67 oldObj: &certificatesapi.CertificateSigningRequest{Spec: certificatesapi.CertificateSigningRequestSpec{ 68 SignerName: "abc.com/xyz", 69 }}, 70 operation: admission.Update, 71 }, 72 allowed: true, 73 }, 74 "should allow request if user is authorized with wildcard": { 75 allowedName: "abc.com/*", 76 attributes: &testAttributes{ 77 resource: certificatesapi.Resource("certificatesigningrequests"), 78 subresource: "approval", 79 oldObj: &certificatesapi.CertificateSigningRequest{Spec: certificatesapi.CertificateSigningRequestSpec{ 80 SignerName: "abc.com/xyz", 81 }}, 82 operation: admission.Update, 83 }, 84 allowed: true, 85 }, 86 "should deny request if user does not have permission for this signerName": { 87 allowedName: "notabc.com/xyz", 88 attributes: &testAttributes{ 89 resource: certificatesapi.Resource("certificatesigningrequests"), 90 subresource: "approval", 91 oldObj: &certificatesapi.CertificateSigningRequest{Spec: certificatesapi.CertificateSigningRequestSpec{ 92 SignerName: "abc.com/xyz", 93 }}, 94 operation: admission.Update, 95 }, 96 allowed: false, 97 }, 98 "should deny request if user attempts to update signerName to a new value they *do* have permission to approve for": { 99 allowedName: "allowed.com/xyz", 100 attributes: &testAttributes{ 101 resource: certificatesapi.Resource("certificatesigningrequests"), 102 subresource: "approval", 103 oldObj: &certificatesapi.CertificateSigningRequest{Spec: certificatesapi.CertificateSigningRequestSpec{ 104 SignerName: "notallowed.com/xyz", 105 }}, 106 obj: &certificatesapi.CertificateSigningRequest{Spec: certificatesapi.CertificateSigningRequestSpec{ 107 SignerName: "allowed.com/xyz", 108 }}, 109 operation: admission.Update, 110 }, 111 allowed: false, 112 }, 113 } 114 115 for n, test := range tests { 116 t.Run(n, func(t *testing.T) { 117 p := Plugin{ 118 authz: fakeAuthorizer{ 119 t: t, 120 verb: "approve", 121 allowedName: test.allowedName, 122 decision: authorizer.DecisionAllow, 123 err: test.authzErr, 124 }, 125 } 126 err := p.Validate(context.Background(), test.attributes, nil) 127 if err == nil && !test.allowed { 128 t.Errorf("Expected authorization policy to reject CSR but it was allowed") 129 } 130 if err != nil && test.allowed { 131 t.Errorf("Expected authorization policy to accept CSR but it was rejected: %v", err) 132 } 133 }) 134 } 135 } 136 137 type fakeAuthorizer struct { 138 t *testing.T 139 verb string 140 allowedName string 141 decision authorizer.Decision 142 err error 143 } 144 145 func (f fakeAuthorizer) Authorize(ctx context.Context, a authorizer.Attributes) (authorizer.Decision, string, error) { 146 if f.err != nil { 147 return f.decision, "forced error", f.err 148 } 149 if a.GetVerb() != f.verb { 150 return authorizer.DecisionDeny, fmt.Sprintf("unrecognised verb '%s'", a.GetVerb()), nil 151 } 152 if a.GetAPIGroup() != "certificates.k8s.io" { 153 return authorizer.DecisionDeny, fmt.Sprintf("unrecognised groupName '%s'", a.GetAPIGroup()), nil 154 } 155 if a.GetAPIVersion() != "*" { 156 return authorizer.DecisionDeny, fmt.Sprintf("unrecognised apiVersion '%s'", a.GetAPIVersion()), nil 157 } 158 if a.GetResource() != "signers" { 159 return authorizer.DecisionDeny, fmt.Sprintf("unrecognised resource '%s'", a.GetResource()), nil 160 } 161 if a.GetName() != f.allowedName { 162 return authorizer.DecisionDeny, fmt.Sprintf("unrecognised resource name '%s'", a.GetName()), nil 163 } 164 if !a.IsResourceRequest() { 165 return authorizer.DecisionDeny, fmt.Sprintf("unrecognised IsResourceRequest '%t'", a.IsResourceRequest()), nil 166 } 167 return f.decision, "", nil 168 } 169 170 type testAttributes struct { 171 resource schema.GroupResource 172 subresource string 173 operation admission.Operation 174 obj, oldObj runtime.Object 175 name string 176 177 admission.Attributes // nil panic if any other methods called 178 } 179 180 func (t *testAttributes) GetResource() schema.GroupVersionResource { 181 return t.resource.WithVersion("ignored") 182 } 183 184 func (t *testAttributes) GetSubresource() string { 185 return t.subresource 186 } 187 188 func (t *testAttributes) GetObject() runtime.Object { 189 return t.obj 190 } 191 192 func (t *testAttributes) GetOldObject() runtime.Object { 193 return t.oldObj 194 } 195 196 func (t *testAttributes) GetName() string { 197 return t.name 198 } 199 200 func (t *testAttributes) GetOperation() admission.Operation { 201 return t.operation 202 } 203 204 func (t *testAttributes) GetUserInfo() user.Info { 205 return &user.DefaultInfo{Name: "ignored"} 206 }