2021-05-11 17:31:33 +00:00
|
|
|
// Copyright 2021 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"
|
|
|
|
|
2021-08-17 22:23:03 +00:00
|
|
|
"go.pinniped.dev/generated/latest/apis/supervisor/idpdiscovery/v1alpha1"
|
2021-05-11 17:31:33 +00:00
|
|
|
"go.pinniped.dev/internal/oidc"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
idpDiscoveryTypeLDAP = "ldap"
|
|
|
|
idpDiscoveryTypeOIDC = "oidc"
|
2021-08-12 17:00:18 +00:00
|
|
|
|
|
|
|
flowOIDCBrowser = "browser_authcode"
|
|
|
|
flowCLIPassword = "cli_password"
|
2021-05-11 17:31:33 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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) {
|
2021-08-17 22:23:03 +00:00
|
|
|
r := v1alpha1.SupervisorIDPDiscoveryResponse{
|
|
|
|
PinnipedIDPs: []v1alpha1.SupervisorPinnipedIDP{},
|
2021-05-11 17:31:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// The cache of IDPs could change at any time, so always recalculate the list.
|
|
|
|
for _, provider := range upstreamIDPs.GetLDAPIdentityProviders() {
|
2021-08-17 22:23:03 +00:00
|
|
|
r.PinnipedIDPs = append(r.PinnipedIDPs, v1alpha1.SupervisorPinnipedIDP{
|
2021-08-12 17:00:18 +00:00
|
|
|
Name: provider.GetName(),
|
|
|
|
Type: idpDiscoveryTypeLDAP,
|
|
|
|
Flows: []string{flowCLIPassword},
|
|
|
|
})
|
2021-05-11 17:31:33 +00:00
|
|
|
}
|
|
|
|
for _, provider := range upstreamIDPs.GetOIDCIdentityProviders() {
|
2021-08-12 17:00:18 +00:00
|
|
|
flows := []string{flowOIDCBrowser}
|
|
|
|
if provider.AllowsPasswordGrant() {
|
|
|
|
flows = append(flows, flowCLIPassword)
|
|
|
|
}
|
2021-08-17 22:23:03 +00:00
|
|
|
r.PinnipedIDPs = append(r.PinnipedIDPs, v1alpha1.SupervisorPinnipedIDP{
|
2021-08-12 17:00:18 +00:00
|
|
|
Name: provider.GetName(),
|
|
|
|
Type: idpDiscoveryTypeOIDC,
|
|
|
|
Flows: flows,
|
|
|
|
})
|
2021-05-11 17:31:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Nobody like an API that changes the results unnecessarily. :)
|
2021-08-17 22:23:03 +00:00
|
|
|
sort.SliceStable(r.PinnipedIDPs, func(i, j int) bool {
|
|
|
|
return r.PinnipedIDPs[i].Name < r.PinnipedIDPs[j].Name
|
2021-05-11 17:31:33 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
var b bytes.Buffer
|
|
|
|
encodeErr := json.NewEncoder(&b).Encode(&r)
|
|
|
|
encodedMetadata := b.Bytes()
|
|
|
|
|
|
|
|
return encodedMetadata, encodeErr
|
|
|
|
}
|