github.com/kiali/kiali@v1.84.0/business/authentication/token_auth_controller_test.go (about) 1 package authentication 2 3 import ( 4 "errors" 5 "net/http" 6 "net/http/httptest" 7 "strings" 8 "testing" 9 "time" 10 11 "github.com/stretchr/testify/assert" 12 v1 "k8s.io/api/core/v1" 13 k8s_errors "k8s.io/apimachinery/pkg/api/errors" 14 meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 "k8s.io/apimachinery/pkg/runtime/schema" 16 17 "github.com/kiali/kiali/config" 18 "github.com/kiali/kiali/kubernetes" 19 "github.com/kiali/kiali/kubernetes/cache" 20 "github.com/kiali/kiali/kubernetes/kubetest" 21 "github.com/kiali/kiali/util" 22 ) 23 24 // Token built with the debugger at jwt.io. Subject is system:serviceaccount:k8s_user 25 const testToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6azhzX3VzZXIifQ.PYnWgochOQsfMInTpJPul7zkDSyMmJwfvJ6nXowITZk" 26 27 func TestTokenAuthControllerAuthenticatesCorrectly(t *testing.T) { 28 rr, sData, _, _ := createValidSession(t) 29 30 expectedExpiration := time.Date(2021, 12, 1, 0, 0, 1, 0, time.UTC) 31 32 assert.NotNil(t, sData) 33 assert.Equal(t, "k8s_user", sData.Username) 34 assert.Equal(t, testToken, sData.AuthInfo.Token) 35 assert.Equal(t, expectedExpiration, sData.ExpiresOn) 36 37 // Simply check that some cookie is set and has the right expiration. Testing cookie content is left to the session_persistor_test.go 38 response := rr.Result() 39 assert.NotEmpty(t, response.Cookies()) 40 assert.Equal(t, expectedExpiration, response.Cookies()[0].Expires) 41 } 42 43 func TestTokenAuthControllerRejectsUserWithoutPrivilegesInAnyNamespace(t *testing.T) { 44 clockTime := time.Date(2021, 12, 1, 0, 0, 0, 0, time.UTC) 45 util.Clock = util.ClockMock{Time: clockTime} 46 47 conf := config.NewConfig() 48 conf.LoginToken.SigningKey = "kiali67890123456" 49 config.Set(conf) 50 51 // Returning no namespace when a cluster API call is made should have the result of 52 // a rejected authentication. 53 k8s := kubetest.NewFakeK8sClient() 54 55 requestBody := strings.NewReader("token=Foo") 56 request := httptest.NewRequest(http.MethodPost, "/api/authenticate", requestBody) 57 request.Header.Add("Content-Type", "application/x-www-form-urlencoded") 58 59 rr := httptest.NewRecorder() 60 mockClientFactory := kubetest.NewK8SClientFactoryMock(k8s) 61 cache := cache.NewTestingCacheWithFactory(t, mockClientFactory, *conf) 62 controller := NewTokenAuthController(NewCookieSessionPersistor(conf), mockClientFactory, cache, conf) 63 sData, err := controller.Authenticate(request, rr) 64 65 assert.Nil(t, sData) 66 assert.IsType(t, &AuthenticationFailureError{}, err) 67 assert.Contains(t, err.Error(), "privileges") 68 69 // Check no cookies are set 70 response := rr.Result() 71 assert.Empty(t, response.Cookies()) 72 } 73 74 func TestTokenAuthControllerRejectsInvalidToken(t *testing.T) { 75 clockTime := time.Date(2021, 12, 1, 0, 0, 0, 0, time.UTC) 76 util.Clock = util.ClockMock{Time: clockTime} 77 78 conf := config.NewConfig() 79 conf.LoginToken.SigningKey = "kiali67890123456" 80 config.Set(conf) 81 82 // Returning a forbidden error when a cluster API call is made should have the result of 83 // a rejected authentication. 84 k8s := kubetest.NewFakeK8sClient(&v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "Foo"}}) 85 mockClientFactory := kubetest.NewK8SClientFactoryMock(forbiddenClient{k8s}) 86 cache := cache.NewTestingCacheWithFactory(t, mockClientFactory, *conf) 87 controller := NewTokenAuthController(NewCookieSessionPersistor(conf), mockClientFactory, cache, conf) 88 89 requestBody := strings.NewReader("token=Foo") 90 request := httptest.NewRequest(http.MethodPost, "/api/authenticate", requestBody) 91 request.Header.Add("Content-Type", "application/x-www-form-urlencoded") 92 93 rr := httptest.NewRecorder() 94 sData, err := controller.Authenticate(request, rr) 95 96 assert.Nil(t, sData) 97 assert.IsType(t, &AuthenticationFailureError{}, err) 98 assert.Contains(t, err.Error(), "token") 99 100 // Check no cookies are set 101 response := rr.Result() 102 assert.Empty(t, response.Cookies()) 103 } 104 105 func TestTokenAuthControllerRejectsEmptyToken(t *testing.T) { 106 requestBody := strings.NewReader("token=") 107 request := httptest.NewRequest(http.MethodPost, "/api/authenticate", requestBody) 108 request.Header.Add("Content-Type", "application/x-www-form-urlencoded") 109 110 conf := config.NewConfig() 111 k8s := kubetest.NewFakeK8sClient() 112 mockClientFactory := kubetest.NewK8SClientFactoryMock(k8s) 113 cache := cache.NewTestingCacheWithFactory(t, mockClientFactory, *conf) 114 controller := NewTokenAuthController(NewCookieSessionPersistor(conf), mockClientFactory, cache, conf) 115 116 rr := httptest.NewRecorder() 117 sData, err := controller.Authenticate(request, rr) 118 119 assert.Nil(t, sData) 120 assert.Error(t, err) 121 assert.Contains(t, err.Error(), "empty") 122 123 // Check no cookies are set 124 response := rr.Result() 125 assert.Empty(t, response.Cookies()) 126 } 127 128 func TestTokenAuthControllerValidatesSessionCorrectly(t *testing.T) { 129 rr, _, _, controller := createValidSession(t) 130 response := rr.Result() 131 132 request := httptest.NewRequest(http.MethodGet, "/api/get", nil) 133 for _, c := range response.Cookies() { 134 request.AddCookie(c) 135 } 136 137 rr = httptest.NewRecorder() 138 sData, err := controller.ValidateSession(request, rr) 139 140 assert.Nil(t, err) 141 assert.NotNil(t, sData) 142 assert.Equal(t, testToken, sData.AuthInfo.Token) 143 assert.Equal(t, "k8s_user", sData.Username) 144 assert.Equal(t, time.Date(2021, 12, 1, 0, 0, 1, 0, time.UTC), sData.ExpiresOn) 145 } 146 147 func TestTokenAuthControllerValidatesSessionWithoutActiveSession(t *testing.T) { 148 conf := config.NewConfig() 149 config.Set(conf) 150 request := httptest.NewRequest(http.MethodGet, "/api/get", nil) 151 152 k8s := kubetest.NewFakeK8sClient() 153 154 rr := httptest.NewRecorder() 155 mockClientFactory := kubetest.NewK8SClientFactoryMock(k8s) 156 cache := cache.NewTestingCacheWithFactory(t, mockClientFactory, *conf) 157 158 controller := NewTokenAuthController(NewCookieSessionPersistor(conf), mockClientFactory, cache, conf) 159 sData, err := controller.ValidateSession(request, rr) 160 161 assert.Nil(t, err) 162 assert.Nil(t, sData) 163 } 164 165 type forbiddenClient struct { 166 kubernetes.ClientInterface 167 } 168 169 func (f forbiddenClient) GetNamespaces(labelSelector string) ([]v1.Namespace, error) { 170 return nil, k8s_errors.NewForbidden(schema.GroupResource{Group: "v1", Resource: "namespaces"}, "", errors.New("err")) 171 } 172 173 func TestTokenAuthControllerValidatesSessionForUserWithMissingPrivileges(t *testing.T) { 174 rr, _, _, controller := createValidSession(t) 175 response := rr.Result() 176 177 request := httptest.NewRequest(http.MethodGet, "/api/get", nil) 178 for _, c := range response.Cookies() { 179 request.AddCookie(c) 180 } 181 182 // Empty cache that will miss. 183 forbiddenClient := &forbiddenClient{kubetest.NewFakeK8sClient()} 184 185 mockClientFactory := kubetest.NewK8SClientFactoryMock(forbiddenClient) 186 cache := cache.NewTestingCacheWithFactory(t, mockClientFactory, *config.Get()) 187 controller.clientFactory = mockClientFactory 188 controller.kialiCache = cache 189 190 rr = httptest.NewRecorder() 191 sData, err := controller.ValidateSession(request, rr) 192 193 assert.Nil(t, err) 194 assert.Nil(t, sData) 195 } 196 197 func createValidSession(t *testing.T) (*httptest.ResponseRecorder, *UserSessionData, *kubetest.FakeK8sClient, *tokenAuthController) { 198 t.Helper() 199 200 clockTime := time.Date(2021, 12, 1, 0, 0, 0, 0, time.UTC) 201 util.Clock = util.ClockMock{Time: clockTime} 202 203 conf := config.NewConfig() 204 conf.LoginToken.SigningKey = "kiali67890123456" 205 conf.LoginToken.ExpirationSeconds = 1 206 config.Set(conf) 207 208 // Returning some namespace when a cluster API call is made should have the result of 209 // a successful authentication. 210 k8s := kubetest.NewFakeK8sClient(&v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "Foo"}}) 211 212 requestBody := strings.NewReader("token=" + testToken) 213 request := httptest.NewRequest(http.MethodPost, "/api/authenticate", requestBody) 214 request.Header.Add("Content-Type", "application/x-www-form-urlencoded") 215 216 rr := httptest.NewRecorder() 217 mockClientFactory := kubetest.NewK8SClientFactoryMock(k8s) 218 cache := cache.NewTestingCacheWithFactory(t, mockClientFactory, *conf) 219 220 controller := NewTokenAuthController(NewCookieSessionPersistor(conf), mockClientFactory, cache, conf) 221 222 sData, err := controller.Authenticate(request, rr) 223 if err != nil { 224 t.Fatal(err) 225 } 226 return rr, sData, k8s, controller 227 }