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_APP_NAME=${supervisor_app_name}
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_CLIENT_ID=pinniped-cli
export PINNIPED_TEST_CLI_OIDC_LOCALHOST_PORT=48095

View File

@ -9,6 +9,8 @@ import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"testing"
"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.
requireDiscoveryEndpointsAreNotFound(t, fmt.Sprintf("http://%s", env.SupervisorAddress))
requireDiscoveryEndpointsAreNotFound(t, supervisorScheme, supervisorAddress, fmt.Sprintf("%s://%s", supervisorScheme, supervisorAddress))
// Define several unique issuer strings.
issuer1 := fmt.Sprintf("http://%s/nested/issuer1", env.SupervisorAddress)
issuer2 := fmt.Sprintf("http://%s/nested/issuer2", env.SupervisorAddress)
issuer3 := fmt.Sprintf("http://%s/issuer3", env.SupervisorAddress)
issuer4 := fmt.Sprintf("http://%s/issuer4", env.SupervisorAddress)
issuer5 := fmt.Sprintf("http://%s/issuer5", env.SupervisorAddress)
issuer6 := fmt.Sprintf("http://%s/issuer6", env.SupervisorAddress)
badIssuer := fmt.Sprintf("http://%s/badIssuer?cannot-use=queries", env.SupervisorAddress)
// We add a "random" port number to this host to validate that our OIDC router works when given a
// host that has a port number in it.
// nonMatchingIssuerHost := "some-issuer-host-that-doesnt-match-supervisor-address:2684"
issuer1 := fmt.Sprintf("%s://%s/nested/issuer1", supervisorScheme, supervisorAddress)
issuer2 := fmt.Sprintf("%s://%s/nested/issuer2", supervisorScheme, supervisorAddress)
issuer3 := fmt.Sprintf("%s://%s/issuer3", supervisorScheme, 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.
config1, jwks1 := requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(ctx, t, issuer1, client)
requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config1, client, ns, issuer1)
config2, jwks2 := requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(ctx, t, issuer2, client)
requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config2, client, ns, issuer2)
config1, jwks1 := requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(ctx, t, supervisorScheme, supervisorAddress, issuer1, client)
requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config1, client, ns, supervisorScheme, supervisorAddress, issuer1)
config2, jwks2 := requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(ctx, t, supervisorScheme, supervisorAddress, issuer2, client)
requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config2, client, ns, supervisorScheme, supervisorAddress, issuer2)
// 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]["y"], jwks2.Keys[0]["y"])
// When multiple OIDCProviderConfigs exist at the same time they each serve a unique discovery endpoint.
config3, jwks3 := requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(ctx, t, issuer3, client)
config4, jwks4 := requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(ctx, t, issuer4, client)
requireDiscoveryEndpointsAreWorking(t, issuer3) // discovery for issuer3 is still working after issuer4 started working
config3, jwks3 := requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(ctx, t, supervisorScheme, supervisorAddress, issuer3, client)
config4, jwks4 := requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(ctx, t, supervisorScheme, supervisorAddress, issuer4, client)
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.
require.NotEqual(t, jwks3.Keys[0]["x"], jwks4.Keys[0]["x"])
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.
updatedConfig4 := editOIDCProviderConfigIssuerName(t, config4, client, ns, issuer5)
requireDiscoveryEndpointsAreNotFound(t, issuer4)
jwks5 := requireDiscoveryEndpointsAreWorking(t, issuer5)
requireDiscoveryEndpointsAreNotFound(t, supervisorScheme, supervisorAddress, issuer4)
jwks5 := requireDiscoveryEndpointsAreWorking(t, supervisorScheme, supervisorAddress, issuer5)
// The JWK did not change when the issuer name was updated.
require.Equal(t, jwks4.Keys[0], jwks5.Keys[0])
// When they are deleted they stop serving discovery endpoints.
requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config3, client, ns, issuer3)
requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, updatedConfig4, client, ns, issuer5)
requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config3, client, ns, supervisorScheme, supervisorAddress, issuer3)
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.
config5Duplicate1, _ := requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(ctx, t, issuer6, client)
config5Duplicate1, _ := requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(ctx, t, supervisorScheme, supervisorAddress, issuer6, client)
config5Duplicate2 := library.CreateTestOIDCProvider(ctx, t, issuer6)
requireStatus(t, client, ns, config5Duplicate1.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.
requireDelete(t, client, ns, config5Duplicate1.Name)
requireWellKnownEndpointIsWorking(t, issuer6)
requireWellKnownEndpointIsWorking(t, supervisorScheme, supervisorAddress, issuer6)
requireStatus(t, client, ns, config5Duplicate2.Name, v1alpha1.SuccessOIDCProviderStatus)
// 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.
badConfig := library.CreateTestOIDCProvider(ctx, t, badIssuer)
requireStatus(t, client, ns, badConfig.Name, v1alpha1.InvalidOIDCProviderStatus)
requireDiscoveryEndpointsAreNotFound(t, badIssuer)
requireDiscoveryEndpointsAreNotFound(t, supervisorScheme, supervisorAddress, badIssuer)
}
func jwksURLForIssuer(issuerName string) string {
return fmt.Sprintf("%s/jwks.json", issuerName)
func jwksURLForIssuer(scheme, host, path string) string {
if strings.HasPrefix(path, "/") {
path = path[1:]
}
return fmt.Sprintf("%s://%s/%s/jwks.json", scheme, host, path)
}
func wellKnownURLForIssuer(issuerName string) string {
return fmt.Sprintf("%s/.well-known/openid-configuration", issuerName)
func wellKnownURLForIssuer(scheme, host, path string) string {
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()
requireEndpointNotFound(t, wellKnownURLForIssuer(issuerName))
requireEndpointNotFound(t, jwksURLForIssuer(issuerName))
issuerURL, err := url.Parse(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()
httpClient := &http.Client{}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
requestNonExistentPath, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
require.NoError(t, err)
requestNonExistentPath.Header.Add("Host", host)
var response *http.Response
assert.Eventually(t, func() bool {
@ -148,6 +168,7 @@ func requireEndpointNotFound(t *testing.T, url string) {
func requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(
ctx context.Context,
t *testing.T,
supervisorScheme, supervisorAddress string,
issuerName string,
client pinnipedclientset.Interface,
) (*v1alpha1.OIDCProviderConfig, *ExpectedJWKSResponseFormat) {
@ -155,16 +176,16 @@ func requireCreatingOIDCProviderConfigCausesDiscoveryEndpointsToAppear(
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)
return newOIDCProviderConfig, jwksResult
}
func requireDiscoveryEndpointsAreWorking(t *testing.T, issuerName string) *ExpectedJWKSResponseFormat {
requireWellKnownEndpointIsWorking(t, issuerName)
jwksResult := requireJWKSEndpointIsWorking(t, issuerName)
func requireDiscoveryEndpointsAreWorking(t *testing.T, supervisorScheme, supervisorAddress, issuerName string) *ExpectedJWKSResponseFormat {
requireWellKnownEndpointIsWorking(t, supervisorScheme, supervisorAddress, issuerName)
jwksResult := requireJWKSEndpointIsWorking(t, supervisorScheme, supervisorAddress, issuerName)
return jwksResult
}
@ -173,6 +194,7 @@ func requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(
existingOIDCProviderConfig *v1alpha1.OIDCProviderConfig,
client pinnipedclientset.Interface,
ns string,
supervisorScheme, supervisorAddress string,
issuerName string,
) {
t.Helper()
@ -184,13 +206,15 @@ func requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(
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.
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()
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.
expectedResultTemplate := here.Doc(`{
@ -216,13 +240,15 @@ type ExpectedJWKSResponseFormat struct {
Keys []map[string]string
}
func requireJWKSEndpointIsWorking(t *testing.T, issuerName string) *ExpectedJWKSResponseFormat {
func requireJWKSEndpointIsWorking(t *testing.T, supervisorScheme, supervisorAddress, issuerName string) *ExpectedJWKSResponseFormat {
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
err := json.Unmarshal([]byte(responseBody), &result)
err = json.Unmarshal([]byte(responseBody), &result)
require.NoError(t, err)
require.Len(t, result.Keys, 1)
@ -241,20 +267,25 @@ func requireJWKSEndpointIsWorking(t *testing.T, issuerName string) *ExpectedJWKS
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{}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
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.
requestDiscoveryEndpoint, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
wellKnownURL,
endpointURL,
nil,
)
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.
var response *http.Response
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.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"`
Capabilities map[Capability]bool `json:"capabilities"`
TestWebhook idpv1alpha1.WebhookIdentityProviderSpec `json:"testWebhook"`
SupervisorAddress string `json:"supervisorAddress"`
SupervisorHTTPAddress string `json:"supervisorAddress"`
TestUser struct {
Token string `json:"token"`
@ -88,7 +88,7 @@ func IntegrationEnv(t *testing.T) *TestEnv {
result.TestWebhook.Endpoint = needEnv("PINNIPED_TEST_WEBHOOK_ENDPOINT")
result.SupervisorNamespace = needEnv("PINNIPED_TEST_SUPERVISOR_NAMESPACE")
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")}
conciergeCustomLabelsYAML := needEnv("PINNIPED_TEST_CONCIERGE_CUSTOM_LABELS")