k8s.io/kubernetes@v1.29.3/test/integration/auth/selfsubjectreview_test.go (about)

     1  /*
     2  Copyright 2022 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 auth
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"net/http"
    23  	"reflect"
    24  	"sync"
    25  	"sync/atomic"
    26  	"testing"
    27  
    28  	authenticationv1 "k8s.io/api/authentication/v1"
    29  	authenticationv1alpha1 "k8s.io/api/authentication/v1alpha1"
    30  	authenticationv1beta1 "k8s.io/api/authentication/v1beta1"
    31  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    32  	"k8s.io/apiserver/pkg/authentication/authenticator"
    33  	"k8s.io/apiserver/pkg/authentication/user"
    34  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    35  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    36  	"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
    37  	"k8s.io/kubernetes/pkg/controlplane"
    38  	"k8s.io/kubernetes/pkg/features"
    39  	"k8s.io/kubernetes/test/integration/framework"
    40  	"k8s.io/kubernetes/test/utils/ktesting"
    41  )
    42  
    43  func TestGetsSelfAttributes(t *testing.T) {
    44  	tests := []struct {
    45  		name           string
    46  		userInfo       *user.DefaultInfo
    47  		expectedName   string
    48  		expectedUID    string
    49  		expectedGroups []string
    50  		expectedExtra  map[string]authenticationv1.ExtraValue
    51  	}{
    52  		{
    53  			name: "Username",
    54  			userInfo: &user.DefaultInfo{
    55  				Name: "alice",
    56  			},
    57  			expectedName: "alice",
    58  		},
    59  		{
    60  			name: "Username with groups and UID",
    61  			userInfo: &user.DefaultInfo{
    62  				Name:   "alice",
    63  				UID:    "unique-id",
    64  				Groups: []string{"devs", "admins"},
    65  			},
    66  			expectedName:   "alice",
    67  			expectedUID:    "unique-id",
    68  			expectedGroups: []string{"devs", "admins"},
    69  		},
    70  		{
    71  			name: "Username with extra attributes",
    72  			userInfo: &user.DefaultInfo{
    73  				Name: "alice",
    74  				Extra: map[string][]string{
    75  					"nicknames": {"cutie", "bestie"},
    76  				},
    77  			},
    78  			expectedName: "alice",
    79  			expectedExtra: map[string]authenticationv1.ExtraValue{
    80  				"nicknames": authenticationv1.ExtraValue([]string{"cutie", "bestie"}),
    81  			},
    82  		},
    83  		{
    84  			name: "Without username",
    85  			userInfo: &user.DefaultInfo{
    86  				UID: "unique-id",
    87  			},
    88  			expectedUID: "unique-id",
    89  		},
    90  	}
    91  
    92  	_, ctx := ktesting.NewTestContext(t)
    93  	ctx, cancel := context.WithCancel(ctx)
    94  	defer cancel()
    95  
    96  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.APISelfSubjectReview, true)()
    97  
    98  	var respMu sync.RWMutex
    99  	response := &user.DefaultInfo{
   100  		Name: "stub",
   101  	}
   102  
   103  	kubeClient, _, tearDownFn := framework.StartTestServer(ctx, t, framework.TestServerSetup{
   104  		ModifyServerRunOptions: func(opts *options.ServerRunOptions) {
   105  			opts.APIEnablement.RuntimeConfig.Set("authentication.k8s.io/v1alpha1=true")
   106  			opts.APIEnablement.RuntimeConfig.Set("authentication.k8s.io/v1beta1=true")
   107  			opts.APIEnablement.RuntimeConfig.Set("authentication.k8s.io/v1=true")
   108  			opts.Authorization.Modes = []string{"AlwaysAllow"}
   109  		},
   110  		ModifyServerConfig: func(config *controlplane.Config) {
   111  			// Unset BearerToken to disable BearerToken authenticator.
   112  			config.GenericConfig.LoopbackClientConfig.BearerToken = ""
   113  			config.GenericConfig.Authentication.Authenticator = authenticator.RequestFunc(func(req *http.Request) (*authenticator.Response, bool, error) {
   114  				respMu.RLock()
   115  				defer respMu.RUnlock()
   116  				return &authenticator.Response{User: response}, true, nil
   117  			})
   118  		},
   119  	})
   120  	defer tearDownFn()
   121  
   122  	for _, tc := range tests {
   123  		t.Run(tc.name, func(t *testing.T) {
   124  			respMu.Lock()
   125  			response = tc.userInfo
   126  			respMu.Unlock()
   127  
   128  			res, err := kubeClient.AuthenticationV1alpha1().
   129  				SelfSubjectReviews().
   130  				Create(ctx, &authenticationv1alpha1.SelfSubjectReview{}, metav1.CreateOptions{})
   131  			if err != nil {
   132  				t.Fatalf("unexpected error: %v", err)
   133  			}
   134  
   135  			if res == nil {
   136  				t.Fatalf("empty response")
   137  			}
   138  
   139  			if res.Status.UserInfo.Username != tc.expectedName {
   140  				t.Fatalf("unexpected username: wanted %s, got %s", tc.expectedName, res.Status.UserInfo.Username)
   141  			}
   142  
   143  			if res.Status.UserInfo.UID != tc.expectedUID {
   144  				t.Fatalf("unexpected uid: wanted %s, got %s", tc.expectedUID, res.Status.UserInfo.UID)
   145  			}
   146  
   147  			if !reflect.DeepEqual(res.Status.UserInfo.Groups, tc.expectedGroups) {
   148  				t.Fatalf("unexpected groups: wanted %v, got %v", tc.expectedGroups, res.Status.UserInfo.Groups)
   149  			}
   150  
   151  			if !reflect.DeepEqual(res.Status.UserInfo.Extra, tc.expectedExtra) {
   152  				t.Fatalf("unexpected extra: wanted %v, got %v", tc.expectedExtra, res.Status.UserInfo.Extra)
   153  			}
   154  
   155  			res2, err := kubeClient.AuthenticationV1beta1().
   156  				SelfSubjectReviews().
   157  				Create(ctx, &authenticationv1beta1.SelfSubjectReview{}, metav1.CreateOptions{})
   158  			if err != nil {
   159  				t.Fatalf("unexpected error: %v", err)
   160  			}
   161  
   162  			if res2 == nil {
   163  				t.Fatalf("empty response")
   164  			}
   165  
   166  			if res2.Status.UserInfo.Username != tc.expectedName {
   167  				t.Fatalf("unexpected username: wanted %s, got %s", tc.expectedName, res.Status.UserInfo.Username)
   168  			}
   169  
   170  			if res2.Status.UserInfo.UID != tc.expectedUID {
   171  				t.Fatalf("unexpected uid: wanted %s, got %s", tc.expectedUID, res.Status.UserInfo.UID)
   172  			}
   173  
   174  			if !reflect.DeepEqual(res2.Status.UserInfo.Groups, tc.expectedGroups) {
   175  				t.Fatalf("unexpected groups: wanted %v, got %v", tc.expectedGroups, res.Status.UserInfo.Groups)
   176  			}
   177  
   178  			if !reflect.DeepEqual(res2.Status.UserInfo.Extra, tc.expectedExtra) {
   179  				t.Fatalf("unexpected extra: wanted %v, got %v", tc.expectedExtra, res.Status.UserInfo.Extra)
   180  			}
   181  
   182  			res3, err := kubeClient.AuthenticationV1().
   183  				SelfSubjectReviews().
   184  				Create(context.TODO(), &authenticationv1.SelfSubjectReview{}, metav1.CreateOptions{})
   185  			if err != nil {
   186  				t.Fatalf("unexpected error: %v", err)
   187  			}
   188  
   189  			if res3 == nil {
   190  				t.Fatalf("empty response")
   191  			}
   192  
   193  			if res3.Status.UserInfo.Username != tc.expectedName {
   194  				t.Fatalf("unexpected username: wanted %s, got %s", tc.expectedName, res.Status.UserInfo.Username)
   195  			}
   196  
   197  			if res3.Status.UserInfo.UID != tc.expectedUID {
   198  				t.Fatalf("unexpected uid: wanted %s, got %s", tc.expectedUID, res.Status.UserInfo.UID)
   199  			}
   200  
   201  			if !reflect.DeepEqual(res3.Status.UserInfo.Groups, tc.expectedGroups) {
   202  				t.Fatalf("unexpected groups: wanted %v, got %v", tc.expectedGroups, res.Status.UserInfo.Groups)
   203  			}
   204  
   205  			if !reflect.DeepEqual(res3.Status.UserInfo.Extra, tc.expectedExtra) {
   206  				t.Fatalf("unexpected extra: wanted %v, got %v", tc.expectedExtra, res.Status.UserInfo.Extra)
   207  			}
   208  		})
   209  	}
   210  }
   211  
   212  func TestGetsSelfAttributesError(t *testing.T) {
   213  	toggle := &atomic.Value{}
   214  	toggle.Store(true)
   215  
   216  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.APISelfSubjectReview, true)()
   217  
   218  	_, ctx := ktesting.NewTestContext(t)
   219  	ctx, cancel := context.WithCancel(ctx)
   220  	defer cancel()
   221  
   222  	kubeClient, _, tearDownFn := framework.StartTestServer(ctx, t, framework.TestServerSetup{
   223  		ModifyServerRunOptions: func(opts *options.ServerRunOptions) {
   224  			opts.APIEnablement.RuntimeConfig.Set("authentication.k8s.io/v1alpha1=true")
   225  			opts.APIEnablement.RuntimeConfig.Set("authentication.k8s.io/v1beta1=true")
   226  			opts.APIEnablement.RuntimeConfig.Set("authentication.k8s.io/v1=true")
   227  			opts.Authorization.Modes = []string{"AlwaysAllow"}
   228  		},
   229  		ModifyServerConfig: func(config *controlplane.Config) {
   230  			// Unset BearerToken to disable BearerToken authenticator.
   231  			config.GenericConfig.LoopbackClientConfig.BearerToken = ""
   232  			config.GenericConfig.Authentication.Authenticator = authenticator.RequestFunc(func(req *http.Request) (*authenticator.Response, bool, error) {
   233  				if toggle.Load().(bool) {
   234  					return &authenticator.Response{
   235  						User: &user.DefaultInfo{
   236  							Name: "alice",
   237  						},
   238  					}, true, nil
   239  				}
   240  
   241  				return nil, false, fmt.Errorf("test error")
   242  			})
   243  		},
   244  	})
   245  	defer tearDownFn()
   246  
   247  	expected := fmt.Errorf("Unauthorized")
   248  
   249  	{ // v1alpha1
   250  		toggle.Store(!toggle.Load().(bool))
   251  
   252  		_, err := kubeClient.AuthenticationV1alpha1().
   253  			SelfSubjectReviews().
   254  			Create(ctx, &authenticationv1alpha1.SelfSubjectReview{}, metav1.CreateOptions{})
   255  		if err == nil {
   256  			t.Fatalf("expected error: %v, got nil", err)
   257  		}
   258  
   259  		toggle.Store(!toggle.Load().(bool))
   260  		if expected.Error() != err.Error() {
   261  			t.Fatalf("expected error: %v, got %v", expected, err)
   262  		}
   263  	}
   264  
   265  	{ // v1beta1
   266  		toggle.Store(!toggle.Load().(bool))
   267  
   268  		_, err := kubeClient.AuthenticationV1beta1().
   269  			SelfSubjectReviews().
   270  			Create(ctx, &authenticationv1beta1.SelfSubjectReview{}, metav1.CreateOptions{})
   271  		if err == nil {
   272  			t.Fatalf("expected error: %v, got nil", err)
   273  		}
   274  
   275  		toggle.Store(!toggle.Load().(bool))
   276  		if expected.Error() != err.Error() {
   277  			t.Fatalf("expected error: %v, got %v", expected, err)
   278  		}
   279  	}
   280  
   281  	{ // v1
   282  		toggle.Store(!toggle.Load().(bool))
   283  
   284  		_, err := kubeClient.AuthenticationV1().
   285  			SelfSubjectReviews().
   286  			Create(context.TODO(), &authenticationv1.SelfSubjectReview{}, metav1.CreateOptions{})
   287  		if err == nil {
   288  			t.Fatalf("expected error: %v, got nil", err)
   289  		}
   290  
   291  		toggle.Store(!toggle.Load().(bool))
   292  		if expected.Error() != err.Error() {
   293  			t.Fatalf("expected error: %v, got %v", expected, err)
   294  		}
   295  	}
   296  }