2022-06-17 16:56:53 +00:00
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package oidcclientwatcher
import (
"context"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
kubeinformers "k8s.io/client-go/informers"
kubernetesfake "k8s.io/client-go/kubernetes/fake"
configv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1"
pinnipedfake "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/fake"
pinnipedinformers "go.pinniped.dev/generated/latest/client/supervisor/informers/externalversions"
"go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/testutil"
)
func TestOIDCClientWatcherControllerFilterSecret ( t * testing . T ) {
t . Parallel ( )
tests := [ ] struct {
name string
secret metav1 . Object
wantAdd bool
wantUpdate bool
wantDelete bool
} {
{
name : "a secret of the right type" ,
secret : & corev1 . Secret {
Type : "storage.pinniped.dev/oidc-client-secret" ,
ObjectMeta : metav1 . ObjectMeta { Name : "some-name" , Namespace : "some-namespace" } ,
} ,
wantAdd : true ,
wantUpdate : true ,
wantDelete : true ,
} ,
{
name : "a secret of the wrong type" ,
secret : & corev1 . Secret {
Type : "secrets.pinniped.dev/some-other-type" ,
ObjectMeta : metav1 . ObjectMeta { Name : "some-name" , Namespace : "some-namespace" } ,
} ,
} ,
{
name : "resource of wrong data type" ,
secret : & corev1 . Namespace {
ObjectMeta : metav1 . ObjectMeta { Name : "some-name" , Namespace : "some-namespace" } ,
} ,
} ,
}
for _ , test := range tests {
tt := test
t . Run ( tt . name , func ( t * testing . T ) {
t . Parallel ( )
secretInformer := kubeinformers . NewSharedInformerFactory (
kubernetesfake . NewSimpleClientset ( ) ,
0 ,
) . Core ( ) . V1 ( ) . Secrets ( )
oidcClientsInformer := pinnipedinformers . NewSharedInformerFactory (
pinnipedfake . NewSimpleClientset ( ) ,
0 ,
) . Config ( ) . V1alpha1 ( ) . OIDCClients ( )
withInformer := testutil . NewObservableWithInformerOption ( )
_ = NewOIDCClientWatcherController (
nil , // pinnipedClient, not needed
secretInformer ,
oidcClientsInformer ,
withInformer . WithInformer ,
)
unrelated := corev1 . Secret { }
filter := withInformer . GetFilterForInformer ( secretInformer )
require . Equal ( t , tt . wantAdd , filter . Add ( tt . secret ) )
require . Equal ( t , tt . wantUpdate , filter . Update ( & unrelated , tt . secret ) )
require . Equal ( t , tt . wantUpdate , filter . Update ( tt . secret , & unrelated ) )
require . Equal ( t , tt . wantDelete , filter . Delete ( tt . secret ) )
} )
}
}
func TestOIDCClientWatcherControllerFilterOIDCClient ( t * testing . T ) {
t . Parallel ( )
tests := [ ] struct {
name string
oidcClient configv1alpha1 . OIDCClient
wantAdd bool
wantUpdate bool
wantDelete bool
} {
{
2022-07-06 17:34:24 +00:00
name : "name has client.oauth.pinniped.dev- prefix" ,
oidcClient : configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Name : "client.oauth.pinniped.dev-foo" } ,
} ,
2022-06-17 16:56:53 +00:00
wantAdd : true ,
wantUpdate : true ,
wantDelete : true ,
} ,
2022-07-06 17:34:24 +00:00
{
name : "name does not have client.oauth.pinniped.dev- prefix" ,
oidcClient : configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Name : "something.oauth.pinniped.dev-foo" } ,
} ,
wantAdd : false ,
wantUpdate : false ,
wantDelete : false ,
} ,
{
name : "other names without any particular pinniped.dev prefixes" ,
oidcClient : configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Name : "something" } ,
} ,
wantAdd : false ,
wantUpdate : false ,
wantDelete : false ,
} ,
2022-06-17 16:56:53 +00:00
}
for _ , test := range tests {
tt := test
t . Run ( tt . name , func ( t * testing . T ) {
t . Parallel ( )
secretInformer := kubeinformers . NewSharedInformerFactory (
kubernetesfake . NewSimpleClientset ( ) ,
0 ,
) . Core ( ) . V1 ( ) . Secrets ( )
oidcClientsInformer := pinnipedinformers . NewSharedInformerFactory (
pinnipedfake . NewSimpleClientset ( ) ,
0 ,
) . Config ( ) . V1alpha1 ( ) . OIDCClients ( )
withInformer := testutil . NewObservableWithInformerOption ( )
_ = NewOIDCClientWatcherController (
nil , // pinnipedClient, not needed
secretInformer ,
oidcClientsInformer ,
withInformer . WithInformer ,
)
unrelated := configv1alpha1 . OIDCClient { }
filter := withInformer . GetFilterForInformer ( oidcClientsInformer )
require . Equal ( t , tt . wantAdd , filter . Add ( & tt . oidcClient ) )
require . Equal ( t , tt . wantUpdate , filter . Update ( & unrelated , & tt . oidcClient ) )
require . Equal ( t , tt . wantUpdate , filter . Update ( & tt . oidcClient , & unrelated ) )
require . Equal ( t , tt . wantDelete , filter . Delete ( & tt . oidcClient ) )
} )
}
}
func TestOIDCClientWatcherControllerSync ( t * testing . T ) {
t . Parallel ( )
const (
2022-07-06 17:34:24 +00:00
testName = "client.oauth.pinniped.dev-test-name"
2022-06-17 16:56:53 +00:00
testNamespace = "test-namespace"
testUID = "test-uid-123"
)
now := metav1 . NewTime ( time . Now ( ) . UTC ( ) )
earlier := metav1 . NewTime ( now . Add ( - 1 * time . Hour ) . UTC ( ) )
happyAllowedGrantTypesCondition := func ( time metav1 . Time , observedGeneration int64 ) configv1alpha1 . Condition {
return configv1alpha1 . Condition {
Type : "AllowedGrantTypesValid" ,
Status : "True" ,
LastTransitionTime : time ,
Reason : "Success" ,
Message : ` "allowedGrantTypes" is valid ` ,
ObservedGeneration : observedGeneration ,
}
}
sadAllowedGrantTypesCondition := func ( time metav1 . Time , observedGeneration int64 , message string ) configv1alpha1 . Condition {
return configv1alpha1 . Condition {
Type : "AllowedGrantTypesValid" ,
Status : "False" ,
LastTransitionTime : time ,
Reason : "MissingRequiredValue" ,
Message : message ,
ObservedGeneration : observedGeneration ,
}
}
happyClientSecretsCondition := func ( howMany int , time metav1 . Time , observedGeneration int64 ) configv1alpha1 . Condition {
return configv1alpha1 . Condition {
Type : "ClientSecretExists" ,
Status : "True" ,
LastTransitionTime : time ,
Reason : "Success" ,
Message : fmt . Sprintf ( ` %d client secret(s) found ` , howMany ) ,
ObservedGeneration : observedGeneration ,
}
}
2022-07-06 17:34:24 +00:00
sadNoClientSecretsCondition := func ( time metav1 . Time , observedGeneration int64 , message string ) configv1alpha1 . Condition {
2022-06-17 16:56:53 +00:00
return configv1alpha1 . Condition {
Type : "ClientSecretExists" ,
Status : "False" ,
LastTransitionTime : time ,
Reason : "NoClientSecretFound" ,
Message : message ,
ObservedGeneration : observedGeneration ,
}
}
2022-07-06 17:34:24 +00:00
sadInvalidClientSecretsCondition := func ( time metav1 . Time , observedGeneration int64 , message string ) configv1alpha1 . Condition {
return configv1alpha1 . Condition {
Type : "ClientSecretExists" ,
Status : "False" ,
LastTransitionTime : time ,
Reason : "InvalidClientSecretFound" ,
Message : message ,
ObservedGeneration : observedGeneration ,
}
}
2022-06-17 16:56:53 +00:00
happyAllowedScopesCondition := func ( time metav1 . Time , observedGeneration int64 ) configv1alpha1 . Condition {
return configv1alpha1 . Condition {
Type : "AllowedScopesValid" ,
Status : "True" ,
LastTransitionTime : time ,
Reason : "Success" ,
Message : ` "allowedScopes" is valid ` ,
ObservedGeneration : observedGeneration ,
}
}
sadAllowedScopesCondition := func ( time metav1 . Time , observedGeneration int64 , message string ) configv1alpha1 . Condition {
return configv1alpha1 . Condition {
Type : "AllowedScopesValid" ,
Status : "False" ,
LastTransitionTime : time ,
Reason : "MissingRequiredValue" ,
Message : message ,
ObservedGeneration : observedGeneration ,
}
}
tests := [ ] struct {
name string
inputObjects [ ] runtime . Object
inputSecrets [ ] runtime . Object
wantErr string
wantResultingOIDCClients [ ] configv1alpha1 . OIDCClient
wantAPIActions int
} {
{
name : "no OIDCClients" ,
wantAPIActions : 0 , // no updates
} ,
{
2022-07-06 17:34:24 +00:00
name : "OIDCClient with wrong prefix is ignored" ,
2022-06-17 16:56:53 +00:00
inputObjects : [ ] runtime . Object { & configv1alpha1 . OIDCClient {
2022-07-06 17:34:24 +00:00
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : "wrong-prefix-name" , Generation : 1234 , UID : testUID } ,
} } ,
wantAPIActions : 0 , // no updates
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : "wrong-prefix-name" , Generation : 1234 , UID : testUID } ,
2022-06-17 16:56:53 +00:00
} } ,
2022-07-06 17:34:24 +00:00
} ,
{
name : "successfully validate minimal OIDCClient and one client secret stored (while ignoring client with wrong prefix)" ,
inputObjects : [ ] runtime . Object {
& configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : "wrong-prefix-name" , Generation : 1234 , UID : testUID } ,
} ,
& configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Spec : configv1alpha1 . OIDCClientSpec {
AllowedGrantTypes : [ ] configv1alpha1 . GrantType { "authorization_code" } ,
AllowedScopes : [ ] configv1alpha1 . Scope { "openid" } ,
} ,
} ,
} ,
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { testutil . HashedPassword1AtSupervisorMinCost } ) } ,
2022-06-17 16:56:53 +00:00
wantAPIActions : 1 , // one update
2022-07-06 17:34:24 +00:00
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient {
{
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : "wrong-prefix-name" , Generation : 1234 , UID : testUID } ,
} ,
{
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Ready" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( now , 1234 ) ,
happyAllowedScopesCondition ( now , 1234 ) ,
happyClientSecretsCondition ( 1 , now , 1234 ) ,
} ,
TotalClientSecrets : 1 ,
2022-06-17 16:56:53 +00:00
} ,
} ,
2022-07-06 17:34:24 +00:00
} ,
2022-06-17 16:56:53 +00:00
} ,
{
name : "successfully validate minimal OIDCClient and two client secrets stored" ,
inputObjects : [ ] runtime . Object { & configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Spec : configv1alpha1 . OIDCClientSpec {
AllowedGrantTypes : [ ] configv1alpha1 . GrantType { "authorization_code" } ,
AllowedScopes : [ ] configv1alpha1 . Scope { "openid" } ,
} ,
} } ,
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { testutil . HashedPassword1AtSupervisorMinCost , testutil . HashedPassword2AtSupervisorMinCost } ) } ,
2022-06-17 16:56:53 +00:00
wantAPIActions : 1 , // one update
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Ready" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( now , 1234 ) ,
happyAllowedScopesCondition ( now , 1234 ) ,
happyClientSecretsCondition ( 2 , now , 1234 ) ,
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 2 ,
2022-06-17 16:56:53 +00:00
} ,
} } ,
} ,
{
name : "an already validated OIDCClient does not have its conditions updated when everything is still valid" ,
inputObjects : [ ] runtime . Object { & configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Spec : configv1alpha1 . OIDCClientSpec {
AllowedGrantTypes : [ ] configv1alpha1 . GrantType { "authorization_code" } ,
AllowedScopes : [ ] configv1alpha1 . Scope { "openid" } ,
} ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Ready" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( earlier , 1234 ) ,
happyAllowedScopesCondition ( earlier , 1234 ) ,
happyClientSecretsCondition ( 1 , earlier , 1234 ) ,
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 1 ,
2022-06-17 16:56:53 +00:00
} ,
} } ,
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { testutil . HashedPassword1AtSupervisorMinCost } ) } ,
2022-06-17 16:56:53 +00:00
wantAPIActions : 0 , // no updates
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Ready" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( earlier , 1234 ) ,
happyAllowedScopesCondition ( earlier , 1234 ) ,
happyClientSecretsCondition ( 1 , earlier , 1234 ) ,
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 1 ,
2022-06-17 16:56:53 +00:00
} ,
} } ,
} ,
{
name : "missing required minimum settings and missing client secret storage" ,
inputObjects : [ ] runtime . Object { & configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Spec : configv1alpha1 . OIDCClientSpec { } ,
} } ,
wantAPIActions : 1 , // one update
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Error" ,
Conditions : [ ] configv1alpha1 . Condition {
sadAllowedGrantTypesCondition ( now , 1234 , ` "authorization_code" must always be included in "allowedGrantTypes" ` ) ,
sadAllowedScopesCondition ( now , 1234 , ` "openid" must always be included in "allowedScopes" ` ) ,
2022-07-06 17:34:24 +00:00
sadNoClientSecretsCondition ( now , 1234 , "no client secret found (no Secret storage found)" ) ,
2022-06-17 16:56:53 +00:00
} ,
} ,
} } ,
} ,
{
name : "client secret storage exists but cannot be read" ,
inputObjects : [ ] runtime . Object { & configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Spec : configv1alpha1 . OIDCClientSpec {
AllowedGrantTypes : [ ] configv1alpha1 . GrantType { "authorization_code" } ,
AllowedScopes : [ ] configv1alpha1 . Scope { "openid" } ,
} ,
} } ,
2022-07-14 16:51:11 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUIDWithWrongVersion ( t , testNamespace , testUID ) } ,
2022-06-17 16:56:53 +00:00
wantAPIActions : 1 , // one update
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Error" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( now , 1234 ) ,
happyAllowedScopesCondition ( now , 1234 ) ,
2022-07-06 17:34:24 +00:00
sadNoClientSecretsCondition ( now , 1234 , "error reading client secret storage: OIDC client secret storage data has wrong version: OIDC client secret storage has version wrong-version instead of 1" ) ,
2022-06-17 16:56:53 +00:00
} ,
} ,
} } ,
} ,
{
name : "client secret storage exists but does not contain any client secrets" ,
inputObjects : [ ] runtime . Object { & configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Spec : configv1alpha1 . OIDCClientSpec {
AllowedGrantTypes : [ ] configv1alpha1 . GrantType { "authorization_code" } ,
AllowedScopes : [ ] configv1alpha1 . Scope { "openid" } ,
} ,
} } ,
2022-07-14 16:51:11 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { } ) } ,
2022-06-17 16:56:53 +00:00
wantAPIActions : 1 , // one update
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Error" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( now , 1234 ) ,
happyAllowedScopesCondition ( now , 1234 ) ,
2022-07-06 17:34:24 +00:00
sadNoClientSecretsCondition ( now , 1234 , "no client secret found (empty list in storage)" ) ,
} ,
TotalClientSecrets : 0 ,
} ,
} } ,
} ,
{
name : "client secret storage exists but some of the client secrets are invalid bcrypt hashes" ,
inputObjects : [ ] runtime . Object { & configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Spec : configv1alpha1 . OIDCClientSpec {
AllowedGrantTypes : [ ] configv1alpha1 . GrantType { "authorization_code" } ,
AllowedScopes : [ ] configv1alpha1 . Scope { "openid" } ,
} ,
} } ,
2022-07-14 16:51:11 +00:00
inputSecrets : [ ] runtime . Object {
testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID ,
2022-07-20 20:55:56 +00:00
[ ] string { testutil . HashedPassword1AtSupervisorMinCost , testutil . HashedPassword1JustBelowSupervisorMinCost , testutil . HashedPassword1InvalidFormat } ) ,
2022-07-14 16:51:11 +00:00
} ,
2022-07-06 17:34:24 +00:00
wantAPIActions : 1 , // one update
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Error" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( now , 1234 ) ,
happyAllowedScopesCondition ( now , 1234 ) ,
sadInvalidClientSecretsCondition ( now , 1234 ,
2022-07-14 16:51:11 +00:00
"3 stored client secrets found, but some were invalid, so none will be used: " +
2022-07-20 20:55:56 +00:00
"hashed client secret at index 1: bcrypt cost 11 is below the required minimum of 12; " +
2022-07-06 17:34:24 +00:00
"hashed client secret at index 2: crypto/bcrypt: hashedSecret too short to be a bcrypted password" ) ,
2022-06-17 16:56:53 +00:00
} ,
2022-07-14 16:51:11 +00:00
TotalClientSecrets : 0 ,
2022-06-17 16:56:53 +00:00
} ,
} } ,
} ,
{
name : "can operate on multiple at a time, e.g. one is valid one another is missing required minimum settings" ,
inputObjects : [ ] runtime . Object {
& configv1alpha1 . OIDCClient {
2022-07-06 17:34:24 +00:00
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : "client.oauth.pinniped.dev-test1" , Generation : 1234 , UID : "uid1" } ,
2022-06-17 16:56:53 +00:00
Spec : configv1alpha1 . OIDCClientSpec {
AllowedGrantTypes : [ ] configv1alpha1 . GrantType { "authorization_code" } ,
AllowedScopes : [ ] configv1alpha1 . Scope { "openid" } ,
} ,
} ,
& configv1alpha1 . OIDCClient {
2022-07-06 17:34:24 +00:00
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : "client.oauth.pinniped.dev-test2" , Generation : 4567 , UID : "uid2" } ,
2022-06-17 16:56:53 +00:00
Spec : configv1alpha1 . OIDCClientSpec { } ,
} ,
} ,
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , "uid1" , [ ] string { testutil . HashedPassword1AtSupervisorMinCost } ) } ,
2022-06-17 16:56:53 +00:00
wantAPIActions : 2 , // one update for each OIDCClient
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient {
{
2022-07-06 17:34:24 +00:00
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : "client.oauth.pinniped.dev-test1" , Generation : 1234 , UID : "uid1" } ,
2022-06-17 16:56:53 +00:00
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Ready" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( now , 1234 ) ,
happyAllowedScopesCondition ( now , 1234 ) ,
happyClientSecretsCondition ( 1 , now , 1234 ) ,
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 1 ,
2022-06-17 16:56:53 +00:00
} ,
} ,
{
2022-07-06 17:34:24 +00:00
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : "client.oauth.pinniped.dev-test2" , Generation : 4567 , UID : "uid2" } ,
2022-06-17 16:56:53 +00:00
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Error" ,
Conditions : [ ] configv1alpha1 . Condition {
sadAllowedGrantTypesCondition ( now , 4567 , ` "authorization_code" must always be included in "allowedGrantTypes" ` ) ,
sadAllowedScopesCondition ( now , 4567 , ` "openid" must always be included in "allowedScopes" ` ) ,
2022-07-06 17:34:24 +00:00
sadNoClientSecretsCondition ( now , 4567 , "no client secret found (no Secret storage found)" ) ,
2022-06-17 16:56:53 +00:00
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 0 ,
2022-06-17 16:56:53 +00:00
} ,
} ,
} ,
} ,
{
name : "a previously invalid OIDCClient has its spec changed to become valid so the conditions are updated" ,
inputObjects : [ ] runtime . Object { & configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 4567 , UID : testUID } ,
Spec : configv1alpha1 . OIDCClientSpec {
AllowedGrantTypes : [ ] configv1alpha1 . GrantType { "authorization_code" } ,
AllowedScopes : [ ] configv1alpha1 . Scope { "openid" } ,
} ,
// was invalid on previous run of controller which observed an old generation at an earlier time
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Error" ,
Conditions : [ ] configv1alpha1 . Condition {
sadAllowedGrantTypesCondition ( earlier , 1234 , ` "authorization_code" must always be included in "allowedGrantTypes" ` ) ,
sadAllowedScopesCondition ( earlier , 1234 , ` "openid" must always be included in "allowedScopes" ` ) ,
happyClientSecretsCondition ( 1 , earlier , 1234 ) ,
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 1 ,
2022-06-17 16:56:53 +00:00
} ,
} } ,
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { testutil . HashedPassword1AtSupervisorMinCost } ) } ,
2022-06-17 16:56:53 +00:00
wantAPIActions : 1 , // one update
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 4567 , UID : testUID } ,
// status was updated to reflect the current generation at the current time
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Ready" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( now , 4567 ) ,
happyAllowedScopesCondition ( now , 4567 ) ,
happyClientSecretsCondition ( 1 , earlier , 4567 ) , // was already validated earlier
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 1 ,
2022-06-17 16:56:53 +00:00
} ,
} } ,
} ,
{
name : "refresh_token must be included in allowedGrantTypes when offline_access is included in allowedScopes" ,
inputObjects : [ ] runtime . Object { & configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Spec : configv1alpha1 . OIDCClientSpec {
AllowedGrantTypes : [ ] configv1alpha1 . GrantType { "authorization_code" } ,
AllowedScopes : [ ] configv1alpha1 . Scope { "openid" , "offline_access" } ,
} ,
} } ,
wantAPIActions : 1 , // one update
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { testutil . HashedPassword1AtSupervisorMinCost } ) } ,
2022-06-17 16:56:53 +00:00
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Error" ,
Conditions : [ ] configv1alpha1 . Condition {
sadAllowedGrantTypesCondition ( now , 1234 , ` "refresh_token" must be included in "allowedGrantTypes" when "offline_access" is included in "allowedScopes" ` ) ,
happyAllowedScopesCondition ( now , 1234 ) ,
happyClientSecretsCondition ( 1 , now , 1234 ) ,
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 1 ,
} ,
} } ,
} ,
{
name : "multiple errors on allowedScopes and allowedGrantTypes" ,
inputObjects : [ ] runtime . Object { & configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Spec : configv1alpha1 . OIDCClientSpec {
AllowedGrantTypes : [ ] configv1alpha1 . GrantType { "refresh_token" } ,
AllowedScopes : [ ] configv1alpha1 . Scope { "pinniped:request-audience" } ,
} ,
} } ,
wantAPIActions : 1 , // one update
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { testutil . HashedPassword1AtSupervisorMinCost } ) } ,
2022-07-06 17:34:24 +00:00
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Error" ,
Conditions : [ ] configv1alpha1 . Condition {
sadAllowedGrantTypesCondition ( now , 1234 ,
` "authorization_code" must always be included in "allowedGrantTypes"; ` +
` "urn:ietf:params:oauth:grant-type:token-exchange" must be included in "allowedGrantTypes" when "pinniped:request-audience" is included in "allowedScopes" ` ) ,
sadAllowedScopesCondition ( now , 1234 ,
` "openid" must always be included in "allowedScopes"; ` +
` "offline_access" must be included in "allowedScopes" when "refresh_token" is included in "allowedGrantTypes"; ` +
` "username" and "groups" must be included in "allowedScopes" when "pinniped:request-audience" is included in "allowedScopes" ` ) ,
happyClientSecretsCondition ( 1 , now , 1234 ) ,
} ,
TotalClientSecrets : 1 ,
} ,
} } ,
} ,
{
name : "another combination of multiple errors on allowedScopes and allowedGrantTypes" ,
inputObjects : [ ] runtime . Object { & configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Spec : configv1alpha1 . OIDCClientSpec {
AllowedGrantTypes : [ ] configv1alpha1 . GrantType { "urn:ietf:params:oauth:grant-type:token-exchange" } ,
AllowedScopes : [ ] configv1alpha1 . Scope { "offline_access" } ,
} ,
} } ,
wantAPIActions : 1 , // one update
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { testutil . HashedPassword1AtSupervisorMinCost } ) } ,
2022-07-06 17:34:24 +00:00
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Error" ,
Conditions : [ ] configv1alpha1 . Condition {
sadAllowedGrantTypesCondition ( now , 1234 ,
` "authorization_code" must always be included in "allowedGrantTypes"; ` +
` "refresh_token" must be included in "allowedGrantTypes" when "offline_access" is included in "allowedScopes" ` ) ,
sadAllowedScopesCondition ( now , 1234 ,
` "openid" must always be included in "allowedScopes"; ` +
` "pinniped:request-audience" must be included in "allowedScopes" when "urn:ietf:params:oauth:grant-type:token-exchange" is included in "allowedGrantTypes" ` ) ,
happyClientSecretsCondition ( 1 , now , 1234 ) ,
} ,
TotalClientSecrets : 1 ,
2022-06-17 16:56:53 +00:00
} ,
} } ,
} ,
{
name : "urn:ietf:params:oauth:grant-type:token-exchange must be included in allowedGrantTypes when pinniped:request-audience is included in allowedScopes" ,
inputObjects : [ ] runtime . Object { & configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Spec : configv1alpha1 . OIDCClientSpec {
AllowedGrantTypes : [ ] configv1alpha1 . GrantType { "authorization_code" } ,
AllowedScopes : [ ] configv1alpha1 . Scope { "openid" , "pinniped:request-audience" , "username" , "groups" } ,
} ,
} } ,
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { testutil . HashedPassword1AtSupervisorMinCost } ) } ,
2022-06-17 16:56:53 +00:00
wantAPIActions : 1 , // one update
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Error" ,
Conditions : [ ] configv1alpha1 . Condition {
sadAllowedGrantTypesCondition ( now , 1234 , ` "urn:ietf:params:oauth:grant-type:token-exchange" must be included in "allowedGrantTypes" when "pinniped:request-audience" is included in "allowedScopes" ` ) ,
happyAllowedScopesCondition ( now , 1234 ) ,
happyClientSecretsCondition ( 1 , now , 1234 ) ,
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 1 ,
2022-06-17 16:56:53 +00:00
} ,
} } ,
} ,
{
name : "offline_access must be included in allowedScopes when refresh_token is included in allowedGrantTypes" ,
inputObjects : [ ] runtime . Object { & 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" } ,
} ,
} } ,
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { testutil . HashedPassword1AtSupervisorMinCost } ) } ,
2022-06-17 16:56:53 +00:00
wantAPIActions : 1 , // one update
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Error" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( now , 1234 ) ,
sadAllowedScopesCondition ( now , 1234 , ` "offline_access" must be included in "allowedScopes" when "refresh_token" is included in "allowedGrantTypes" ` ) ,
happyClientSecretsCondition ( 1 , now , 1234 ) ,
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 1 ,
2022-06-17 16:56:53 +00:00
} ,
} } ,
} ,
{
name : "username and groups must also be included in allowedScopes when pinniped:request-audience is included in allowedScopes: both missing" ,
inputObjects : [ ] runtime . Object { & configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Spec : configv1alpha1 . OIDCClientSpec {
AllowedGrantTypes : [ ] configv1alpha1 . GrantType { "authorization_code" , "urn:ietf:params:oauth:grant-type:token-exchange" } ,
AllowedScopes : [ ] configv1alpha1 . Scope { "openid" , "pinniped:request-audience" } ,
} ,
} } ,
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { testutil . HashedPassword1AtSupervisorMinCost } ) } ,
2022-06-17 16:56:53 +00:00
wantAPIActions : 1 , // one update
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Error" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( now , 1234 ) ,
sadAllowedScopesCondition ( now , 1234 , ` "username" and "groups" must be included in "allowedScopes" when "pinniped:request-audience" is included in "allowedScopes" ` ) ,
happyClientSecretsCondition ( 1 , now , 1234 ) ,
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 1 ,
2022-06-17 16:56:53 +00:00
} ,
} } ,
} ,
{
name : "username and groups must also be included in allowedScopes when pinniped:request-audience is included in allowedScopes: username missing" ,
inputObjects : [ ] runtime . Object { & configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Spec : configv1alpha1 . OIDCClientSpec {
AllowedGrantTypes : [ ] configv1alpha1 . GrantType { "authorization_code" , "urn:ietf:params:oauth:grant-type:token-exchange" } ,
AllowedScopes : [ ] configv1alpha1 . Scope { "openid" , "pinniped:request-audience" , "groups" } ,
} ,
} } ,
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { testutil . HashedPassword1AtSupervisorMinCost } ) } ,
2022-06-17 16:56:53 +00:00
wantAPIActions : 1 , // one update
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Error" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( now , 1234 ) ,
sadAllowedScopesCondition ( now , 1234 , ` "username" and "groups" must be included in "allowedScopes" when "pinniped:request-audience" is included in "allowedScopes" ` ) ,
happyClientSecretsCondition ( 1 , now , 1234 ) ,
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 1 ,
2022-06-17 16:56:53 +00:00
} ,
} } ,
} ,
{
name : "username and groups must also be included in allowedScopes when pinniped:request-audience is included in allowedScopes: groups missing" ,
inputObjects : [ ] runtime . Object { & configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Spec : configv1alpha1 . OIDCClientSpec {
AllowedGrantTypes : [ ] configv1alpha1 . GrantType { "authorization_code" , "urn:ietf:params:oauth:grant-type:token-exchange" } ,
AllowedScopes : [ ] configv1alpha1 . Scope { "openid" , "pinniped:request-audience" , "username" } ,
} ,
} } ,
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { testutil . HashedPassword1AtSupervisorMinCost } ) } ,
2022-06-17 16:56:53 +00:00
wantAPIActions : 1 , // one update
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Error" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( now , 1234 ) ,
sadAllowedScopesCondition ( now , 1234 , ` "username" and "groups" must be included in "allowedScopes" when "pinniped:request-audience" is included in "allowedScopes" ` ) ,
happyClientSecretsCondition ( 1 , now , 1234 ) ,
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 1 ,
2022-06-17 16:56:53 +00:00
} ,
} } ,
} ,
{
name : "pinniped:request-audience must be included in allowedScopes when urn:ietf:params:oauth:grant-type:token-exchange is included in allowedGrantTypes" ,
inputObjects : [ ] runtime . Object { & configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Spec : configv1alpha1 . OIDCClientSpec {
AllowedGrantTypes : [ ] configv1alpha1 . GrantType { "authorization_code" , "urn:ietf:params:oauth:grant-type:token-exchange" } ,
AllowedScopes : [ ] configv1alpha1 . Scope { "openid" } ,
} ,
} } ,
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { testutil . HashedPassword1AtSupervisorMinCost } ) } ,
2022-06-17 16:56:53 +00:00
wantAPIActions : 1 , // one update
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Error" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( now , 1234 ) ,
sadAllowedScopesCondition ( now , 1234 , ` "pinniped:request-audience" must be included in "allowedScopes" when "urn:ietf:params:oauth:grant-type:token-exchange" is included in "allowedGrantTypes" ` ) ,
happyClientSecretsCondition ( 1 , now , 1234 ) ,
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 1 ,
2022-06-17 16:56:53 +00:00
} ,
} } ,
} ,
{
name : "successfully validate an OIDCClient with all allowedGrantTypes and all allowedScopes" ,
inputObjects : [ ] runtime . Object { & configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Spec : configv1alpha1 . OIDCClientSpec {
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" } ,
} ,
} } ,
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { testutil . HashedPassword1AtSupervisorMinCost } ) } ,
2022-06-17 16:56:53 +00:00
wantAPIActions : 1 , // one update
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Ready" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( now , 1234 ) ,
happyAllowedScopesCondition ( now , 1234 ) ,
happyClientSecretsCondition ( 1 , now , 1234 ) ,
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 1 ,
2022-06-17 16:56:53 +00:00
} ,
} } ,
} ,
{
name : "successfully validate an OIDCClient for offline access without kube API access without username/groups" ,
inputObjects : [ ] runtime . Object { & 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" } ,
} ,
} } ,
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { testutil . HashedPassword1AtSupervisorMinCost } ) } ,
2022-06-17 16:56:53 +00:00
wantAPIActions : 1 , // one update
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Ready" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( now , 1234 ) ,
happyAllowedScopesCondition ( now , 1234 ) ,
happyClientSecretsCondition ( 1 , now , 1234 ) ,
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 1 ,
2022-06-17 16:56:53 +00:00
} ,
} } ,
} ,
{
name : "successfully validate an OIDCClient for offline access without kube API access with username" ,
inputObjects : [ ] runtime . Object { & 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" } ,
} ,
} } ,
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { testutil . HashedPassword1AtSupervisorMinCost } ) } ,
2022-06-17 16:56:53 +00:00
wantAPIActions : 1 , // one update
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Ready" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( now , 1234 ) ,
happyAllowedScopesCondition ( now , 1234 ) ,
happyClientSecretsCondition ( 1 , now , 1234 ) ,
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 1 ,
2022-06-17 16:56:53 +00:00
} ,
} } ,
} ,
{
name : "successfully validate an OIDCClient for offline access without kube API access with groups" ,
inputObjects : [ ] runtime . Object { & 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" , "groups" } ,
} ,
} } ,
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { testutil . HashedPassword1AtSupervisorMinCost } ) } ,
2022-06-17 16:56:53 +00:00
wantAPIActions : 1 , // one update
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Ready" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( now , 1234 ) ,
happyAllowedScopesCondition ( now , 1234 ) ,
happyClientSecretsCondition ( 1 , now , 1234 ) ,
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 1 ,
2022-06-17 16:56:53 +00:00
} ,
} } ,
} ,
{
name : "successfully validate an OIDCClient for offline access without kube API access with both username and groups" ,
inputObjects : [ ] runtime . Object { & 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" } ,
} ,
} } ,
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { testutil . HashedPassword1AtSupervisorMinCost } ) } ,
2022-06-17 16:56:53 +00:00
wantAPIActions : 1 , // one update
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Ready" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( now , 1234 ) ,
happyAllowedScopesCondition ( now , 1234 ) ,
happyClientSecretsCondition ( 1 , now , 1234 ) ,
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 1 ,
2022-06-17 16:56:53 +00:00
} ,
} } ,
} ,
{
name : "successfully validate an OIDCClient without offline access without kube API access with username" ,
inputObjects : [ ] runtime . Object { & configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Spec : configv1alpha1 . OIDCClientSpec {
AllowedGrantTypes : [ ] configv1alpha1 . GrantType { "authorization_code" } ,
AllowedScopes : [ ] configv1alpha1 . Scope { "openid" , "username" } ,
} ,
} } ,
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { testutil . HashedPassword1AtSupervisorMinCost } ) } ,
2022-06-17 16:56:53 +00:00
wantAPIActions : 1 , // one update
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Ready" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( now , 1234 ) ,
happyAllowedScopesCondition ( now , 1234 ) ,
happyClientSecretsCondition ( 1 , now , 1234 ) ,
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 1 ,
2022-06-17 16:56:53 +00:00
} ,
} } ,
} ,
{
name : "successfully validate an OIDCClient without offline access without kube API access with groups" ,
inputObjects : [ ] runtime . Object { & configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Spec : configv1alpha1 . OIDCClientSpec {
AllowedGrantTypes : [ ] configv1alpha1 . GrantType { "authorization_code" } ,
AllowedScopes : [ ] configv1alpha1 . Scope { "openid" , "username" } ,
} ,
} } ,
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { testutil . HashedPassword1AtSupervisorMinCost } ) } ,
2022-06-17 16:56:53 +00:00
wantAPIActions : 1 , // one update
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Ready" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( now , 1234 ) ,
happyAllowedScopesCondition ( now , 1234 ) ,
happyClientSecretsCondition ( 1 , now , 1234 ) ,
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 1 ,
2022-06-17 16:56:53 +00:00
} ,
} } ,
} ,
{
name : "successfully validate an OIDCClient without offline access without kube API access with both username and groups" ,
inputObjects : [ ] runtime . Object { & configv1alpha1 . OIDCClient {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Spec : configv1alpha1 . OIDCClientSpec {
AllowedGrantTypes : [ ] configv1alpha1 . GrantType { "authorization_code" } ,
AllowedScopes : [ ] configv1alpha1 . Scope { "openid" , "username" , "groups" } ,
} ,
} } ,
2022-07-20 20:55:56 +00:00
inputSecrets : [ ] runtime . Object { testutil . OIDCClientSecretStorageSecretForUID ( t , testNamespace , testUID , [ ] string { testutil . HashedPassword1AtSupervisorMinCost } ) } ,
2022-06-17 16:56:53 +00:00
wantAPIActions : 1 , // one update
wantResultingOIDCClients : [ ] configv1alpha1 . OIDCClient { {
ObjectMeta : metav1 . ObjectMeta { Namespace : testNamespace , Name : testName , Generation : 1234 , UID : testUID } ,
Status : configv1alpha1 . OIDCClientStatus {
Phase : "Ready" ,
Conditions : [ ] configv1alpha1 . Condition {
happyAllowedGrantTypesCondition ( now , 1234 ) ,
happyAllowedScopesCondition ( now , 1234 ) ,
happyClientSecretsCondition ( 1 , now , 1234 ) ,
} ,
2022-07-06 17:34:24 +00:00
TotalClientSecrets : 1 ,
2022-06-17 16:56:53 +00:00
} ,
} } ,
} ,
}
for _ , tt := range tests {
tt := tt
t . Run ( tt . name , func ( t * testing . T ) {
t . Parallel ( )
fakePinnipedClient := pinnipedfake . NewSimpleClientset ( tt . inputObjects ... )
fakePinnipedClientForInformers := pinnipedfake . NewSimpleClientset ( tt . inputObjects ... )
pinnipedInformers := pinnipedinformers . NewSharedInformerFactory ( fakePinnipedClientForInformers , 0 )
fakeKubeClient := kubernetesfake . NewSimpleClientset ( tt . inputSecrets ... )
kubeInformers := kubeinformers . NewSharedInformerFactoryWithOptions ( fakeKubeClient , 0 )
controller := NewOIDCClientWatcherController (
fakePinnipedClient ,
kubeInformers . Core ( ) . V1 ( ) . Secrets ( ) ,
pinnipedInformers . Config ( ) . V1alpha1 ( ) . OIDCClients ( ) ,
controllerlib . WithInformer ,
)
ctx , cancel := context . WithCancel ( context . Background ( ) )
defer cancel ( )
pinnipedInformers . Start ( ctx . Done ( ) )
kubeInformers . Start ( ctx . Done ( ) )
controllerlib . TestRunSynchronously ( t , controller )
syncCtx := controllerlib . Context { Context : ctx , Key : controllerlib . Key { } }
if err := controllerlib . TestSync ( t , controller , syncCtx ) ; tt . wantErr != "" {
require . EqualError ( t , err , tt . wantErr )
} else {
require . NoError ( t , err )
}
require . Len ( t , fakePinnipedClient . Actions ( ) , tt . wantAPIActions )
actualOIDCClients , err := fakePinnipedClient . ConfigV1alpha1 ( ) . OIDCClients ( testNamespace ) . List ( ctx , metav1 . ListOptions { } )
require . NoError ( t , err )
// Assert on the expected Status of the OIDCClients. Preprocess them a bit so that they're easier to assert against.
require . ElementsMatch ( t , tt . wantResultingOIDCClients , normalizeOIDCClients ( actualOIDCClients . Items , now ) )
} )
}
}
func normalizeOIDCClients ( oidcClients [ ] configv1alpha1 . OIDCClient , now metav1 . Time ) [ ] configv1alpha1 . OIDCClient {
result := make ( [ ] configv1alpha1 . OIDCClient , 0 , len ( oidcClients ) )
for _ , u := range oidcClients {
normalized := u . DeepCopy ( )
// We're only interested in comparing the status, so zero out the spec.
normalized . Spec = configv1alpha1 . OIDCClientSpec { }
// Round down the LastTransitionTime values to `now` if they were just updated. This makes
// it much easier to encode assertions about the expected timestamps.
for i := range normalized . Status . Conditions {
if time . Since ( normalized . Status . Conditions [ i ] . LastTransitionTime . Time ) < 5 * time . Second {
normalized . Status . Conditions [ i ] . LastTransitionTime = now
}
}
result = append ( result , * normalized )
}
return result
}