ContainerImage.Pinniped/internal/oidc/provider/dynamic_upstream_idp_provider.go
Ryan Richard f6ded84f07 Implement upstream LDAP support in auth_handler.go
- When the upstream IDP is an LDAP IDP and the user's LDAP username and
  password are received as new custom headers, then authenticate the
  user and, if authentication was successful, return a redirect with
  an authcode. Handle errors according to the OAuth/OIDC specs.
- Still does not support having multiple upstream IDPs defined at the
  same time, which was an existing limitation of this endpoint.
- Does not yet include the actual LDAP authentication, which is
  hidden behind an interface from the point of view of auth_handler.go
- Move the oidctestutil package to the testutil directory.
- Add an interface for Fosite storage to avoid a cyclical test
  dependency.
- Add GetURL() to the UpstreamLDAPIdentityProviderI interface.
- Extract test helpers to be shared between callback_handler_test.go
  and auth_handler_test.go because the authcode and fosite storage
  assertions should be identical.
- Backfill Content-Type assertions in callback_handler_test.go.

Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-04-08 17:28:01 -07:00

108 lines
3.5 KiB
Go

// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package provider
import (
"context"
"net/url"
"sync"
"golang.org/x/oauth2"
"go.pinniped.dev/internal/ldap"
"go.pinniped.dev/pkg/oidcclient/nonce"
"go.pinniped.dev/pkg/oidcclient/oidctypes"
"go.pinniped.dev/pkg/oidcclient/pkce"
)
type UpstreamOIDCIdentityProviderI interface {
// A name for this upstream provider, which will be used as a component of the path for the callback endpoint
// hosted by the Supervisor.
GetName() string
// The Oauth client ID registered with the upstream provider to be used in the authorization code flow.
GetClientID() string
// The Authorization Endpoint fetched from discovery.
GetAuthorizationURL() *url.URL
// Scopes to request in authorization flow.
GetScopes() []string
// ID Token username claim name. May return empty string, in which case we will use some reasonable defaults.
GetUsernameClaim() string
// ID Token groups claim name. May return empty string, in which case we won't try to read groups from the upstream provider.
GetGroupsClaim() string
// Performs upstream OIDC authorization code exchange and token validation.
// Returns the validated raw tokens as well as the parsed claims of the ID token.
ExchangeAuthcodeAndValidateTokens(
ctx context.Context,
authcode string,
pkceCodeVerifier pkce.Code,
expectedIDTokenNonce nonce.Nonce,
redirectURI string,
) (*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
// Return a URL which uniquely identifies this LDAP provider, e.g. "ldaps://host.example.com:1234".
// This URL is not used for connecting to the provider, but rather is used for creating a globally unique user
// identifier by being combined with the user's UID, since user UIDs are only unique within one provider.
GetURL() string
// A method for performing user authentication against the upstream LDAP provider.
ldap.UserAuthenticator
}
type DynamicUpstreamIDPProvider interface {
SetOIDCIdentityProviders(oidcIDPs []UpstreamOIDCIdentityProviderI)
GetOIDCIdentityProviders() []UpstreamOIDCIdentityProviderI
SetLDAPIdentityProviders(ldapIDPs []UpstreamLDAPIdentityProviderI)
GetLDAPIdentityProviders() []UpstreamLDAPIdentityProviderI
}
type dynamicUpstreamIDPProvider struct {
oidcUpstreams []UpstreamOIDCIdentityProviderI
ldapUpstreams []UpstreamLDAPIdentityProviderI
mutex sync.RWMutex
}
func NewDynamicUpstreamIDPProvider() DynamicUpstreamIDPProvider {
return &dynamicUpstreamIDPProvider{
oidcUpstreams: []UpstreamOIDCIdentityProviderI{},
ldapUpstreams: []UpstreamLDAPIdentityProviderI{},
}
}
func (p *dynamicUpstreamIDPProvider) SetOIDCIdentityProviders(oidcIDPs []UpstreamOIDCIdentityProviderI) {
p.mutex.Lock() // acquire a write lock
defer p.mutex.Unlock()
p.oidcUpstreams = oidcIDPs
}
func (p *dynamicUpstreamIDPProvider) GetOIDCIdentityProviders() []UpstreamOIDCIdentityProviderI {
p.mutex.RLock() // acquire a read lock
defer p.mutex.RUnlock()
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
}