mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-09-20 15:04:34 +00:00
Merge branch 'dynamic_clients' into token_exchange_aud
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
webhookutil "k8s.io/apiserver/pkg/util/webhook"
|
||||
"k8s.io/apiserver/plugin/pkg/authenticator/token/webhook"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
@@ -118,7 +119,28 @@ func newWebhookAuthenticator(
|
||||
// custom proxy stuff used by the API server.
|
||||
var customDial net.DialFunc
|
||||
|
||||
// TODO refactor this code to directly construct the rest.Config
|
||||
// ideally we would keep rest config generation contained to the kubeclient package
|
||||
// but this will require some form of a new WithTLSConfigFunc kubeclient.Option
|
||||
// ex:
|
||||
// _, caBundle, err := pinnipedauthenticator.CABundle(spec.TLS)
|
||||
// ...
|
||||
// restConfig := &rest.Config{
|
||||
// Host: spec.Endpoint,
|
||||
// TLSClientConfig: rest.TLSClientConfig{CAData: caBundle},
|
||||
// // copied from k8s.io/apiserver/pkg/util/webhook
|
||||
// Timeout: 30 * time.Second,
|
||||
// QPS: -1,
|
||||
// }
|
||||
// client, err := kubeclient.New(kubeclient.WithConfig(restConfig), kubeclient.WithTLSConfigFunc(ptls.Default))
|
||||
// ...
|
||||
// then use client.JSONConfig as clientConfig
|
||||
clientConfig, err := webhookutil.LoadKubeconfig(temp.Name(), customDial)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// this uses a http client that does not honor our TLS config
|
||||
// TODO fix when we pick up https://github.com/kubernetes/kubernetes/pull/106155
|
||||
return webhook.New(temp.Name(), version, implicitAuds, *webhook.DefaultRetryBackoff(), customDial)
|
||||
return webhook.New(clientConfig, version, implicitAuds, *webhook.DefaultRetryBackoff())
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
@@ -80,7 +81,7 @@ func (mr *MockDynamicCertPrivateMockRecorder) Name() *gomock.Call {
|
||||
}
|
||||
|
||||
// Run mocks base method.
|
||||
func (m *MockDynamicCertPrivate) Run(arg0 int, arg1 <-chan struct{}) {
|
||||
func (m *MockDynamicCertPrivate) Run(arg0 context.Context, arg1 int) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "Run", arg0, arg1)
|
||||
}
|
||||
@@ -92,17 +93,17 @@ func (mr *MockDynamicCertPrivateMockRecorder) Run(arg0, arg1 interface{}) *gomoc
|
||||
}
|
||||
|
||||
// RunOnce mocks base method.
|
||||
func (m *MockDynamicCertPrivate) RunOnce() error {
|
||||
func (m *MockDynamicCertPrivate) RunOnce(arg0 context.Context) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "RunOnce")
|
||||
ret := m.ctrl.Call(m, "RunOnce", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// RunOnce indicates an expected call of RunOnce.
|
||||
func (mr *MockDynamicCertPrivateMockRecorder) RunOnce() *gomock.Call {
|
||||
func (mr *MockDynamicCertPrivateMockRecorder) RunOnce(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunOnce", reflect.TypeOf((*MockDynamicCertPrivate)(nil).RunOnce))
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunOnce", reflect.TypeOf((*MockDynamicCertPrivate)(nil).RunOnce), arg0)
|
||||
}
|
||||
|
||||
// SetCertKeyContent mocks base method.
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package dynamiccert
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
@@ -148,10 +149,10 @@ func (p *provider) AddListener(listener dynamiccertificates.Listener) {
|
||||
p.listeners = append(p.listeners, listener)
|
||||
}
|
||||
|
||||
func (p *provider) RunOnce() error {
|
||||
func (p *provider) RunOnce(_ context.Context) error {
|
||||
return nil // no-op, but we want to make sure to stay in sync with dynamiccertificates.ControllerRunner
|
||||
}
|
||||
|
||||
func (p *provider) Run(workers int, stopCh <-chan struct{}) {
|
||||
func (p *provider) Run(_ context.Context, workers int) {
|
||||
// no-op, but we want to make sure to stay in sync with dynamiccertificates.ControllerRunner
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
someClusterName = "some cluster name"
|
||||
someUID = "some fake UID"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -115,13 +115,13 @@ func TestKubeclient(t *testing.T) {
|
||||
require.EqualError(t, err, `couldn't find object for path "/api/v1/namespaces/good-namespace/pods/this-pod-does-not-exist"`)
|
||||
|
||||
// update
|
||||
goodPodWithAnnotationsAndLabelsAndClusterName := with(goodPod, annotations(), labels(), clusterName()).(*corev1.Pod)
|
||||
goodPodWithAnnotationsAndLabelsAndUID := with(goodPod, annotations(), labels(), uid()).(*corev1.Pod)
|
||||
pod, err = c.Kubernetes.
|
||||
CoreV1().
|
||||
Pods(pod.Namespace).
|
||||
Update(context.Background(), goodPodWithAnnotationsAndLabelsAndClusterName, metav1.UpdateOptions{})
|
||||
Update(context.Background(), goodPodWithAnnotationsAndLabelsAndUID, metav1.UpdateOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, goodPodWithAnnotationsAndLabelsAndClusterName, pod)
|
||||
require.Equal(t, goodPodWithAnnotationsAndLabelsAndUID, pod)
|
||||
|
||||
// delete
|
||||
err = c.Kubernetes.
|
||||
@@ -135,14 +135,14 @@ func TestKubeclient(t *testing.T) {
|
||||
with(goodPod, gvk(podGVK)),
|
||||
with(&metav1.PartialObjectMetadata{}, gvk(podGVK)),
|
||||
with(&metav1.PartialObjectMetadata{}, gvk(podGVK)),
|
||||
with(goodPod, annotations(), labels(), clusterName(), gvk(podGVK)),
|
||||
with(goodPod, annotations(), labels(), uid(), gvk(podGVK)),
|
||||
with(&metav1.PartialObjectMetadata{}, gvk(podGVK)),
|
||||
},
|
||||
{
|
||||
with(goodPod, annotations(), gvk(podGVK)),
|
||||
with(&metav1.PartialObjectMetadata{}, gvk(podGVK)),
|
||||
with(&metav1.PartialObjectMetadata{}, gvk(podGVK)),
|
||||
with(goodPod, annotations(), labels(), clusterName(), gvk(podGVK)),
|
||||
with(goodPod, annotations(), labels(), uid(), gvk(podGVK)),
|
||||
with(&metav1.PartialObjectMetadata{}, gvk(podGVK)),
|
||||
},
|
||||
},
|
||||
@@ -150,12 +150,12 @@ func TestKubeclient(t *testing.T) {
|
||||
{
|
||||
with(goodPod, annotations(), labels(), gvk(podGVK)),
|
||||
with(goodPod, annotations(), labels(), gvk(podGVK)),
|
||||
with(goodPod, annotations(), labels(), clusterName(), gvk(podGVK)),
|
||||
with(goodPod, annotations(), labels(), uid(), gvk(podGVK)),
|
||||
},
|
||||
{
|
||||
with(goodPod, emptyAnnotations(), labels(), gvk(podGVK)),
|
||||
with(goodPod, annotations(), labels(), gvk(podGVK)),
|
||||
with(goodPod, annotations(), labels(), clusterName(), gvk(podGVK)),
|
||||
with(goodPod, annotations(), labels(), uid(), gvk(podGVK)),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -217,13 +217,13 @@ func TestKubeclient(t *testing.T) {
|
||||
require.Equal(t, with(goodAPIService, annotations(), labels()), apiService)
|
||||
|
||||
// update
|
||||
goodAPIServiceWithAnnotationsAndLabelsAndClusterName := with(goodAPIService, annotations(), labels(), clusterName()).(*apiregistrationv1.APIService)
|
||||
goodAPIServiceWithAnnotationsAndLabelsAndUID := with(goodAPIService, annotations(), labels(), uid()).(*apiregistrationv1.APIService)
|
||||
apiService, err = c.Aggregation.
|
||||
ApiregistrationV1().
|
||||
APIServices().
|
||||
Update(context.Background(), goodAPIServiceWithAnnotationsAndLabelsAndClusterName, metav1.UpdateOptions{})
|
||||
Update(context.Background(), goodAPIServiceWithAnnotationsAndLabelsAndUID, metav1.UpdateOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, goodAPIServiceWithAnnotationsAndLabelsAndClusterName, apiService)
|
||||
require.Equal(t, goodAPIServiceWithAnnotationsAndLabelsAndUID, apiService)
|
||||
|
||||
// delete
|
||||
err = c.Aggregation.
|
||||
@@ -236,13 +236,13 @@ func TestKubeclient(t *testing.T) {
|
||||
{
|
||||
with(goodAPIService, gvk(apiServiceGVK)),
|
||||
with(&metav1.PartialObjectMetadata{}, gvk(apiServiceGVK)),
|
||||
with(goodAPIService, annotations(), labels(), clusterName(), gvk(apiServiceGVK)),
|
||||
with(goodAPIService, annotations(), labels(), uid(), gvk(apiServiceGVK)),
|
||||
with(&metav1.PartialObjectMetadata{}, gvk(apiServiceGVK)),
|
||||
},
|
||||
{
|
||||
with(goodAPIService, annotations(), gvk(apiServiceGVK)),
|
||||
with(&metav1.PartialObjectMetadata{}, gvk(apiServiceGVK)),
|
||||
with(goodAPIService, annotations(), labels(), clusterName(), gvk(apiServiceGVK)),
|
||||
with(goodAPIService, annotations(), labels(), uid(), gvk(apiServiceGVK)),
|
||||
with(&metav1.PartialObjectMetadata{}, gvk(apiServiceGVK)),
|
||||
},
|
||||
},
|
||||
@@ -250,12 +250,12 @@ func TestKubeclient(t *testing.T) {
|
||||
{
|
||||
with(goodAPIService, annotations(), labels(), gvk(apiServiceGVK)),
|
||||
with(goodAPIService, annotations(), labels(), gvk(apiServiceGVK)),
|
||||
with(goodAPIService, annotations(), labels(), clusterName(), gvk(apiServiceGVK)),
|
||||
with(goodAPIService, annotations(), labels(), uid(), gvk(apiServiceGVK)),
|
||||
},
|
||||
{
|
||||
with(goodAPIService, emptyAnnotations(), labels(), gvk(apiServiceGVK)),
|
||||
with(goodAPIService, annotations(), labels(), gvk(apiServiceGVK)),
|
||||
with(goodAPIService, annotations(), labels(), clusterName(), gvk(apiServiceGVK)),
|
||||
with(goodAPIService, annotations(), labels(), uid(), gvk(apiServiceGVK)),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -282,13 +282,13 @@ func TestKubeclient(t *testing.T) {
|
||||
require.Equal(t, with(goodCredentialIssuer, annotations(), labels()), tokenCredentialRequest)
|
||||
|
||||
// update
|
||||
goodCredentialIssuerWithAnnotationsAndLabelsAndClusterName := with(goodCredentialIssuer, annotations(), labels(), clusterName()).(*conciergeconfigv1alpha1.CredentialIssuer)
|
||||
goodCredentialIssuerWithAnnotationsAndLabelsAndUID := with(goodCredentialIssuer, annotations(), labels(), uid()).(*conciergeconfigv1alpha1.CredentialIssuer)
|
||||
tokenCredentialRequest, err = c.PinnipedConcierge.
|
||||
ConfigV1alpha1().
|
||||
CredentialIssuers().
|
||||
Update(context.Background(), goodCredentialIssuerWithAnnotationsAndLabelsAndClusterName, metav1.UpdateOptions{})
|
||||
Update(context.Background(), goodCredentialIssuerWithAnnotationsAndLabelsAndUID, metav1.UpdateOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, goodCredentialIssuerWithAnnotationsAndLabelsAndClusterName, tokenCredentialRequest)
|
||||
require.Equal(t, goodCredentialIssuerWithAnnotationsAndLabelsAndUID, tokenCredentialRequest)
|
||||
|
||||
// delete
|
||||
err = c.PinnipedConcierge.
|
||||
@@ -301,13 +301,13 @@ func TestKubeclient(t *testing.T) {
|
||||
{
|
||||
with(goodCredentialIssuer, gvk(credentialIssuerGVK)),
|
||||
with(&metav1.PartialObjectMetadata{}, gvk(credentialIssuerGVK)),
|
||||
with(goodCredentialIssuer, annotations(), labels(), clusterName(), gvk(credentialIssuerGVK)),
|
||||
with(goodCredentialIssuer, annotations(), labels(), uid(), gvk(credentialIssuerGVK)),
|
||||
with(&metav1.PartialObjectMetadata{}, gvk(credentialIssuerGVK)),
|
||||
},
|
||||
{
|
||||
with(goodCredentialIssuer, annotations(), gvk(credentialIssuerGVK)),
|
||||
with(&metav1.PartialObjectMetadata{}, gvk(credentialIssuerGVK)),
|
||||
with(goodCredentialIssuer, annotations(), labels(), clusterName(), gvk(credentialIssuerGVK)),
|
||||
with(goodCredentialIssuer, annotations(), labels(), uid(), gvk(credentialIssuerGVK)),
|
||||
with(&metav1.PartialObjectMetadata{}, gvk(credentialIssuerGVK)),
|
||||
},
|
||||
},
|
||||
@@ -315,12 +315,12 @@ func TestKubeclient(t *testing.T) {
|
||||
{
|
||||
with(goodCredentialIssuer, annotations(), labels(), gvk(credentialIssuerGVK)),
|
||||
with(goodCredentialIssuer, annotations(), labels(), gvk(credentialIssuerGVK)),
|
||||
with(goodCredentialIssuer, annotations(), labels(), clusterName(), gvk(credentialIssuerGVK)),
|
||||
with(goodCredentialIssuer, annotations(), labels(), uid(), gvk(credentialIssuerGVK)),
|
||||
},
|
||||
{
|
||||
with(goodCredentialIssuer, emptyAnnotations(), labels(), gvk(credentialIssuerGVK)),
|
||||
with(goodCredentialIssuer, annotations(), labels(), gvk(credentialIssuerGVK)),
|
||||
with(goodCredentialIssuer, annotations(), labels(), clusterName(), gvk(credentialIssuerGVK)),
|
||||
with(goodCredentialIssuer, annotations(), labels(), uid(), gvk(credentialIssuerGVK)),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -347,13 +347,13 @@ func TestKubeclient(t *testing.T) {
|
||||
require.Equal(t, with(goodFederationDomain, annotations(), labels()), federationDomain)
|
||||
|
||||
// update
|
||||
goodFederationDomainWithAnnotationsAndLabelsAndClusterName := with(goodFederationDomain, annotations(), labels(), clusterName()).(*supervisorconfigv1alpha1.FederationDomain)
|
||||
goodFederationDomainWithAnnotationsAndLabelsAndUID := with(goodFederationDomain, annotations(), labels(), uid()).(*supervisorconfigv1alpha1.FederationDomain)
|
||||
federationDomain, err = c.PinnipedSupervisor.
|
||||
ConfigV1alpha1().
|
||||
FederationDomains(federationDomain.Namespace).
|
||||
Update(context.Background(), goodFederationDomainWithAnnotationsAndLabelsAndClusterName, metav1.UpdateOptions{})
|
||||
Update(context.Background(), goodFederationDomainWithAnnotationsAndLabelsAndUID, metav1.UpdateOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, goodFederationDomainWithAnnotationsAndLabelsAndClusterName, federationDomain)
|
||||
require.Equal(t, goodFederationDomainWithAnnotationsAndLabelsAndUID, federationDomain)
|
||||
|
||||
// delete
|
||||
err = c.PinnipedSupervisor.
|
||||
@@ -366,13 +366,13 @@ func TestKubeclient(t *testing.T) {
|
||||
{
|
||||
with(goodFederationDomain, gvk(federationDomainGVK)),
|
||||
with(&metav1.PartialObjectMetadata{}, gvk(federationDomainGVK)),
|
||||
with(goodFederationDomain, annotations(), labels(), clusterName(), gvk(federationDomainGVK)),
|
||||
with(goodFederationDomain, annotations(), labels(), uid(), gvk(federationDomainGVK)),
|
||||
with(&metav1.PartialObjectMetadata{}, gvk(federationDomainGVK)),
|
||||
},
|
||||
{
|
||||
with(goodFederationDomain, annotations(), gvk(federationDomainGVK)),
|
||||
with(&metav1.PartialObjectMetadata{}, gvk(federationDomainGVK)),
|
||||
with(goodFederationDomain, annotations(), labels(), clusterName(), gvk(federationDomainGVK)),
|
||||
with(goodFederationDomain, annotations(), labels(), uid(), gvk(federationDomainGVK)),
|
||||
with(&metav1.PartialObjectMetadata{}, gvk(federationDomainGVK)),
|
||||
},
|
||||
},
|
||||
@@ -380,12 +380,12 @@ func TestKubeclient(t *testing.T) {
|
||||
{
|
||||
with(goodFederationDomain, annotations(), labels(), gvk(federationDomainGVK)),
|
||||
with(goodFederationDomain, annotations(), labels(), gvk(federationDomainGVK)),
|
||||
with(goodFederationDomain, annotations(), labels(), clusterName(), gvk(federationDomainGVK)),
|
||||
with(goodFederationDomain, annotations(), labels(), uid(), gvk(federationDomainGVK)),
|
||||
},
|
||||
{
|
||||
with(goodFederationDomain, emptyAnnotations(), labels(), gvk(federationDomainGVK)),
|
||||
with(goodFederationDomain, annotations(), labels(), gvk(federationDomainGVK)),
|
||||
with(goodFederationDomain, annotations(), labels(), clusterName(), gvk(federationDomainGVK)),
|
||||
with(goodFederationDomain, annotations(), labels(), uid(), gvk(federationDomainGVK)),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -428,7 +428,7 @@ func TestKubeclient(t *testing.T) {
|
||||
FederationDomains(goodFederationDomain.Namespace).
|
||||
Create(context.Background(), goodFederationDomain, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, with(goodFederationDomain, clusterName()), federationDomain)
|
||||
require.Equal(t, with(goodFederationDomain, uid()), federationDomain)
|
||||
|
||||
// read
|
||||
federationDomain, err = c.PinnipedSupervisor.
|
||||
@@ -436,7 +436,7 @@ func TestKubeclient(t *testing.T) {
|
||||
FederationDomains(federationDomain.Namespace).
|
||||
Get(context.Background(), federationDomain.Name, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, with(goodFederationDomain, clusterName()), federationDomain)
|
||||
require.Equal(t, with(goodFederationDomain, uid()), federationDomain)
|
||||
},
|
||||
wantMiddlewareReqs: [][]Object{
|
||||
{
|
||||
@@ -444,7 +444,7 @@ func TestKubeclient(t *testing.T) {
|
||||
with(&metav1.PartialObjectMetadata{}, gvk(federationDomainGVK)),
|
||||
},
|
||||
{
|
||||
with(goodFederationDomain, clusterName(), gvk(federationDomainGVK)),
|
||||
with(goodFederationDomain, uid(), gvk(federationDomainGVK)),
|
||||
with(&metav1.PartialObjectMetadata{}, gvk(federationDomainGVK)),
|
||||
},
|
||||
},
|
||||
@@ -502,7 +502,7 @@ func TestKubeclient(t *testing.T) {
|
||||
name: "non-pertinent mutater",
|
||||
t: t,
|
||||
mutateReq: func(rt RoundTrip, obj Object) error {
|
||||
clusterName()(obj)
|
||||
uid()(obj)
|
||||
return nil
|
||||
},
|
||||
}}
|
||||
@@ -728,7 +728,7 @@ func newSimpleMiddleware(t *testing.T, hasMutateReqFunc, mutatedReq, hasMutateRe
|
||||
m.mutateReq = func(rt RoundTrip, obj Object) error {
|
||||
if mutatedReq {
|
||||
if rt.Verb() == VerbCreate {
|
||||
obj.SetClusterName(someClusterName)
|
||||
obj.SetUID(someUID)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -916,9 +916,9 @@ func labels() withFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func clusterName() withFunc {
|
||||
func uid() withFunc {
|
||||
return func(obj Object) {
|
||||
obj.SetClusterName(someClusterName)
|
||||
obj.SetUID(someUID)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"go.pinniped.dev/internal/oidc/downstreamsession"
|
||||
"go.pinniped.dev/internal/oidc/login"
|
||||
"go.pinniped.dev/internal/oidc/provider"
|
||||
"go.pinniped.dev/internal/oidc/provider/formposthtml"
|
||||
"go.pinniped.dev/internal/plog"
|
||||
"go.pinniped.dev/internal/psession"
|
||||
"go.pinniped.dev/pkg/oidcclient/nonce"
|
||||
@@ -46,7 +47,7 @@ func NewHandler(
|
||||
upstreamStateEncoder oidc.Encoder,
|
||||
cookieCodec oidc.Codec,
|
||||
) http.Handler {
|
||||
return securityheader.Wrap(httperr.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||
handler := httperr.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||
if r.Method != http.MethodPost && r.Method != http.MethodGet {
|
||||
// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
||||
// Authorization Servers MUST support the use of the HTTP GET and POST methods defined in
|
||||
@@ -105,7 +106,12 @@ func NewHandler(
|
||||
upstreamStateEncoder,
|
||||
cookieCodec,
|
||||
)
|
||||
}))
|
||||
})
|
||||
|
||||
// During a response_mode=form_post auth request using the browser flow, the custom form_post html page may
|
||||
// be used to post certain errors back to the CLI from this handler's response, so allow the form_post
|
||||
// page's CSS and JS to run.
|
||||
return securityheader.WrapWithCustomCSP(handler, formposthtml.ContentSecurityPolicy())
|
||||
}
|
||||
|
||||
func handleAuthRequestForLDAPUpstreamCLIFlow(
|
||||
|
||||
@@ -521,6 +521,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
||||
wantStatus int
|
||||
wantContentType string
|
||||
wantBodyString string
|
||||
wantBodyRegex string
|
||||
wantBodyJSON string
|
||||
wantCSRFValueInCookieHeader string
|
||||
wantBodyStringWithLocationInHref bool
|
||||
@@ -1537,6 +1538,20 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
||||
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeInvalidScopeErrorQuery),
|
||||
wantBodyString: "",
|
||||
},
|
||||
{
|
||||
name: "form_post page is used to send errors to client using OIDC upstream browser flow with response_mode=form_post",
|
||||
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(upstreamOIDCIdentityProviderBuilder().Build()),
|
||||
generateCSRF: happyCSRFGenerator,
|
||||
generatePKCE: happyPKCEGenerator,
|
||||
generateNonce: happyNonceGenerator,
|
||||
stateEncoder: happyStateEncoder,
|
||||
cookieEncoder: happyCookieEncoder,
|
||||
method: http.MethodGet,
|
||||
path: modifiedHappyGetRequestPath(map[string]string{"response_mode": "form_post", "scope": "openid profile email tuna"}),
|
||||
wantStatus: http.StatusOK,
|
||||
wantContentType: htmlContentType,
|
||||
wantBodyRegex: `<input type="hidden" name="encoded_params" value="error=invalid_scope&error_description=The+requested+scope+is+invalid`,
|
||||
},
|
||||
{
|
||||
name: "downstream scopes do not match what is configured for client using LDAP upstream",
|
||||
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithLDAP(&upstreamLDAPIdentityProvider),
|
||||
@@ -2578,7 +2593,9 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
||||
|
||||
require.Equal(t, test.wantStatus, rsp.Code)
|
||||
testutil.RequireEqualContentType(t, rsp.Header().Get("Content-Type"), test.wantContentType)
|
||||
testutil.RequireSecurityHeadersWithoutCustomCSPs(t, rsp)
|
||||
|
||||
// Use form_post page's CSPs because sometimes errors are sent to the client via the form_post page.
|
||||
testutil.RequireSecurityHeadersWithFormPostPageCSPs(t, rsp)
|
||||
|
||||
if test.wantPasswordGrantCall != nil {
|
||||
test.wantPasswordGrantCall.args.Ctx = reqContext
|
||||
@@ -2645,6 +2662,8 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
||||
default:
|
||||
t.Errorf("unexpected response code: %v", code)
|
||||
}
|
||||
case test.wantBodyRegex != "":
|
||||
require.Regexp(t, test.wantBodyRegex, rsp.Body.String())
|
||||
default:
|
||||
require.Equal(t, test.wantBodyString, rsp.Body.String())
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ var (
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>body{font-family:metropolis-light,Helvetica,sans-serif}h1{font-size:20px}.state{position:absolute;top:100px;left:50%;width:400px;height:80px;margin-top:-40px;margin-left:-200px;font-size:14px;line-height:24px}button{margin:-10px;padding:10px;text-align:left;width:100%;display:inline;border:none;background:0 0;cursor:pointer;transition:all .1s}button:hover{background-color:#eee;transform:scale(1.01)}button:active{background-color:#ddd;transform:scale(.99)}code{display:block;word-wrap:break-word;word-break:break-all;font-size:12px;font-family:monospace;color:#333}.copy-icon{float:left;width:36px;height:36px;margin-top:-3px;margin-right:10px;background-size:contain;background-repeat:no-repeat;background-image:url("data:image/svg+xml,%3Csvg version='1.1' width='36' height='36' viewBox='0 0 36 36' preserveAspectRatio='xMidYMid meet' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Ctitle%3Ecopy-to-clipboard-line%3C/title%3E%3Cpath d='M22.6,4H21.55a3.89,3.89,0,0,0-7.31,0H13.4A2.41,2.41,0,0,0,11,6.4V10H25V6.4A2.41,2.41,0,0,0,22.6,4ZM23,8H13V6.25A.25.25,0,0,1,13.25,6h2.69l.12-1.11A1.24,1.24,0,0,1,16.61,4a2,2,0,0,1,3.15,1.18l.09.84h2.9a.25.25,0,0,1,.25.25Z' class='clr-i-outline clr-i-outline-path-1'%3E%3C/path%3E%3Cpath d='M33.25,18.06H21.33l2.84-2.83a1,1,0,1,0-1.42-1.42L17.5,19.06l5.25,5.25a1,1,0,0,0,.71.29,1,1,0,0,0,.71-1.7l-2.84-2.84H33.25a1,1,0,0,0,0-2Z' class='clr-i-outline clr-i-outline-path-2'%3E%3C/path%3E%3Cpath d='M29,16h2V6.68A1.66,1.66,0,0,0,29.35,5H27.08V7H29Z' class='clr-i-outline clr-i-outline-path-3'%3E%3C/path%3E%3Cpath d='M29,31H7V7H9V5H6.64A1.66,1.66,0,0,0,5,6.67V31.32A1.66,1.66,0,0,0,6.65,33H29.36A1.66,1.66,0,0,0,31,31.33V22.06H29Z' class='clr-i-outline clr-i-outline-path-4'%3E%3C/path%3E%3Crect x='0' y='0' width='36' height='36' fill-opacity='0'/%3E%3C/svg%3E")}@keyframes loader{to{transform:rotate(360deg)}}#loading{content:'';box-sizing:border-box;width:80px;height:80px;margin-top:-40px;margin-left:-40px;border-radius:50%;border:2px solid #fff;border-top-color:#1b3951;animation:loader .6s linear infinite}</style>
|
||||
<script>window.onload=()=>{const e=t=>{Array.from(document.querySelectorAll(".state")).forEach(e=>e.hidden=!0);const e=document.getElementById(t);e.hidden=!1,document.title=e.dataset.title,document.getElementById("favicon").setAttribute("href","data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>"+e.dataset.favicon+"</text></svg>")};e("loading"),window.history.replaceState(null,'',"./"),document.getElementById("manual-copy-button").onclick=()=>{const e=document.getElementById("manual-copy-button").innerText;navigator.clipboard.writeText(e).then(()=>console.info("copied authorization code "+e+" to clipboard")).catch(t=>console.error("failed to copy code "+e+" to clipboard: "+t))};const n=setTimeout(()=>e("manual"),2e3),t=document.forms[0].elements;fetch(t.redirect_uri.value,{method:"POST",mode:"no-cors",headers:{'Content-Type':"application/x-www-form-urlencoded;charset=UTF-8"},body:t.encoded_params.value}).then(t=>{clearTimeout(n),e("success")}).catch(()=>e("manual"))}</script>
|
||||
<script>window.onload=()=>{const e=e=>{Array.from(document.querySelectorAll(".state")).forEach(e=>e.hidden=!0);const t=document.getElementById(e);t.hidden=!1,document.title=t.dataset.title,document.getElementById("favicon").setAttribute("href","data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>"+t.dataset.favicon+"</text></svg>")};e("loading"),window.history.replaceState(null,"","./"),document.getElementById("manual-copy-button").onclick=()=>{const e=document.getElementById("manual-copy-button").innerText;navigator.clipboard.writeText(e).then(()=>console.info("copied authorization code "+e+" to clipboard")).catch(t=>console.error("failed to copy code "+e+" to clipboard: "+t))};const n=setTimeout(()=>e("manual"),2e3),t=document.forms[0].elements;fetch(t.redirect_uri.value,{method:"POST",mode:"no-cors",headers:{"Content-Type":"application/x-www-form-urlencoded;charset=UTF-8"},body:t.encoded_params.value}).then(t=>{clearTimeout(n),e("success")}).catch(()=>e("manual"))}</script>
|
||||
<link id="favicon" rel="icon"/>
|
||||
</head>
|
||||
<body>
|
||||
@@ -61,7 +61,7 @@ var (
|
||||
// It's okay if this changes in the future, but this gives us a chance to eyeball the formatting.
|
||||
// Our browser-based integration tests should find any incompatibilities.
|
||||
testExpectedCSP = `default-src 'none'; ` +
|
||||
`script-src 'sha256-1LS3gM7wTGc0dYXZiqW6HK1LHk74YSG8GsJBC/j1/i8='; ` +
|
||||
`script-src 'sha256-uIWC0J7wd7tWtcXmugZCkKsQpqOsQzqBI/mfQMtUde0='; ` +
|
||||
`style-src 'sha256-kXh6OrB2z7wkx7v1N3ay9deQhV5edwuogARaUtvNYN4='; ` +
|
||||
`img-src data:; ` +
|
||||
`connect-src *; ` +
|
||||
|
||||
Reference in New Issue
Block a user