ContainerImage.Pinniped/internal/oidc/idpdiscovery/idp_discovery_handler.go
Margo Crawford 694e4d6df6 Advertise browser_authcode flow in ldap idp discovery
To keep this backwards compatible, this PR changes how
the cli deals with ambiguous flows. Previously, if there
was more than one flow advertised, the cli would require users
to set the flag --upstream-identity-provider-flow. Now it
chooses the first one in the list.

Signed-off-by: Margo Crawford <margaretc@vmware.com>
2022-04-25 14:54:21 -07:00

80 lines
2.6 KiB
Go

// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Package idpdiscovery provides a handler for the upstream IDP discovery endpoint.
package idpdiscovery
import (
"bytes"
"encoding/json"
"net/http"
"sort"
"go.pinniped.dev/generated/latest/apis/supervisor/idpdiscovery/v1alpha1"
"go.pinniped.dev/internal/oidc"
)
// NewHandler returns an http.Handler that serves the upstream IDP discovery endpoint.
func NewHandler(upstreamIDPs oidc.UpstreamIdentityProvidersLister) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, `Method not allowed (try GET)`, http.StatusMethodNotAllowed)
return
}
encodedMetadata, encodeErr := responseAsJSON(upstreamIDPs)
if encodeErr != nil {
http.Error(w, encodeErr.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
if _, err := w.Write(encodedMetadata); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
}
func responseAsJSON(upstreamIDPs oidc.UpstreamIdentityProvidersLister) ([]byte, error) {
r := v1alpha1.IDPDiscoveryResponse{PinnipedIDPs: []v1alpha1.PinnipedIDP{}}
// The cache of IDPs could change at any time, so always recalculate the list.
for _, provider := range upstreamIDPs.GetLDAPIdentityProviders() {
r.PinnipedIDPs = append(r.PinnipedIDPs, v1alpha1.PinnipedIDP{
Name: provider.GetName(),
Type: v1alpha1.IDPTypeLDAP,
Flows: []v1alpha1.IDPFlow{v1alpha1.IDPFlowCLIPassword, v1alpha1.IDPFlowBrowserAuthcode},
})
}
for _, provider := range upstreamIDPs.GetActiveDirectoryIdentityProviders() {
r.PinnipedIDPs = append(r.PinnipedIDPs, v1alpha1.PinnipedIDP{
Name: provider.GetName(),
Type: v1alpha1.IDPTypeActiveDirectory,
Flows: []v1alpha1.IDPFlow{v1alpha1.IDPFlowCLIPassword, v1alpha1.IDPFlowBrowserAuthcode},
})
}
for _, provider := range upstreamIDPs.GetOIDCIdentityProviders() {
flows := []v1alpha1.IDPFlow{v1alpha1.IDPFlowBrowserAuthcode}
if provider.AllowsPasswordGrant() {
flows = append(flows, v1alpha1.IDPFlowCLIPassword)
}
r.PinnipedIDPs = append(r.PinnipedIDPs, v1alpha1.PinnipedIDP{
Name: provider.GetName(),
Type: v1alpha1.IDPTypeOIDC,
Flows: flows,
})
}
// Nobody like an API that changes the results unnecessarily. :)
sort.SliceStable(r.PinnipedIDPs, func(i, j int) bool {
return r.PinnipedIDPs[i].Name < r.PinnipedIDPs[j].Name
})
var b bytes.Buffer
encodeErr := json.NewEncoder(&b).Encode(&r)
encodedMetadata := b.Bytes()
return encodedMetadata, encodeErr
}