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  }