Supervisor pre-factor to make room for upstream LDAP identity providers
This commit is contained in:
parent
1c55c857f4
commit
1f5978aa1a
@ -66,7 +66,7 @@ const (
|
|||||||
|
|
||||||
// IDPCache is a thread safe cache that holds a list of validated upstream OIDC IDP configurations.
|
// IDPCache is a thread safe cache that holds a list of validated upstream OIDC IDP configurations.
|
||||||
type IDPCache interface {
|
type IDPCache interface {
|
||||||
SetIDPList([]provider.UpstreamOIDCIdentityProviderI)
|
SetOIDCIdentityProviders([]provider.UpstreamOIDCIdentityProviderI)
|
||||||
}
|
}
|
||||||
|
|
||||||
// lruValidatorCache caches the *oidc.Provider associated with a particular issuer/TLS configuration.
|
// lruValidatorCache caches the *oidc.Provider associated with a particular issuer/TLS configuration.
|
||||||
@ -159,7 +159,7 @@ func (c *controller) Sync(ctx controllerlib.Context) error {
|
|||||||
validatedUpstreams = append(validatedUpstreams, provider.UpstreamOIDCIdentityProviderI(valid))
|
validatedUpstreams = append(validatedUpstreams, provider.UpstreamOIDCIdentityProviderI(valid))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.cache.SetIDPList(validatedUpstreams)
|
c.cache.SetOIDCIdentityProviders(validatedUpstreams)
|
||||||
if requeue {
|
if requeue {
|
||||||
return controllerlib.ErrSyntheticRequeue
|
return controllerlib.ErrSyntheticRequeue
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ func TestControllerFilterSecret(t *testing.T) {
|
|||||||
kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0)
|
kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0)
|
||||||
testLog := testlogger.New(t)
|
testLog := testlogger.New(t)
|
||||||
cache := provider.NewDynamicUpstreamIDPProvider()
|
cache := provider.NewDynamicUpstreamIDPProvider()
|
||||||
cache.SetIDPList([]provider.UpstreamOIDCIdentityProviderI{
|
cache.SetOIDCIdentityProviders([]provider.UpstreamOIDCIdentityProviderI{
|
||||||
&upstreamoidc.ProviderConfig{Name: "initial-entry"},
|
&upstreamoidc.ProviderConfig{Name: "initial-entry"},
|
||||||
})
|
})
|
||||||
secretInformer := kubeInformers.Core().V1().Secrets()
|
secretInformer := kubeInformers.Core().V1().Secrets()
|
||||||
@ -611,7 +611,7 @@ func TestController(t *testing.T) {
|
|||||||
kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0)
|
kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0)
|
||||||
testLog := testlogger.New(t)
|
testLog := testlogger.New(t)
|
||||||
cache := provider.NewDynamicUpstreamIDPProvider()
|
cache := provider.NewDynamicUpstreamIDPProvider()
|
||||||
cache.SetIDPList([]provider.UpstreamOIDCIdentityProviderI{
|
cache.SetOIDCIdentityProviders([]provider.UpstreamOIDCIdentityProviderI{
|
||||||
&upstreamoidc.ProviderConfig{Name: "initial-entry"},
|
&upstreamoidc.ProviderConfig{Name: "initial-entry"},
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -640,7 +640,7 @@ func TestController(t *testing.T) {
|
|||||||
}
|
}
|
||||||
require.Equal(t, strings.Join(tt.wantLogs, "\n"), strings.Join(testLog.Lines(), "\n"))
|
require.Equal(t, strings.Join(tt.wantLogs, "\n"), strings.Join(testLog.Lines(), "\n"))
|
||||||
|
|
||||||
actualIDPList := cache.GetIDPList()
|
actualIDPList := cache.GetOIDCIdentityProviders()
|
||||||
require.Equal(t, len(tt.wantResultingCache), len(actualIDPList))
|
require.Equal(t, len(tt.wantResultingCache), len(actualIDPList))
|
||||||
for i := range actualIDPList {
|
for i := range actualIDPList {
|
||||||
actualIDP := actualIDPList[i].(*upstreamoidc.ProviderConfig)
|
actualIDP := actualIDPList[i].(*upstreamoidc.ProviderConfig)
|
||||||
|
20
internal/ldap/ldap.go
Normal file
20
internal/ldap/ldap.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Package ldap contains common LDAP functionality needed by Pinniped.
|
||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This interface is similar to the k8s token authenticator, but works with username/passwords instead
|
||||||
|
// of a single token string.
|
||||||
|
//
|
||||||
|
// See k8s.io/apiserver/pkg/authentication/authenticator/interfaces.go for the token authenticator
|
||||||
|
// interface, as well as the Response type.
|
||||||
|
type UserAuthenticator interface {
|
||||||
|
AuthenticateUser(ctx context.Context, username, password string) (*authenticator.Response, bool, error)
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
// Package auth provides a handler for the OIDC authorization endpoint.
|
// Package auth provides a handler for the OIDC authorization endpoint.
|
||||||
@ -27,7 +27,7 @@ import (
|
|||||||
|
|
||||||
func NewHandler(
|
func NewHandler(
|
||||||
downstreamIssuer string,
|
downstreamIssuer string,
|
||||||
idpListGetter oidc.IDPListGetter,
|
idpLister oidc.UpstreamIdentityProvidersLister,
|
||||||
oauthHelper fosite.OAuth2Provider,
|
oauthHelper fosite.OAuth2Provider,
|
||||||
generateCSRF func() (csrftoken.CSRFToken, error),
|
generateCSRF func() (csrftoken.CSRFToken, error),
|
||||||
generatePKCE func() (pkce.Code, error),
|
generatePKCE func() (pkce.Code, error),
|
||||||
@ -52,7 +52,7 @@ func NewHandler(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
upstreamIDP, err := chooseUpstreamIDP(idpListGetter)
|
upstreamIDP, err := chooseUpstreamIDP(idpLister)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
plog.WarningErr("authorize upstream config", err)
|
plog.WarningErr("authorize upstream config", err)
|
||||||
return err
|
return err
|
||||||
@ -165,8 +165,8 @@ func readCSRFCookie(r *http.Request, codec oidc.Decoder) csrftoken.CSRFToken {
|
|||||||
return csrfFromCookie
|
return csrfFromCookie
|
||||||
}
|
}
|
||||||
|
|
||||||
func chooseUpstreamIDP(idpListGetter oidc.IDPListGetter) (provider.UpstreamOIDCIdentityProviderI, error) {
|
func chooseUpstreamIDP(idpLister oidc.UpstreamOIDCIdentityProvidersLister) (provider.UpstreamOIDCIdentityProviderI, error) {
|
||||||
allUpstreamIDPs := idpListGetter.GetIDPList()
|
allUpstreamIDPs := idpLister.GetOIDCIdentityProviders()
|
||||||
if len(allUpstreamIDPs) == 0 {
|
if len(allUpstreamIDPs) == 0 {
|
||||||
return nil, httperr.New(
|
return nil, httperr.New(
|
||||||
http.StatusUnprocessableEntity,
|
http.StatusUnprocessableEntity,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package auth
|
package auth
|
||||||
@ -244,7 +244,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
|
|
||||||
issuer string
|
issuer string
|
||||||
idpListGetter provider.DynamicUpstreamIDPProvider
|
idpLister provider.DynamicUpstreamIDPProvider
|
||||||
generateCSRF func() (csrftoken.CSRFToken, error)
|
generateCSRF func() (csrftoken.CSRFToken, error)
|
||||||
generatePKCE func() (pkce.Code, error)
|
generatePKCE func() (pkce.Code, error)
|
||||||
generateNonce func() (nonce.Nonce, error)
|
generateNonce func() (nonce.Nonce, error)
|
||||||
@ -270,7 +270,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "happy path using GET without a CSRF cookie",
|
name: "happy path using GET without a CSRF cookie",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -288,7 +288,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "happy path using GET with a CSRF cookie",
|
name: "happy path using GET with a CSRF cookie",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -306,7 +306,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "happy path using POST",
|
name: "happy path using POST",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -326,7 +326,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "happy path with prompt param login passed through to redirect uri",
|
name: "happy path with prompt param login passed through to redirect uri",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -346,7 +346,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "error while decoding CSRF cookie just generates a new cookie and succeeds as usual",
|
name: "error while decoding CSRF cookie just generates a new cookie and succeeds as usual",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -366,7 +366,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "happy path when downstream redirect uri matches what is configured for client except for the port number",
|
name: "happy path when downstream redirect uri matches what is configured for client except for the port number",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -388,7 +388,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "happy path when downstream requested scopes include offline_access",
|
name: "happy path when downstream requested scopes include offline_access",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -408,7 +408,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "downstream redirect uri does not match what is configured for client",
|
name: "downstream redirect uri does not match what is configured for client",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -425,7 +425,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "downstream client does not exist",
|
name: "downstream client does not exist",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -440,7 +440,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "response type is unsupported",
|
name: "response type is unsupported",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -456,7 +456,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "downstream scopes do not match what is configured for client",
|
name: "downstream scopes do not match what is configured for client",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -472,7 +472,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "missing response type in request",
|
name: "missing response type in request",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -488,7 +488,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "missing client id in request",
|
name: "missing client id in request",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -503,7 +503,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "missing PKCE code_challenge in request", // See https://tools.ietf.org/html/rfc7636#section-4.4.1
|
name: "missing PKCE code_challenge in request", // See https://tools.ietf.org/html/rfc7636#section-4.4.1
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -519,7 +519,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "invalid value for PKCE code_challenge_method in request", // https://tools.ietf.org/html/rfc7636#section-4.3
|
name: "invalid value for PKCE code_challenge_method in request", // https://tools.ietf.org/html/rfc7636#section-4.3
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -535,7 +535,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "when PKCE code_challenge_method in request is `plain`", // https://tools.ietf.org/html/rfc7636#section-4.3
|
name: "when PKCE code_challenge_method in request is `plain`", // https://tools.ietf.org/html/rfc7636#section-4.3
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -551,7 +551,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "missing PKCE code_challenge_method in request", // See https://tools.ietf.org/html/rfc7636#section-4.4.1
|
name: "missing PKCE code_challenge_method in request", // See https://tools.ietf.org/html/rfc7636#section-4.4.1
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -569,7 +569,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
// through that part of the fosite library.
|
// through that part of the fosite library.
|
||||||
name: "prompt param is not allowed to have none and another legal value at the same time",
|
name: "prompt param is not allowed to have none and another legal value at the same time",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -585,7 +585,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "OIDC validations are skipped when the openid scope was not requested",
|
name: "OIDC validations are skipped when the openid scope was not requested",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -606,7 +606,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "state does not have enough entropy",
|
name: "state does not have enough entropy",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -622,7 +622,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "error while encoding upstream state param",
|
name: "error while encoding upstream state param",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -637,7 +637,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "error while encoding CSRF cookie value for new cookie",
|
name: "error while encoding CSRF cookie value for new cookie",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -652,7 +652,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "error while generating CSRF token",
|
name: "error while generating CSRF token",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: func() (csrftoken.CSRFToken, error) { return "", fmt.Errorf("some csrf generator error") },
|
generateCSRF: func() (csrftoken.CSRFToken, error) { return "", fmt.Errorf("some csrf generator error") },
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -667,7 +667,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "error while generating nonce",
|
name: "error while generating nonce",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: happyPKCEGenerator,
|
generatePKCE: happyPKCEGenerator,
|
||||||
generateNonce: func() (nonce.Nonce, error) { return "", fmt.Errorf("some nonce generator error") },
|
generateNonce: func() (nonce.Nonce, error) { return "", fmt.Errorf("some nonce generator error") },
|
||||||
@ -682,7 +682,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "error while generating PKCE",
|
name: "error while generating PKCE",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
generateCSRF: happyCSRFGenerator,
|
generateCSRF: happyCSRFGenerator,
|
||||||
generatePKCE: func() (pkce.Code, error) { return "", fmt.Errorf("some PKCE generator error") },
|
generatePKCE: func() (pkce.Code, error) { return "", fmt.Errorf("some PKCE generator error") },
|
||||||
generateNonce: happyNonceGenerator,
|
generateNonce: happyNonceGenerator,
|
||||||
@ -697,7 +697,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "no upstream providers are configured",
|
name: "no upstream providers are configured",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(), // empty
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC().Build(), // empty
|
||||||
method: http.MethodGet,
|
method: http.MethodGet,
|
||||||
path: happyGetRequestPath,
|
path: happyGetRequestPath,
|
||||||
wantStatus: http.StatusUnprocessableEntity,
|
wantStatus: http.StatusUnprocessableEntity,
|
||||||
@ -707,7 +707,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "too many upstream providers are configured",
|
name: "too many upstream providers are configured",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider, &upstreamOIDCIdentityProvider), // more than one not allowed
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider, &upstreamOIDCIdentityProvider).Build(), // more than one not allowed
|
||||||
method: http.MethodGet,
|
method: http.MethodGet,
|
||||||
path: happyGetRequestPath,
|
path: happyGetRequestPath,
|
||||||
wantStatus: http.StatusUnprocessableEntity,
|
wantStatus: http.StatusUnprocessableEntity,
|
||||||
@ -717,7 +717,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "PUT is a bad method",
|
name: "PUT is a bad method",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
method: http.MethodPut,
|
method: http.MethodPut,
|
||||||
path: "/some/path",
|
path: "/some/path",
|
||||||
wantStatus: http.StatusMethodNotAllowed,
|
wantStatus: http.StatusMethodNotAllowed,
|
||||||
@ -727,7 +727,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "PATCH is a bad method",
|
name: "PATCH is a bad method",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
method: http.MethodPatch,
|
method: http.MethodPatch,
|
||||||
path: "/some/path",
|
path: "/some/path",
|
||||||
wantStatus: http.StatusMethodNotAllowed,
|
wantStatus: http.StatusMethodNotAllowed,
|
||||||
@ -737,7 +737,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "DELETE is a bad method",
|
name: "DELETE is a bad method",
|
||||||
issuer: downstreamIssuer,
|
issuer: downstreamIssuer,
|
||||||
idpListGetter: oidctestutil.NewIDPListGetter(&upstreamOIDCIdentityProvider),
|
idpLister: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&upstreamOIDCIdentityProvider).Build(),
|
||||||
method: http.MethodDelete,
|
method: http.MethodDelete,
|
||||||
path: "/some/path",
|
path: "/some/path",
|
||||||
wantStatus: http.StatusMethodNotAllowed,
|
wantStatus: http.StatusMethodNotAllowed,
|
||||||
@ -803,7 +803,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
test := test
|
test := test
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
subject := NewHandler(test.issuer, test.idpListGetter, oauthHelper, test.generateCSRF, test.generatePKCE, test.generateNonce, test.stateEncoder, test.cookieEncoder)
|
subject := NewHandler(test.issuer, test.idpLister, oauthHelper, test.generateCSRF, test.generatePKCE, test.generateNonce, test.stateEncoder, test.cookieEncoder)
|
||||||
runOneTestCase(t, test, subject)
|
runOneTestCase(t, test, subject)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -812,7 +812,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
test := tests[0]
|
test := tests[0]
|
||||||
require.Equal(t, "happy path using GET without a CSRF cookie", test.name) // re-use the happy path test case
|
require.Equal(t, "happy path using GET without a CSRF cookie", test.name) // re-use the happy path test case
|
||||||
|
|
||||||
subject := NewHandler(test.issuer, test.idpListGetter, oauthHelper, test.generateCSRF, test.generatePKCE, test.generateNonce, test.stateEncoder, test.cookieEncoder)
|
subject := NewHandler(test.issuer, test.idpLister, oauthHelper, test.generateCSRF, test.generatePKCE, test.generateNonce, test.stateEncoder, test.cookieEncoder)
|
||||||
|
|
||||||
runOneTestCase(t, test, subject)
|
runOneTestCase(t, test, subject)
|
||||||
|
|
||||||
@ -823,7 +823,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
AuthorizationURL: *upstreamAuthURL,
|
AuthorizationURL: *upstreamAuthURL,
|
||||||
Scopes: []string{"other-scope1", "other-scope2"},
|
Scopes: []string{"other-scope1", "other-scope2"},
|
||||||
}
|
}
|
||||||
test.idpListGetter.SetIDPList([]provider.UpstreamOIDCIdentityProviderI{provider.UpstreamOIDCIdentityProviderI(&newProviderSettings)})
|
test.idpLister.SetOIDCIdentityProviders([]provider.UpstreamOIDCIdentityProviderI{provider.UpstreamOIDCIdentityProviderI(&newProviderSettings)})
|
||||||
|
|
||||||
// Update the expectations of the test case to match the new upstream IDP settings.
|
// Update the expectations of the test case to match the new upstream IDP settings.
|
||||||
test.wantLocationHeader = urlWithQuery(upstreamAuthURL.String(),
|
test.wantLocationHeader = urlWithQuery(upstreamAuthURL.String(),
|
||||||
|
@ -33,7 +33,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func NewHandler(
|
func NewHandler(
|
||||||
idpListGetter oidc.IDPListGetter,
|
upstreamIDPs oidc.UpstreamOIDCIdentityProvidersLister,
|
||||||
oauthHelper fosite.OAuth2Provider,
|
oauthHelper fosite.OAuth2Provider,
|
||||||
stateDecoder, cookieDecoder oidc.Decoder,
|
stateDecoder, cookieDecoder oidc.Decoder,
|
||||||
redirectURI string,
|
redirectURI string,
|
||||||
@ -44,7 +44,7 @@ func NewHandler(
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
upstreamIDPConfig := findUpstreamIDPConfig(state.UpstreamName, idpListGetter)
|
upstreamIDPConfig := findUpstreamIDPConfig(state.UpstreamName, upstreamIDPs)
|
||||||
if upstreamIDPConfig == nil {
|
if upstreamIDPConfig == nil {
|
||||||
plog.Warning("upstream provider not found")
|
plog.Warning("upstream provider not found")
|
||||||
return httperr.New(http.StatusUnprocessableEntity, "upstream provider not found")
|
return httperr.New(http.StatusUnprocessableEntity, "upstream provider not found")
|
||||||
@ -143,8 +143,8 @@ func validateRequest(r *http.Request, stateDecoder, cookieDecoder oidc.Decoder)
|
|||||||
return state, nil
|
return state, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func findUpstreamIDPConfig(upstreamName string, idpListGetter oidc.IDPListGetter) provider.UpstreamOIDCIdentityProviderI {
|
func findUpstreamIDPConfig(upstreamName string, upstreamIDPs oidc.UpstreamOIDCIdentityProvidersLister) provider.UpstreamOIDCIdentityProviderI {
|
||||||
for _, p := range idpListGetter.GetIDPList() {
|
for _, p := range upstreamIDPs.GetOIDCIdentityProviders() {
|
||||||
if p.GetName() == upstreamName {
|
if p.GetName() == upstreamName {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
@ -626,8 +626,8 @@ func TestCallbackEndpoint(t *testing.T) {
|
|||||||
jwksProviderIsUnused := jwks.NewDynamicJWKSProvider()
|
jwksProviderIsUnused := jwks.NewDynamicJWKSProvider()
|
||||||
oauthHelper := oidc.FositeOauth2Helper(oauthStore, downstreamIssuer, hmacSecretFunc, jwksProviderIsUnused, timeoutsConfiguration)
|
oauthHelper := oidc.FositeOauth2Helper(oauthStore, downstreamIssuer, hmacSecretFunc, jwksProviderIsUnused, timeoutsConfiguration)
|
||||||
|
|
||||||
idpListGetter := oidctestutil.NewIDPListGetter(&test.idp)
|
idpLister := oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&test.idp).Build()
|
||||||
subject := NewHandler(idpListGetter, oauthHelper, happyStateCodec, happyCookieCodec, happyUpstreamRedirectURI)
|
subject := NewHandler(idpLister, oauthHelper, happyStateCodec, happyCookieCodec, happyUpstreamRedirectURI)
|
||||||
req := httptest.NewRequest(test.method, test.path, nil)
|
req := httptest.NewRequest(test.method, test.path, nil)
|
||||||
if test.csrfCookie != "" {
|
if test.csrfCookie != "" {
|
||||||
req.Header.Set("Cookie", test.csrfCookie)
|
req.Header.Set("Cookie", test.csrfCookie)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
// Package oidc contains common OIDC functionality needed by Pinniped.
|
// Package oidc contains common OIDC functionality needed by Pinniped.
|
||||||
@ -274,8 +274,17 @@ func FositeErrorForLog(err error) []interface{} {
|
|||||||
return keysAndValues
|
return keysAndValues
|
||||||
}
|
}
|
||||||
|
|
||||||
type IDPListGetter interface {
|
type UpstreamOIDCIdentityProvidersLister interface {
|
||||||
GetIDPList() []provider.UpstreamOIDCIdentityProviderI
|
GetOIDCIdentityProviders() []provider.UpstreamOIDCIdentityProviderI
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpstreamLDAPIdentityProvidersLister interface {
|
||||||
|
GetLDAPIdentityProviders() []provider.UpstreamLDAPIdentityProviderI
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpstreamIdentityProvidersLister interface {
|
||||||
|
UpstreamOIDCIdentityProvidersLister
|
||||||
|
UpstreamLDAPIdentityProvidersLister
|
||||||
}
|
}
|
||||||
|
|
||||||
func GrantScopeIfRequested(authorizeRequester fosite.AuthorizeRequester, scopeName string) {
|
func GrantScopeIfRequested(authorizeRequester fosite.AuthorizeRequester, scopeName string) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package oidctestutil
|
package oidctestutil
|
||||||
@ -112,16 +112,29 @@ func (u *TestUpstreamOIDCIdentityProvider) ValidateToken(_ context.Context, _ *o
|
|||||||
panic("implement me")
|
panic("implement me")
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIDPListGetter(upstreamOIDCIdentityProviders ...*TestUpstreamOIDCIdentityProvider) provider.DynamicUpstreamIDPProvider {
|
type UpstreamIDPListerBuilder struct {
|
||||||
idpProvider := provider.NewDynamicUpstreamIDPProvider()
|
upstreamOIDCIdentityProviders []*TestUpstreamOIDCIdentityProvider
|
||||||
upstreams := make([]provider.UpstreamOIDCIdentityProviderI, len(upstreamOIDCIdentityProviders))
|
|
||||||
for i := range upstreamOIDCIdentityProviders {
|
|
||||||
upstreams[i] = provider.UpstreamOIDCIdentityProviderI(upstreamOIDCIdentityProviders[i])
|
|
||||||
}
|
}
|
||||||
idpProvider.SetIDPList(upstreams)
|
|
||||||
|
func (b *UpstreamIDPListerBuilder) WithOIDC(upstreamOIDCIdentityProviders ...*TestUpstreamOIDCIdentityProvider) *UpstreamIDPListerBuilder {
|
||||||
|
b.upstreamOIDCIdentityProviders = append(b.upstreamOIDCIdentityProviders, upstreamOIDCIdentityProviders...)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *UpstreamIDPListerBuilder) Build() provider.DynamicUpstreamIDPProvider {
|
||||||
|
idpProvider := provider.NewDynamicUpstreamIDPProvider()
|
||||||
|
upstreams := make([]provider.UpstreamOIDCIdentityProviderI, len(b.upstreamOIDCIdentityProviders))
|
||||||
|
for i := range b.upstreamOIDCIdentityProviders {
|
||||||
|
upstreams[i] = provider.UpstreamOIDCIdentityProviderI(b.upstreamOIDCIdentityProviders[i])
|
||||||
|
}
|
||||||
|
idpProvider.SetOIDCIdentityProviders(upstreams)
|
||||||
return idpProvider
|
return idpProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewUpstreamIDPListerBuilder() *UpstreamIDPListerBuilder {
|
||||||
|
return &UpstreamIDPListerBuilder{}
|
||||||
|
}
|
||||||
|
|
||||||
// Declare a separate type from the production code to ensure that the state param's contents was serialized
|
// Declare a separate type from the production code to ensure that the state param's contents was serialized
|
||||||
// in the format that we expect, with the json keys that we expect, etc. This also ensure that the order of
|
// in the format that we expect, with the json keys that we expect, etc. This also ensure that the order of
|
||||||
// the serialized fields is the same, which doesn't really matter expect that we can make simpler equality
|
// the serialized fields is the same, which doesn't really matter expect that we can make simpler equality
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package provider
|
package provider
|
||||||
@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
|
||||||
|
"go.pinniped.dev/internal/ldap"
|
||||||
"go.pinniped.dev/pkg/oidcclient/nonce"
|
"go.pinniped.dev/pkg/oidcclient/nonce"
|
||||||
"go.pinniped.dev/pkg/oidcclient/oidctypes"
|
"go.pinniped.dev/pkg/oidcclient/oidctypes"
|
||||||
"go.pinniped.dev/pkg/oidcclient/pkce"
|
"go.pinniped.dev/pkg/oidcclient/pkce"
|
||||||
@ -48,30 +49,54 @@ type UpstreamOIDCIdentityProviderI interface {
|
|||||||
ValidateToken(ctx context.Context, tok *oauth2.Token, expectedIDTokenNonce nonce.Nonce) (*oidctypes.Token, error)
|
ValidateToken(ctx context.Context, tok *oauth2.Token, expectedIDTokenNonce nonce.Nonce) (*oidctypes.Token, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpstreamLDAPIdentityProviderI interface {
|
||||||
|
// A name for this upstream provider.
|
||||||
|
GetName() string
|
||||||
|
|
||||||
|
// A method for performing user authentication against the upstream LDAP provider.
|
||||||
|
ldap.UserAuthenticator
|
||||||
|
}
|
||||||
|
|
||||||
type DynamicUpstreamIDPProvider interface {
|
type DynamicUpstreamIDPProvider interface {
|
||||||
SetIDPList(oidcIDPs []UpstreamOIDCIdentityProviderI)
|
SetOIDCIdentityProviders(oidcIDPs []UpstreamOIDCIdentityProviderI)
|
||||||
GetIDPList() []UpstreamOIDCIdentityProviderI
|
GetOIDCIdentityProviders() []UpstreamOIDCIdentityProviderI
|
||||||
|
SetLDAPIdentityProviders(ldapIDPs []UpstreamLDAPIdentityProviderI)
|
||||||
|
GetLDAPIdentityProviders() []UpstreamLDAPIdentityProviderI
|
||||||
}
|
}
|
||||||
|
|
||||||
type dynamicUpstreamIDPProvider struct {
|
type dynamicUpstreamIDPProvider struct {
|
||||||
federationDomains []UpstreamOIDCIdentityProviderI
|
oidcUpstreams []UpstreamOIDCIdentityProviderI
|
||||||
|
ldapUpstreams []UpstreamLDAPIdentityProviderI
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDynamicUpstreamIDPProvider() DynamicUpstreamIDPProvider {
|
func NewDynamicUpstreamIDPProvider() DynamicUpstreamIDPProvider {
|
||||||
return &dynamicUpstreamIDPProvider{
|
return &dynamicUpstreamIDPProvider{
|
||||||
federationDomains: []UpstreamOIDCIdentityProviderI{},
|
oidcUpstreams: []UpstreamOIDCIdentityProviderI{},
|
||||||
|
ldapUpstreams: []UpstreamLDAPIdentityProviderI{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *dynamicUpstreamIDPProvider) SetIDPList(oidcIDPs []UpstreamOIDCIdentityProviderI) {
|
func (p *dynamicUpstreamIDPProvider) SetOIDCIdentityProviders(oidcIDPs []UpstreamOIDCIdentityProviderI) {
|
||||||
p.mutex.Lock() // acquire a write lock
|
p.mutex.Lock() // acquire a write lock
|
||||||
defer p.mutex.Unlock()
|
defer p.mutex.Unlock()
|
||||||
p.federationDomains = oidcIDPs
|
p.oidcUpstreams = oidcIDPs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *dynamicUpstreamIDPProvider) GetIDPList() []UpstreamOIDCIdentityProviderI {
|
func (p *dynamicUpstreamIDPProvider) GetOIDCIdentityProviders() []UpstreamOIDCIdentityProviderI {
|
||||||
p.mutex.RLock() // acquire a read lock
|
p.mutex.RLock() // acquire a read lock
|
||||||
defer p.mutex.RUnlock()
|
defer p.mutex.RUnlock()
|
||||||
return p.federationDomains
|
return p.oidcUpstreams
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *dynamicUpstreamIDPProvider) SetLDAPIdentityProviders(ldapIDPs []UpstreamLDAPIdentityProviderI) {
|
||||||
|
p.mutex.Lock() // acquire a write lock
|
||||||
|
defer p.mutex.Unlock()
|
||||||
|
p.ldapUpstreams = ldapIDPs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *dynamicUpstreamIDPProvider) GetLDAPIdentityProviders() []UpstreamLDAPIdentityProviderI {
|
||||||
|
p.mutex.RLock() // acquire a read lock
|
||||||
|
defer p.mutex.RUnlock()
|
||||||
|
return p.ldapUpstreams
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package manager
|
package manager
|
||||||
@ -8,10 +8,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"go.pinniped.dev/internal/secret"
|
|
||||||
|
|
||||||
"go.pinniped.dev/internal/oidc/dynamiccodec"
|
|
||||||
|
|
||||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
|
|
||||||
"go.pinniped.dev/internal/oidc"
|
"go.pinniped.dev/internal/oidc"
|
||||||
@ -19,10 +15,12 @@ import (
|
|||||||
"go.pinniped.dev/internal/oidc/callback"
|
"go.pinniped.dev/internal/oidc/callback"
|
||||||
"go.pinniped.dev/internal/oidc/csrftoken"
|
"go.pinniped.dev/internal/oidc/csrftoken"
|
||||||
"go.pinniped.dev/internal/oidc/discovery"
|
"go.pinniped.dev/internal/oidc/discovery"
|
||||||
|
"go.pinniped.dev/internal/oidc/dynamiccodec"
|
||||||
"go.pinniped.dev/internal/oidc/jwks"
|
"go.pinniped.dev/internal/oidc/jwks"
|
||||||
"go.pinniped.dev/internal/oidc/provider"
|
"go.pinniped.dev/internal/oidc/provider"
|
||||||
"go.pinniped.dev/internal/oidc/token"
|
"go.pinniped.dev/internal/oidc/token"
|
||||||
"go.pinniped.dev/internal/plog"
|
"go.pinniped.dev/internal/plog"
|
||||||
|
"go.pinniped.dev/internal/secret"
|
||||||
"go.pinniped.dev/pkg/oidcclient/nonce"
|
"go.pinniped.dev/pkg/oidcclient/nonce"
|
||||||
"go.pinniped.dev/pkg/oidcclient/pkce"
|
"go.pinniped.dev/pkg/oidcclient/pkce"
|
||||||
)
|
)
|
||||||
@ -36,7 +34,7 @@ type Manager struct {
|
|||||||
providerHandlers map[string]http.Handler // map of all routes for all providers
|
providerHandlers map[string]http.Handler // map of all routes for all providers
|
||||||
nextHandler http.Handler // the next handler in a chain, called when this manager didn't know how to handle a request
|
nextHandler http.Handler // the next handler in a chain, called when this manager didn't know how to handle a request
|
||||||
dynamicJWKSProvider jwks.DynamicJWKSProvider // in-memory cache of per-issuer JWKS data
|
dynamicJWKSProvider jwks.DynamicJWKSProvider // in-memory cache of per-issuer JWKS data
|
||||||
idpListGetter oidc.IDPListGetter // in-memory cache of upstream IDPs
|
upstreamIDPs oidc.UpstreamIdentityProvidersLister // in-memory cache of upstream IDPs
|
||||||
secretCache *secret.Cache // in-memory cache of cryptographic material
|
secretCache *secret.Cache // in-memory cache of cryptographic material
|
||||||
secretsClient corev1client.SecretInterface
|
secretsClient corev1client.SecretInterface
|
||||||
}
|
}
|
||||||
@ -44,11 +42,11 @@ type Manager struct {
|
|||||||
// NewManager returns an empty Manager.
|
// NewManager returns an empty Manager.
|
||||||
// nextHandler will be invoked for any requests that could not be handled by this manager's providers.
|
// nextHandler will be invoked for any requests that could not be handled by this manager's providers.
|
||||||
// dynamicJWKSProvider will be used as an in-memory cache for per-issuer JWKS data.
|
// dynamicJWKSProvider will be used as an in-memory cache for per-issuer JWKS data.
|
||||||
// idpListGetter will be used as an in-memory cache of currently configured upstream IDPs.
|
// upstreamIDPs will be used as an in-memory cache of currently configured upstream IDPs.
|
||||||
func NewManager(
|
func NewManager(
|
||||||
nextHandler http.Handler,
|
nextHandler http.Handler,
|
||||||
dynamicJWKSProvider jwks.DynamicJWKSProvider,
|
dynamicJWKSProvider jwks.DynamicJWKSProvider,
|
||||||
idpListGetter oidc.IDPListGetter,
|
upstreamIDPs oidc.UpstreamIdentityProvidersLister,
|
||||||
secretCache *secret.Cache,
|
secretCache *secret.Cache,
|
||||||
secretsClient corev1client.SecretInterface,
|
secretsClient corev1client.SecretInterface,
|
||||||
) *Manager {
|
) *Manager {
|
||||||
@ -56,7 +54,7 @@ func NewManager(
|
|||||||
providerHandlers: make(map[string]http.Handler),
|
providerHandlers: make(map[string]http.Handler),
|
||||||
nextHandler: nextHandler,
|
nextHandler: nextHandler,
|
||||||
dynamicJWKSProvider: dynamicJWKSProvider,
|
dynamicJWKSProvider: dynamicJWKSProvider,
|
||||||
idpListGetter: idpListGetter,
|
upstreamIDPs: upstreamIDPs,
|
||||||
secretCache: secretCache,
|
secretCache: secretCache,
|
||||||
secretsClient: secretsClient,
|
secretsClient: secretsClient,
|
||||||
}
|
}
|
||||||
@ -110,7 +108,7 @@ func (m *Manager) SetProviders(federationDomains ...*provider.FederationDomainIs
|
|||||||
|
|
||||||
m.providerHandlers[(issuerHostWithPath + oidc.AuthorizationEndpointPath)] = auth.NewHandler(
|
m.providerHandlers[(issuerHostWithPath + oidc.AuthorizationEndpointPath)] = auth.NewHandler(
|
||||||
issuer,
|
issuer,
|
||||||
m.idpListGetter,
|
m.upstreamIDPs,
|
||||||
oauthHelperWithNullStorage,
|
oauthHelperWithNullStorage,
|
||||||
csrftoken.Generate,
|
csrftoken.Generate,
|
||||||
pkce.Generate,
|
pkce.Generate,
|
||||||
@ -120,7 +118,7 @@ func (m *Manager) SetProviders(federationDomains ...*provider.FederationDomainIs
|
|||||||
)
|
)
|
||||||
|
|
||||||
m.providerHandlers[(issuerHostWithPath + oidc.CallbackEndpointPath)] = callback.NewHandler(
|
m.providerHandlers[(issuerHostWithPath + oidc.CallbackEndpointPath)] = callback.NewHandler(
|
||||||
m.idpListGetter,
|
m.upstreamIDPs,
|
||||||
oauthHelperWithKubeStorage,
|
oauthHelperWithKubeStorage,
|
||||||
upstreamStateEncoder,
|
upstreamStateEncoder,
|
||||||
csrfCookieEncoder,
|
csrfCookieEncoder,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package manager
|
package manager
|
||||||
@ -221,7 +221,7 @@ func TestManager(t *testing.T) {
|
|||||||
|
|
||||||
parsedUpstreamIDPAuthorizationURL, err := url.Parse(upstreamIDPAuthorizationURL)
|
parsedUpstreamIDPAuthorizationURL, err := url.Parse(upstreamIDPAuthorizationURL)
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
idpListGetter := oidctestutil.NewIDPListGetter(&oidctestutil.TestUpstreamOIDCIdentityProvider{
|
idpLister := oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(&oidctestutil.TestUpstreamOIDCIdentityProvider{
|
||||||
Name: "test-idp",
|
Name: "test-idp",
|
||||||
ClientID: "test-client-id",
|
ClientID: "test-client-id",
|
||||||
AuthorizationURL: *parsedUpstreamIDPAuthorizationURL,
|
AuthorizationURL: *parsedUpstreamIDPAuthorizationURL,
|
||||||
@ -238,7 +238,7 @@ func TestManager(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
})
|
}).Build()
|
||||||
|
|
||||||
kubeClient = fake.NewSimpleClientset()
|
kubeClient = fake.NewSimpleClientset()
|
||||||
secretsClient := kubeClient.CoreV1().Secrets("some-namespace")
|
secretsClient := kubeClient.CoreV1().Secrets("some-namespace")
|
||||||
@ -254,7 +254,7 @@ func TestManager(t *testing.T) {
|
|||||||
cache.SetStateEncoderHashKey(issuer2, []byte("some-state-encoder-hash-key-2"))
|
cache.SetStateEncoderHashKey(issuer2, []byte("some-state-encoder-hash-key-2"))
|
||||||
cache.SetStateEncoderBlockKey(issuer2, []byte("16-bytes-STATE02"))
|
cache.SetStateEncoderBlockKey(issuer2, []byte("16-bytes-STATE02"))
|
||||||
|
|
||||||
subject = NewManager(nextHandler, dynamicJWKSProvider, idpListGetter, &cache, secretsClient)
|
subject = NewManager(nextHandler, dynamicJWKSProvider, idpLister, &cache, secretsClient)
|
||||||
})
|
})
|
||||||
|
|
||||||
when("given no providers via SetProviders()", func() {
|
when("given no providers via SetProviders()", func() {
|
||||||
|
@ -388,7 +388,9 @@ func requestAuthorizationUsingLDAPIdentityProvider(t *testing.T, downstreamAutho
|
|||||||
defer authResponse.Body.Close()
|
defer authResponse.Body.Close()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
t.Skip("The rest of this test will not work until we implement the corresponding production code.") // TODO remove this skip
|
// TODO remove this skip
|
||||||
|
_ = responseBody // suppress linter until we remove the below skip
|
||||||
|
t.Skip("The rest of this test will not work until we implement the corresponding production code.")
|
||||||
|
|
||||||
require.Equalf(t, http.StatusOK, authResponse.StatusCode, "response body was: %s", string(responseBody))
|
require.Equalf(t, http.StatusOK, authResponse.StatusCode, "response body was: %s", string(responseBody))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user