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