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 }