2023-06-07 23:07:43 +00:00
|
|
|
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
|
2020-10-08 02:18:34 +00:00
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
2023-06-22 20:12:50 +00:00
|
|
|
package federationdomainproviders
|
2020-10-08 02:18:34 +00:00
|
|
|
|
|
|
|
import (
|
2020-10-08 18:28:21 +00:00
|
|
|
"fmt"
|
2020-10-08 02:18:34 +00:00
|
|
|
"net/url"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"go.pinniped.dev/internal/constable"
|
|
|
|
)
|
|
|
|
|
2023-06-22 20:12:50 +00:00
|
|
|
// FederationDomainIssuer is a parsed FederationDomain representing all the settings for a downstream OIDC provider
|
|
|
|
// and contains configuration representing a set of upstream identity providers.
|
2020-12-17 19:34:49 +00:00
|
|
|
type FederationDomainIssuer struct {
|
2020-10-08 18:28:21 +00:00
|
|
|
issuer string
|
|
|
|
issuerHost string
|
|
|
|
issuerPath string
|
2023-05-08 21:07:38 +00:00
|
|
|
|
|
|
|
// identityProviders should be used when they are explicitly specified in the FederationDomain's spec.
|
|
|
|
identityProviders []*FederationDomainIdentityProvider
|
|
|
|
// defaultIdentityProvider should be used only for the backwards compatibility mode where identity providers
|
|
|
|
// are not explicitly specified in the FederationDomain's spec, and there is exactly one IDP CR defined in the
|
|
|
|
// Supervisor's namespace.
|
|
|
|
defaultIdentityProvider *FederationDomainIdentityProvider
|
2020-10-08 02:18:34 +00:00
|
|
|
}
|
|
|
|
|
2023-06-07 23:07:43 +00:00
|
|
|
// NewFederationDomainIssuer returns a FederationDomainIssuer.
|
|
|
|
// Performs validation, and returns any error from validation.
|
2023-05-08 21:07:38 +00:00
|
|
|
func NewFederationDomainIssuer(
|
|
|
|
issuer string,
|
|
|
|
identityProviders []*FederationDomainIdentityProvider,
|
|
|
|
) (*FederationDomainIssuer, error) {
|
|
|
|
p := FederationDomainIssuer{issuer: issuer, identityProviders: identityProviders}
|
|
|
|
err := p.validateURL()
|
2020-10-08 18:28:21 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &p, nil
|
|
|
|
}
|
|
|
|
|
2023-05-08 21:07:38 +00:00
|
|
|
func NewFederationDomainIssuerWithDefaultIDP(
|
|
|
|
issuer string,
|
|
|
|
defaultIdentityProvider *FederationDomainIdentityProvider,
|
|
|
|
) (*FederationDomainIssuer, error) {
|
|
|
|
fdi, err := NewFederationDomainIssuer(issuer, []*FederationDomainIdentityProvider{defaultIdentityProvider})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
fdi.defaultIdentityProvider = defaultIdentityProvider
|
|
|
|
return fdi, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *FederationDomainIssuer) validateURL() error {
|
2020-10-08 18:28:21 +00:00
|
|
|
if p.issuer == "" {
|
2020-12-17 19:34:49 +00:00
|
|
|
return constable.Error("federation domain must have an issuer")
|
2020-10-08 02:18:34 +00:00
|
|
|
}
|
|
|
|
|
2020-10-08 18:28:21 +00:00
|
|
|
issuerURL, err := url.Parse(p.issuer)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not parse issuer as URL: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-10-28 19:49:41 +00:00
|
|
|
if issuerURL.Scheme != "https" {
|
2020-10-08 02:18:34 +00:00
|
|
|
return constable.Error(`issuer must have "https" scheme`)
|
|
|
|
}
|
|
|
|
|
2023-06-08 02:33:54 +00:00
|
|
|
if issuerURL.Hostname() == "" {
|
|
|
|
return constable.Error(`issuer must have a hostname`)
|
|
|
|
}
|
|
|
|
|
2020-10-08 18:28:21 +00:00
|
|
|
if issuerURL.User != nil {
|
2020-10-08 02:18:34 +00:00
|
|
|
return constable.Error(`issuer must not have username or password`)
|
|
|
|
}
|
|
|
|
|
2020-10-08 18:28:21 +00:00
|
|
|
if strings.HasSuffix(issuerURL.Path, "/") {
|
2020-10-08 02:18:34 +00:00
|
|
|
return constable.Error(`issuer must not have trailing slash in path`)
|
|
|
|
}
|
|
|
|
|
2020-10-08 18:28:21 +00:00
|
|
|
if issuerURL.RawQuery != "" {
|
2020-10-08 02:18:34 +00:00
|
|
|
return constable.Error(`issuer must not have query`)
|
|
|
|
}
|
|
|
|
|
2020-10-08 18:28:21 +00:00
|
|
|
if issuerURL.Fragment != "" {
|
2020-10-08 02:18:34 +00:00
|
|
|
return constable.Error(`issuer must not have fragment`)
|
|
|
|
}
|
|
|
|
|
2020-10-08 18:28:21 +00:00
|
|
|
p.issuerHost = issuerURL.Host
|
|
|
|
p.issuerPath = issuerURL.Path
|
|
|
|
|
2020-10-08 02:18:34 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-07-07 16:52:32 +00:00
|
|
|
// Issuer returns the issuer.
|
2020-12-17 19:34:49 +00:00
|
|
|
func (p *FederationDomainIssuer) Issuer() string {
|
2020-10-08 18:28:21 +00:00
|
|
|
return p.issuer
|
|
|
|
}
|
|
|
|
|
2023-07-07 16:52:32 +00:00
|
|
|
// IssuerHost returns the issuerHost.
|
2020-12-17 19:34:49 +00:00
|
|
|
func (p *FederationDomainIssuer) IssuerHost() string {
|
2020-10-08 18:28:21 +00:00
|
|
|
return p.issuerHost
|
|
|
|
}
|
|
|
|
|
2023-07-07 16:52:32 +00:00
|
|
|
// IssuerPath returns the issuerPath.
|
2020-12-17 19:34:49 +00:00
|
|
|
func (p *FederationDomainIssuer) IssuerPath() string {
|
2020-10-08 18:28:21 +00:00
|
|
|
return p.issuerPath
|
|
|
|
}
|
2023-05-08 21:07:38 +00:00
|
|
|
|
|
|
|
// IdentityProviders returns the IdentityProviders.
|
|
|
|
func (p *FederationDomainIssuer) IdentityProviders() []*FederationDomainIdentityProvider {
|
|
|
|
return p.identityProviders
|
|
|
|
}
|
|
|
|
|
|
|
|
// DefaultIdentityProvider will return nil when there is no default.
|
|
|
|
func (p *FederationDomainIssuer) DefaultIdentityProvider() *FederationDomainIdentityProvider {
|
|
|
|
return p.defaultIdentityProvider
|
|
|
|
}
|