mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-09-21 07:24:21 +00:00
Create an interface to abstract the upstream IDP from the authorize, IDP discovery, callback, choose IDP, and login endpoints. This commit does not refactor the token endpoint, which will be refactored in a similar way in the next commit.
152 lines
6.7 KiB
Go
152 lines
6.7 KiB
Go
// Copyright 2023-2024 the Pinniped contributors. All Rights Reserved.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package chooseidp
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"go.pinniped.dev/internal/federationdomain/endpoints/chooseidp/chooseidphtml"
|
|
"go.pinniped.dev/internal/federationdomain/federationdomainproviders"
|
|
"go.pinniped.dev/internal/federationdomain/oidc"
|
|
"go.pinniped.dev/internal/testutil"
|
|
"go.pinniped.dev/internal/testutil/oidctestutil"
|
|
"go.pinniped.dev/internal/testutil/testidplister"
|
|
)
|
|
|
|
func TestChooseIDPHandler(t *testing.T) {
|
|
const testIssuer = "https://pinniped.dev/issuer"
|
|
|
|
testReqQuery := url.Values{
|
|
"client_id": []string{"foo"},
|
|
"redirect_uri": []string{"bar"},
|
|
"scope": []string{"baz"},
|
|
"response_type": []string{"bat"},
|
|
}
|
|
testIssuerWithTestReqQuery := testIssuer + "?" + testReqQuery.Encode()
|
|
|
|
tests := []struct {
|
|
name string
|
|
|
|
method string
|
|
reqTarget string
|
|
idps federationdomainproviders.FederationDomainIdentityProvidersListerI
|
|
|
|
wantStatus int
|
|
wantContentType string
|
|
wantBodyString string
|
|
}{
|
|
{
|
|
name: "happy path",
|
|
method: http.MethodGet,
|
|
reqTarget: "/some/path" + oidc.ChooseIDPEndpointPath + "?" + testReqQuery.Encode(),
|
|
idps: testidplister.NewUpstreamIDPListerBuilder().
|
|
WithOIDC(oidctestutil.NewTestUpstreamOIDCIdentityProviderBuilder().WithName("oidc2").Build()).
|
|
WithLDAP(oidctestutil.NewTestUpstreamLDAPIdentityProviderBuilder().WithName("ldap1").Build()).
|
|
WithActiveDirectory(oidctestutil.NewTestUpstreamLDAPIdentityProviderBuilder().WithName("z-ad1").Build()).
|
|
WithLDAP(oidctestutil.NewTestUpstreamLDAPIdentityProviderBuilder().WithName("ldap2").Build()).
|
|
WithOIDC(oidctestutil.NewTestUpstreamOIDCIdentityProviderBuilder().WithName("oidc1").Build()).
|
|
WithActiveDirectory(oidctestutil.NewTestUpstreamLDAPIdentityProviderBuilder().WithName("ad2").Build()).
|
|
BuildFederationDomainIdentityProvidersListerFinder(),
|
|
wantStatus: http.StatusOK,
|
|
wantContentType: "text/html; charset=utf-8",
|
|
wantBodyString: testutil.ExpectedChooseIDPPageHTML(chooseidphtml.CSS(), chooseidphtml.JS(), []testutil.ChooseIDPPageExpectedValue{
|
|
// Should be sorted alphabetically by displayName.
|
|
{DisplayName: "ad2", URL: testIssuerWithTestReqQuery + "&pinniped_idp_name=ad2"},
|
|
{DisplayName: "ldap1", URL: testIssuerWithTestReqQuery + "&pinniped_idp_name=ldap1"},
|
|
{DisplayName: "ldap2", URL: testIssuerWithTestReqQuery + "&pinniped_idp_name=ldap2"},
|
|
{DisplayName: "oidc1", URL: testIssuerWithTestReqQuery + "&pinniped_idp_name=oidc1"},
|
|
{DisplayName: "oidc2", URL: testIssuerWithTestReqQuery + "&pinniped_idp_name=oidc2"},
|
|
{DisplayName: "z-ad1", URL: testIssuerWithTestReqQuery + "&pinniped_idp_name=z-ad1"},
|
|
}),
|
|
},
|
|
{
|
|
name: "happy path when there are special characters in the IDP name",
|
|
method: http.MethodGet,
|
|
reqTarget: "/some/path" + oidc.ChooseIDPEndpointPath + "?" + testReqQuery.Encode(),
|
|
idps: testidplister.NewUpstreamIDPListerBuilder().
|
|
WithOIDC(oidctestutil.NewTestUpstreamOIDCIdentityProviderBuilder().WithName(`This is Ryan's IDP 👍\~!@#$%^&*()-+[]{}\|;'"<>,.?`).Build()).
|
|
WithLDAP(oidctestutil.NewTestUpstreamLDAPIdentityProviderBuilder().WithName(`This is Josh's IDP 🦭`).Build()).
|
|
BuildFederationDomainIdentityProvidersListerFinder(),
|
|
wantStatus: http.StatusOK,
|
|
wantContentType: "text/html; charset=utf-8",
|
|
wantBodyString: testutil.ExpectedChooseIDPPageHTML(chooseidphtml.CSS(), chooseidphtml.JS(), []testutil.ChooseIDPPageExpectedValue{
|
|
// Should be sorted alphabetically by displayName.
|
|
{
|
|
DisplayName: `This is Josh's IDP 🦭`,
|
|
URL: testIssuerWithTestReqQuery + `&pinniped_idp_name=` + url.QueryEscape(`This is Josh's IDP 🦭`),
|
|
},
|
|
{
|
|
DisplayName: `This is Ryan's IDP 👍\~!@#$%^&*()-+[]{}\|;'"<>,.?`,
|
|
URL: testIssuerWithTestReqQuery + `&pinniped_idp_name=` + url.QueryEscape(`This is Ryan's IDP 👍\~!@#$%^&*()-+[]{}\|;'"<>,.?`),
|
|
},
|
|
}),
|
|
},
|
|
{
|
|
name: "no valid IDPs are configured on the FederationDomain",
|
|
method: http.MethodGet,
|
|
reqTarget: "/some/path" + oidc.ChooseIDPEndpointPath + "?" + testReqQuery.Encode(),
|
|
idps: testidplister.NewUpstreamIDPListerBuilder().
|
|
BuildFederationDomainIdentityProvidersListerFinder(),
|
|
wantStatus: http.StatusInternalServerError,
|
|
wantContentType: "text/plain; charset=utf-8",
|
|
wantBodyString: "Internal Server Error: please check the server's configuration: no valid identity providers found for this FederationDomain\n",
|
|
},
|
|
{
|
|
name: "no query params on the request",
|
|
method: http.MethodGet,
|
|
reqTarget: "/some/path" + oidc.ChooseIDPEndpointPath,
|
|
idps: testidplister.NewUpstreamIDPListerBuilder().
|
|
WithOIDC(oidctestutil.NewTestUpstreamOIDCIdentityProviderBuilder().WithName("x-some-idp").Build()).
|
|
BuildFederationDomainIdentityProvidersListerFinder(),
|
|
wantStatus: http.StatusBadRequest,
|
|
wantContentType: "text/plain; charset=utf-8",
|
|
wantBodyString: "Bad Request: missing required query params (must include client_id, redirect_uri, scope, and response_type)\n",
|
|
},
|
|
{
|
|
name: "missing required query param(s) on the request",
|
|
method: http.MethodGet,
|
|
reqTarget: "/some/path" + oidc.ChooseIDPEndpointPath + "?client_id=foo",
|
|
idps: testidplister.NewUpstreamIDPListerBuilder().
|
|
WithOIDC(oidctestutil.NewTestUpstreamOIDCIdentityProviderBuilder().WithName("x-some-idp").Build()).
|
|
BuildFederationDomainIdentityProvidersListerFinder(),
|
|
wantStatus: http.StatusBadRequest,
|
|
wantContentType: "text/plain; charset=utf-8",
|
|
wantBodyString: "Bad Request: missing required query params (must include client_id, redirect_uri, scope, and response_type)\n",
|
|
},
|
|
{
|
|
name: "bad request method",
|
|
method: http.MethodPost,
|
|
reqTarget: oidc.ChooseIDPEndpointPath,
|
|
idps: testidplister.NewUpstreamIDPListerBuilder().
|
|
WithOIDC(oidctestutil.NewTestUpstreamOIDCIdentityProviderBuilder().WithName("x-some-idp").Build()).
|
|
BuildFederationDomainIdentityProvidersListerFinder(),
|
|
wantStatus: http.StatusMethodNotAllowed,
|
|
wantContentType: "text/plain; charset=utf-8",
|
|
wantBodyString: "Method Not Allowed: POST (try GET)\n",
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
test := test
|
|
t.Run(test.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
handler := NewHandler(testIssuer, test.idps)
|
|
|
|
req := httptest.NewRequest(test.method, test.reqTarget, nil)
|
|
rsp := httptest.NewRecorder()
|
|
handler.ServeHTTP(rsp, req)
|
|
|
|
require.Equal(t, test.wantStatus, rsp.Code)
|
|
require.Equal(t, test.wantContentType, rsp.Header().Get("Content-Type"))
|
|
require.Equal(t, test.wantBodyString, rsp.Body.String())
|
|
testutil.RequireSecurityHeadersWithIDPChooserPageCSPs(t, rsp)
|
|
})
|
|
}
|
|
}
|