github.com/verrazzano/verrazzano@v1.7.0/authproxy/src/proxy/proxy_test.go (about) 1 // Copyright (c) 2023, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package proxy 5 6 import ( 7 "fmt" 8 "net/http" 9 "net/http/httptest" 10 "net/url" 11 "os" 12 "reflect" 13 "runtime" 14 "strings" 15 "testing" 16 17 "github.com/stretchr/testify/assert" 18 "github.com/verrazzano/verrazzano/authproxy/internal/testutil/file" 19 "github.com/verrazzano/verrazzano/authproxy/internal/testutil/testserver" 20 "github.com/verrazzano/verrazzano/authproxy/src/auth" 21 "github.com/verrazzano/verrazzano/pkg/k8sutil" 22 "go.uber.org/zap" 23 "k8s.io/client-go/rest" 24 "sigs.k8s.io/controller-runtime/pkg/client/fake" 25 ) 26 27 const ( 28 apiPath = "/api/v1/pods" 29 testAPIServerURL = "https://api-server.io" 30 caCertFile = "./testdata/test-ca.crt" 31 ) 32 33 var serverURL string 34 35 // TestConfigureKubernetesAPIProxy tests the configuration of the API proxy 36 // GIVEN an Auth proxy object 37 // WHEN the Kubernetes API proxy is configured 38 // THEN the handler exists and there is no error 39 func TestConfigureKubernetesAPIProxy(t *testing.T) { 40 c := fake.NewClientBuilder().Build() 41 authproxy := InitializeProxy(8777) 42 log := zap.S() 43 44 getConfigFunc = testConfig 45 defer func() { getConfigFunc = k8sutil.GetConfigFromController }() 46 47 err := ConfigureKubernetesAPIProxy(authproxy, c, log) 48 assert.NoError(t, err) 49 assert.NotNil(t, authproxy.Handler) 50 } 51 52 // TestLoadCAData tests that the CA data is properly loaded from sources 53 func TestLoadCAData(t *testing.T) { 54 // GIVEN a config with the CA Data populated 55 // WHEN the cert pool is generated 56 // THEN no error is returned 57 caData, err := os.ReadFile(caCertFile) 58 assert.NoError(t, err) 59 config := &rest.Config{ 60 TLSClientConfig: rest.TLSClientConfig{ 61 CAData: caData, 62 }, 63 } 64 log := zap.S() 65 pool, err := loadCAData(config, log) 66 assert.NoError(t, err) 67 assert.NotEmpty(t, pool) 68 69 // GIVEN a config with the CA File populated 70 // WHEN the cert pool is generated 71 // THEN no error is returned 72 config = &rest.Config{ 73 TLSClientConfig: rest.TLSClientConfig{ 74 CAFile: caCertFile, 75 }, 76 } 77 pool, err = loadCAData(config, log) 78 assert.NoError(t, err) 79 assert.NotEmpty(t, pool) 80 } 81 82 // TestLoadBearerToken tests that the bearer token is properly loaded from the config 83 func TestLoadBearerToken(t *testing.T) { 84 log := zap.S() 85 86 // GIVEN a config with Bearer Token populated 87 // WHEN the bearer token is loaded 88 // THEN the handler gets the bearer token data 89 testToken := "test-token" 90 config := &rest.Config{ 91 BearerToken: testToken, 92 } 93 bearerToken, err := loadBearerToken(config, log) 94 assert.NoError(t, err) 95 assert.Equal(t, testToken, bearerToken) 96 97 // GIVEN a config with the Bearer Token file populated 98 // WHEN the bearer token is loaded 99 // THEN the handler gets the bearer token data 100 testTokenFile, err := file.MakeTempFile(testToken) 101 if testTokenFile != nil { 102 defer os.Remove(testTokenFile.Name()) 103 } 104 assert.NoError(t, err) 105 config = &rest.Config{ 106 BearerTokenFile: testTokenFile.Name(), 107 } 108 bearerToken, err = loadBearerToken(config, log) 109 assert.NoError(t, err) 110 assert.Equal(t, testToken, bearerToken) 111 112 // GIVEN a config with no bearer information 113 // WHEN the bearer token is loaded 114 // THEN the handler gets not bearer token data 115 config = &rest.Config{} 116 bearerToken, err = loadBearerToken(config, log) 117 assert.NoError(t, err) 118 assert.Empty(t, bearerToken) 119 } 120 121 // TestInitializeAuthenticator tests that the authenticator gets initialized if it has not previously 122 func TestInitializeAuthenticator(t *testing.T) { 123 handler := Handler{ 124 URL: testAPIServerURL, 125 K8sClient: fake.NewClientBuilder().Build(), 126 Log: zap.S(), 127 } 128 129 server := testserver.FakeOIDCProviderServer(t) 130 serverURL = server.URL 131 132 getOIDCConfigFunc = fakeOIDCConfig 133 defer func() { getOIDCConfigFunc = getOIDCConfiguration }() 134 135 // GIVEN a request to initialize the authenticator 136 // WHEN the authenticator has already been initialized 137 // THEN no error is returned 138 handler.AuthInited.Store(true) 139 err := handler.initializeAuthenticator() 140 assert.NoError(t, err) 141 142 // GIVEN a request to initialize the authenticator 143 // WHEN the authenticator has not been initialized 144 // THEN no error is returned 145 handler.AuthInited.Store(false) 146 err = handler.initializeAuthenticator() 147 assert.NoError(t, err) 148 } 149 150 // TestFindPathHandler tests that the correct handler is returned for a given request 151 func TestFindPathHandler(t *testing.T) { 152 handler := Handler{ 153 URL: testAPIServerURL, 154 K8sClient: fake.NewClientBuilder().Build(), 155 Log: zap.S(), 156 } 157 158 // GIVEN a request 159 // WHEN the url has the callback path 160 // THEN the callback function is returned 161 callbackURL, err := url.Parse(fmt.Sprintf("%s%s", testAPIServerURL, callbackPath)) 162 assert.NoError(t, err) 163 req := &http.Request{URL: callbackURL} 164 handlerfunc := handler.findPathHandler(req) 165 handlerName := runtime.FuncForPC(reflect.ValueOf(handlerfunc).Pointer()).Name() 166 authCallbackName := runtime.FuncForPC(reflect.ValueOf(handler.handleAuthCallback).Pointer()).Name() 167 assert.Equal(t, handlerName, authCallbackName) 168 169 // GIVEN a request 170 // WHEN the url has the logout path 171 // THEN the logout function is returned 172 logoutURL, err := url.Parse(fmt.Sprintf("%s%s", testAPIServerURL, logoutPath)) 173 assert.NoError(t, err) 174 req = &http.Request{URL: logoutURL} 175 handlerfunc = handler.findPathHandler(req) 176 handlerName = runtime.FuncForPC(reflect.ValueOf(handlerfunc).Pointer()).Name() 177 logoutName := runtime.FuncForPC(reflect.ValueOf(handler.handleLogout).Pointer()).Name() 178 assert.Equal(t, handlerName, logoutName) 179 180 // GIVEN a request 181 // WHEN the url has any path 182 // THEN the api server function is returned 183 apiReqURL, err := url.Parse(testAPIServerURL) 184 assert.NoError(t, err) 185 req = &http.Request{URL: apiReqURL} 186 handlerfunc = handler.findPathHandler(req) 187 handlerName = runtime.FuncForPC(reflect.ValueOf(handlerfunc).Pointer()).Name() 188 apiReqName := runtime.FuncForPC(reflect.ValueOf(handler.handleAPIRequest).Pointer()).Name() 189 assert.Equal(t, handlerName, apiReqName) 190 191 } 192 193 // TestServeHTTP tests that the incoming HTTP requests can be properly handled and forwarded 194 // GIVEN an HTTP request 195 // WHEN the request is processed 196 // THEN no error is returned 197 func TestServeHTTP(t *testing.T) { 198 handler := Handler{ 199 URL: testAPIServerURL, 200 K8sClient: fake.NewClientBuilder().Build(), 201 Log: zap.S(), 202 } 203 204 server := testserver.FakeOIDCProviderServer(t) 205 serverURL = server.URL 206 207 getOIDCConfigFunc = fakeOIDCConfig 208 defer func() { getOIDCConfigFunc = getOIDCConfiguration }() 209 210 // Sending an option request so the API Server request terminates early 211 req := httptest.NewRequest(http.MethodOptions, serverURL, strings.NewReader("")) 212 rw := httptest.NewRecorder() 213 handler.ServeHTTP(rw, req) 214 } 215 216 func testConfig() (*rest.Config, error) { 217 return &rest.Config{ 218 Host: "test-host", 219 TLSClientConfig: rest.TLSClientConfig{ 220 CAFile: caCertFile, 221 }, 222 }, nil 223 } 224 225 func fakeOIDCConfig() auth.OIDCConfiguration { 226 return auth.OIDCConfiguration{ 227 ExternalURL: serverURL, 228 ServiceURL: serverURL, 229 } 230 }