Make assertions about how many secrets were stored by fosite in tests

In both callback_handler_test.go and token_handler_test.go

Signed-off-by: Aram Price <pricear@vmware.com>
This commit is contained in:
Ryan Richard 2020-12-04 15:40:17 -08:00 committed by Aram Price
parent 26a8747509
commit 858356610c
9 changed files with 84 additions and 46 deletions

View File

@ -22,8 +22,9 @@ import (
//nolint:gosec // ignore lint warnings that these are credentials //nolint:gosec // ignore lint warnings that these are credentials
const ( const (
SecretLabelKey = "storage.pinniped.dev/type"
secretNameFormat = "pinniped-storage-%s-%s" secretNameFormat = "pinniped-storage-%s-%s"
secretLabelKey = "storage.pinniped.dev/type"
secretTypeFormat = "storage.pinniped.dev/%s" secretTypeFormat = "storage.pinniped.dev/%s"
secretVersion = "1" secretVersion = "1"
secretDataKey = "pinniped-storage-data" secretDataKey = "pinniped-storage-data"
@ -90,7 +91,7 @@ func (s *secretsStorage) validateSecret(secret *corev1.Secret) error {
if secret.Type != s.secretType { if secret.Type != s.secretType {
return fmt.Errorf("%w: %s must equal %s", ErrSecretTypeMismatch, secret.Type, s.secretType) return fmt.Errorf("%w: %s must equal %s", ErrSecretTypeMismatch, secret.Type, s.secretType)
} }
if labelResource := secret.Labels[secretLabelKey]; labelResource != s.resource { if labelResource := secret.Labels[SecretLabelKey]; labelResource != s.resource {
return fmt.Errorf("%w: %s must equal %s", ErrSecretLabelMismatch, labelResource, s.resource) return fmt.Errorf("%w: %s must equal %s", ErrSecretLabelMismatch, labelResource, s.resource)
} }
if !bytes.Equal(secret.Data[secretVersionKey], s.secretVersion) { if !bytes.Equal(secret.Data[secretVersionKey], s.secretVersion) {
@ -121,7 +122,7 @@ func (s *secretsStorage) Delete(ctx context.Context, signature string) error {
func (s *secretsStorage) DeleteByLabel(ctx context.Context, labelName string, labelValue string) error { func (s *secretsStorage) DeleteByLabel(ctx context.Context, labelName string, labelValue string) error {
list, err := s.secrets.List(ctx, metav1.ListOptions{ list, err := s.secrets.List(ctx, metav1.ListOptions{
LabelSelector: labels.Set{ LabelSelector: labels.Set{
secretLabelKey: s.resource, SecretLabelKey: s.resource,
labelName: labelValue, labelName: labelValue,
}.String(), }.String(),
}) })
@ -158,7 +159,7 @@ func (s *secretsStorage) toSecret(signature, resourceVersion string, data JSON,
} }
labels := map[string]string{ labels := map[string]string{
secretLabelKey: s.resource, // make it easier to find this stuff via kubectl SecretLabelKey: s.resource, // make it easier to find this stuff via kubectl
} }
for labelName, labelValue := range additionalLabels { for labelName, labelValue := range additionalLabels {
labels[labelName] = labelValue labels[labelName] = labelValue

View File

@ -19,6 +19,8 @@ import (
) )
const ( const (
TypeLabelValue = "access-token"
ErrInvalidAccessTokenRequestVersion = constable.Error("access token request data has wrong version") ErrInvalidAccessTokenRequestVersion = constable.Error("access token request data has wrong version")
ErrInvalidAccessTokenRequestData = constable.Error("access token request data must be present") ErrInvalidAccessTokenRequestData = constable.Error("access token request data must be present")
@ -42,7 +44,7 @@ type session struct {
} }
func New(secrets corev1client.SecretInterface) RevocationStorage { func New(secrets corev1client.SecretInterface) RevocationStorage {
return &accessTokenStorage{storage: crud.New("access-token", secrets)} return &accessTokenStorage{storage: crud.New(TypeLabelValue, secrets)}
} }
func (a *accessTokenStorage) RevokeAccessToken(ctx context.Context, requestID string) error { func (a *accessTokenStorage) RevokeAccessToken(ctx context.Context, requestID string) error {

View File

@ -20,6 +20,8 @@ import (
) )
const ( const (
TypeLabelValue = "authcode"
ErrInvalidAuthorizeRequestData = constable.Error("authorization request data must be present") ErrInvalidAuthorizeRequestData = constable.Error("authorization request data must be present")
ErrInvalidAuthorizeRequestVersion = constable.Error("authorization request data has wrong version") ErrInvalidAuthorizeRequestVersion = constable.Error("authorization request data has wrong version")
@ -39,7 +41,7 @@ type AuthorizeCodeSession struct {
} }
func New(secrets corev1client.SecretInterface) oauth2.AuthorizeCodeStorage { func New(secrets corev1client.SecretInterface) oauth2.AuthorizeCodeStorage {
return &authorizeCodeStorage{storage: crud.New("authcode", secrets)} return &authorizeCodeStorage{storage: crud.New(TypeLabelValue, secrets)}
} }
func (a *authorizeCodeStorage) CreateAuthorizeCodeSession(ctx context.Context, signature string, requester fosite.Requester) error { func (a *authorizeCodeStorage) CreateAuthorizeCodeSession(ctx context.Context, signature string, requester fosite.Requester) error {

View File

@ -19,6 +19,8 @@ import (
) )
const ( const (
TypeLabelValue = "oidc"
ErrInvalidOIDCRequestVersion = constable.Error("oidc request data has wrong version") ErrInvalidOIDCRequestVersion = constable.Error("oidc request data has wrong version")
ErrInvalidOIDCRequestData = constable.Error("oidc request data must be present") ErrInvalidOIDCRequestData = constable.Error("oidc request data must be present")
ErrMalformedAuthorizationCode = constable.Error("malformed authorization code") ErrMalformedAuthorizationCode = constable.Error("malformed authorization code")
@ -38,7 +40,7 @@ type session struct {
} }
func New(secrets corev1client.SecretInterface) openid.OpenIDConnectRequestStorage { func New(secrets corev1client.SecretInterface) openid.OpenIDConnectRequestStorage {
return &openIDConnectRequestStorage{storage: crud.New("oidc", secrets)} return &openIDConnectRequestStorage{storage: crud.New(TypeLabelValue, secrets)}
} }
func (a *openIDConnectRequestStorage) CreateOpenIDConnectSession(ctx context.Context, authcode string, requester fosite.Requester) error { func (a *openIDConnectRequestStorage) CreateOpenIDConnectSession(ctx context.Context, authcode string, requester fosite.Requester) error {

View File

@ -19,6 +19,8 @@ import (
) )
const ( const (
TypeLabelValue = "pkce"
ErrInvalidPKCERequestVersion = constable.Error("pkce request data has wrong version") ErrInvalidPKCERequestVersion = constable.Error("pkce request data has wrong version")
ErrInvalidPKCERequestData = constable.Error("pkce request data must be present") ErrInvalidPKCERequestData = constable.Error("pkce request data must be present")
@ -37,7 +39,7 @@ type session struct {
} }
func New(secrets corev1client.SecretInterface) pkce.PKCERequestStorage { func New(secrets corev1client.SecretInterface) pkce.PKCERequestStorage {
return &pkceStorage{storage: crud.New("pkce", secrets)} return &pkceStorage{storage: crud.New(TypeLabelValue, secrets)}
} }
func (a *pkceStorage) CreatePKCERequestSession(ctx context.Context, signature string, requester fosite.Requester) error { func (a *pkceStorage) CreatePKCERequestSession(ctx context.Context, signature string, requester fosite.Requester) error {

View File

@ -19,6 +19,8 @@ import (
) )
const ( const (
TypeLabelValue = "refresh-token"
ErrInvalidRefreshTokenRequestVersion = constable.Error("refresh token request data has wrong version") ErrInvalidRefreshTokenRequestVersion = constable.Error("refresh token request data has wrong version")
ErrInvalidRefreshTokenRequestData = constable.Error("refresh token request data must be present") ErrInvalidRefreshTokenRequestData = constable.Error("refresh token request data must be present")
@ -42,7 +44,7 @@ type session struct {
} }
func New(secrets corev1client.SecretInterface) RevocationStorage { func New(secrets corev1client.SecretInterface) RevocationStorage {
return &refreshTokenStorage{storage: crud.New("refresh-token", secrets)} return &refreshTokenStorage{storage: crud.New(TypeLabelValue, secrets)}
} }
func (a *refreshTokenStorage) RevokeRefreshToken(ctx context.Context, requestID string) error { func (a *refreshTokenStorage) RevokeRefreshToken(ctx context.Context, requestID string) error {

View File

@ -18,18 +18,20 @@ import (
"github.com/ory/fosite" "github.com/ory/fosite"
"github.com/ory/fosite/handler/openid" "github.com/ory/fosite/handler/openid"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
kubetesting "k8s.io/client-go/testing"
"go.pinniped.dev/internal/crud"
"go.pinniped.dev/internal/fositestorage/authorizationcode"
"go.pinniped.dev/internal/fositestorage/openidconnect"
"go.pinniped.dev/internal/fositestorage/pkce"
"go.pinniped.dev/internal/oidc" "go.pinniped.dev/internal/oidc"
"go.pinniped.dev/internal/oidc/jwks" "go.pinniped.dev/internal/oidc/jwks"
"go.pinniped.dev/internal/oidc/oidctestutil" "go.pinniped.dev/internal/oidc/oidctestutil"
"go.pinniped.dev/internal/testutil" "go.pinniped.dev/internal/testutil"
"go.pinniped.dev/pkg/oidcclient/nonce" "go.pinniped.dev/pkg/oidcclient/nonce"
"go.pinniped.dev/pkg/oidcclient/oidctypes" "go.pinniped.dev/pkg/oidcclient/oidctypes"
"go.pinniped.dev/pkg/oidcclient/pkce" oidcpkce "go.pinniped.dev/pkg/oidcclient/pkce"
) )
const ( const (
@ -106,7 +108,7 @@ func TestCallbackEndpoint(t *testing.T) {
happyExchangeAndValidateTokensArgs := &oidctestutil.ExchangeAuthcodeAndValidateTokenArgs{ happyExchangeAndValidateTokensArgs := &oidctestutil.ExchangeAuthcodeAndValidateTokenArgs{
Authcode: happyUpstreamAuthcode, Authcode: happyUpstreamAuthcode,
PKCECodeVerifier: pkce.Code(happyDownstreamPKCE), PKCECodeVerifier: oidcpkce.Code(happyDownstreamPKCE),
ExpectedIDTokenNonce: nonce.Nonce(happyDownstreamNonce), ExpectedIDTokenNonce: nonce.Nonce(happyDownstreamNonce),
RedirectURI: happyUpstreamRedirectURI, RedirectURI: happyUpstreamRedirectURI,
} }
@ -484,18 +486,8 @@ func TestCallbackEndpoint(t *testing.T) {
} }
require.Len(t, client.Actions(), expectedNumberOfCreatedSecrets) require.Len(t, client.Actions(), expectedNumberOfCreatedSecrets)
actualSecretNames := []string{}
for i := range client.Actions() {
actualAction := client.Actions()[i].(kubetesting.CreateActionImpl)
require.Equal(t, "create", actualAction.GetVerb())
require.Equal(t, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}, actualAction.GetResource())
actualSecret := actualAction.GetObject().(*corev1.Secret)
require.Empty(t, actualSecret.Namespace) // because the secrets client is already scoped to a namespace
actualSecretNames = append(actualSecretNames, actualSecret.Name)
}
// One authcode should have been stored. // One authcode should have been stored.
requireAnyStringHasPrefix(t, actualSecretNames, "pinniped-storage-authcode-") testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: authorizationcode.TypeLabelValue}, 1)
storedRequestFromAuthcode, storedSessionFromAuthcode := validateAuthcodeStorage( storedRequestFromAuthcode, storedSessionFromAuthcode := validateAuthcodeStorage(
t, t,
@ -508,7 +500,7 @@ func TestCallbackEndpoint(t *testing.T) {
) )
// One PKCE should have been stored. // One PKCE should have been stored.
requireAnyStringHasPrefix(t, actualSecretNames, "pinniped-storage-pkce-") testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: pkce.TypeLabelValue}, 1)
validatePKCEStorage( validatePKCEStorage(
t, t,
@ -522,7 +514,7 @@ func TestCallbackEndpoint(t *testing.T) {
// One IDSession should have been stored, if the downstream actually requested the "openid" scope // One IDSession should have been stored, if the downstream actually requested the "openid" scope
if test.wantGrantedOpenidScope { if test.wantGrantedOpenidScope {
requireAnyStringHasPrefix(t, actualSecretNames, "pinniped-storage-oidc") testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: openidconnect.TypeLabelValue}, 1)
validateIDSessionStorage( validateIDSessionStorage(
t, t,
@ -684,7 +676,7 @@ func (u *upstreamOIDCIdentityProviderBuilder) Build() oidctestutil.TestUpstreamO
UsernameClaim: u.usernameClaim, UsernameClaim: u.usernameClaim,
GroupsClaim: u.groupsClaim, GroupsClaim: u.groupsClaim,
Scopes: []string{"scope1", "scope2"}, Scopes: []string{"scope1", "scope2"},
ExchangeAuthcodeAndValidateTokensFunc: func(ctx context.Context, authcode string, pkceCodeVerifier pkce.Code, expectedIDTokenNonce nonce.Nonce) (oidctypes.Token, map[string]interface{}, error) { ExchangeAuthcodeAndValidateTokensFunc: func(ctx context.Context, authcode string, pkceCodeVerifier oidcpkce.Code, expectedIDTokenNonce nonce.Nonce) (oidctypes.Token, map[string]interface{}, error) {
return oidctypes.Token{}, u.idToken, u.authcodeExchangeErr return oidctypes.Token{}, u.idToken, u.authcodeExchangeErr
}, },
} }
@ -847,15 +839,3 @@ func castStoredAuthorizeRequest(t *testing.T, storedAuthorizeRequest fosite.Requ
return storedRequest, storedSession return storedRequest, storedSession
} }
func requireAnyStringHasPrefix(t *testing.T, stringList []string, prefix string) {
t.Helper()
containsPrefix := false
for i := range stringList {
if strings.HasPrefix(stringList[i], prefix) {
containsPrefix = true
}
}
require.Truef(t, containsPrefix, "list %v did not contain any strings with prefix %s", stringList, prefix)
}

View File

@ -26,8 +26,15 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"gopkg.in/square/go-jose.v2" "gopkg.in/square/go-jose.v2"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
"go.pinniped.dev/internal/crud"
"go.pinniped.dev/internal/fositestorage/accesstoken"
"go.pinniped.dev/internal/fositestorage/authorizationcode"
"go.pinniped.dev/internal/fositestorage/openidconnect"
storagepkce "go.pinniped.dev/internal/fositestorage/pkce"
"go.pinniped.dev/internal/fositestorage/refreshtoken"
"go.pinniped.dev/internal/here" "go.pinniped.dev/internal/here"
"go.pinniped.dev/internal/oidc" "go.pinniped.dev/internal/oidc"
"go.pinniped.dev/internal/oidc/jwks" "go.pinniped.dev/internal/oidc/jwks"
@ -394,7 +401,14 @@ func TestTokenEndpoint(t *testing.T) {
} }
subject := NewHandler(oauthHelper) subject := NewHandler(oauthHelper)
// TODO add assertions about how many of each storage type exist at this point as a pre-condition testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: authorizationcode.TypeLabelValue}, 1)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: storagepkce.TypeLabelValue}, 1)
if strings.Contains(authRequest.Form.Get("scope"), "openid") {
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: openidconnect.TypeLabelValue}, 1)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{}, 3)
} else {
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{}, 2)
}
req := httptest.NewRequest("POST", "/path/shouldn't/matter", happyBody(authCode).ReadCloser()) req := httptest.NewRequest("POST", "/path/shouldn't/matter", happyBody(authCode).ReadCloser())
req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
@ -421,11 +435,18 @@ func TestTokenEndpoint(t *testing.T) {
requireInvalidPKCEStorage(t, code, oauthStore) requireInvalidPKCEStorage(t, code, oauthStore)
requireValidOIDCStorage(t, m, code, oauthStore, wantOpenidScope) requireValidOIDCStorage(t, m, code, oauthStore, wantOpenidScope)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: authorizationcode.TypeLabelValue}, 1)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: accesstoken.TypeLabelValue}, 1)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: refreshtoken.TypeLabelValue}, 0)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: storagepkce.TypeLabelValue}, 0)
if wantOpenidScope { if wantOpenidScope {
requireValidIDToken(t, m, jwtSigningKey) requireValidIDToken(t, m, jwtSigningKey)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: openidconnect.TypeLabelValue}, 1)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{}, 3)
} else {
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{}, 2)
} }
// TODO add assertions about how many of each storage type are remaining at this point
} else { } else {
require.JSONEq(t, test.wantExactBody, rsp.Body.String()) require.JSONEq(t, test.wantExactBody, rsp.Body.String())
} }
@ -442,7 +463,10 @@ func TestTokenEndpoint(t *testing.T) {
oauthHelper, authCode, jwtSigningKey := makeHappyOauthHelper(t, authRequest, oauthStore) oauthHelper, authCode, jwtSigningKey := makeHappyOauthHelper(t, authRequest, oauthStore)
subject := NewHandler(oauthHelper) subject := NewHandler(oauthHelper)
// TODO add assertions about how many of each storage type exist at this point as a pre-condition testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: authorizationcode.TypeLabelValue}, 1)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: storagepkce.TypeLabelValue}, 1)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: openidconnect.TypeLabelValue}, 1)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{}, 3)
req := httptest.NewRequest("POST", "/path/shouldn't/matter", happyBody(authCode).ReadCloser()) req := httptest.NewRequest("POST", "/path/shouldn't/matter", happyBody(authCode).ReadCloser())
req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
@ -469,7 +493,12 @@ func TestTokenEndpoint(t *testing.T) {
requireValidOIDCStorage(t, m, code, oauthStore, wantOpenidScope) requireValidOIDCStorage(t, m, code, oauthStore, wantOpenidScope)
requireValidIDToken(t, m, jwtSigningKey) requireValidIDToken(t, m, jwtSigningKey)
// TODO add assertions about how many of each storage type are remaining at this point testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: authorizationcode.TypeLabelValue}, 1)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: accesstoken.TypeLabelValue}, 1)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: refreshtoken.TypeLabelValue}, 0)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: storagepkce.TypeLabelValue}, 0)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: openidconnect.TypeLabelValue}, 1)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{}, 3)
// Second call - should be unsuccessful since auth code was already used. // Second call - should be unsuccessful since auth code was already used.
// //
@ -488,7 +517,12 @@ func TestTokenEndpoint(t *testing.T) {
requireInvalidPKCEStorage(t, code, oauthStore) requireInvalidPKCEStorage(t, code, oauthStore)
requireValidOIDCStorage(t, m, code, oauthStore, wantOpenidScope) requireValidOIDCStorage(t, m, code, oauthStore, wantOpenidScope)
// TODO add assertions about how many of each storage type are remaining at this point testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: authorizationcode.TypeLabelValue}, 1)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: accesstoken.TypeLabelValue}, 0)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: refreshtoken.TypeLabelValue}, 0)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: storagepkce.TypeLabelValue}, 0)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{crud.SecretLabelKey: openidconnect.TypeLabelValue}, 1)
testutil.RequireNumberOfSecretsMatchingLabelSelector(t, secrets, labels.Set{}, 2)
}) })
} }

View File

@ -4,11 +4,15 @@
package testutil package testutil
import ( import (
"context"
"mime" "mime"
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
v1 "k8s.io/client-go/kubernetes/typed/core/v1"
) )
func RequireTimeInDelta(t *testing.T, t1 time.Time, t2 time.Time, delta time.Duration) { func RequireTimeInDelta(t *testing.T, t1 time.Time, t2 time.Time, delta time.Duration) {
@ -39,3 +43,12 @@ func RequireEqualContentType(t *testing.T, actual string, expected string) {
require.Equal(t, actualContentType, expectedContentType) require.Equal(t, actualContentType, expectedContentType)
require.Equal(t, actualContentTypeParams, expectedContentTypeParams) require.Equal(t, actualContentTypeParams, expectedContentTypeParams)
} }
func RequireNumberOfSecretsMatchingLabelSelector(t *testing.T, secrets v1.SecretInterface, labelSet labels.Set, expectedNumberOfSecrets int) {
t.Helper()
storedAuthcodeSecrets, err := secrets.List(context.Background(), v12.ListOptions{
LabelSelector: labelSet.String(),
})
require.NoError(t, err)
require.Len(t, storedAuthcodeSecrets.Items, expectedNumberOfSecrets)
}