k8s.io/apiserver@v0.31.1/pkg/authentication/serviceaccount/util_test.go (about) 1 /* 2 Copyright 2014 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 serviceaccount 18 19 import ( 20 "reflect" 21 "testing" 22 23 v1 "k8s.io/api/core/v1" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/apiserver/pkg/authentication/user" 26 ) 27 28 func TestUserInfo(t *testing.T) { 29 tests := map[string]struct { 30 info ServiceAccountInfo 31 expectedUserInfo *user.DefaultInfo 32 }{ 33 "extracts pod name/uid": { 34 info: ServiceAccountInfo{Name: "name", Namespace: "ns", PodName: "test", PodUID: "uid"}, 35 expectedUserInfo: &user.DefaultInfo{ 36 Name: "system:serviceaccount:ns:name", 37 Groups: []string{"system:serviceaccounts", "system:serviceaccounts:ns"}, 38 Extra: map[string][]string{ 39 "authentication.kubernetes.io/pod-name": {"test"}, 40 "authentication.kubernetes.io/pod-uid": {"uid"}, 41 }, 42 }, 43 }, 44 "extracts node name/uid": { 45 info: ServiceAccountInfo{Name: "name", Namespace: "ns", NodeName: "test", NodeUID: "uid"}, 46 expectedUserInfo: &user.DefaultInfo{ 47 Name: "system:serviceaccount:ns:name", 48 Groups: []string{"system:serviceaccounts", "system:serviceaccounts:ns"}, 49 Extra: map[string][]string{ 50 "authentication.kubernetes.io/node-name": {"test"}, 51 "authentication.kubernetes.io/node-uid": {"uid"}, 52 }, 53 }, 54 }, 55 "extracts node name only": { 56 info: ServiceAccountInfo{Name: "name", Namespace: "ns", NodeName: "test"}, 57 expectedUserInfo: &user.DefaultInfo{ 58 Name: "system:serviceaccount:ns:name", 59 Groups: []string{"system:serviceaccounts", "system:serviceaccounts:ns"}, 60 Extra: map[string][]string{ 61 "authentication.kubernetes.io/node-name": {"test"}, 62 }, 63 }, 64 }, 65 "does not extract node UID if name is not set": { 66 info: ServiceAccountInfo{Name: "name", Namespace: "ns", NodeUID: "test"}, 67 expectedUserInfo: &user.DefaultInfo{ 68 Name: "system:serviceaccount:ns:name", 69 Groups: []string{"system:serviceaccounts", "system:serviceaccounts:ns"}, 70 }, 71 }, 72 } 73 74 for name, test := range tests { 75 t.Run(name, func(t *testing.T) { 76 userInfo := test.info.UserInfo() 77 if !reflect.DeepEqual(userInfo, test.expectedUserInfo) { 78 t.Errorf("expected %#v but got %#v", test.expectedUserInfo, userInfo) 79 } 80 }) 81 } 82 } 83 84 func TestMakeUsername(t *testing.T) { 85 86 testCases := map[string]struct { 87 Namespace string 88 Name string 89 ExpectedErr bool 90 }{ 91 "valid": { 92 Namespace: "foo", 93 Name: "bar", 94 ExpectedErr: false, 95 }, 96 "empty": { 97 ExpectedErr: true, 98 }, 99 "empty namespace": { 100 Namespace: "", 101 Name: "foo", 102 ExpectedErr: true, 103 }, 104 "empty name": { 105 Namespace: "foo", 106 Name: "", 107 ExpectedErr: true, 108 }, 109 "extra segments": { 110 Namespace: "foo", 111 Name: "bar:baz", 112 ExpectedErr: true, 113 }, 114 "invalid chars in namespace": { 115 Namespace: "foo ", 116 Name: "bar", 117 ExpectedErr: true, 118 }, 119 "invalid chars in name": { 120 Namespace: "foo", 121 Name: "bar ", 122 ExpectedErr: true, 123 }, 124 } 125 126 for k, tc := range testCases { 127 username := MakeUsername(tc.Namespace, tc.Name) 128 if !MatchesUsername(tc.Namespace, tc.Name, username) { 129 t.Errorf("%s: Expected to match username", k) 130 } 131 namespace, name, err := SplitUsername(username) 132 if (err != nil) != tc.ExpectedErr { 133 t.Errorf("%s: Expected error=%v, got %v", k, tc.ExpectedErr, err) 134 continue 135 } 136 if err != nil { 137 continue 138 } 139 140 if namespace != tc.Namespace { 141 t.Errorf("%s: Expected namespace %q, got %q", k, tc.Namespace, namespace) 142 } 143 if name != tc.Name { 144 t.Errorf("%s: Expected name %q, got %q", k, tc.Name, name) 145 } 146 } 147 } 148 149 func TestMatchUsername(t *testing.T) { 150 151 testCases := []struct { 152 TestName string 153 Namespace string 154 Name string 155 Username string 156 Expect bool 157 }{ 158 {Namespace: "foo", Name: "bar", Username: "foo", Expect: false}, 159 {Namespace: "foo", Name: "bar", Username: "system:serviceaccount:", Expect: false}, 160 {Namespace: "foo", Name: "bar", Username: "system:serviceaccount:foo", Expect: false}, 161 {Namespace: "foo", Name: "bar", Username: "system:serviceaccount:foo:", Expect: false}, 162 {Namespace: "foo", Name: "bar", Username: "system:serviceaccount:foo:bar", Expect: true}, 163 {Namespace: "foo", Name: "bar", Username: "system:serviceaccount::bar", Expect: false}, 164 {Namespace: "foo", Name: "bar", Username: "system:serviceaccount:bar", Expect: false}, 165 {Namespace: "foo", Name: "bar", Username: ":bar", Expect: false}, 166 {Namespace: "foo", Name: "bar", Username: "foo:bar", Expect: false}, 167 {Namespace: "foo", Name: "bar", Username: "", Expect: false}, 168 169 {Namespace: "foo2", Name: "bar", Username: "system:serviceaccount:foo:bar", Expect: false}, 170 {Namespace: "foo", Name: "bar2", Username: "system:serviceaccount:foo:bar", Expect: false}, 171 {Namespace: "foo:", Name: "bar", Username: "system:serviceaccount:foo:bar", Expect: false}, 172 {Namespace: "foo", Name: ":bar", Username: "system:serviceaccount:foo:bar", Expect: false}, 173 } 174 175 for _, tc := range testCases { 176 t.Run(tc.TestName, func(t *testing.T) { 177 actual := MatchesUsername(tc.Namespace, tc.Name, tc.Username) 178 if actual != tc.Expect { 179 t.Fatalf("unexpected match") 180 } 181 }) 182 } 183 } 184 185 func TestIsServiceAccountToken(t *testing.T) { 186 187 secretIns := &v1.Secret{ 188 ObjectMeta: metav1.ObjectMeta{ 189 Name: "token-secret-1", 190 Namespace: "default", 191 UID: "23456", 192 ResourceVersion: "1", 193 Annotations: map[string]string{ 194 v1.ServiceAccountNameKey: "default", 195 v1.ServiceAccountUIDKey: "12345", 196 }, 197 }, 198 Type: v1.SecretTypeServiceAccountToken, 199 Data: map[string][]byte{ 200 "token": []byte("ABC"), 201 "ca.crt": []byte("CA Data"), 202 "namespace": []byte("default"), 203 }, 204 } 205 206 secretTypeMistmatch := &v1.Secret{ 207 ObjectMeta: metav1.ObjectMeta{ 208 Name: "token-secret-2", 209 Namespace: "default", 210 UID: "23456", 211 ResourceVersion: "1", 212 Annotations: map[string]string{ 213 v1.ServiceAccountNameKey: "default", 214 v1.ServiceAccountUIDKey: "12345", 215 }, 216 }, 217 Type: v1.SecretTypeOpaque, 218 } 219 220 saIns := &v1.ServiceAccount{ 221 ObjectMeta: metav1.ObjectMeta{ 222 Name: "default", 223 UID: "12345", 224 Namespace: "default", 225 ResourceVersion: "1", 226 }, 227 } 228 229 saInsNameNotEqual := &v1.ServiceAccount{ 230 ObjectMeta: metav1.ObjectMeta{ 231 Name: "non-default", 232 UID: "12345", 233 Namespace: "default", 234 ResourceVersion: "1", 235 }, 236 } 237 238 saInsUIDNotEqual := &v1.ServiceAccount{ 239 ObjectMeta: metav1.ObjectMeta{ 240 Name: "default", 241 UID: "67890", 242 Namespace: "default", 243 ResourceVersion: "1", 244 }, 245 } 246 247 tests := map[string]struct { 248 secret *v1.Secret 249 sa *v1.ServiceAccount 250 expect bool 251 }{ 252 "correct service account": { 253 secret: secretIns, 254 sa: saIns, 255 expect: true, 256 }, 257 "service account name not equal": { 258 secret: secretIns, 259 sa: saInsNameNotEqual, 260 expect: false, 261 }, 262 "service account uid not equal": { 263 secret: secretIns, 264 sa: saInsUIDNotEqual, 265 expect: false, 266 }, 267 "service account type not equal": { 268 secret: secretTypeMistmatch, 269 sa: saIns, 270 expect: false, 271 }, 272 } 273 274 for k, v := range tests { 275 actual := IsServiceAccountToken(v.secret, v.sa) 276 if actual != v.expect { 277 t.Errorf("%s failed, expected %t but received %t", k, v.expect, actual) 278 } 279 } 280 281 }