Merge remote-tracking branch 'upstream/main' into callback-endpoint

This commit is contained in:
Andrew Keesler 2020-11-30 11:06:20 -05:00
commit 25bbd28527
No known key found for this signature in database
GPG Key ID: 27CE0444346F9413
1 changed files with 109 additions and 59 deletions

View File

@ -11,6 +11,8 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"net/url" "net/url"
"path"
"strings"
"testing" "testing"
"time" "time"
@ -26,78 +28,124 @@ import (
) )
func TestSupervisorLogin(t *testing.T) { func TestSupervisorLogin(t *testing.T) {
t.Skip("waiting on new callback path logic to get merged in from the callback endpoint work")
env := library.IntegrationEnv(t) env := library.IntegrationEnv(t)
client := library.NewSupervisorClientset(t) client := library.NewSupervisorClientset(t)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel() defer cancel()
// Create downstream OIDC provider (i.e., update supervisor with OIDC provider). tests := []struct {
scheme := "http" Scheme string
addr := env.SupervisorHTTPAddress Address string
caBundle := "" CABundle string
path := "/some/path" }{
issuer := fmt.Sprintf("https://%s%s", addr, path) {Scheme: "http", Address: env.SupervisorHTTPAddress},
_, _ = requireCreatingOIDCProviderCausesDiscoveryEndpointsToAppear( {Scheme: "https", Address: env.SupervisorHTTPSIngressAddress, CABundle: env.SupervisorHTTPSIngressCABundle},
ctx,
t,
scheme,
addr,
caBundle,
issuer,
client,
)
// Create HTTP client.
httpClient := newHTTPClient(t, caBundle, nil)
httpClient.CheckRedirect = func(_ *http.Request, _ []*http.Request) error {
// Don't follow any redirects right now, since we simply want to validate that our auth endpoint
// redirects us.
return http.ErrUseLastResponse
} }
// Declare the downstream auth endpoint url we will use. for _, test := range tests {
downstreamAuthURL := makeDownstreamAuthURL(t, scheme, addr, path) scheme := test.Scheme
addr := test.Address
caBundle := test.CABundle
// Make request to auth endpoint - should fail, since we have no upstreams. if addr == "" {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, downstreamAuthURL, nil) // Both cases are not required, so when one is empty skip it.
require.NoError(t, err) continue
rsp, err := httpClient.Do(req) }
require.NoError(t, err)
defer rsp.Body.Close()
require.Equal(t, http.StatusUnprocessableEntity, rsp.StatusCode)
// Create upstream OIDC provider. // Create downstream OIDC provider (i.e., update supervisor with OIDC provider).
spec := idpv1alpha1.UpstreamOIDCProviderSpec{ path := getDownstreamIssuerPathFromUpstreamRedirectURI(t, env.SupervisorTestUpstream.CallbackURL)
Issuer: env.SupervisorTestUpstream.Issuer, issuer := fmt.Sprintf("https://%s%s", addr, path)
TLS: &idpv1alpha1.TLSSpec{ _, _ = requireCreatingOIDCProviderCausesDiscoveryEndpointsToAppear(
CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorTestUpstream.CABundle)), ctx,
}, t,
Client: idpv1alpha1.OIDCClient{ scheme,
SecretName: makeTestClientCredsSecret(t, env.SupervisorTestUpstream.ClientID, env.SupervisorTestUpstream.ClientSecret).Name, addr,
}, caBundle,
issuer,
client,
)
// Create HTTP client.
httpClient := newHTTPClient(t, caBundle, nil)
httpClient.CheckRedirect = func(_ *http.Request, _ []*http.Request) error {
// Don't follow any redirects right now, since we simply want to validate that our auth endpoint
// redirects us.
return http.ErrUseLastResponse
}
// Declare the downstream auth endpoint url we will use.
downstreamAuthURL := makeDownstreamAuthURL(t, scheme, addr, path)
// Make request to auth endpoint - should fail, since we have no upstreams.
req, err := http.NewRequestWithContext(ctx, http.MethodGet, downstreamAuthURL, nil)
require.NoError(t, err)
rsp, err := httpClient.Do(req)
require.NoError(t, err)
defer rsp.Body.Close()
require.Equal(t, http.StatusUnprocessableEntity, rsp.StatusCode)
// Create upstream OIDC provider.
spec := idpv1alpha1.UpstreamOIDCProviderSpec{
Issuer: env.SupervisorTestUpstream.Issuer,
TLS: &idpv1alpha1.TLSSpec{
CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorTestUpstream.CABundle)),
},
Client: idpv1alpha1.OIDCClient{
SecretName: makeTestClientCredsSecret(t, env.SupervisorTestUpstream.ClientID, env.SupervisorTestUpstream.ClientSecret).Name,
},
}
upstream := makeTestUpstream(t, spec, idpv1alpha1.PhaseReady)
// Make request to authorize endpoint - should pass, since we now have an upstream.
req, err = http.NewRequestWithContext(ctx, http.MethodGet, downstreamAuthURL, nil)
require.NoError(t, err)
rsp, err = httpClient.Do(req)
require.NoError(t, err)
defer rsp.Body.Close()
require.Equal(t, http.StatusFound, rsp.StatusCode)
requireValidRedirectLocation(
ctx,
t,
upstream.Spec.Issuer,
env.SupervisorTestUpstream.ClientID,
env.SupervisorTestUpstream.CallbackURL,
rsp.Header.Get("Location"),
)
} }
upstream := makeTestUpstream(t, spec, idpv1alpha1.PhaseReady)
upstreamRedirectURI := fmt.Sprintf("https://%s/some/path/callback/%s", env.SupervisorHTTPAddress, upstream.Name)
// Make request to authorize endpoint - should pass, since we now have an upstream.
req, err = http.NewRequestWithContext(ctx, http.MethodGet, downstreamAuthURL, nil)
require.NoError(t, err)
rsp, err = httpClient.Do(req)
require.NoError(t, err)
defer rsp.Body.Close()
require.Equal(t, http.StatusFound, rsp.StatusCode)
requireValidRedirectLocation(
ctx,
t,
upstream.Spec.Issuer,
env.SupervisorTestUpstream.ClientID,
upstreamRedirectURI,
rsp.Header.Get("Location"),
)
} }
//nolint:unused
func getDownstreamIssuerPathFromUpstreamRedirectURI(t *testing.T, upstreamRedirectURI string) string {
// We need to construct the downstream issuer path from the upstream redirect URI since the two
// are related, and the upstream redirect URI is supplied via a static test environment
// variable. The upstream redirect URI should be something like
// https://supervisor.com/some/supervisor/path/callback
// and therefore the downstream issuer should be something like
// https://supervisor.com/some/supervisor/path
// since the /callback endpoint is placed at the root of the downstream issuer path.
upstreamRedirectURL, err := url.Parse(upstreamRedirectURI)
require.NoError(t, err)
redirectURIPathWithoutLastSegment, lastUpstreamRedirectURIPathSegment := path.Split(upstreamRedirectURL.Path)
require.Equalf(
t,
"callback",
lastUpstreamRedirectURIPathSegment,
"expected upstream redirect URI (%q) to follow supervisor callback path conventions (i.e., end in /callback)",
upstreamRedirectURI,
)
if strings.HasSuffix(redirectURIPathWithoutLastSegment, "/") {
redirectURIPathWithoutLastSegment = redirectURIPathWithoutLastSegment[:len(redirectURIPathWithoutLastSegment)-1]
}
return redirectURIPathWithoutLastSegment
}
//nolint:unused
func makeDownstreamAuthURL(t *testing.T, scheme, addr, path string) string { func makeDownstreamAuthURL(t *testing.T, scheme, addr, path string) string {
t.Helper() t.Helper()
downstreamOAuth2Config := oauth2.Config{ downstreamOAuth2Config := oauth2.Config{
@ -119,6 +167,7 @@ func makeDownstreamAuthURL(t *testing.T, scheme, addr, path string) string {
) )
} }
//nolint:unused
func generateAuthRequestParams(t *testing.T) (state.State, nonce.Nonce, pkce.Code) { func generateAuthRequestParams(t *testing.T) (state.State, nonce.Nonce, pkce.Code) {
t.Helper() t.Helper()
state, err := state.Generate() state, err := state.Generate()
@ -130,6 +179,7 @@ func generateAuthRequestParams(t *testing.T) (state.State, nonce.Nonce, pkce.Cod
return state, nonce, pkce return state, nonce, pkce
} }
//nolint:unused
func requireValidRedirectLocation( func requireValidRedirectLocation(
ctx context.Context, ctx context.Context,
t *testing.T, t *testing.T,