Passing integration test for LDAP login! 🚀

This commit is contained in:
Ryan Richard 2021-04-13 18:11:16 -07:00
parent 6bba529b10
commit 47b66ceaa7
2 changed files with 41 additions and 20 deletions

View File

@ -2,7 +2,6 @@
#! SPDX-License-Identifier: Apache-2.0 #! SPDX-License-Identifier: Apache-2.0
#@ load("@ytt:data", "data") #@ load("@ytt:data", "data")
#@ load("@ytt:base64", "base64")
--- ---
apiVersion: v1 apiVersion: v1
kind: Secret kind: Secret
@ -48,7 +47,7 @@ stringData:
sn: Seal sn: Seal
givenName: Pinny givenName: Pinny
mail: pinny.ldap@example.com mail: pinny.ldap@example.com
userPassword:: (@= base64.encode(data.values.pinny_ldap_password) @) userPassword: (@= data.values.pinny_ldap_password @)
uid: pinny uid: pinny
uidNumber: 1000 uidNumber: 1000
gidNumber: 1000 gidNumber: 1000

View File

@ -80,7 +80,7 @@ func TestSupervisorLogin(t *testing.T) {
library.CreateTestLDAPIdentityProvider(t, idpv1alpha1.LDAPIdentityProviderSpec{ library.CreateTestLDAPIdentityProvider(t, idpv1alpha1.LDAPIdentityProviderSpec{
Host: env.SupervisorUpstreamLDAP.Host, Host: env.SupervisorUpstreamLDAP.Host,
TLS: &idpv1alpha1.LDAPIdentityProviderTLSSpec{ TLS: &idpv1alpha1.LDAPIdentityProviderTLSSpec{
CertificateAuthorityData: env.SupervisorUpstreamLDAP.CABundle, CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.CABundle)),
}, },
Bind: idpv1alpha1.LDAPIdentityProviderBindSpec{ Bind: idpv1alpha1.LDAPIdentityProviderBindSpec{
SecretName: secret.Name, SecretName: secret.Name,
@ -93,7 +93,7 @@ func TestSupervisorLogin(t *testing.T) {
UniqueID: env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeName, UniqueID: env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeName,
}, },
}, },
}, "") // TODO: this should be idpv1alpha1.LDAPPhaseReady once we have a controller }, idpv1alpha1.LDAPPhaseReady)
}, },
requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) { requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) {
requestAuthorizationUsingLDAPIdentityProvider(t, requestAuthorizationUsingLDAPIdentityProvider(t,
@ -152,6 +152,10 @@ func testSupervisorLogin(
Transport: &http.Transport{ Transport: &http.Transport{
TLSClientConfig: &tls.Config{RootCAs: ca.Pool()}, TLSClientConfig: &tls.Config{RootCAs: ca.Pool()},
Proxy: func(req *http.Request) (*url.URL, error) { Proxy: func(req *http.Request) (*url.URL, error) {
if strings.HasPrefix(req.URL.Host, "127.0.0.1") {
// don't proxy requests to localhost to avoid proxying calls to our local callback listener
return nil, nil
}
if env.Proxy == "" { if env.Proxy == "" {
t.Logf("passing request for %s with no proxy", req.URL) t.Logf("passing request for %s with no proxy", req.URL)
return nil, nil return nil, nil
@ -249,14 +253,6 @@ func testSupervisorLogin(
pkceParam.Method(), pkceParam.Method(),
) )
// Make the authorize request once "manually" so we can check its response security headers.
authorizeRequest, err := http.NewRequestWithContext(ctx, http.MethodGet, downstreamAuthorizeURL, nil)
require.NoError(t, err)
authorizeResp, err := httpClient.Do(authorizeRequest)
require.NoError(t, err)
require.NoError(t, authorizeResp.Body.Close())
expectSecurityHeaders(t, authorizeResp)
// Perform parameterized auth code acquisition. // Perform parameterized auth code acquisition.
requestAuthorization(t, downstreamAuthorizeURL, localCallbackServer.URL, httpClient) requestAuthorization(t, downstreamAuthorizeURL, localCallbackServer.URL, httpClient)
@ -350,10 +346,21 @@ func verifyTokenResponse(
require.NotEmpty(t, tokenResponse.RefreshToken) require.NotEmpty(t, tokenResponse.RefreshToken)
} }
func requestAuthorizationUsingOIDCIdentityProvider(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL string, _ *http.Client) { func requestAuthorizationUsingOIDCIdentityProvider(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL string, httpClient *http.Client) {
t.Helper() t.Helper()
env := library.IntegrationEnv(t) env := library.IntegrationEnv(t)
ctx, cancelFunc := context.WithTimeout(context.Background(), time.Minute)
defer cancelFunc()
// Make the authorize request once "manually" so we can check its response security headers.
authorizeRequest, err := http.NewRequestWithContext(ctx, http.MethodGet, downstreamAuthorizeURL, nil)
require.NoError(t, err)
authorizeResp, err := httpClient.Do(authorizeRequest)
require.NoError(t, err)
require.NoError(t, authorizeResp.Body.Close())
expectSecurityHeaders(t, authorizeResp, false)
// Open the web browser and navigate to the downstream authorize URL. // Open the web browser and navigate to the downstream authorize URL.
page := browsertest.Open(t) page := browsertest.Open(t)
t.Logf("opening browser to downstream authorize URL %s", library.MaskTokens(downstreamAuthorizeURL)) t.Logf("opening browser to downstream authorize URL %s", library.MaskTokens(downstreamAuthorizeURL))
@ -381,18 +388,29 @@ func requestAuthorizationUsingLDAPIdentityProvider(t *testing.T, downstreamAutho
authRequest.Header.Set("X-Pinniped-Upstream-Username", upstreamUsername) authRequest.Header.Set("X-Pinniped-Upstream-Username", upstreamUsername)
authRequest.Header.Set("X-Pinniped-Upstream-Password", upstreamPassword) authRequest.Header.Set("X-Pinniped-Upstream-Password", upstreamPassword)
// The authorize request is supposed to redirect to this test's callback handler, which in turn is supposed to return 200 OK.
authResponse, err := httpClient.Do(authRequest) authResponse, err := httpClient.Do(authRequest)
require.NoError(t, err) require.NoError(t, err)
responseBody, err := ioutil.ReadAll(authResponse.Body) responseBody, err := ioutil.ReadAll(authResponse.Body)
defer authResponse.Body.Close() defer authResponse.Body.Close()
require.NoError(t, err) require.NoError(t, err)
expectSecurityHeaders(t, authResponse, true)
// TODO remove this skip // A successful authorize request results in a redirect to our localhost callback listener with an authcode param.
_ = responseBody // suppress linter until we remove the below skip require.Equalf(t, http.StatusFound, authResponse.StatusCode, "response body was: %s", string(responseBody))
t.Skip("The rest of this test will not work until we implement the corresponding production code.") redirectLocation := authResponse.Header.Get("Location")
require.Contains(t, redirectLocation, "127.0.0.1")
require.Contains(t, redirectLocation, "/callback")
require.Contains(t, redirectLocation, "code=")
require.Equalf(t, http.StatusOK, authResponse.StatusCode, "response body was: %s", string(responseBody)) // Follow the redirect.
callbackRequest, err := http.NewRequestWithContext(ctx, http.MethodGet, redirectLocation, nil)
require.NoError(t, err)
// Our localhost callback listener should have returned 200 OK.
callbackResponse, err := httpClient.Do(callbackRequest)
require.NoError(t, err)
defer callbackResponse.Body.Close()
require.Equal(t, http.StatusOK, callbackResponse.StatusCode)
} }
func startLocalCallbackServer(t *testing.T) *localCallbackServer { func startLocalCallbackServer(t *testing.T) *localCallbackServer {
@ -462,7 +480,7 @@ func doTokenExchange(t *testing.T, config *oauth2.Config, tokenResponse *oauth2.
t.Logf("exchanged token claims:\n%s", string(indentedClaims)) t.Logf("exchanged token claims:\n%s", string(indentedClaims))
} }
func expectSecurityHeaders(t *testing.T, response *http.Response) { func expectSecurityHeaders(t *testing.T, response *http.Response, expectFositeToOverrideSome bool) {
h := response.Header h := response.Header
assert.Equal(t, "default-src 'none'; frame-ancestors 'none'", h.Get("Content-Security-Policy")) assert.Equal(t, "default-src 'none'; frame-ancestors 'none'", h.Get("Content-Security-Policy"))
assert.Equal(t, "DENY", h.Get("X-Frame-Options")) assert.Equal(t, "DENY", h.Get("X-Frame-Options"))
@ -470,7 +488,11 @@ func expectSecurityHeaders(t *testing.T, response *http.Response) {
assert.Equal(t, "nosniff", h.Get("X-Content-Type-Options")) assert.Equal(t, "nosniff", h.Get("X-Content-Type-Options"))
assert.Equal(t, "no-referrer", h.Get("Referrer-Policy")) assert.Equal(t, "no-referrer", h.Get("Referrer-Policy"))
assert.Equal(t, "off", h.Get("X-DNS-Prefetch-Control")) assert.Equal(t, "off", h.Get("X-DNS-Prefetch-Control"))
assert.Equal(t, "no-cache,no-store,max-age=0,must-revalidate", h.Get("Cache-Control")) if expectFositeToOverrideSome {
assert.Equal(t, "no-store", h.Get("Cache-Control"))
} else {
assert.Equal(t, "no-cache,no-store,max-age=0,must-revalidate", h.Get("Cache-Control"))
}
assert.Equal(t, "no-cache", h.Get("Pragma")) assert.Equal(t, "no-cache", h.Get("Pragma"))
assert.Equal(t, "0", h.Get("Expires")) assert.Equal(t, "0", h.Get("Expires"))
} }