k8s.io/apiserver@v0.31.1/plugin/pkg/authorizer/webhook/webhook_test.go (about) 1 /* 2 Copyright 2024 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 webhook 18 19 import ( 20 "errors" 21 "reflect" 22 "testing" 23 24 authorizationv1 "k8s.io/api/authorization/v1" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/fields" 27 "k8s.io/apimachinery/pkg/labels" 28 "k8s.io/apimachinery/pkg/selection" 29 "k8s.io/apiserver/pkg/authorization/authorizer" 30 genericfeatures "k8s.io/apiserver/pkg/features" 31 utilfeature "k8s.io/apiserver/pkg/util/feature" 32 featuregatetesting "k8s.io/component-base/featuregate/testing" 33 ) 34 35 func mustLabelRequirement(selector string) labels.Requirements { 36 ret, err := labels.Parse(selector) 37 if err != nil { 38 panic(err) 39 } 40 requirements, _ := ret.Requirements() 41 return requirements 42 } 43 44 func Test_resourceAttributesFrom(t *testing.T) { 45 type args struct { 46 attr authorizer.Attributes 47 } 48 tests := []struct { 49 name string 50 args args 51 want *authorizationv1.ResourceAttributes 52 enableAuthorizationSelector bool 53 }{ 54 { 55 name: "field selector: don't parse when disabled", 56 args: args{ 57 attr: authorizer.AttributesRecord{ 58 FieldSelectorRequirements: fields.Requirements{ 59 fields.OneTermEqualSelector("foo", "bar").Requirements()[0], 60 }, 61 FieldSelectorParsingErr: nil, 62 }, 63 }, 64 want: &authorizationv1.ResourceAttributes{}, 65 }, 66 { 67 name: "label selector: don't parse when disabled", 68 args: args{ 69 attr: authorizer.AttributesRecord{ 70 LabelSelectorRequirements: mustLabelRequirement("foo in (bar,baz)"), 71 LabelSelectorParsingErr: nil, 72 }, 73 }, 74 want: &authorizationv1.ResourceAttributes{}, 75 }, 76 { 77 name: "field selector: ignore error", 78 args: args{ 79 attr: authorizer.AttributesRecord{ 80 FieldSelectorRequirements: fields.Requirements{ 81 fields.OneTermEqualSelector("foo", "bar").Requirements()[0], 82 }, 83 FieldSelectorParsingErr: errors.New("failed"), 84 }, 85 }, 86 want: &authorizationv1.ResourceAttributes{ 87 FieldSelector: &authorizationv1.FieldSelectorAttributes{ 88 Requirements: []metav1.FieldSelectorRequirement{{Key: "foo", Operator: "In", Values: []string{"bar"}}}, 89 }, 90 }, 91 enableAuthorizationSelector: true, 92 }, 93 { 94 name: "label selector: ignore error", 95 args: args{ 96 attr: authorizer.AttributesRecord{ 97 LabelSelectorRequirements: mustLabelRequirement("foo in (bar,baz)"), 98 LabelSelectorParsingErr: errors.New("failed"), 99 }, 100 }, 101 want: &authorizationv1.ResourceAttributes{ 102 LabelSelector: &authorizationv1.LabelSelectorAttributes{ 103 Requirements: []metav1.LabelSelectorRequirement{{Key: "foo", Operator: "In", Values: []string{"bar", "baz"}}}, 104 }, 105 }, 106 enableAuthorizationSelector: true, 107 }, 108 { 109 name: "field selector: equals, double equals, in", 110 args: args{ 111 attr: authorizer.AttributesRecord{ 112 FieldSelectorRequirements: fields.Requirements{ 113 {Operator: selection.Equals, Field: "foo", Value: "bar"}, 114 {Operator: selection.DoubleEquals, Field: "one", Value: "two"}, 115 {Operator: selection.In, Field: "apple", Value: "banana"}, 116 }, 117 FieldSelectorParsingErr: nil, 118 }, 119 }, 120 want: &authorizationv1.ResourceAttributes{ 121 FieldSelector: &authorizationv1.FieldSelectorAttributes{ 122 Requirements: []metav1.FieldSelectorRequirement{ 123 { 124 Key: "foo", 125 Operator: "In", 126 Values: []string{"bar"}, 127 }, 128 { 129 Key: "one", 130 Operator: "In", 131 Values: []string{"two"}, 132 }, 133 { 134 Key: "apple", 135 Operator: "In", 136 Values: []string{"banana"}, 137 }, 138 }, 139 }, 140 }, 141 enableAuthorizationSelector: true, 142 }, 143 { 144 name: "field selector: not equals, not in", 145 args: args{ 146 attr: authorizer.AttributesRecord{ 147 FieldSelectorRequirements: fields.Requirements{ 148 {Operator: selection.NotEquals, Field: "foo", Value: "bar"}, 149 {Operator: selection.NotIn, Field: "apple", Value: "banana"}, 150 }, 151 FieldSelectorParsingErr: nil, 152 }, 153 }, 154 want: &authorizationv1.ResourceAttributes{ 155 FieldSelector: &authorizationv1.FieldSelectorAttributes{ 156 Requirements: []metav1.FieldSelectorRequirement{ 157 { 158 Key: "foo", 159 Operator: "NotIn", 160 Values: []string{"bar"}, 161 }, 162 { 163 Key: "apple", 164 Operator: "NotIn", 165 Values: []string{"banana"}, 166 }, 167 }, 168 }, 169 }, 170 enableAuthorizationSelector: true, 171 }, 172 { 173 name: "field selector: unknown operator skipped", 174 args: args{ 175 attr: authorizer.AttributesRecord{ 176 FieldSelectorRequirements: fields.Requirements{ 177 {Operator: selection.NotEquals, Field: "foo", Value: "bar"}, 178 {Operator: selection.Operator("bad"), Field: "apple", Value: "banana"}, 179 }, 180 FieldSelectorParsingErr: nil, 181 }, 182 }, 183 want: &authorizationv1.ResourceAttributes{ 184 FieldSelector: &authorizationv1.FieldSelectorAttributes{ 185 Requirements: []metav1.FieldSelectorRequirement{ 186 { 187 Key: "foo", 188 Operator: "NotIn", 189 Values: []string{"bar"}, 190 }, 191 }, 192 }, 193 }, 194 enableAuthorizationSelector: true, 195 }, 196 { 197 name: "field selector: no requirements has no fieldselector", 198 args: args{ 199 attr: authorizer.AttributesRecord{ 200 FieldSelectorRequirements: fields.Requirements{ 201 {Operator: selection.Operator("bad"), Field: "apple", Value: "banana"}, 202 }, 203 FieldSelectorParsingErr: nil, 204 }, 205 }, 206 want: &authorizationv1.ResourceAttributes{}, 207 enableAuthorizationSelector: true, 208 }, 209 { 210 name: "label selector: in, equals, double equals", 211 args: args{ 212 attr: authorizer.AttributesRecord{ 213 LabelSelectorRequirements: mustLabelRequirement("foo in (bar,baz), one=two, apple==banana"), 214 LabelSelectorParsingErr: nil, 215 }, 216 }, 217 want: &authorizationv1.ResourceAttributes{ 218 LabelSelector: &authorizationv1.LabelSelectorAttributes{ 219 Requirements: []metav1.LabelSelectorRequirement{ 220 { 221 Key: "apple", 222 Operator: "In", 223 Values: []string{"banana"}, 224 }, 225 { 226 Key: "foo", 227 Operator: "In", 228 Values: []string{"bar", "baz"}, 229 }, 230 { 231 Key: "one", 232 Operator: "In", 233 Values: []string{"two"}, 234 }, 235 }, 236 }, 237 }, 238 enableAuthorizationSelector: true, 239 }, 240 { 241 name: "label selector: not in, not equals", 242 args: args{ 243 attr: authorizer.AttributesRecord{ 244 LabelSelectorRequirements: mustLabelRequirement("foo notin (bar,baz), one!=two"), 245 LabelSelectorParsingErr: nil, 246 }, 247 }, 248 want: &authorizationv1.ResourceAttributes{ 249 LabelSelector: &authorizationv1.LabelSelectorAttributes{ 250 Requirements: []metav1.LabelSelectorRequirement{ 251 { 252 Key: "foo", 253 Operator: "NotIn", 254 Values: []string{"bar", "baz"}, 255 }, 256 { 257 Key: "one", 258 Operator: "NotIn", 259 Values: []string{"two"}, 260 }, 261 }, 262 }, 263 }, 264 enableAuthorizationSelector: true, 265 }, 266 { 267 name: "label selector: exists, not exists", 268 args: args{ 269 attr: authorizer.AttributesRecord{ 270 LabelSelectorRequirements: mustLabelRequirement("foo, !one"), 271 LabelSelectorParsingErr: nil, 272 }, 273 }, 274 want: &authorizationv1.ResourceAttributes{ 275 LabelSelector: &authorizationv1.LabelSelectorAttributes{ 276 Requirements: []metav1.LabelSelectorRequirement{ 277 { 278 Key: "foo", 279 Operator: "Exists", 280 }, 281 { 282 Key: "one", 283 Operator: "DoesNotExist", 284 }, 285 }, 286 }, 287 }, 288 enableAuthorizationSelector: true, 289 }, 290 { 291 name: "label selector: unknown operator skipped", 292 args: args{ 293 attr: authorizer.AttributesRecord{ 294 LabelSelectorRequirements: mustLabelRequirement("foo != bar, apple > 1"), 295 LabelSelectorParsingErr: nil, 296 }, 297 }, 298 want: &authorizationv1.ResourceAttributes{ 299 LabelSelector: &authorizationv1.LabelSelectorAttributes{ 300 Requirements: []metav1.LabelSelectorRequirement{ 301 { 302 Key: "foo", 303 Operator: "NotIn", 304 Values: []string{"bar"}, 305 }, 306 }, 307 }, 308 }, 309 enableAuthorizationSelector: true, 310 }, 311 { 312 name: "label selector: no requirements has no labelselector", 313 args: args{ 314 attr: authorizer.AttributesRecord{ 315 LabelSelectorRequirements: mustLabelRequirement("apple > 1"), 316 LabelSelectorParsingErr: nil, 317 }, 318 }, 319 want: &authorizationv1.ResourceAttributes{}, 320 enableAuthorizationSelector: true, 321 }, 322 } 323 for _, tt := range tests { 324 t.Run(tt.name, func(t *testing.T) { 325 if tt.enableAuthorizationSelector { 326 featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.AuthorizeWithSelectors, true) 327 } 328 329 if got := resourceAttributesFrom(tt.args.attr); !reflect.DeepEqual(got, tt.want) { 330 t.Errorf("resourceAttributesFrom() = %v, want %v", got, tt.want) 331 } 332 }) 333 } 334 }