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  }