mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-09-05 23:57:12 +00:00
WIP towards revoking upstream refresh tokens during GC
- Discover the revocation endpoint of the upstream provider in oidc_upstream_watcher.go and save it into the cache for future use by the garbage collector controller - Adds RevokeRefreshToken to UpstreamOIDCIdentityProviderI - Implements the production version of RevokeRefreshToken - Implements test doubles for RevokeRefreshToken for future use in garbage collector's unit tests - Prefactors the crud and session storage types for future use in the garbage collector controller - See remaining TODOs in garbage_collector.go
This commit is contained in:
@@ -351,6 +351,44 @@ func (c *oidcWatcherController) validateIssuer(ctx context.Context, upstream *v1
|
||||
c.validatorCache.putProvider(&upstream.Spec, discoveredProvider, httpClient)
|
||||
}
|
||||
|
||||
// Get the revocation endpoint, if there is one. Many providers do not offer a revocation endpoint.
|
||||
var additionalDiscoveryClaims struct {
|
||||
// "revocation_endpoint" is specified by https://datatracker.ietf.org/doc/html/rfc8414#section-2
|
||||
RevocationEndpoint string `json:"revocation_endpoint"`
|
||||
}
|
||||
if err := discoveredProvider.Claims(&additionalDiscoveryClaims); err != nil {
|
||||
// This shouldn't actually happen because the above call to NewProvider() would have already returned this error.
|
||||
return &v1alpha1.Condition{
|
||||
Type: typeOIDCDiscoverySucceeded,
|
||||
Status: v1alpha1.ConditionFalse,
|
||||
Reason: reasonInvalidResponse,
|
||||
Message: fmt.Sprintf("failed to unmarshal OIDC discovery response from %q:\n%s", upstream.Spec.Issuer, truncateMostLongErr(err)),
|
||||
}
|
||||
}
|
||||
if additionalDiscoveryClaims.RevocationEndpoint != "" {
|
||||
// Found a revocation URL. Try to parse it.
|
||||
revocationURL, err := url.Parse(additionalDiscoveryClaims.RevocationEndpoint)
|
||||
if err != nil {
|
||||
return &v1alpha1.Condition{
|
||||
Type: typeOIDCDiscoverySucceeded,
|
||||
Status: v1alpha1.ConditionFalse,
|
||||
Reason: reasonInvalidResponse,
|
||||
Message: fmt.Sprintf("failed to parse revocation endpoint URL: %v", err),
|
||||
}
|
||||
}
|
||||
// Don't want to send refresh tokens to an insecure revocation endpoint, so require that it use https.
|
||||
if revocationURL.Scheme != "https" {
|
||||
return &v1alpha1.Condition{
|
||||
Type: typeOIDCDiscoverySucceeded,
|
||||
Status: v1alpha1.ConditionFalse,
|
||||
Reason: reasonInvalidResponse,
|
||||
Message: fmt.Sprintf(`revocation endpoint URL scheme must be "https", not %q`, revocationURL.Scheme),
|
||||
}
|
||||
}
|
||||
// Remember the URL for later use.
|
||||
result.RevocationURL = revocationURL
|
||||
}
|
||||
|
||||
// Parse out and validate the discovered authorize endpoint.
|
||||
authURL, err := url.Parse(discoveredProvider.Endpoint().AuthURL)
|
||||
if err != nil {
|
||||
|
||||
+218
-29
@@ -114,6 +114,8 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
testIssuerCABase64 := base64.StdEncoding.EncodeToString([]byte(testIssuerCA))
|
||||
testIssuerAuthorizeURL, err := url.Parse("https://example.com/authorize")
|
||||
require.NoError(t, err)
|
||||
testIssuerRevocationURL, err := url.Parse("https://example.com/revoke")
|
||||
require.NoError(t, err)
|
||||
|
||||
wrongCA, err := certauthority.New("foo", time.Hour)
|
||||
require.NoError(t, err)
|
||||
@@ -151,7 +153,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
inputSecrets []runtime.Object
|
||||
wantErr string
|
||||
wantLogs []string
|
||||
wantResultingCache []provider.UpstreamOIDCIdentityProviderI
|
||||
wantResultingCache []*oidctestutil.TestUpstreamOIDCIdentityProvider
|
||||
wantResultingUpstreams []v1alpha1.OIDCIdentityProvider
|
||||
}{
|
||||
{
|
||||
@@ -175,7 +177,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="secret \"test-client-secret\" not found" "name"="test-name" "namespace"="test-namespace" "reason"="SecretNotFound" "type"="ClientCredentialsValid"`,
|
||||
},
|
||||
wantResultingCache: []provider.UpstreamOIDCIdentityProviderI{},
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{},
|
||||
wantResultingUpstreams: []v1alpha1.OIDCIdentityProvider{{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName},
|
||||
Status: v1alpha1.OIDCIdentityProviderStatus{
|
||||
@@ -222,7 +224,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="referenced Secret \"test-client-secret\" has wrong type \"some-other-type\" (should be \"secrets.pinniped.dev/oidc-client\")" "name"="test-name" "namespace"="test-namespace" "reason"="SecretWrongType" "type"="ClientCredentialsValid"`,
|
||||
},
|
||||
wantResultingCache: []provider.UpstreamOIDCIdentityProviderI{},
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{},
|
||||
wantResultingUpstreams: []v1alpha1.OIDCIdentityProvider{{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName},
|
||||
Status: v1alpha1.OIDCIdentityProviderStatus{
|
||||
@@ -268,7 +270,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="referenced Secret \"test-client-secret\" is missing required keys [\"clientID\" \"clientSecret\"]" "name"="test-name" "namespace"="test-namespace" "reason"="SecretMissingKeys" "type"="ClientCredentialsValid"`,
|
||||
},
|
||||
wantResultingCache: []provider.UpstreamOIDCIdentityProviderI{},
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{},
|
||||
wantResultingUpstreams: []v1alpha1.OIDCIdentityProvider{{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName},
|
||||
Status: v1alpha1.OIDCIdentityProviderStatus{
|
||||
@@ -317,7 +319,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="spec.certificateAuthorityData is invalid: illegal base64 data at input byte 7" "name"="test-name" "namespace"="test-namespace" "reason"="InvalidTLSConfig" "type"="OIDCDiscoverySucceeded"`,
|
||||
},
|
||||
wantResultingCache: []provider.UpstreamOIDCIdentityProviderI{},
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{},
|
||||
wantResultingUpstreams: []v1alpha1.OIDCIdentityProvider{{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName},
|
||||
Status: v1alpha1.OIDCIdentityProviderStatus{
|
||||
@@ -366,7 +368,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="spec.certificateAuthorityData is invalid: no certificates found" "name"="test-name" "namespace"="test-namespace" "reason"="InvalidTLSConfig" "type"="OIDCDiscoverySucceeded"`,
|
||||
},
|
||||
wantResultingCache: []provider.UpstreamOIDCIdentityProviderI{},
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{},
|
||||
wantResultingUpstreams: []v1alpha1.OIDCIdentityProvider{{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName},
|
||||
Status: v1alpha1.OIDCIdentityProviderStatus{
|
||||
@@ -413,7 +415,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="failed to perform OIDC discovery against \"invalid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\":\nGet \"invalid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee/.well-known/openid-configuration\": unsupported protocol [truncated 9 chars]" "name"="test-name" "namespace"="test-namespace" "reason"="Unreachable" "type"="OIDCDiscoverySucceeded"`,
|
||||
},
|
||||
wantResultingCache: []provider.UpstreamOIDCIdentityProviderI{},
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{},
|
||||
wantResultingUpstreams: []v1alpha1.OIDCIdentityProvider{{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName},
|
||||
Status: v1alpha1.OIDCIdentityProviderStatus{
|
||||
@@ -462,7 +464,7 @@ Get "invalid-url-that-is-really-really-long-nanananananananannanananan-batman-na
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="failed to perform OIDC discovery against \"` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\":\nGet \"` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee/.well-known/openid-configuration\": x509: certificate signed by unknown authority" "name"="test-name" "namespace"="test-namespace" "reason"="Unreachable" "type"="OIDCDiscoverySucceeded"`,
|
||||
},
|
||||
wantResultingCache: []provider.UpstreamOIDCIdentityProviderI{},
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{},
|
||||
wantResultingUpstreams: []v1alpha1.OIDCIdentityProvider{{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName},
|
||||
Status: v1alpha1.OIDCIdentityProviderStatus{
|
||||
@@ -510,7 +512,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="failed to parse authorization endpoint URL: parse \"%\": invalid URL escape \"%\"" "name"="test-name" "namespace"="test-namespace" "reason"="InvalidResponse" "type"="OIDCDiscoverySucceeded"`,
|
||||
},
|
||||
wantResultingCache: []provider.UpstreamOIDCIdentityProviderI{},
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{},
|
||||
wantResultingUpstreams: []v1alpha1.OIDCIdentityProvider{{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName},
|
||||
Status: v1alpha1.OIDCIdentityProviderStatus{
|
||||
@@ -535,6 +537,53 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "issuer returns invalid revocation URL",
|
||||
inputUpstreams: []runtime.Object{&v1alpha1.OIDCIdentityProvider{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName},
|
||||
Spec: v1alpha1.OIDCIdentityProviderSpec{
|
||||
Issuer: testIssuerURL + "/invalid-revocation-url",
|
||||
TLS: &v1alpha1.TLSSpec{CertificateAuthorityData: testIssuerCABase64},
|
||||
Client: v1alpha1.OIDCClient{SecretName: testSecretName},
|
||||
},
|
||||
}},
|
||||
inputSecrets: []runtime.Object{&corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName},
|
||||
Type: "secrets.pinniped.dev/oidc-client",
|
||||
Data: testValidSecretData,
|
||||
}},
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="failed to parse revocation endpoint URL: parse \"%\": invalid URL escape \"%\"" "reason"="InvalidResponse" "status"="False" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="failed to parse revocation endpoint URL: parse \"%\": invalid URL escape \"%\"" "name"="test-name" "namespace"="test-namespace" "reason"="InvalidResponse" "type"="OIDCDiscoverySucceeded"`,
|
||||
},
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{},
|
||||
wantResultingUpstreams: []v1alpha1.OIDCIdentityProvider{{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName},
|
||||
Status: v1alpha1.OIDCIdentityProviderStatus{
|
||||
Phase: "Error",
|
||||
Conditions: []v1alpha1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Status: "True",
|
||||
LastTransitionTime: now,
|
||||
Reason: "Success",
|
||||
Message: "loaded client credentials",
|
||||
},
|
||||
{
|
||||
Type: "OIDCDiscoverySucceeded",
|
||||
Status: "False",
|
||||
LastTransitionTime: now,
|
||||
Reason: "InvalidResponse",
|
||||
Message: `failed to parse revocation endpoint URL: parse "%": invalid URL escape "%"`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "issuer returns insecure authorize URL",
|
||||
inputUpstreams: []runtime.Object{&v1alpha1.OIDCIdentityProvider{
|
||||
@@ -557,7 +606,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="authorization endpoint URL scheme must be \"https\", not \"http\"" "name"="test-name" "namespace"="test-namespace" "reason"="InvalidResponse" "type"="OIDCDiscoverySucceeded"`,
|
||||
},
|
||||
wantResultingCache: []provider.UpstreamOIDCIdentityProviderI{},
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{},
|
||||
wantResultingUpstreams: []v1alpha1.OIDCIdentityProvider{{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName},
|
||||
Status: v1alpha1.OIDCIdentityProviderStatus{
|
||||
@@ -582,6 +631,53 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "issuer returns insecure revocation URL",
|
||||
inputUpstreams: []runtime.Object{&v1alpha1.OIDCIdentityProvider{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName},
|
||||
Spec: v1alpha1.OIDCIdentityProviderSpec{
|
||||
Issuer: testIssuerURL + "/insecure-revocation-url",
|
||||
TLS: &v1alpha1.TLSSpec{CertificateAuthorityData: testIssuerCABase64},
|
||||
Client: v1alpha1.OIDCClient{SecretName: testSecretName},
|
||||
},
|
||||
}},
|
||||
inputSecrets: []runtime.Object{&corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName},
|
||||
Type: "secrets.pinniped.dev/oidc-client",
|
||||
Data: testValidSecretData,
|
||||
}},
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="revocation endpoint URL scheme must be \"https\", not \"http\"" "reason"="InvalidResponse" "status"="False" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="revocation endpoint URL scheme must be \"https\", not \"http\"" "name"="test-name" "namespace"="test-namespace" "reason"="InvalidResponse" "type"="OIDCDiscoverySucceeded"`,
|
||||
},
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{},
|
||||
wantResultingUpstreams: []v1alpha1.OIDCIdentityProvider{{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName},
|
||||
Status: v1alpha1.OIDCIdentityProviderStatus{
|
||||
Phase: "Error",
|
||||
Conditions: []v1alpha1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Status: "True",
|
||||
LastTransitionTime: now,
|
||||
Reason: "Success",
|
||||
Message: "loaded client credentials",
|
||||
},
|
||||
{
|
||||
Type: "OIDCDiscoverySucceeded",
|
||||
Status: "False",
|
||||
LastTransitionTime: now,
|
||||
Reason: "InvalidResponse",
|
||||
Message: `revocation endpoint URL scheme must be "https", not "http"`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "upstream with error becomes valid",
|
||||
inputUpstreams: []runtime.Object{&v1alpha1.OIDCIdentityProvider{
|
||||
@@ -614,11 +710,12 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="discovered issuer configuration" "reason"="Success" "status"="True" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
},
|
||||
wantResultingCache: []provider.UpstreamOIDCIdentityProviderI{
|
||||
&oidctestutil.TestUpstreamOIDCIdentityProvider{
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{
|
||||
{
|
||||
Name: testName,
|
||||
ClientID: testClientID,
|
||||
AuthorizationURL: *testIssuerAuthorizeURL,
|
||||
RevocationURL: testIssuerRevocationURL,
|
||||
Scopes: append(testExpectedScopes, "xyz"), // includes openid only once
|
||||
UsernameClaim: testUsernameClaim,
|
||||
GroupsClaim: testGroupsClaim,
|
||||
@@ -668,11 +765,67 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="discovered issuer configuration" "reason"="Success" "status"="True" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
},
|
||||
wantResultingCache: []provider.UpstreamOIDCIdentityProviderI{
|
||||
&oidctestutil.TestUpstreamOIDCIdentityProvider{
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{
|
||||
{
|
||||
Name: testName,
|
||||
ClientID: testClientID,
|
||||
AuthorizationURL: *testIssuerAuthorizeURL,
|
||||
RevocationURL: testIssuerRevocationURL,
|
||||
Scopes: testDefaultExpectedScopes,
|
||||
UsernameClaim: testUsernameClaim,
|
||||
GroupsClaim: testGroupsClaim,
|
||||
AllowPasswordGrant: false,
|
||||
AdditionalAuthcodeParams: map[string]string{},
|
||||
ResourceUID: testUID,
|
||||
},
|
||||
},
|
||||
wantResultingUpstreams: []v1alpha1.OIDCIdentityProvider{{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testUID},
|
||||
Status: v1alpha1.OIDCIdentityProviderStatus{
|
||||
Phase: "Ready",
|
||||
Conditions: []v1alpha1.Condition{
|
||||
{Type: "AdditionalAuthorizeParametersValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "additionalAuthorizeParameters parameter names are allowed", ObservedGeneration: 1234},
|
||||
{Type: "ClientCredentialsValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials", ObservedGeneration: 1234},
|
||||
{Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234},
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "existing valid upstream with no revocation endpoint in the discovery document",
|
||||
inputUpstreams: []runtime.Object{&v1alpha1.OIDCIdentityProvider{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testUID},
|
||||
Spec: v1alpha1.OIDCIdentityProviderSpec{
|
||||
Issuer: testIssuerURL + "/valid-without-revocation",
|
||||
TLS: &v1alpha1.TLSSpec{CertificateAuthorityData: testIssuerCABase64},
|
||||
Client: v1alpha1.OIDCClient{SecretName: testSecretName},
|
||||
Claims: v1alpha1.OIDCClaims{Groups: testGroupsClaim, Username: testUsernameClaim},
|
||||
},
|
||||
Status: v1alpha1.OIDCIdentityProviderStatus{
|
||||
Phase: "Ready",
|
||||
Conditions: []v1alpha1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidConditionEarlier,
|
||||
{Type: "ClientCredentialsValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials"},
|
||||
{Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration"},
|
||||
},
|
||||
},
|
||||
}},
|
||||
inputSecrets: []runtime.Object{&corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName},
|
||||
Type: "secrets.pinniped.dev/oidc-client",
|
||||
Data: testValidSecretData,
|
||||
}},
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="discovered issuer configuration" "reason"="Success" "status"="True" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
},
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{
|
||||
{
|
||||
Name: testName,
|
||||
ClientID: testClientID,
|
||||
AuthorizationURL: *testIssuerAuthorizeURL,
|
||||
RevocationURL: nil, // no revocation URL is set in the cached provider because none was returned by discovery
|
||||
Scopes: testDefaultExpectedScopes,
|
||||
UsernameClaim: testUsernameClaim,
|
||||
GroupsClaim: testGroupsClaim,
|
||||
@@ -725,11 +878,12 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="discovered issuer configuration" "reason"="Success" "status"="True" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
},
|
||||
wantResultingCache: []provider.UpstreamOIDCIdentityProviderI{
|
||||
&oidctestutil.TestUpstreamOIDCIdentityProvider{
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{
|
||||
{
|
||||
Name: testName,
|
||||
ClientID: testClientID,
|
||||
AuthorizationURL: *testIssuerAuthorizeURL,
|
||||
RevocationURL: testIssuerRevocationURL,
|
||||
Scopes: testExpectedScopes,
|
||||
UsernameClaim: testUsernameClaim,
|
||||
GroupsClaim: testGroupsClaim,
|
||||
@@ -784,11 +938,12 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="discovered issuer configuration" "reason"="Success" "status"="True" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
},
|
||||
wantResultingCache: []provider.UpstreamOIDCIdentityProviderI{
|
||||
&oidctestutil.TestUpstreamOIDCIdentityProvider{
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{
|
||||
{
|
||||
Name: testName,
|
||||
ClientID: testClientID,
|
||||
AuthorizationURL: *testIssuerAuthorizeURL,
|
||||
RevocationURL: testIssuerRevocationURL,
|
||||
Scopes: testExpectedScopes, // does not include the default scopes
|
||||
UsernameClaim: testUsernameClaim,
|
||||
GroupsClaim: testGroupsClaim,
|
||||
@@ -845,7 +1000,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="the following additionalAuthorizeParameters are not allowed: response_type,scope,client_id,state,nonce,code_challenge,code_challenge_method,redirect_uri,hd" "reason"="DisallowedParameterName" "status"="False" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="the following additionalAuthorizeParameters are not allowed: response_type,scope,client_id,state,nonce,code_challenge,code_challenge_method,redirect_uri,hd" "name"="test-name" "namespace"="test-namespace" "reason"="DisallowedParameterName" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
},
|
||||
wantResultingCache: []provider.UpstreamOIDCIdentityProviderI{},
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{},
|
||||
wantResultingUpstreams: []v1alpha1.OIDCIdentityProvider{{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testUID},
|
||||
Status: v1alpha1.OIDCIdentityProviderStatus{
|
||||
@@ -883,7 +1038,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="failed to perform OIDC discovery against \"` + testIssuerURL + `/ends-with-slash\":\noidc: issuer did not match the issuer returned by provider, expected \"` + testIssuerURL + `/ends-with-slash\" got \"` + testIssuerURL + `/ends-with-slash/\"" "name"="test-name" "namespace"="test-namespace" "reason"="Unreachable" "type"="OIDCDiscoverySucceeded"`,
|
||||
},
|
||||
wantResultingCache: []provider.UpstreamOIDCIdentityProviderI{},
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{},
|
||||
wantResultingUpstreams: []v1alpha1.OIDCIdentityProvider{{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName},
|
||||
Status: v1alpha1.OIDCIdentityProviderStatus{
|
||||
@@ -932,7 +1087,7 @@ oidc: issuer did not match the issuer returned by provider, expected "` + testIs
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="failed to perform OIDC discovery against \"` + testIssuerURL + `/\":\noidc: issuer did not match the issuer returned by provider, expected \"` + testIssuerURL + `/\" got \"` + testIssuerURL + `\"" "name"="test-name" "namespace"="test-namespace" "reason"="Unreachable" "type"="OIDCDiscoverySucceeded"`,
|
||||
},
|
||||
wantResultingCache: []provider.UpstreamOIDCIdentityProviderI{},
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{},
|
||||
wantResultingUpstreams: []v1alpha1.OIDCIdentityProvider{{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName},
|
||||
Status: v1alpha1.OIDCIdentityProviderStatus{
|
||||
@@ -1010,6 +1165,7 @@ oidc: issuer did not match the issuer returned by provider, expected "` + testIs
|
||||
require.Equal(t, tt.wantResultingCache[i].AllowsPasswordGrant(), actualIDP.AllowsPasswordGrant())
|
||||
require.Equal(t, tt.wantResultingCache[i].GetAdditionalAuthcodeParams(), actualIDP.GetAdditionalAuthcodeParams())
|
||||
require.Equal(t, tt.wantResultingCache[i].GetResourceUID(), actualIDP.GetResourceUID())
|
||||
require.Equal(t, tt.wantResultingCache[i].GetRevocationURL(), actualIDP.GetRevocationURL())
|
||||
require.ElementsMatch(t, tt.wantResultingCache[i].GetScopes(), actualIDP.GetScopes())
|
||||
|
||||
// We always want to use the proxy from env on these clients, so although the following assertions
|
||||
@@ -1067,18 +1223,30 @@ func newTestIssuer(t *testing.T) (string, string) {
|
||||
caBundlePEM, testURL := testutil.TLSTestServer(t, mux.ServeHTTP)
|
||||
|
||||
type providerJSON struct {
|
||||
Issuer string `json:"issuer"`
|
||||
AuthURL string `json:"authorization_endpoint"`
|
||||
TokenURL string `json:"token_endpoint"`
|
||||
JWKSURL string `json:"jwks_uri"`
|
||||
Issuer string `json:"issuer"`
|
||||
AuthURL string `json:"authorization_endpoint"`
|
||||
TokenURL string `json:"token_endpoint"`
|
||||
RevocationURL string `json:"revocation_endpoint,omitempty"`
|
||||
JWKSURL string `json:"jwks_uri"`
|
||||
}
|
||||
|
||||
// At the root of the server, serve an issuer with a valid discovery response.
|
||||
mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(&providerJSON{
|
||||
Issuer: testURL,
|
||||
AuthURL: "https://example.com/authorize",
|
||||
Issuer: testURL,
|
||||
AuthURL: "https://example.com/authorize",
|
||||
RevocationURL: "https://example.com/revoke",
|
||||
})
|
||||
})
|
||||
|
||||
// At "/valid-without-revocation", serve an issuer with a valid discovery response which does not have a revocation endpoint.
|
||||
mux.HandleFunc("/valid-without-revocation/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(&providerJSON{
|
||||
Issuer: testURL + "/valid-without-revocation",
|
||||
AuthURL: "https://example.com/authorize",
|
||||
RevocationURL: "", // none
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1091,6 +1259,16 @@ func newTestIssuer(t *testing.T) (string, string) {
|
||||
})
|
||||
})
|
||||
|
||||
// At "/invalid-revocation-url", serve an issuer that returns an invalid revocation URL (not parseable).
|
||||
mux.HandleFunc("/invalid-revocation-url/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(&providerJSON{
|
||||
Issuer: testURL + "/invalid-revocation-url",
|
||||
AuthURL: "https://example.com/authorize",
|
||||
RevocationURL: "%",
|
||||
})
|
||||
})
|
||||
|
||||
// At "/insecure", serve an issuer that returns an insecure authorization URL (not https://).
|
||||
mux.HandleFunc("/insecure/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
@@ -1100,6 +1278,16 @@ func newTestIssuer(t *testing.T) (string, string) {
|
||||
})
|
||||
})
|
||||
|
||||
// At "/insecure", serve an issuer that returns an insecure authorization URL (not https://).
|
||||
mux.HandleFunc("/insecure-revocation-url/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(&providerJSON{
|
||||
Issuer: testURL + "/insecure-revocation-url",
|
||||
AuthURL: "https://example.com/authorize",
|
||||
RevocationURL: "http://example.com/revoke",
|
||||
})
|
||||
})
|
||||
|
||||
// handle the four issuer with trailing slash configs
|
||||
|
||||
// valid case in= out=
|
||||
@@ -1109,8 +1297,9 @@ func newTestIssuer(t *testing.T) (string, string) {
|
||||
mux.HandleFunc("/ends-with-slash/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(&providerJSON{
|
||||
Issuer: testURL + "/ends-with-slash/",
|
||||
AuthURL: "https://example.com/authorize",
|
||||
Issuer: testURL + "/ends-with-slash/",
|
||||
AuthURL: "https://example.com/authorize",
|
||||
RevocationURL: "https://example.com/revoke",
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user