test/integration: prefactoring for testing virtual hosts

Signed-off-by: Andrew Keesler <akeesler@vmware.com>
This commit is contained in:
Ryan Richard 2020-10-20 17:00:36 -04:00 committed by Andrew Keesler
parent aff85acf37
commit 9ba93d66c3
No known key found for this signature in database
GPG Key ID: 27CE0444346F9413
3 changed files with 85 additions and 47 deletions

View File

@ -277,7 +277,7 @@ export PINNIPED_TEST_WEBHOOK_CA_BUNDLE=${webhook_ca_bundle}
export PINNIPED_TEST_SUPERVISOR_NAMESPACE=${supervisor_namespace} export PINNIPED_TEST_SUPERVISOR_NAMESPACE=${supervisor_namespace}
export PINNIPED_TEST_SUPERVISOR_APP_NAME=${supervisor_app_name} export PINNIPED_TEST_SUPERVISOR_APP_NAME=${supervisor_app_name}
export PINNIPED_TEST_SUPERVISOR_CUSTOM_LABELS='${supervisor_custom_labels}' export PINNIPED_TEST_SUPERVISOR_CUSTOM_LABELS='${supervisor_custom_labels}'
export PINNIPED_TEST_SUPERVISOR_ADDRESS="127.0.0.1:12345" export PINNIPED_TEST_SUPERVISOR_HTTP_ADDRESS="127.0.0.1:12345"
export PINNIPED_TEST_CLI_OIDC_ISSUER=http://127.0.0.1:12346/dex export PINNIPED_TEST_CLI_OIDC_ISSUER=http://127.0.0.1:12346/dex
export PINNIPED_TEST_CLI_OIDC_CLIENT_ID=pinniped-cli export PINNIPED_TEST_CLI_OIDC_CLIENT_ID=pinniped-cli
export PINNIPED_TEST_CLI_OIDC_LOCALHOST_PORT=48095 export PINNIPED_TEST_CLI_OIDC_LOCALHOST_PORT=48095

View File

@ -9,6 +9,8 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url"
"strings"
"testing" "testing"
"time" "time"
@ -51,88 +53,106 @@ func TestSupervisorOIDCDiscovery(t *testing.T) {
} }
}) })
supervisorScheme := "http"
supervisorAddress := env.SupervisorHTTPAddress
// Test that there is no default discovery endpoint available when there are no OIDCProviderConfigs. // Test that there is no default discovery endpoint available when there are no OIDCProviderConfigs.
requireDiscoveryEndpointsAreNotFound(t, fmt.Sprintf("http://%s", env.SupervisorAddress)) requireDiscoveryEndpointsAreNotFound(t, supervisorScheme, supervisorAddress, fmt.Sprintf("%s://%s", supervisorScheme, supervisorAddress))
// Define several unique issuer strings. // Define several unique issuer strings.
issuer1 := fmt.Sprintf("http://%s/nested/issuer1", env.SupervisorAddress) // We add a "random" port number to this host to validate that our OIDC router works when given a
issuer2 := fmt.Sprintf("http://%s/nested/issuer2", env.SupervisorAddress) // host that has a port number in it.
issuer3 := fmt.Sprintf("http://%s/issuer3", env.SupervisorAddress) // nonMatchingIssuerHost := "some-issuer-host-that-doesnt-match-supervisor-address:2684"
issuer4 := fmt.Sprintf("http://%s/issuer4", env.SupervisorAddress) issuer1 := fmt.Sprintf("%s://%s/nested/issuer1", supervisorScheme, supervisorAddress)
issuer5 := fmt.Sprintf("http://%s/issuer5", env.SupervisorAddress) issuer2 := fmt.Sprintf("%s://%s/nested/issuer2", supervisorScheme, supervisorAddress)
issuer6 := fmt.Sprintf("http://%s/issuer6", env.SupervisorAddress) issuer3 := fmt.Sprintf("%s://%s/issuer3", supervisorScheme, supervisorAddress)
badIssuer := fmt.Sprintf("http://%s/badIssuer?cannot-use=queries", env.SupervisorAddress) issuer4 := fmt.Sprintf("%s://%s/issuer4", supervisorScheme, supervisorAddress)
issuer5 := fmt.Sprintf("%s://%s/issuer5", supervisorScheme, supervisorAddress)
issuer6 := fmt.Sprintf("%s://%s/issuer6", supervisorScheme, supervisorAddress)
// issuer7 := fmt.Sprintf("%s://%s:%d/issuer6", supervisorScheme, nonMatchingIssuerHost)
badIssuer := fmt.Sprintf("%s://%s/badIssuer?cannot-use=queries", supervisorScheme, supervisorAddress)
// When OIDCProviderConfig are created in sequence they each cause a discovery endpoint to appear only for as long as the OIDCProviderConfig exists. // When OIDCProviderConfig are created in sequence they each cause a discovery endpoint to appear only for as long as the OIDCProviderConfig exists.
config1, jwks1 := requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(ctx, t, issuer1, client) config1, jwks1 := requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(ctx, t, supervisorScheme, supervisorAddress, issuer1, client)
requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config1, client, ns, issuer1) requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config1, client, ns, supervisorScheme, supervisorAddress, issuer1)
config2, jwks2 := requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(ctx, t, issuer2, client) config2, jwks2 := requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(ctx, t, supervisorScheme, supervisorAddress, issuer2, client)
requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config2, client, ns, issuer2) requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config2, client, ns, supervisorScheme, supervisorAddress, issuer2)
// The auto-created JWK's were different from each other. // The auto-created JWK's were different from each other.
require.NotEqual(t, jwks1.Keys[0]["x"], jwks2.Keys[0]["x"]) require.NotEqual(t, jwks1.Keys[0]["x"], jwks2.Keys[0]["x"])
require.NotEqual(t, jwks1.Keys[0]["y"], jwks2.Keys[0]["y"]) require.NotEqual(t, jwks1.Keys[0]["y"], jwks2.Keys[0]["y"])
// When multiple OIDCProviderConfigs exist at the same time they each serve a unique discovery endpoint. // When multiple OIDCProviderConfigs exist at the same time they each serve a unique discovery endpoint.
config3, jwks3 := requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(ctx, t, issuer3, client) config3, jwks3 := requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(ctx, t, supervisorScheme, supervisorAddress, issuer3, client)
config4, jwks4 := requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(ctx, t, issuer4, client) config4, jwks4 := requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(ctx, t, supervisorScheme, supervisorAddress, issuer4, client)
requireDiscoveryEndpointsAreWorking(t, issuer3) // discovery for issuer3 is still working after issuer4 started working requireDiscoveryEndpointsAreWorking(t, supervisorScheme, supervisorAddress, issuer3) // discovery for issuer3 is still working after issuer4 started working
// The auto-created JWK's were different from each other. // The auto-created JWK's were different from each other.
require.NotEqual(t, jwks3.Keys[0]["x"], jwks4.Keys[0]["x"]) require.NotEqual(t, jwks3.Keys[0]["x"], jwks4.Keys[0]["x"])
require.NotEqual(t, jwks3.Keys[0]["y"], jwks4.Keys[0]["y"]) require.NotEqual(t, jwks3.Keys[0]["y"], jwks4.Keys[0]["y"])
// Editing a provider to change the issuer name updates the endpoints that are being served. // Editing a provider to change the issuer name updates the endpoints that are being served.
updatedConfig4 := editOIDCProviderConfigIssuerName(t, config4, client, ns, issuer5) updatedConfig4 := editOIDCProviderConfigIssuerName(t, config4, client, ns, issuer5)
requireDiscoveryEndpointsAreNotFound(t, issuer4) requireDiscoveryEndpointsAreNotFound(t, supervisorScheme, supervisorAddress, issuer4)
jwks5 := requireDiscoveryEndpointsAreWorking(t, issuer5) jwks5 := requireDiscoveryEndpointsAreWorking(t, supervisorScheme, supervisorAddress, issuer5)
// The JWK did not change when the issuer name was updated. // The JWK did not change when the issuer name was updated.
require.Equal(t, jwks4.Keys[0], jwks5.Keys[0]) require.Equal(t, jwks4.Keys[0], jwks5.Keys[0])
// When they are deleted they stop serving discovery endpoints. // When they are deleted they stop serving discovery endpoints.
requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config3, client, ns, issuer3) requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config3, client, ns, supervisorScheme, supervisorAddress, issuer3)
requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, updatedConfig4, client, ns, issuer5) requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, updatedConfig4, client, ns, supervisorScheme, supervisorAddress, issuer5)
// When the same issuer is added twice, both issuers are marked as duplicates, and neither provider is serving. // When the same issuer is added twice, both issuers are marked as duplicates, and neither provider is serving.
config5Duplicate1, _ := requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(ctx, t, issuer6, client) config5Duplicate1, _ := requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(ctx, t, supervisorScheme, supervisorAddress, issuer6, client)
config5Duplicate2 := library.CreateTestOIDCProvider(ctx, t, issuer6) config5Duplicate2 := library.CreateTestOIDCProvider(ctx, t, issuer6)
requireStatus(t, client, ns, config5Duplicate1.Name, v1alpha1.DuplicateOIDCProviderStatus) requireStatus(t, client, ns, config5Duplicate1.Name, v1alpha1.DuplicateOIDCProviderStatus)
requireStatus(t, client, ns, config5Duplicate2.Name, v1alpha1.DuplicateOIDCProviderStatus) requireStatus(t, client, ns, config5Duplicate2.Name, v1alpha1.DuplicateOIDCProviderStatus)
requireDiscoveryEndpointsAreNotFound(t, issuer6) requireDiscoveryEndpointsAreNotFound(t, supervisorScheme, supervisorAddress, issuer6)
// If we delete the first duplicate issuer, the second duplicate issuer starts serving. // If we delete the first duplicate issuer, the second duplicate issuer starts serving.
requireDelete(t, client, ns, config5Duplicate1.Name) requireDelete(t, client, ns, config5Duplicate1.Name)
requireWellKnownEndpointIsWorking(t, issuer6) requireWellKnownEndpointIsWorking(t, supervisorScheme, supervisorAddress, issuer6)
requireStatus(t, client, ns, config5Duplicate2.Name, v1alpha1.SuccessOIDCProviderStatus) requireStatus(t, client, ns, config5Duplicate2.Name, v1alpha1.SuccessOIDCProviderStatus)
// When we finally delete all issuers, the endpoint should be down. // When we finally delete all issuers, the endpoint should be down.
requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config5Duplicate2, client, ns, issuer6) requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config5Duplicate2, client, ns, supervisorScheme, supervisorAddress, issuer6)
// When we create a provider with an invalid issuer, the status is set to invalid. // When we create a provider with an invalid issuer, the status is set to invalid.
badConfig := library.CreateTestOIDCProvider(ctx, t, badIssuer) badConfig := library.CreateTestOIDCProvider(ctx, t, badIssuer)
requireStatus(t, client, ns, badConfig.Name, v1alpha1.InvalidOIDCProviderStatus) requireStatus(t, client, ns, badConfig.Name, v1alpha1.InvalidOIDCProviderStatus)
requireDiscoveryEndpointsAreNotFound(t, badIssuer) requireDiscoveryEndpointsAreNotFound(t, supervisorScheme, supervisorAddress, badIssuer)
} }
func jwksURLForIssuer(issuerName string) string { func jwksURLForIssuer(scheme, host, path string) string {
return fmt.Sprintf("%s/jwks.json", issuerName) if strings.HasPrefix(path, "/") {
path = path[1:]
}
return fmt.Sprintf("%s://%s/%s/jwks.json", scheme, host, path)
} }
func wellKnownURLForIssuer(issuerName string) string { func wellKnownURLForIssuer(scheme, host, path string) string {
return fmt.Sprintf("%s/.well-known/openid-configuration", issuerName) if strings.HasPrefix(path, "/") {
path = path[1:]
}
return fmt.Sprintf("%s://%s/%s/.well-known/openid-configuration", scheme, host, path)
} }
func requireDiscoveryEndpointsAreNotFound(t *testing.T, issuerName string) { func requireDiscoveryEndpointsAreNotFound(t *testing.T, supervisorScheme, supervisorAddress, issuerName string) {
t.Helper() t.Helper()
requireEndpointNotFound(t, wellKnownURLForIssuer(issuerName)) issuerURL, err := url.Parse(issuerName)
requireEndpointNotFound(t, jwksURLForIssuer(issuerName)) require.NoError(t, err)
requireEndpointNotFound(t, wellKnownURLForIssuer(supervisorScheme, supervisorAddress, issuerURL.Path), issuerURL.Host)
requireEndpointNotFound(t, jwksURLForIssuer(supervisorScheme, supervisorAddress, issuerURL.Path), issuerURL.Host)
} }
func requireEndpointNotFound(t *testing.T, url string) { func requireEndpointNotFound(t *testing.T, url, host string) {
t.Helper() t.Helper()
httpClient := &http.Client{} httpClient := &http.Client{}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel() defer cancel()
requestNonExistentPath, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) requestNonExistentPath, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
require.NoError(t, err)
requestNonExistentPath.Header.Add("Host", host)
var response *http.Response var response *http.Response
assert.Eventually(t, func() bool { assert.Eventually(t, func() bool {
@ -148,6 +168,7 @@ func requireEndpointNotFound(t *testing.T, url string) {
func requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear( func requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(
ctx context.Context, ctx context.Context,
t *testing.T, t *testing.T,
supervisorScheme, supervisorAddress string,
issuerName string, issuerName string,
client pinnipedclientset.Interface, client pinnipedclientset.Interface,
) (*v1alpha1.OIDCProviderConfig, *ExpectedJWKSResponseFormat) { ) (*v1alpha1.OIDCProviderConfig, *ExpectedJWKSResponseFormat) {
@ -155,16 +176,16 @@ func requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(
newOIDCProviderConfig := library.CreateTestOIDCProvider(ctx, t, issuerName) newOIDCProviderConfig := library.CreateTestOIDCProvider(ctx, t, issuerName)
jwksResult := requireDiscoveryEndpointsAreWorking(t, issuerName) jwksResult := requireDiscoveryEndpointsAreWorking(t, supervisorScheme, supervisorAddress, issuerName)
requireStatus(t, client, newOIDCProviderConfig.Namespace, newOIDCProviderConfig.Name, v1alpha1.SuccessOIDCProviderStatus) requireStatus(t, client, newOIDCProviderConfig.Namespace, newOIDCProviderConfig.Name, v1alpha1.SuccessOIDCProviderStatus)
return newOIDCProviderConfig, jwksResult return newOIDCProviderConfig, jwksResult
} }
func requireDiscoveryEndpointsAreWorking(t *testing.T, issuerName string) *ExpectedJWKSResponseFormat { func requireDiscoveryEndpointsAreWorking(t *testing.T, supervisorScheme, supervisorAddress, issuerName string) *ExpectedJWKSResponseFormat {
requireWellKnownEndpointIsWorking(t, issuerName) requireWellKnownEndpointIsWorking(t, supervisorScheme, supervisorAddress, issuerName)
jwksResult := requireJWKSEndpointIsWorking(t, issuerName) jwksResult := requireJWKSEndpointIsWorking(t, supervisorScheme, supervisorAddress, issuerName)
return jwksResult return jwksResult
} }
@ -173,6 +194,7 @@ func requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(
existingOIDCProviderConfig *v1alpha1.OIDCProviderConfig, existingOIDCProviderConfig *v1alpha1.OIDCProviderConfig,
client pinnipedclientset.Interface, client pinnipedclientset.Interface,
ns string, ns string,
supervisorScheme, supervisorAddress string,
issuerName string, issuerName string,
) { ) {
t.Helper() t.Helper()
@ -184,13 +206,15 @@ func requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(
require.NoError(t, err) require.NoError(t, err)
// Fetch that same discovery endpoint as before, but now it should not exist anymore. Give it some time for the endpoint to go away. // Fetch that same discovery endpoint as before, but now it should not exist anymore. Give it some time for the endpoint to go away.
requireDiscoveryEndpointsAreNotFound(t, issuerName) requireDiscoveryEndpointsAreNotFound(t, supervisorScheme, supervisorAddress, issuerName)
} }
func requireWellKnownEndpointIsWorking(t *testing.T, issuerName string) { func requireWellKnownEndpointIsWorking(t *testing.T, supervisorScheme, supervisorAddress, issuerName string) {
t.Helper() t.Helper()
response, responseBody := requireSuccessEndpointResponse(t, wellKnownURLForIssuer(issuerName)) //nolint:bodyclose issuerURL, err := url.Parse(issuerName)
require.NoError(t, err)
response, responseBody := requireSuccessEndpointResponse(t, wellKnownURLForIssuer(supervisorScheme, supervisorAddress, issuerURL.Path), issuerName) //nolint:bodyclose
// Check that the response matches our expectations. // Check that the response matches our expectations.
expectedResultTemplate := here.Doc(`{ expectedResultTemplate := here.Doc(`{
@ -216,13 +240,15 @@ type ExpectedJWKSResponseFormat struct {
Keys []map[string]string Keys []map[string]string
} }
func requireJWKSEndpointIsWorking(t *testing.T, issuerName string) *ExpectedJWKSResponseFormat { func requireJWKSEndpointIsWorking(t *testing.T, supervisorScheme, supervisorAddress, issuerName string) *ExpectedJWKSResponseFormat {
t.Helper() t.Helper()
response, responseBody := requireSuccessEndpointResponse(t, jwksURLForIssuer(issuerName)) //nolint:bodyclose issuerURL, err := url.Parse(issuerName)
require.NoError(t, err)
response, responseBody := requireSuccessEndpointResponse(t, jwksURLForIssuer(supervisorScheme, supervisorAddress, issuerURL.Path), issuerName) //nolint:bodyclose
var result ExpectedJWKSResponseFormat var result ExpectedJWKSResponseFormat
err := json.Unmarshal([]byte(responseBody), &result) err = json.Unmarshal([]byte(responseBody), &result)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, result.Keys, 1) require.Len(t, result.Keys, 1)
@ -241,20 +267,25 @@ func requireJWKSEndpointIsWorking(t *testing.T, issuerName string) *ExpectedJWKS
return &result return &result
} }
func requireSuccessEndpointResponse(t *testing.T, wellKnownURL string) (*http.Response, string) { func requireSuccessEndpointResponse(t *testing.T, endpointURL, issuer string) (*http.Response, string) {
httpClient := &http.Client{} httpClient := &http.Client{}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel() defer cancel()
issuerURL, err := url.Parse(issuer)
require.NoError(t, err)
// Define a request to the new discovery endpoint which should have been created by an OIDCProviderConfig. // Define a request to the new discovery endpoint which should have been created by an OIDCProviderConfig.
requestDiscoveryEndpoint, err := http.NewRequestWithContext( requestDiscoveryEndpoint, err := http.NewRequestWithContext(
ctx, ctx,
http.MethodGet, http.MethodGet,
wellKnownURL, endpointURL,
nil, nil,
) )
require.NoError(t, err) require.NoError(t, err)
requestDiscoveryEndpoint.Header.Add("Host", issuerURL.Host)
// Fetch that discovery endpoint. Give it some time for the endpoint to come into existence. // Fetch that discovery endpoint. Give it some time for the endpoint to come into existence.
var response *http.Response var response *http.Response
assert.Eventually(t, func() bool { assert.Eventually(t, func() bool {
@ -315,3 +346,10 @@ func requireStatus(t *testing.T, client pinnipedclientset.Interface, ns, name st
require.NoError(t, err) require.NoError(t, err)
require.Equalf(t, status, opc.Status.Status, "unexpected status (message = '%s')", opc.Status.Message) require.Equalf(t, status, opc.Status.Status, "unexpected status (message = '%s')", opc.Status.Message)
} }
func getHostFromIssuer(t *testing.T, issuerName string) string {
t.Helper()
issuerURL, err := url.Parse(issuerName)
require.NoError(t, err)
return issuerURL.Host
}

View File

@ -34,7 +34,7 @@ type TestEnv struct {
ConciergeCustomLabels map[string]string `json:"conciergeCustomLabels"` ConciergeCustomLabels map[string]string `json:"conciergeCustomLabels"`
Capabilities map[Capability]bool `json:"capabilities"` Capabilities map[Capability]bool `json:"capabilities"`
TestWebhook idpv1alpha1.WebhookIdentityProviderSpec `json:"testWebhook"` TestWebhook idpv1alpha1.WebhookIdentityProviderSpec `json:"testWebhook"`
SupervisorAddress string `json:"supervisorAddress"` SupervisorHTTPAddress string `json:"supervisorAddress"`
TestUser struct { TestUser struct {
Token string `json:"token"` Token string `json:"token"`
@ -88,7 +88,7 @@ func IntegrationEnv(t *testing.T) *TestEnv {
result.TestWebhook.Endpoint = needEnv("PINNIPED_TEST_WEBHOOK_ENDPOINT") result.TestWebhook.Endpoint = needEnv("PINNIPED_TEST_WEBHOOK_ENDPOINT")
result.SupervisorNamespace = needEnv("PINNIPED_TEST_SUPERVISOR_NAMESPACE") result.SupervisorNamespace = needEnv("PINNIPED_TEST_SUPERVISOR_NAMESPACE")
result.SupervisorAppName = needEnv("PINNIPED_TEST_SUPERVISOR_APP_NAME") result.SupervisorAppName = needEnv("PINNIPED_TEST_SUPERVISOR_APP_NAME")
result.SupervisorAddress = needEnv("PINNIPED_TEST_SUPERVISOR_ADDRESS") result.SupervisorHTTPAddress = needEnv("PINNIPED_TEST_SUPERVISOR_HTTP_ADDRESS")
result.TestWebhook.TLS = &idpv1alpha1.TLSSpec{CertificateAuthorityData: needEnv("PINNIPED_TEST_WEBHOOK_CA_BUNDLE")} result.TestWebhook.TLS = &idpv1alpha1.TLSSpec{CertificateAuthorityData: needEnv("PINNIPED_TEST_WEBHOOK_CA_BUNDLE")}
conciergeCustomLabelsYAML := needEnv("PINNIPED_TEST_CONCIERGE_CUSTOM_LABELS") conciergeCustomLabelsYAML := needEnv("PINNIPED_TEST_CONCIERGE_CUSTOM_LABELS")