mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-07-20 23:12:40 +00:00
Update more unit tests for configurable token lifetimes
This commit is contained in:
@@ -200,7 +200,7 @@ func (s *secretsStorage) toSecret(signature, resourceVersion string, data JSON,
|
||||
labelsToAdd[SecretLabelKey] = s.resource // make it easier to find this stuff via kubectl
|
||||
|
||||
var annotations map[string]string
|
||||
if lifetime > 0 && s.clock != nil {
|
||||
if lifetime > 0 {
|
||||
annotations = map[string]string{
|
||||
SecretLifetimeAnnotationKey: s.clock().Add(lifetime).UTC().Format(SecretLifetimeAnnotationDateFormat),
|
||||
}
|
||||
|
||||
@@ -1235,7 +1235,6 @@ func TestStorage(t *testing.T) {
|
||||
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
|
||||
|
||||
data := &testJSON{Data: "create-and-get"}
|
||||
// TODO: Note that this test will pass with just about any value for lifetime
|
||||
rv1, err := storage.Create(ctx, signature, data, nil, nil, 0) // 0 == infinity
|
||||
require.Empty(t, rv1) // fake client does not set this
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
coretesting "k8s.io/client-go/testing"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
configv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1"
|
||||
supervisorfake "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/fake"
|
||||
@@ -180,7 +181,7 @@ func TestClientManager(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "find a valid dynamic client",
|
||||
name: "find a valid dynamic client without an ID token lifetime configuration",
|
||||
oidcClients: []*configv1alpha1.OIDCClient{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testUID},
|
||||
@@ -188,6 +189,7 @@ func TestClientManager(t *testing.T) {
|
||||
AllowedGrantTypes: []configv1alpha1.GrantType{"authorization_code", "urn:ietf:params:oauth:grant-type:token-exchange", "refresh_token"},
|
||||
AllowedScopes: []configv1alpha1.Scope{"openid", "offline_access", "pinniped:request-audience", "username", "groups"},
|
||||
AllowedRedirectURIs: []configv1alpha1.RedirectURI{"http://localhost:80", "https://foobar.com/callback"},
|
||||
TokenLifetimes: configv1alpha1.OIDCClientTokenLifetimes{IDTokenSeconds: nil},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -203,24 +205,49 @@ func TestClientManager(t *testing.T) {
|
||||
require.IsType(t, &Client{}, got)
|
||||
c := got.(*Client)
|
||||
|
||||
require.Equal(t, testName, c.GetID())
|
||||
require.Nil(t, c.GetHashedSecret())
|
||||
require.Len(t, c.GetRotatedHashes(), 2)
|
||||
require.Equal(t, testutil.HashedPassword1AtSupervisorMinCost, string(c.GetRotatedHashes()[0]))
|
||||
require.Equal(t, testutil.HashedPassword2AtSupervisorMinCost, string(c.GetRotatedHashes()[1]))
|
||||
require.Equal(t, []string{"http://localhost:80", "https://foobar.com/callback"}, c.GetRedirectURIs())
|
||||
require.Equal(t, fosite.Arguments{"authorization_code", "urn:ietf:params:oauth:grant-type:token-exchange", "refresh_token"}, c.GetGrantTypes())
|
||||
require.Equal(t, fosite.Arguments{"code"}, c.GetResponseTypes())
|
||||
require.Equal(t, fosite.Arguments{"openid", "offline_access", "pinniped:request-audience", "username", "groups"}, c.GetScopes())
|
||||
require.False(t, c.IsPublic())
|
||||
require.Nil(t, c.GetAudience())
|
||||
require.Nil(t, c.GetRequestURIs())
|
||||
require.Nil(t, c.GetJSONWebKeys())
|
||||
require.Equal(t, "", c.GetJSONWebKeysURI())
|
||||
require.Equal(t, "", c.GetRequestObjectSigningAlgorithm())
|
||||
require.Equal(t, "client_secret_basic", c.GetTokenEndpointAuthMethod())
|
||||
require.Equal(t, "RS256", c.GetTokenEndpointAuthSigningAlgorithm())
|
||||
require.Equal(t, []fosite.ResponseModeType{"", "query"}, c.GetResponseModes())
|
||||
requireDynamicOIDCClient(t, c,
|
||||
testName,
|
||||
[]string{testutil.HashedPassword1AtSupervisorMinCost, testutil.HashedPassword2AtSupervisorMinCost},
|
||||
fosite.Arguments{"authorization_code", "urn:ietf:params:oauth:grant-type:token-exchange", "refresh_token"},
|
||||
fosite.Arguments{"openid", "offline_access", "pinniped:request-audience", "username", "groups"},
|
||||
[]string{"http://localhost:80", "https://foobar.com/callback"},
|
||||
0*time.Second,
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "find a valid dynamic client with an ID token lifetime configuration",
|
||||
oidcClients: []*configv1alpha1.OIDCClient{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testUID},
|
||||
Spec: configv1alpha1.OIDCClientSpec{
|
||||
AllowedGrantTypes: []configv1alpha1.GrantType{"authorization_code", "refresh_token"},
|
||||
AllowedScopes: []configv1alpha1.Scope{"openid", "offline_access", "username", "groups"},
|
||||
AllowedRedirectURIs: []configv1alpha1.RedirectURI{"http://localhost:8080"},
|
||||
TokenLifetimes: configv1alpha1.OIDCClientTokenLifetimes{IDTokenSeconds: ptr.To[int32]((4242))},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "other-client", Generation: 1234, UID: testUID},
|
||||
},
|
||||
},
|
||||
secrets: []*corev1.Secret{
|
||||
testutil.OIDCClientSecretStorageSecretForUID(t, testNamespace, testUID, []string{testutil.HashedPassword2AtSupervisorMinCost, testutil.HashedPassword1AtSupervisorMinCost}),
|
||||
},
|
||||
run: func(t *testing.T, subject *ClientManager) {
|
||||
got, err := subject.GetClient(ctx, testName)
|
||||
require.NoError(t, err)
|
||||
require.IsType(t, &Client{}, got)
|
||||
c := got.(*Client)
|
||||
|
||||
requireDynamicOIDCClient(t, c,
|
||||
testName,
|
||||
[]string{testutil.HashedPassword2AtSupervisorMinCost, testutil.HashedPassword1AtSupervisorMinCost},
|
||||
fosite.Arguments{"authorization_code", "refresh_token"},
|
||||
fosite.Arguments{"openid", "offline_access", "username", "groups"},
|
||||
[]string{"http://localhost:8080"},
|
||||
4242*time.Second,
|
||||
)
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -279,6 +306,7 @@ func requireEqualsPinnipedCLI(t *testing.T, c *Client) {
|
||||
require.Equal(t, "none", c.GetTokenEndpointAuthMethod())
|
||||
require.Equal(t, "RS256", c.GetTokenEndpointAuthSigningAlgorithm())
|
||||
require.Equal(t, []fosite.ResponseModeType{"", "query", "form_post"}, c.GetResponseModes())
|
||||
require.Equal(t, 0*time.Second, c.IDTokenLifetimeConfiguration)
|
||||
|
||||
marshaled, err := json.Marshal(c)
|
||||
require.NoError(t, err)
|
||||
@@ -316,3 +344,39 @@ func requireEqualsPinnipedCLI(t *testing.T, c *Client) {
|
||||
"IDTokenLifetimeConfiguration": 0
|
||||
}`, string(marshaled))
|
||||
}
|
||||
|
||||
func requireDynamicOIDCClient(
|
||||
t *testing.T,
|
||||
c *Client,
|
||||
wantClientID string,
|
||||
wantHashes []string,
|
||||
wantGrantTypes fosite.Arguments,
|
||||
wantScopes fosite.Arguments,
|
||||
wantRedirectURIs []string,
|
||||
wantIDTokenLifetimeConfiguration time.Duration,
|
||||
) {
|
||||
require.Equal(t, wantClientID, c.GetID())
|
||||
|
||||
require.Len(t, c.GetRotatedHashes(), len(wantHashes))
|
||||
for i := range wantHashes {
|
||||
require.Equal(t, wantHashes[i], string(c.GetRotatedHashes()[i]))
|
||||
}
|
||||
|
||||
require.Equal(t, wantRedirectURIs, c.GetRedirectURIs())
|
||||
require.Equal(t, wantGrantTypes, c.GetGrantTypes())
|
||||
require.Equal(t, wantScopes, c.GetScopes())
|
||||
require.Equal(t, wantIDTokenLifetimeConfiguration, c.IDTokenLifetimeConfiguration)
|
||||
|
||||
// The following are always the same for all OIDCClients.
|
||||
require.Nil(t, c.GetHashedSecret())
|
||||
require.Equal(t, fosite.Arguments{"code"}, c.GetResponseTypes())
|
||||
require.False(t, c.IsPublic())
|
||||
require.Nil(t, c.GetAudience())
|
||||
require.Nil(t, c.GetRequestURIs())
|
||||
require.Nil(t, c.GetJSONWebKeys())
|
||||
require.Equal(t, "", c.GetJSONWebKeysURI())
|
||||
require.Equal(t, "", c.GetRequestObjectSigningAlgorithm())
|
||||
require.Equal(t, "client_secret_basic", c.GetTokenEndpointAuthMethod())
|
||||
require.Equal(t, "RS256", c.GetTokenEndpointAuthSigningAlgorithm())
|
||||
require.Equal(t, []fosite.ResponseModeType{"", "query"}, c.GetResponseModes())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package idtokenlifespan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/fosite"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestOverrideIDTokenLifespanInContext(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
defaultLifespan time.Duration
|
||||
overrideLifespan func(ctx context.Context) context.Context
|
||||
wantLifespan time.Duration
|
||||
}{
|
||||
{
|
||||
name: "does not override the context's default timeout",
|
||||
defaultLifespan: 10 * time.Second,
|
||||
overrideLifespan: func(baseCtx context.Context) context.Context {
|
||||
return baseCtx // no-op on the context
|
||||
},
|
||||
wantLifespan: 10 * time.Second,
|
||||
},
|
||||
{
|
||||
name: "overrides the context's default to be 42 seconds",
|
||||
defaultLifespan: 10 * time.Second,
|
||||
overrideLifespan: func(baseCtx context.Context) context.Context {
|
||||
return OverrideIDTokenLifespanInContext(baseCtx, 42*time.Second)
|
||||
},
|
||||
wantLifespan: 42 * time.Second,
|
||||
},
|
||||
{
|
||||
name: "overrides the context's default to be 42 minutes",
|
||||
defaultLifespan: 10 * time.Second,
|
||||
overrideLifespan: func(baseCtx context.Context) context.Context {
|
||||
return OverrideIDTokenLifespanInContext(baseCtx, 42*time.Minute)
|
||||
},
|
||||
wantLifespan: 42 * time.Minute,
|
||||
},
|
||||
{
|
||||
name: "somehow accidentally overrides the context's default timeout to be the wrong type",
|
||||
defaultLifespan: 10 * time.Second,
|
||||
overrideLifespan: func(baseCtx context.Context) context.Context {
|
||||
return context.WithValue(baseCtx, idTokenLifetimeOverrideKey, "this should be a duration but is a string")
|
||||
},
|
||||
wantLifespan: 10 * time.Second, // should ignore the illegal value and just return the default
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
baseConfig := fosite.Config{
|
||||
IDTokenLifespan: tt.defaultLifespan,
|
||||
}
|
||||
|
||||
contextAwareProvider := &contextAwareIDTokenLifespanProvider{
|
||||
DelegateConfig: &baseConfig,
|
||||
}
|
||||
|
||||
// Possibly override the default lifespan on the context.
|
||||
updatedCtx := tt.overrideLifespan(context.Background())
|
||||
|
||||
// Read the lifespan from the context.
|
||||
gotLifespan := contextAwareProvider.GetIDTokenLifespan(updatedCtx)
|
||||
require.Equal(t, tt.wantLifespan, gotLifespan)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/fosite"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.pinniped.dev/internal/federationdomain/clientregistry"
|
||||
)
|
||||
|
||||
func TestDefaultLifespans(t *testing.T) {
|
||||
c := DefaultOIDCTimeoutsConfiguration()
|
||||
|
||||
require.Equal(t, 90*time.Minute, c.UpstreamStateParamLifespan)
|
||||
require.Equal(t, 10*time.Minute, c.AuthorizeCodeLifespan)
|
||||
require.Equal(t, 2*time.Minute, c.AccessTokenLifespan)
|
||||
require.Equal(t, 2*time.Minute, c.IDTokenLifespan)
|
||||
require.Equal(t, 9*time.Hour, c.RefreshTokenLifespan)
|
||||
}
|
||||
|
||||
func TestStorageLifetimes(t *testing.T) {
|
||||
c := DefaultOIDCTimeoutsConfiguration()
|
||||
|
||||
// These are currently hard-coded.
|
||||
require.Equal(t, 9*time.Hour+10*time.Minute, c.AuthorizationCodeSessionStorageLifetime(nil))
|
||||
require.Equal(t, 11*time.Minute, c.PKCESessionStorageLifetime(nil))
|
||||
require.Equal(t, 11*time.Minute, c.OIDCSessionStorageLifetime(nil))
|
||||
require.Equal(t, 9*time.Hour+2*time.Minute, c.AccessTokenSessionStorageLifetime(nil))
|
||||
require.Equal(t, 9*time.Hour+2*time.Minute, c.RefreshTokenSessionStorageLifetime(nil))
|
||||
}
|
||||
|
||||
func TestOverrideDefaultAccessTokenLifespan(t *testing.T) {
|
||||
c := DefaultOIDCTimeoutsConfiguration()
|
||||
|
||||
// We are not yet overriding access token lifetimes.
|
||||
doOverride, newLifespan := c.OverrideDefaultAccessTokenLifespan(nil)
|
||||
require.Equal(t, false, doOverride)
|
||||
require.Equal(t, time.Duration(0), newLifespan)
|
||||
}
|
||||
|
||||
func TestOverrideIDTokenLifespan(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
accessRequest fosite.AccessRequester
|
||||
wantOverride bool
|
||||
wantLifespan time.Duration
|
||||
}{
|
||||
{
|
||||
name: "the client does not override the default ID token lifespan",
|
||||
accessRequest: &fosite.AccessRequest{
|
||||
GrantTypes: fosite.Arguments{"foo"},
|
||||
Request: fosite.Request{
|
||||
Client: &clientregistry.Client{
|
||||
IDTokenLifetimeConfiguration: 0, // 0 means use the default, so this is not an override
|
||||
},
|
||||
},
|
||||
},
|
||||
wantOverride: false,
|
||||
wantLifespan: 0,
|
||||
},
|
||||
{
|
||||
name: "the client overrides the default ID token lifespan",
|
||||
accessRequest: &fosite.AccessRequest{
|
||||
GrantTypes: fosite.Arguments{"foo"},
|
||||
Request: fosite.Request{
|
||||
Client: &clientregistry.Client{
|
||||
IDTokenLifetimeConfiguration: 42 * time.Second,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantOverride: true,
|
||||
wantLifespan: 42 * time.Second,
|
||||
},
|
||||
{
|
||||
name: "the client overrides the default ID token lifespan, but the request is for the token exchange, so the override is ignored",
|
||||
accessRequest: &fosite.AccessRequest{
|
||||
GrantTypes: fosite.Arguments{"urn:ietf:params:oauth:grant-type:token-exchange"},
|
||||
Request: fosite.Request{
|
||||
Client: &clientregistry.Client{
|
||||
IDTokenLifetimeConfiguration: 42 * time.Second,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantOverride: false,
|
||||
wantLifespan: 0,
|
||||
},
|
||||
{
|
||||
name: "the client is not the expected data type (which shouldn't really happen), so it is assumed to not override the ID token lifespan",
|
||||
accessRequest: &fosite.AccessRequest{
|
||||
GrantTypes: fosite.Arguments{"foo"},
|
||||
Request: fosite.Request{
|
||||
Client: &fosite.DefaultClient{},
|
||||
},
|
||||
},
|
||||
wantOverride: false,
|
||||
wantLifespan: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
c := DefaultOIDCTimeoutsConfiguration()
|
||||
|
||||
doOverride, newLifespan := c.OverrideDefaultIDTokenLifespan(tt.accessRequest)
|
||||
require.Equal(t, tt.wantOverride, doOverride)
|
||||
require.Equal(t, tt.wantLifespan, newLifespan)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
clocktesting "k8s.io/utils/clock/testing"
|
||||
|
||||
"go.pinniped.dev/internal/federationdomain/clientregistry"
|
||||
"go.pinniped.dev/internal/federationdomain/timeouts"
|
||||
"go.pinniped.dev/internal/psession"
|
||||
"go.pinniped.dev/internal/testutil"
|
||||
)
|
||||
@@ -32,6 +33,7 @@ const namespace = "test-ns"
|
||||
var fakeNow = time.Date(2030, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||
var lifetime = time.Minute * 10
|
||||
var fakeNowPlusLifetimeAsString = metav1.Time{Time: fakeNow.Add(lifetime)}.Format(time.RFC3339)
|
||||
var lifetimeFunc = func(requester fosite.Requester) time.Duration { return lifetime }
|
||||
|
||||
var secretsGVR = schema.GroupVersionResource{
|
||||
Group: "",
|
||||
@@ -54,7 +56,7 @@ func TestAccessTokenStorage(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":"","IDTokenLifetimeConfiguration":0},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","upstreamUsername":"fake-upstream-username","upstreamGroups":["fake-upstream-group1","fake-upstream-group2"],"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"6"}`),
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":"","IDTokenLifetimeConfiguration":42000000000},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","upstreamUsername":"fake-upstream-username","upstreamGroups":["fake-upstream-group1","fake-upstream-group2"],"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"6"}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/access-token",
|
||||
@@ -63,12 +65,19 @@ func TestAccessTokenStorage(t *testing.T) {
|
||||
coretesting.NewDeleteAction(secretsGVR, namespace, "pinniped-storage-access-token-pwu5zs7lekbhnln2w4"),
|
||||
}
|
||||
|
||||
ctx, client, _, storage := makeTestSubject()
|
||||
storageLifetimeFuncCallCount := 0
|
||||
var storageLifetimeFuncCallRequesterArg fosite.Requester
|
||||
ctx, client, _, storage := makeTestSubject(func(requester fosite.Requester) time.Duration {
|
||||
storageLifetimeFuncCallCount++
|
||||
storageLifetimeFuncCallRequesterArg = requester
|
||||
return lifetime
|
||||
})
|
||||
|
||||
request := &fosite.Request{
|
||||
ID: "abcd-1",
|
||||
RequestedAt: time.Time{},
|
||||
Client: &clientregistry.Client{
|
||||
IDTokenLifetimeConfiguration: 42 * time.Second,
|
||||
DefaultOpenIDConnectClient: fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinny",
|
||||
@@ -96,6 +105,8 @@ func TestAccessTokenStorage(t *testing.T) {
|
||||
}
|
||||
err := storage.CreateAccessTokenSession(ctx, "fancy-signature", request)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, storageLifetimeFuncCallCount)
|
||||
require.Equal(t, request, storageLifetimeFuncCallRequesterArg)
|
||||
|
||||
newRequest, err := storage.GetAccessTokenSession(ctx, "fancy-signature", nil)
|
||||
require.NoError(t, err)
|
||||
@@ -106,6 +117,9 @@ func TestAccessTokenStorage(t *testing.T) {
|
||||
|
||||
testutil.LogActualJSONFromCreateAction(t, client, 0) // makes it easier to update expected values when needed
|
||||
require.Equal(t, wantActions, client.Actions())
|
||||
|
||||
// Check that there were no more calls to the lifetime func since the original create.
|
||||
require.Equal(t, 1, storageLifetimeFuncCallCount)
|
||||
}
|
||||
|
||||
func TestAccessTokenStorageRevocation(t *testing.T) {
|
||||
@@ -134,7 +148,7 @@ func TestAccessTokenStorageRevocation(t *testing.T) {
|
||||
coretesting.NewDeleteAction(secretsGVR, namespace, "pinniped-storage-access-token-pwu5zs7lekbhnln2w4"),
|
||||
}
|
||||
|
||||
ctx, client, _, storage := makeTestSubject()
|
||||
ctx, client, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
request := &fosite.Request{
|
||||
ID: "abcd-1",
|
||||
@@ -164,7 +178,7 @@ func TestAccessTokenStorageRevocation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetNotFound(t *testing.T) {
|
||||
ctx, _, _, storage := makeTestSubject()
|
||||
ctx, _, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
_, notFoundErr := storage.GetAccessTokenSession(ctx, "non-existent-signature", nil)
|
||||
require.EqualError(t, notFoundErr, "not_found")
|
||||
@@ -172,7 +186,7 @@ func TestGetNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestWrongVersion(t *testing.T) {
|
||||
ctx, _, secrets, storage := makeTestSubject()
|
||||
ctx, _, secrets, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -200,7 +214,7 @@ func TestWrongVersion(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNilSessionRequest(t *testing.T) {
|
||||
ctx, _, secrets, storage := makeTestSubject()
|
||||
ctx, _, secrets, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -228,14 +242,14 @@ func TestNilSessionRequest(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateWithNilRequester(t *testing.T) {
|
||||
ctx, _, _, storage := makeTestSubject()
|
||||
ctx, _, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
err := storage.CreateAccessTokenSession(ctx, "signature-doesnt-matter", nil)
|
||||
require.EqualError(t, err, "requester must be of type fosite.Request")
|
||||
}
|
||||
|
||||
func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
|
||||
ctx, _, _, storage := makeTestSubject()
|
||||
ctx, _, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
request := &fosite.Request{
|
||||
Session: nil,
|
||||
@@ -253,7 +267,7 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateWithoutRequesterID(t *testing.T) {
|
||||
ctx, client, _, storage := makeTestSubject()
|
||||
ctx, client, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
request := &fosite.Request{
|
||||
ID: "", // empty ID
|
||||
@@ -274,10 +288,13 @@ func TestCreateWithoutRequesterID(t *testing.T) {
|
||||
require.Equal(t, request.ID, actualSecret.Labels["storage.pinniped.dev/request-id"])
|
||||
}
|
||||
|
||||
func makeTestSubject() (context.Context, *fake.Clientset, corev1client.SecretInterface, RevocationStorage) {
|
||||
func makeTestSubject(lifetimeFunc timeouts.StorageLifetime) (context.Context, *fake.Clientset, corev1client.SecretInterface, RevocationStorage) {
|
||||
client := fake.NewSimpleClientset()
|
||||
secrets := client.CoreV1().Secrets(namespace)
|
||||
return context.Background(), client, secrets, New(secrets, clocktesting.NewFakeClock(fakeNow).Now, func(requester fosite.Requester) time.Duration { return lifetime })
|
||||
return context.Background(),
|
||||
client,
|
||||
secrets,
|
||||
New(secrets, clocktesting.NewFakeClock(fakeNow).Now, lifetimeFunc)
|
||||
}
|
||||
|
||||
func TestReadFromSecret(t *testing.T) {
|
||||
|
||||
@@ -35,6 +35,7 @@ import (
|
||||
clocktesting "k8s.io/utils/clock/testing"
|
||||
|
||||
"go.pinniped.dev/internal/federationdomain/clientregistry"
|
||||
"go.pinniped.dev/internal/federationdomain/timeouts"
|
||||
"go.pinniped.dev/internal/fositestorage"
|
||||
"go.pinniped.dev/internal/psession"
|
||||
"go.pinniped.dev/internal/testutil"
|
||||
@@ -45,6 +46,7 @@ const namespace = "test-ns"
|
||||
var fakeNow = time.Date(2030, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||
var lifetime = time.Minute * 10
|
||||
var fakeNowPlusLifetimeAsString = metav1.Time{Time: fakeNow.Add(lifetime)}.Format(time.RFC3339)
|
||||
var lifetimeFunc = func(requester fosite.Requester) time.Duration { return lifetime }
|
||||
|
||||
func TestAuthorizationCodeStorage(t *testing.T) {
|
||||
secretsGVR := schema.GroupVersionResource{
|
||||
@@ -66,7 +68,7 @@ func TestAuthorizationCodeStorage(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"active":true,"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":"","IDTokenLifetimeConfiguration":0},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","upstreamUsername":"fake-upstream-username","upstreamGroups":["fake-upstream-group1","fake-upstream-group2"],"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"6"}`),
|
||||
"pinniped-storage-data": []byte(`{"active":true,"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":"","IDTokenLifetimeConfiguration":42000000000},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","upstreamUsername":"fake-upstream-username","upstreamGroups":["fake-upstream-group1","fake-upstream-group2"],"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"6"}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/authcode",
|
||||
@@ -86,19 +88,26 @@ func TestAuthorizationCodeStorage(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"active":false,"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":"","IDTokenLifetimeConfiguration":0},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","upstreamUsername":"fake-upstream-username","upstreamGroups":["fake-upstream-group1","fake-upstream-group2"],"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"6"}`),
|
||||
"pinniped-storage-data": []byte(`{"active":false,"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":"","IDTokenLifetimeConfiguration":42000000000},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","upstreamUsername":"fake-upstream-username","upstreamGroups":["fake-upstream-group1","fake-upstream-group2"],"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"6"}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/authcode",
|
||||
}),
|
||||
}
|
||||
|
||||
ctx, client, _, storage := makeTestSubject()
|
||||
storageLifetimeFuncCallCount := 0
|
||||
var storageLifetimeFuncCallRequesterArg fosite.Requester
|
||||
ctx, client, _, storage := makeTestSubject(func(requester fosite.Requester) time.Duration {
|
||||
storageLifetimeFuncCallCount++
|
||||
storageLifetimeFuncCallRequesterArg = requester
|
||||
return lifetime
|
||||
})
|
||||
|
||||
request := &fosite.Request{
|
||||
ID: "abcd-1",
|
||||
RequestedAt: time.Time{},
|
||||
Client: &clientregistry.Client{
|
||||
IDTokenLifetimeConfiguration: 42 * time.Second,
|
||||
DefaultOpenIDConnectClient: fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinny",
|
||||
@@ -127,6 +136,8 @@ func TestAuthorizationCodeStorage(t *testing.T) {
|
||||
}
|
||||
err := storage.CreateAuthorizeCodeSession(ctx, "fancy-signature", request)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, storageLifetimeFuncCallCount)
|
||||
require.Equal(t, request, storageLifetimeFuncCallRequesterArg)
|
||||
|
||||
newRequest, err := storage.GetAuthorizeCodeSession(ctx, "fancy-signature", nil)
|
||||
require.NoError(t, err)
|
||||
@@ -143,10 +154,13 @@ func TestAuthorizationCodeStorage(t *testing.T) {
|
||||
invalidatedRequest, err := storage.GetAuthorizeCodeSession(ctx, "fancy-signature", nil)
|
||||
require.EqualError(t, err, "authorization code session for fancy-signature has already been used: Authorization code has ben invalidated")
|
||||
require.Equal(t, "abcd-1", invalidatedRequest.GetID())
|
||||
|
||||
// Check that there were no more calls to the lifetime func since the original create.
|
||||
require.Equal(t, 1, storageLifetimeFuncCallCount)
|
||||
}
|
||||
|
||||
func TestGetNotFound(t *testing.T) {
|
||||
ctx, _, _, storage := makeTestSubject()
|
||||
ctx, _, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
_, notFoundErr := storage.GetAuthorizeCodeSession(ctx, "non-existent-signature", nil)
|
||||
require.EqualError(t, notFoundErr, "not_found")
|
||||
@@ -154,7 +168,7 @@ func TestGetNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInvalidateWhenNotFound(t *testing.T) {
|
||||
ctx, _, _, storage := makeTestSubject()
|
||||
ctx, _, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
notFoundErr := storage.InvalidateAuthorizeCodeSession(ctx, "non-existent-signature")
|
||||
require.EqualError(t, notFoundErr, "not_found")
|
||||
@@ -162,7 +176,7 @@ func TestInvalidateWhenNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInvalidateWhenConflictOnUpdateHappens(t *testing.T) {
|
||||
ctx, client, _, storage := makeTestSubject()
|
||||
ctx, client, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
client.PrependReactor("update", "secrets", func(_ kubetesting.Action) (bool, runtime.Object, error) {
|
||||
return true, nil, apierrors.NewConflict(schema.GroupResource{
|
||||
@@ -183,7 +197,7 @@ func TestInvalidateWhenConflictOnUpdateHappens(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestWrongVersion(t *testing.T) {
|
||||
ctx, _, secrets, storage := makeTestSubject()
|
||||
ctx, _, secrets, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -208,7 +222,7 @@ func TestWrongVersion(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNilSessionRequest(t *testing.T) {
|
||||
ctx, _, secrets, storage := makeTestSubject()
|
||||
ctx, _, secrets, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -233,14 +247,14 @@ func TestNilSessionRequest(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateWithNilRequester(t *testing.T) {
|
||||
ctx, _, _, storage := makeTestSubject()
|
||||
ctx, _, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
err := storage.CreateAuthorizeCodeSession(ctx, "signature-doesnt-matter", nil)
|
||||
require.EqualError(t, err, "requester must be of type fosite.Request")
|
||||
}
|
||||
|
||||
func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
|
||||
ctx, _, _, storage := makeTestSubject()
|
||||
ctx, _, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
request := &fosite.Request{
|
||||
Session: nil,
|
||||
@@ -257,10 +271,13 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
|
||||
require.EqualError(t, err, "requester's client must be of type clientregistry.Client")
|
||||
}
|
||||
|
||||
func makeTestSubject() (context.Context, *fake.Clientset, corev1client.SecretInterface, oauth2.AuthorizeCodeStorage) {
|
||||
func makeTestSubject(lifetimeFunc timeouts.StorageLifetime) (context.Context, *fake.Clientset, corev1client.SecretInterface, oauth2.AuthorizeCodeStorage) {
|
||||
client := fake.NewSimpleClientset()
|
||||
secrets := client.CoreV1().Secrets(namespace)
|
||||
return context.Background(), client, secrets, New(secrets, clocktesting.NewFakeClock(fakeNow).Now, func(requester fosite.Requester) time.Duration { return lifetime })
|
||||
return context.Background(),
|
||||
client,
|
||||
secrets,
|
||||
New(secrets, clocktesting.NewFakeClock(fakeNow).Now, lifetimeFunc)
|
||||
}
|
||||
|
||||
// TestFuzzAndJSONNewValidEmptyAuthorizeCodeSession asserts that we can correctly round trip our authorize code session.
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
clocktesting "k8s.io/utils/clock/testing"
|
||||
|
||||
"go.pinniped.dev/internal/federationdomain/clientregistry"
|
||||
"go.pinniped.dev/internal/federationdomain/timeouts"
|
||||
"go.pinniped.dev/internal/psession"
|
||||
"go.pinniped.dev/internal/testutil"
|
||||
)
|
||||
@@ -31,6 +32,7 @@ const namespace = "test-ns"
|
||||
var fakeNow = time.Date(2030, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||
var lifetime = time.Minute * 10
|
||||
var fakeNowPlusLifetimeAsString = metav1.Time{Time: fakeNow.Add(lifetime)}.Format(time.RFC3339)
|
||||
var lifetimeFunc = func(requester fosite.Requester) time.Duration { return lifetime }
|
||||
|
||||
func TestOpenIdConnectStorage(t *testing.T) {
|
||||
secretsGVR := schema.GroupVersionResource{
|
||||
@@ -52,7 +54,7 @@ func TestOpenIdConnectStorage(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":"","IDTokenLifetimeConfiguration":0},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","upstreamUsername":"fake-upstream-username","upstreamGroups":["fake-upstream-group1","fake-upstream-group2"],"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"6"}`),
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":"","IDTokenLifetimeConfiguration":42000000000},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","upstreamUsername":"fake-upstream-username","upstreamGroups":["fake-upstream-group1","fake-upstream-group2"],"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"6"}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/oidc",
|
||||
@@ -61,12 +63,19 @@ func TestOpenIdConnectStorage(t *testing.T) {
|
||||
coretesting.NewDeleteAction(secretsGVR, namespace, "pinniped-storage-oidc-pwu5zs7lekbhnln2w4"),
|
||||
}
|
||||
|
||||
ctx, client, _, storage := makeTestSubject()
|
||||
storageLifetimeFuncCallCount := 0
|
||||
var storageLifetimeFuncCallRequesterArg fosite.Requester
|
||||
ctx, client, _, storage := makeTestSubject(func(requester fosite.Requester) time.Duration {
|
||||
storageLifetimeFuncCallCount++
|
||||
storageLifetimeFuncCallRequesterArg = requester
|
||||
return lifetime
|
||||
})
|
||||
|
||||
request := &fosite.Request{
|
||||
ID: "abcd-1",
|
||||
RequestedAt: time.Time{},
|
||||
Client: &clientregistry.Client{
|
||||
IDTokenLifetimeConfiguration: 42 * time.Second,
|
||||
DefaultOpenIDConnectClient: fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinny",
|
||||
@@ -95,6 +104,8 @@ func TestOpenIdConnectStorage(t *testing.T) {
|
||||
}
|
||||
err := storage.CreateOpenIDConnectSession(ctx, "fancy-code.fancy-signature", request)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, storageLifetimeFuncCallCount)
|
||||
require.Equal(t, request, storageLifetimeFuncCallRequesterArg)
|
||||
|
||||
newRequest, err := storage.GetOpenIDConnectSession(ctx, "fancy-code.fancy-signature", nil)
|
||||
require.NoError(t, err)
|
||||
@@ -105,10 +116,13 @@ func TestOpenIdConnectStorage(t *testing.T) {
|
||||
|
||||
testutil.LogActualJSONFromCreateAction(t, client, 0) // makes it easier to update expected values when needed
|
||||
require.Equal(t, wantActions, client.Actions())
|
||||
|
||||
// Check that there were no more calls to the lifetime func since the original create.
|
||||
require.Equal(t, 1, storageLifetimeFuncCallCount)
|
||||
}
|
||||
|
||||
func TestGetNotFound(t *testing.T) {
|
||||
ctx, _, _, storage := makeTestSubject()
|
||||
ctx, _, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
_, notFoundErr := storage.GetOpenIDConnectSession(ctx, "authcode.non-existent-signature", nil)
|
||||
require.EqualError(t, notFoundErr, "not_found")
|
||||
@@ -116,7 +130,7 @@ func TestGetNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestWrongVersion(t *testing.T) {
|
||||
ctx, _, secrets, storage := makeTestSubject()
|
||||
ctx, _, secrets, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -141,7 +155,7 @@ func TestWrongVersion(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNilSessionRequest(t *testing.T) {
|
||||
ctx, _, secrets, storage := makeTestSubject()
|
||||
ctx, _, secrets, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -166,14 +180,14 @@ func TestNilSessionRequest(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateWithNilRequester(t *testing.T) {
|
||||
ctx, _, _, storage := makeTestSubject()
|
||||
ctx, _, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
err := storage.CreateOpenIDConnectSession(ctx, "authcode.signature-doesnt-matter", nil)
|
||||
require.EqualError(t, err, "requester must be of type fosite.Request")
|
||||
}
|
||||
|
||||
func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
|
||||
ctx, _, _, storage := makeTestSubject()
|
||||
ctx, _, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
request := &fosite.Request{
|
||||
Session: nil,
|
||||
@@ -191,14 +205,17 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAuthcodeHasNoDot(t *testing.T) {
|
||||
ctx, _, _, storage := makeTestSubject()
|
||||
ctx, _, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
err := storage.CreateOpenIDConnectSession(ctx, "all-one-part", nil)
|
||||
require.EqualError(t, err, "malformed authorization code")
|
||||
}
|
||||
|
||||
func makeTestSubject() (context.Context, *fake.Clientset, corev1client.SecretInterface, openid.OpenIDConnectRequestStorage) {
|
||||
func makeTestSubject(lifetimeFunc timeouts.StorageLifetime) (context.Context, *fake.Clientset, corev1client.SecretInterface, openid.OpenIDConnectRequestStorage) {
|
||||
client := fake.NewSimpleClientset()
|
||||
secrets := client.CoreV1().Secrets(namespace)
|
||||
return context.Background(), client, secrets, New(secrets, clocktesting.NewFakeClock(fakeNow).Now, func(requester fosite.Requester) time.Duration { return lifetime })
|
||||
return context.Background(),
|
||||
client,
|
||||
secrets,
|
||||
New(secrets, clocktesting.NewFakeClock(fakeNow).Now, lifetimeFunc)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
clocktesting "k8s.io/utils/clock/testing"
|
||||
|
||||
"go.pinniped.dev/internal/federationdomain/clientregistry"
|
||||
"go.pinniped.dev/internal/federationdomain/timeouts"
|
||||
"go.pinniped.dev/internal/psession"
|
||||
"go.pinniped.dev/internal/testutil"
|
||||
)
|
||||
@@ -31,6 +32,7 @@ const namespace = "test-ns"
|
||||
var fakeNow = time.Date(2030, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||
var lifetime = time.Minute * 10
|
||||
var fakeNowPlusLifetimeAsString = metav1.Time{Time: fakeNow.Add(lifetime)}.Format(time.RFC3339)
|
||||
var lifetimeFunc = func(requester fosite.Requester) time.Duration { return lifetime }
|
||||
|
||||
func TestPKCEStorage(t *testing.T) {
|
||||
secretsGVR := schema.GroupVersionResource{
|
||||
@@ -52,7 +54,7 @@ func TestPKCEStorage(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":"","IDTokenLifetimeConfiguration":0},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","upstreamUsername":"fake-upstream-username","upstreamGroups":["fake-upstream-group1","fake-upstream-group2"],"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"6"}`),
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":"","IDTokenLifetimeConfiguration":42000000000},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","upstreamUsername":"fake-upstream-username","upstreamGroups":["fake-upstream-group1","fake-upstream-group2"],"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"6"}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/pkce",
|
||||
@@ -61,12 +63,19 @@ func TestPKCEStorage(t *testing.T) {
|
||||
coretesting.NewDeleteAction(secretsGVR, namespace, "pinniped-storage-pkce-pwu5zs7lekbhnln2w4"),
|
||||
}
|
||||
|
||||
ctx, client, _, storage := makeTestSubject()
|
||||
storageLifetimeFuncCallCount := 0
|
||||
var storageLifetimeFuncCallRequesterArg fosite.Requester
|
||||
ctx, client, _, storage := makeTestSubject(func(requester fosite.Requester) time.Duration {
|
||||
storageLifetimeFuncCallCount++
|
||||
storageLifetimeFuncCallRequesterArg = requester
|
||||
return lifetime
|
||||
})
|
||||
|
||||
request := &fosite.Request{
|
||||
ID: "abcd-1",
|
||||
RequestedAt: time.Time{},
|
||||
Client: &clientregistry.Client{
|
||||
IDTokenLifetimeConfiguration: 42 * time.Second,
|
||||
DefaultOpenIDConnectClient: fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinny",
|
||||
@@ -95,6 +104,8 @@ func TestPKCEStorage(t *testing.T) {
|
||||
}
|
||||
err := storage.CreatePKCERequestSession(ctx, "fancy-signature", request)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, storageLifetimeFuncCallCount)
|
||||
require.Equal(t, request, storageLifetimeFuncCallRequesterArg)
|
||||
|
||||
newRequest, err := storage.GetPKCERequestSession(ctx, "fancy-signature", nil)
|
||||
require.NoError(t, err)
|
||||
@@ -105,10 +116,12 @@ func TestPKCEStorage(t *testing.T) {
|
||||
|
||||
testutil.LogActualJSONFromCreateAction(t, client, 0) // makes it easier to update expected values when needed
|
||||
require.Equal(t, wantActions, client.Actions())
|
||||
// Check that there were no more calls to the lifetime func since the original create.
|
||||
require.Equal(t, 1, storageLifetimeFuncCallCount)
|
||||
}
|
||||
|
||||
func TestGetNotFound(t *testing.T) {
|
||||
ctx, _, _, storage := makeTestSubject()
|
||||
ctx, _, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
_, notFoundErr := storage.GetPKCERequestSession(ctx, "non-existent-signature", nil)
|
||||
require.EqualError(t, notFoundErr, "not_found")
|
||||
@@ -116,7 +129,7 @@ func TestGetNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestWrongVersion(t *testing.T) {
|
||||
ctx, _, secrets, storage := makeTestSubject()
|
||||
ctx, _, secrets, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -144,7 +157,7 @@ func TestWrongVersion(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNilSessionRequest(t *testing.T) {
|
||||
ctx, _, secrets, storage := makeTestSubject()
|
||||
ctx, _, secrets, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -172,14 +185,14 @@ func TestNilSessionRequest(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateWithNilRequester(t *testing.T) {
|
||||
ctx, _, _, storage := makeTestSubject()
|
||||
ctx, _, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
err := storage.CreatePKCERequestSession(ctx, "signature-doesnt-matter", nil)
|
||||
require.EqualError(t, err, "requester must be of type fosite.Request")
|
||||
}
|
||||
|
||||
func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
|
||||
ctx, _, _, storage := makeTestSubject()
|
||||
ctx, _, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
request := &fosite.Request{
|
||||
Session: nil,
|
||||
@@ -196,8 +209,11 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
|
||||
require.EqualError(t, err, "requester's client must be of type clientregistry.Client")
|
||||
}
|
||||
|
||||
func makeTestSubject() (context.Context, *fake.Clientset, corev1client.SecretInterface, pkce.PKCERequestStorage) {
|
||||
func makeTestSubject(lifetimeFunc timeouts.StorageLifetime) (context.Context, *fake.Clientset, corev1client.SecretInterface, pkce.PKCERequestStorage) {
|
||||
client := fake.NewSimpleClientset()
|
||||
secrets := client.CoreV1().Secrets(namespace)
|
||||
return context.Background(), client, secrets, New(secrets, clocktesting.NewFakeClock(fakeNow).Now, func(requester fosite.Requester) time.Duration { return lifetime })
|
||||
return context.Background(),
|
||||
client,
|
||||
secrets,
|
||||
New(secrets, clocktesting.NewFakeClock(fakeNow).Now, lifetimeFunc)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
clocktesting "k8s.io/utils/clock/testing"
|
||||
|
||||
"go.pinniped.dev/internal/federationdomain/clientregistry"
|
||||
"go.pinniped.dev/internal/federationdomain/timeouts"
|
||||
"go.pinniped.dev/internal/psession"
|
||||
"go.pinniped.dev/internal/testutil"
|
||||
)
|
||||
@@ -37,6 +38,7 @@ var secretsGVR = schema.GroupVersionResource{
|
||||
var fakeNow = time.Date(2030, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||
var lifetime = time.Minute * 10
|
||||
var fakeNowPlusLifetimeAsString = metav1.Time{Time: fakeNow.Add(lifetime)}.Format(time.RFC3339)
|
||||
var lifetimeFunc = func(requester fosite.Requester) time.Duration { return lifetime }
|
||||
|
||||
func TestRefreshTokenStorage(t *testing.T) {
|
||||
wantActions := []coretesting.Action{
|
||||
@@ -53,7 +55,7 @@ func TestRefreshTokenStorage(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":"","IDTokenLifetimeConfiguration":0},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","upstreamUsername":"fake-upstream-username","upstreamGroups":["fake-upstream-group1","fake-upstream-group2"],"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"6"}`),
|
||||
"pinniped-storage-data": []byte(`{"request":{"id":"abcd-1","requestedAt":"0001-01-01T00:00:00Z","client":{"id":"pinny","redirect_uris":null,"grant_types":null,"response_types":null,"scopes":null,"audience":null,"public":true,"jwks_uri":"where","jwks":null,"token_endpoint_auth_method":"something","request_uris":null,"request_object_signing_alg":"","token_endpoint_auth_signing_alg":"","IDTokenLifetimeConfiguration":42000000000},"scopes":null,"grantedScopes":null,"form":{"key":["val"]},"session":{"fosite":{"id_token_claims":null,"headers":null,"expires_at":null,"username":"snorlax","subject":"panda"},"custom":{"username":"fake-username","upstreamUsername":"fake-upstream-username","upstreamGroups":["fake-upstream-group1","fake-upstream-group2"],"providerUID":"fake-provider-uid","providerName":"fake-provider-name","providerType":"fake-provider-type","warnings":null,"oidc":{"upstreamRefreshToken":"fake-upstream-refresh-token","upstreamAccessToken":"","upstreamSubject":"some-subject","upstreamIssuer":"some-issuer"}}},"requestedAudience":null,"grantedAudience":null},"version":"6"}`),
|
||||
"pinniped-storage-version": []byte("1"),
|
||||
},
|
||||
Type: "storage.pinniped.dev/refresh-token",
|
||||
@@ -62,12 +64,19 @@ func TestRefreshTokenStorage(t *testing.T) {
|
||||
coretesting.NewDeleteAction(secretsGVR, namespace, "pinniped-storage-refresh-token-pwu5zs7lekbhnln2w4"),
|
||||
}
|
||||
|
||||
ctx, client, _, storage := makeTestSubject()
|
||||
storageLifetimeFuncCallCount := 0
|
||||
var storageLifetimeFuncCallRequesterArg fosite.Requester
|
||||
ctx, client, _, storage := makeTestSubject(func(requester fosite.Requester) time.Duration {
|
||||
storageLifetimeFuncCallCount++
|
||||
storageLifetimeFuncCallRequesterArg = requester
|
||||
return lifetime
|
||||
})
|
||||
|
||||
request := &fosite.Request{
|
||||
ID: "abcd-1",
|
||||
RequestedAt: time.Time{},
|
||||
Client: &clientregistry.Client{
|
||||
IDTokenLifetimeConfiguration: 42 * time.Second,
|
||||
DefaultOpenIDConnectClient: fosite.DefaultOpenIDConnectClient{
|
||||
DefaultClient: &fosite.DefaultClient{
|
||||
ID: "pinny",
|
||||
@@ -96,6 +105,8 @@ func TestRefreshTokenStorage(t *testing.T) {
|
||||
}
|
||||
err := storage.CreateRefreshTokenSession(ctx, "fancy-signature", request)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, storageLifetimeFuncCallCount)
|
||||
require.Equal(t, request, storageLifetimeFuncCallRequesterArg)
|
||||
|
||||
newRequest, err := storage.GetRefreshTokenSession(ctx, "fancy-signature", nil)
|
||||
require.NoError(t, err)
|
||||
@@ -106,6 +117,9 @@ func TestRefreshTokenStorage(t *testing.T) {
|
||||
|
||||
testutil.LogActualJSONFromCreateAction(t, client, 0) // makes it easier to update expected values when needed
|
||||
require.Equal(t, wantActions, client.Actions())
|
||||
|
||||
// Check that there were no more calls to the lifetime func since the original create.
|
||||
require.Equal(t, 1, storageLifetimeFuncCallCount)
|
||||
}
|
||||
|
||||
func TestRefreshTokenStorageRevocation(t *testing.T) {
|
||||
@@ -134,7 +148,7 @@ func TestRefreshTokenStorageRevocation(t *testing.T) {
|
||||
coretesting.NewDeleteAction(secretsGVR, namespace, "pinniped-storage-refresh-token-pwu5zs7lekbhnln2w4"),
|
||||
}
|
||||
|
||||
ctx, client, _, storage := makeTestSubject()
|
||||
ctx, client, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
request := &fosite.Request{
|
||||
ID: "abcd-1",
|
||||
@@ -189,7 +203,7 @@ func TestRefreshTokenStorageRevokeRefreshTokenMaybeGracePeriod(t *testing.T) {
|
||||
coretesting.NewDeleteAction(secretsGVR, namespace, "pinniped-storage-refresh-token-pwu5zs7lekbhnln2w4"),
|
||||
}
|
||||
|
||||
ctx, client, _, storage := makeTestSubject()
|
||||
ctx, client, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
request := &fosite.Request{
|
||||
ID: "abcd-1",
|
||||
@@ -220,7 +234,7 @@ func TestRefreshTokenStorageRevokeRefreshTokenMaybeGracePeriod(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetNotFound(t *testing.T) {
|
||||
ctx, _, _, storage := makeTestSubject()
|
||||
ctx, _, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
_, notFoundErr := storage.GetRefreshTokenSession(ctx, "non-existent-signature", nil)
|
||||
require.EqualError(t, notFoundErr, "not_found")
|
||||
@@ -228,7 +242,7 @@ func TestGetNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestWrongVersion(t *testing.T) {
|
||||
ctx, _, secrets, storage := makeTestSubject()
|
||||
ctx, _, secrets, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -256,7 +270,7 @@ func TestWrongVersion(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNilSessionRequest(t *testing.T) {
|
||||
ctx, _, secrets, storage := makeTestSubject()
|
||||
ctx, _, secrets, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -284,14 +298,14 @@ func TestNilSessionRequest(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateWithNilRequester(t *testing.T) {
|
||||
ctx, _, _, storage := makeTestSubject()
|
||||
ctx, _, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
err := storage.CreateRefreshTokenSession(ctx, "signature-doesnt-matter", nil)
|
||||
require.EqualError(t, err, "requester must be of type fosite.Request")
|
||||
}
|
||||
|
||||
func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
|
||||
ctx, _, _, storage := makeTestSubject()
|
||||
ctx, _, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
request := &fosite.Request{
|
||||
Session: nil,
|
||||
@@ -309,7 +323,7 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateWithoutRequesterID(t *testing.T) {
|
||||
ctx, client, _, storage := makeTestSubject()
|
||||
ctx, client, _, storage := makeTestSubject(lifetimeFunc)
|
||||
|
||||
request := &fosite.Request{
|
||||
ID: "", // empty ID
|
||||
@@ -330,10 +344,13 @@ func TestCreateWithoutRequesterID(t *testing.T) {
|
||||
require.Equal(t, request.ID, actualSecret.Labels["storage.pinniped.dev/request-id"])
|
||||
}
|
||||
|
||||
func makeTestSubject() (context.Context, *fake.Clientset, corev1client.SecretInterface, RevocationStorage) {
|
||||
func makeTestSubject(lifetimeFunc timeouts.StorageLifetime) (context.Context, *fake.Clientset, corev1client.SecretInterface, RevocationStorage) {
|
||||
client := fake.NewSimpleClientset()
|
||||
secrets := client.CoreV1().Secrets(namespace)
|
||||
return context.Background(), client, secrets, New(secrets, clocktesting.NewFakeClock(fakeNow).Now, func(requester fosite.Requester) time.Duration { return lifetime })
|
||||
return context.Background(),
|
||||
client,
|
||||
secrets,
|
||||
New(secrets, clocktesting.NewFakeClock(fakeNow).Now, lifetimeFunc)
|
||||
}
|
||||
|
||||
func TestReadFromSecret(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user