CRD already validates that IDP transform constant names are unique
- Remove that validation from the controller since the CRD already validates it during creates and updates. - Also finish the supervisor_federationdomain_status_test.go by adding more tests for both controller validations and CRD validations
This commit is contained in:
parent
bd5cabf0ff
commit
6d82a11645
@ -45,7 +45,6 @@ const (
|
|||||||
typeIdentityProvidersDisplayNamesUnique = "IdentityProvidersDisplayNamesUnique"
|
typeIdentityProvidersDisplayNamesUnique = "IdentityProvidersDisplayNamesUnique"
|
||||||
typeIdentityProvidersAPIGroupSuffixValid = "IdentityProvidersObjectRefAPIGroupSuffixValid"
|
typeIdentityProvidersAPIGroupSuffixValid = "IdentityProvidersObjectRefAPIGroupSuffixValid"
|
||||||
typeIdentityProvidersObjectRefKindValid = "IdentityProvidersObjectRefKindValid"
|
typeIdentityProvidersObjectRefKindValid = "IdentityProvidersObjectRefKindValid"
|
||||||
typeTransformsConstantsNamesUnique = "TransformsConstantsNamesUnique"
|
|
||||||
typeTransformsExpressionsValid = "TransformsExpressionsValid"
|
typeTransformsExpressionsValid = "TransformsExpressionsValid"
|
||||||
typeTransformsExamplesPassed = "TransformsExamplesPassed"
|
typeTransformsExamplesPassed = "TransformsExamplesPassed"
|
||||||
|
|
||||||
@ -62,7 +61,6 @@ const (
|
|||||||
reasonDuplicateDisplayNames = "DuplicateDisplayNames"
|
reasonDuplicateDisplayNames = "DuplicateDisplayNames"
|
||||||
reasonAPIGroupNameUnrecognized = "APIGroupUnrecognized"
|
reasonAPIGroupNameUnrecognized = "APIGroupUnrecognized"
|
||||||
reasonKindUnrecognized = "KindUnrecognized"
|
reasonKindUnrecognized = "KindUnrecognized"
|
||||||
reasonDuplicateConstantsNames = "DuplicateConstantsNames"
|
|
||||||
reasonInvalidTransformsExpressions = "InvalidTransformsExpressions"
|
reasonInvalidTransformsExpressions = "InvalidTransformsExpressions"
|
||||||
reasonTransformsExamplesFailed = "TransformsExamplesFailed"
|
reasonTransformsExamplesFailed = "TransformsExamplesFailed"
|
||||||
|
|
||||||
@ -330,7 +328,6 @@ func (c *federationDomainWatcherController) makeLegacyFederationDomainIssuer(
|
|||||||
conditions = appendIdentityProviderDuplicateDisplayNamesCondition(sets.Set[string]{}, conditions)
|
conditions = appendIdentityProviderDuplicateDisplayNamesCondition(sets.Set[string]{}, conditions)
|
||||||
conditions = appendIdentityProviderObjectRefAPIGroupSuffixCondition(c.apiGroup, []string{}, conditions)
|
conditions = appendIdentityProviderObjectRefAPIGroupSuffixCondition(c.apiGroup, []string{}, conditions)
|
||||||
conditions = appendIdentityProviderObjectRefKindCondition(c.sortedAllowedKinds(), []string{}, conditions)
|
conditions = appendIdentityProviderObjectRefKindCondition(c.sortedAllowedKinds(), []string{}, conditions)
|
||||||
conditions = appendTransformsConstantsNamesUniqueCondition([]string{}, conditions)
|
|
||||||
conditions = appendTransformsExpressionsValidCondition([]string{}, conditions)
|
conditions = appendTransformsExpressionsValidCondition([]string{}, conditions)
|
||||||
conditions = appendTransformsExamplesPassedCondition([]string{}, conditions)
|
conditions = appendTransformsExamplesPassedCondition([]string{}, conditions)
|
||||||
|
|
||||||
@ -431,7 +428,6 @@ func (c *federationDomainWatcherController) makeFederationDomainIssuerWithExplic
|
|||||||
conditions = appendIdentityProviderObjectRefAPIGroupSuffixCondition(c.apiGroup, badAPIGroupNames, conditions)
|
conditions = appendIdentityProviderObjectRefAPIGroupSuffixCondition(c.apiGroup, badAPIGroupNames, conditions)
|
||||||
conditions = appendIdentityProviderObjectRefKindCondition(c.sortedAllowedKinds(), badKinds, conditions)
|
conditions = appendIdentityProviderObjectRefKindCondition(c.sortedAllowedKinds(), badKinds, conditions)
|
||||||
|
|
||||||
conditions = appendTransformsConstantsNamesUniqueCondition(validationErrorMessages.errorsForConstants, conditions)
|
|
||||||
conditions = appendTransformsExpressionsValidCondition(validationErrorMessages.errorsForExpressions, conditions)
|
conditions = appendTransformsExpressionsValidCondition(validationErrorMessages.errorsForExpressions, conditions)
|
||||||
conditions = appendTransformsExamplesPassedCondition(validationErrorMessages.errorsForExamples, conditions)
|
conditions = appendTransformsExamplesPassedCondition(validationErrorMessages.errorsForExamples, conditions)
|
||||||
|
|
||||||
@ -472,13 +468,10 @@ func (c *federationDomainWatcherController) makeTransformationPipelineAndEvaluat
|
|||||||
idpIndex int,
|
idpIndex int,
|
||||||
validationErrorMessages *transformsValidationErrorMessages,
|
validationErrorMessages *transformsValidationErrorMessages,
|
||||||
) (*idtransform.TransformationPipeline, bool, error) {
|
) (*idtransform.TransformationPipeline, bool, error) {
|
||||||
consts, errorsForConstants, err := c.makeTransformsConstantsForIdentityProvider(idp, idpIndex)
|
consts, err := c.makeTransformsConstantsForIdentityProvider(idp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
if len(errorsForConstants) > 0 {
|
|
||||||
validationErrorMessages.errorsForConstants = append(validationErrorMessages.errorsForConstants, errorsForConstants)
|
|
||||||
}
|
|
||||||
|
|
||||||
pipeline, errorsForExpressions, err := c.makeTransformationPipelineForIdentityProvider(idp, idpIndex, consts)
|
pipeline, errorsForExpressions, err := c.makeTransformationPipelineForIdentityProvider(idp, idpIndex, consts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -498,22 +491,17 @@ func (c *federationDomainWatcherController) makeTransformationPipelineAndEvaluat
|
|||||||
|
|
||||||
func (c *federationDomainWatcherController) makeTransformsConstantsForIdentityProvider(
|
func (c *federationDomainWatcherController) makeTransformsConstantsForIdentityProvider(
|
||||||
idp configv1alpha1.FederationDomainIdentityProvider,
|
idp configv1alpha1.FederationDomainIdentityProvider,
|
||||||
idpIndex int,
|
) (*celtransformer.TransformationConstants, error) {
|
||||||
) (*celtransformer.TransformationConstants, string, error) {
|
|
||||||
consts := &celtransformer.TransformationConstants{
|
consts := &celtransformer.TransformationConstants{
|
||||||
StringConstants: map[string]string{},
|
StringConstants: map[string]string{},
|
||||||
StringListConstants: map[string][]string{},
|
StringListConstants: map[string][]string{},
|
||||||
}
|
}
|
||||||
constNames := sets.Set[string]{}
|
constNames := sets.Set[string]{}
|
||||||
duplicateConstNames := sets.Set[string]{}
|
|
||||||
|
|
||||||
// Read all the declared constants.
|
// Read all the declared constants.
|
||||||
for _, constant := range idp.Transforms.Constants {
|
for _, constant := range idp.Transforms.Constants {
|
||||||
// The CRD requires the name field, and validates that it has at least one character,
|
// The CRD requires the name field, and validates that it has at least one character,
|
||||||
// so here we only need to validate that they are unique.
|
// and validates that the names are unique within the list.
|
||||||
if constNames.Has(constant.Name) {
|
|
||||||
duplicateConstNames.Insert(constant.Name)
|
|
||||||
}
|
|
||||||
constNames.Insert(constant.Name)
|
constNames.Insert(constant.Name)
|
||||||
switch constant.Type {
|
switch constant.Type {
|
||||||
case "string":
|
case "string":
|
||||||
@ -522,17 +510,11 @@ func (c *federationDomainWatcherController) makeTransformsConstantsForIdentityPr
|
|||||||
consts.StringListConstants[constant.Name] = constant.StringListValue
|
consts.StringListConstants[constant.Name] = constant.StringListValue
|
||||||
default:
|
default:
|
||||||
// This shouldn't really happen since the CRD validates it, but handle it as an error.
|
// This shouldn't really happen since the CRD validates it, but handle it as an error.
|
||||||
return nil, "", fmt.Errorf("one of spec.identityProvider[].transforms.constants[].type is invalid: %q", constant.Type)
|
return nil, fmt.Errorf("one of spec.identityProvider[].transforms.constants[].type is invalid: %q", constant.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if duplicateConstNames.Len() > 0 {
|
return consts, nil
|
||||||
return consts, fmt.Sprintf(
|
|
||||||
"the names specified by .spec.identityProviders[%d].transforms.constants[].name contain duplicates: %s",
|
|
||||||
idpIndex, strings.Join(sortAndQuote(duplicateConstNames.UnsortedList()), ", ")), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return consts, "", nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *federationDomainWatcherController) makeTransformationPipelineForIdentityProvider(
|
func (c *federationDomainWatcherController) makeTransformationPipelineForIdentityProvider(
|
||||||
@ -764,25 +746,6 @@ func appendTransformsExamplesPassedCondition(messages []string, conditions []*co
|
|||||||
return conditions
|
return conditions
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendTransformsConstantsNamesUniqueCondition(messages []string, conditions []*configv1alpha1.Condition) []*configv1alpha1.Condition {
|
|
||||||
if len(messages) > 0 {
|
|
||||||
conditions = append(conditions, &configv1alpha1.Condition{
|
|
||||||
Type: typeTransformsConstantsNamesUnique,
|
|
||||||
Status: configv1alpha1.ConditionFalse,
|
|
||||||
Reason: reasonDuplicateConstantsNames,
|
|
||||||
Message: strings.Join(messages, "\n\n"),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
conditions = append(conditions, &configv1alpha1.Condition{
|
|
||||||
Type: typeTransformsConstantsNamesUnique,
|
|
||||||
Status: configv1alpha1.ConditionTrue,
|
|
||||||
Reason: reasonSuccess,
|
|
||||||
Message: "the names specified by .spec.identityProviders[].transforms.constants[].name are unique",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return conditions
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendIdentityProviderDuplicateDisplayNamesCondition(duplicateDisplayNames sets.Set[string], conditions []*configv1alpha1.Condition) []*configv1alpha1.Condition {
|
func appendIdentityProviderDuplicateDisplayNamesCondition(duplicateDisplayNames sets.Set[string], conditions []*configv1alpha1.Condition) []*configv1alpha1.Condition {
|
||||||
if duplicateDisplayNames.Len() > 0 {
|
if duplicateDisplayNames.Len() > 0 {
|
||||||
conditions = append(conditions, &configv1alpha1.Condition{
|
conditions = append(conditions, &configv1alpha1.Condition{
|
||||||
@ -878,7 +841,6 @@ func (c *federationDomainWatcherController) sortedAllowedKinds() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type transformsValidationErrorMessages struct {
|
type transformsValidationErrorMessages struct {
|
||||||
errorsForConstants []string
|
|
||||||
errorsForExpressions []string
|
errorsForExpressions []string
|
||||||
errorsForExamples []string
|
errorsForExamples []string
|
||||||
}
|
}
|
||||||
|
@ -400,28 +400,6 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
happyConstNamesUniqueCondition := func(time metav1.Time, observedGeneration int64) configv1alpha1.Condition {
|
|
||||||
return configv1alpha1.Condition{
|
|
||||||
Type: "TransformsConstantsNamesUnique",
|
|
||||||
Status: "True",
|
|
||||||
ObservedGeneration: observedGeneration,
|
|
||||||
LastTransitionTime: time,
|
|
||||||
Reason: "Success",
|
|
||||||
Message: "the names specified by .spec.identityProviders[].transforms.constants[].name are unique",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sadConstNamesUniqueCondition := func(errorMessages string, time metav1.Time, observedGeneration int64) configv1alpha1.Condition {
|
|
||||||
return configv1alpha1.Condition{
|
|
||||||
Type: "TransformsConstantsNamesUnique",
|
|
||||||
Status: "False",
|
|
||||||
ObservedGeneration: observedGeneration,
|
|
||||||
LastTransitionTime: time,
|
|
||||||
Reason: "DuplicateConstantsNames",
|
|
||||||
Message: errorMessages,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
happyTransformationExpressionsCondition := func(time metav1.Time, observedGeneration int64) configv1alpha1.Condition {
|
happyTransformationExpressionsCondition := func(time metav1.Time, observedGeneration int64) configv1alpha1.Condition {
|
||||||
return configv1alpha1.Condition{
|
return configv1alpha1.Condition{
|
||||||
Type: "TransformsExpressionsValid",
|
Type: "TransformsExpressionsValid",
|
||||||
@ -537,7 +515,6 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
|||||||
return sortConditionsByType([]configv1alpha1.Condition{
|
return sortConditionsByType([]configv1alpha1.Condition{
|
||||||
happyTransformationExamplesCondition(frozenMetav1Now, 123),
|
happyTransformationExamplesCondition(frozenMetav1Now, 123),
|
||||||
happyTransformationExpressionsCondition(frozenMetav1Now, 123),
|
happyTransformationExpressionsCondition(frozenMetav1Now, 123),
|
||||||
happyConstNamesUniqueCondition(frozenMetav1Now, 123),
|
|
||||||
happyKindCondition(frozenMetav1Now, 123),
|
happyKindCondition(frozenMetav1Now, 123),
|
||||||
happyAPIGroupSuffixCondition(frozenMetav1Now, 123),
|
happyAPIGroupSuffixCondition(frozenMetav1Now, 123),
|
||||||
happyDisplayNamesUniqueCondition(frozenMetav1Now, 123),
|
happyDisplayNamesUniqueCondition(frozenMetav1Now, 123),
|
||||||
@ -1341,55 +1318,6 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "the federation domain has duplicate transformation const names",
|
|
||||||
inputObjects: []runtime.Object{
|
|
||||||
oidcIdentityProvider,
|
|
||||||
&configv1alpha1.FederationDomain{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "config1", Namespace: namespace, Generation: 123},
|
|
||||||
Spec: configv1alpha1.FederationDomainSpec{
|
|
||||||
Issuer: "https://issuer1.com",
|
|
||||||
IdentityProviders: []configv1alpha1.FederationDomainIdentityProvider{
|
|
||||||
{
|
|
||||||
DisplayName: "name1",
|
|
||||||
ObjectRef: corev1.TypedLocalObjectReference{
|
|
||||||
APIGroup: pointer.String(apiGroupSupervisor),
|
|
||||||
Kind: "OIDCIdentityProvider",
|
|
||||||
Name: oidcIdentityProvider.Name,
|
|
||||||
},
|
|
||||||
Transforms: configv1alpha1.FederationDomainTransforms{
|
|
||||||
Constants: []configv1alpha1.FederationDomainTransformsConstant{
|
|
||||||
{Name: "duplicate1", Type: "string", StringValue: "abc"},
|
|
||||||
{Name: "duplicate1", Type: "stringList", StringListValue: []string{"def"}},
|
|
||||||
{Name: "duplicate1", Type: "string", StringValue: "efg"},
|
|
||||||
{Name: "duplicate2", Type: "string", StringValue: "123"},
|
|
||||||
{Name: "duplicate2", Type: "string", StringValue: "456"},
|
|
||||||
{Name: "uniqueName", Type: "string", StringValue: "hij"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantFDIssuers: []*federationdomainproviders.FederationDomainIssuer{},
|
|
||||||
wantStatusUpdates: []*configv1alpha1.FederationDomain{
|
|
||||||
expectedFederationDomainStatusUpdate(
|
|
||||||
&configv1alpha1.FederationDomain{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "config1", Namespace: namespace, Generation: 123},
|
|
||||||
},
|
|
||||||
configv1alpha1.FederationDomainPhaseError,
|
|
||||||
replaceConditions(
|
|
||||||
allHappyConditionsSuccess("https://issuer1.com", frozenMetav1Now, 123),
|
|
||||||
[]configv1alpha1.Condition{
|
|
||||||
sadConstNamesUniqueCondition(
|
|
||||||
`the names specified by .spec.identityProviders[0].transforms.constants[].name contain duplicates: "duplicate1", "duplicate2"`,
|
|
||||||
frozenMetav1Now, 123),
|
|
||||||
sadReadyCondition(frozenMetav1Now, 123),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "the federation domain has transformation expressions which don't compile",
|
name: "the federation domain has transformation expressions which don't compile",
|
||||||
inputObjects: []runtime.Object{
|
inputObjects: []runtime.Object{
|
||||||
@ -1707,7 +1635,7 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
|||||||
Transforms: configv1alpha1.FederationDomainTransforms{
|
Transforms: configv1alpha1.FederationDomainTransforms{
|
||||||
Constants: []configv1alpha1.FederationDomainTransformsConstant{
|
Constants: []configv1alpha1.FederationDomainTransformsConstant{
|
||||||
{Name: "foo", Type: "string", StringValue: "bar"},
|
{Name: "foo", Type: "string", StringValue: "bar"},
|
||||||
{Name: "foo", Type: "string", StringValue: "baz"},
|
{Name: "bar", Type: "string", StringValue: "baz"},
|
||||||
},
|
},
|
||||||
Expressions: []configv1alpha1.FederationDomainTransformsExpression{
|
Expressions: []configv1alpha1.FederationDomainTransformsExpression{
|
||||||
{Type: "username/v1", Expression: `username + ":suffix"`},
|
{Type: "username/v1", Expression: `username + ":suffix"`},
|
||||||
@ -1742,7 +1670,7 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
|||||||
Transforms: configv1alpha1.FederationDomainTransforms{
|
Transforms: configv1alpha1.FederationDomainTransforms{
|
||||||
Constants: []configv1alpha1.FederationDomainTransformsConstant{
|
Constants: []configv1alpha1.FederationDomainTransformsConstant{
|
||||||
{Name: "foo", Type: "string", StringValue: "bar"},
|
{Name: "foo", Type: "string", StringValue: "bar"},
|
||||||
{Name: "foo", Type: "string", StringValue: "baz"},
|
{Name: "bar", Type: "string", StringValue: "baz"},
|
||||||
},
|
},
|
||||||
Expressions: []configv1alpha1.FederationDomainTransformsExpression{
|
Expressions: []configv1alpha1.FederationDomainTransformsExpression{
|
||||||
{Type: "username/v1", Expression: `username + ":suffix"`},
|
{Type: "username/v1", Expression: `username + ":suffix"`},
|
||||||
@ -1821,11 +1749,6 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
|||||||
replaceConditions(
|
replaceConditions(
|
||||||
allHappyConditionsSuccess("https://not-unique.com", frozenMetav1Now, 123),
|
allHappyConditionsSuccess("https://not-unique.com", frozenMetav1Now, 123),
|
||||||
[]configv1alpha1.Condition{
|
[]configv1alpha1.Condition{
|
||||||
sadConstNamesUniqueCondition(here.Doc(
|
|
||||||
`the names specified by .spec.identityProviders[0].transforms.constants[].name contain duplicates: "foo"
|
|
||||||
|
|
||||||
the names specified by .spec.identityProviders[1].transforms.constants[].name contain duplicates: "foo"`,
|
|
||||||
), frozenMetav1Now, 123),
|
|
||||||
sadAPIGroupSuffixCondition(`"this is wrong"`, frozenMetav1Now, 123),
|
sadAPIGroupSuffixCondition(`"this is wrong"`, frozenMetav1Now, 123),
|
||||||
sadDisplayNamesUniqueCondition(`"not unique"`, frozenMetav1Now, 123),
|
sadDisplayNamesUniqueCondition(`"not unique"`, frozenMetav1Now, 123),
|
||||||
sadIdentityProvidersFoundConditionIdentityProvidersObjectRefsNotFound(here.Doc(
|
sadIdentityProvidersFoundConditionIdentityProvidersObjectRefsNotFound(here.Doc(
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/utils/pointer"
|
"k8s.io/utils/pointer"
|
||||||
|
|
||||||
@ -23,12 +24,13 @@ import (
|
|||||||
// Never run this test in parallel since deleting all federation domains is disruptive, see main_test.go.
|
// Never run this test in parallel since deleting all federation domains is disruptive, see main_test.go.
|
||||||
func TestSupervisorFederationDomainStatus_Disruptive(t *testing.T) {
|
func TestSupervisorFederationDomainStatus_Disruptive(t *testing.T) {
|
||||||
env := testlib.IntegrationEnv(t)
|
env := testlib.IntegrationEnv(t)
|
||||||
client := testlib.NewSupervisorClientset(t)
|
supervisorClient := testlib.NewSupervisorClientset(t)
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
||||||
defer cancel()
|
t.Cleanup(cancel)
|
||||||
|
|
||||||
temporarilyRemoveAllFederationDomainsAndDefaultTLSCertSecret(ctx, t, env.SupervisorNamespace, defaultTLSCertSecretName(env), client, testlib.NewKubernetesClientset(t))
|
temporarilyRemoveAllFederationDomainsAndDefaultTLSCertSecret(ctx, t,
|
||||||
|
env.SupervisorNamespace, defaultTLSCertSecretName(env), supervisorClient, testlib.NewKubernetesClientset(t))
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@ -164,7 +166,7 @@ func TestSupervisorFederationDomainStatus_Disruptive(t *testing.T) {
|
|||||||
allSuccessfulFederationDomainConditions(fd.Spec))
|
allSuccessfulFederationDomainConditions(fd.Spec))
|
||||||
|
|
||||||
// Removing one IDP should put the FederationDomain back into an error status again.
|
// Removing one IDP should put the FederationDomain back into an error status again.
|
||||||
oidcIDPClient := testlib.NewSupervisorClientset(t).IDPV1alpha1().OIDCIdentityProviders(env.SupervisorNamespace)
|
oidcIDPClient := supervisorClient.IDPV1alpha1().OIDCIdentityProviders(env.SupervisorNamespace)
|
||||||
err := oidcIDPClient.Delete(ctx, oidcIdentityProvider1.Name, metav1.DeleteOptions{})
|
err := oidcIDPClient.Delete(ctx, oidcIdentityProvider1.Name, metav1.DeleteOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
testlib.WaitForFederationDomainStatusPhase(ctx, t, fd.Name, v1alpha1.FederationDomainPhaseError)
|
testlib.WaitForFederationDomainStatusPhase(ctx, t, fd.Name, v1alpha1.FederationDomainPhaseError)
|
||||||
@ -183,6 +185,347 @@ func TestSupervisorFederationDomainStatus_Disruptive(t *testing.T) {
|
|||||||
))
|
))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "spec with explicit identity providers and lots of validation errors",
|
||||||
|
run: func(t *testing.T) {
|
||||||
|
oidcIdentityProvider := testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{
|
||||||
|
Issuer: "https://example.cluster.local/fake-issuer-url-does-not-matter",
|
||||||
|
Client: idpv1alpha1.OIDCClient{SecretName: "this-will-not-exist-but-does-not-matter"},
|
||||||
|
}, idpv1alpha1.PhaseError)
|
||||||
|
|
||||||
|
fd := testlib.CreateTestFederationDomain(ctx, t, v1alpha1.FederationDomainSpec{
|
||||||
|
Issuer: "https://example.com/fake",
|
||||||
|
IdentityProviders: []v1alpha1.FederationDomainIdentityProvider{
|
||||||
|
{
|
||||||
|
DisplayName: "not unique",
|
||||||
|
ObjectRef: corev1.TypedLocalObjectReference{
|
||||||
|
APIGroup: pointer.String("this is the wrong api group"),
|
||||||
|
Kind: "OIDCIdentityProvider",
|
||||||
|
Name: "will not be found",
|
||||||
|
},
|
||||||
|
Transforms: v1alpha1.FederationDomainTransforms{
|
||||||
|
Constants: []v1alpha1.FederationDomainTransformsConstant{
|
||||||
|
{Name: "foo", Type: "string", StringValue: "bar"},
|
||||||
|
},
|
||||||
|
Expressions: []v1alpha1.FederationDomainTransformsExpression{
|
||||||
|
{Type: "username/v1", Expression: "this is not a valid cel expression"},
|
||||||
|
{Type: "groups/v1", Expression: "this is also not a valid cel expression"},
|
||||||
|
{Type: "username/v1", Expression: "username"}, // valid
|
||||||
|
{Type: "policy/v1", Expression: "still not a valid cel expression"},
|
||||||
|
},
|
||||||
|
Examples: []v1alpha1.FederationDomainTransformsExample{
|
||||||
|
{
|
||||||
|
Username: "does not matter because expressions did not compile",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // this identity provider should be valid
|
||||||
|
DisplayName: "unique",
|
||||||
|
ObjectRef: corev1.TypedLocalObjectReference{
|
||||||
|
APIGroup: pointer.String("idp.supervisor." + env.APIGroupSuffix),
|
||||||
|
Kind: "OIDCIdentityProvider",
|
||||||
|
Name: oidcIdentityProvider.Name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DisplayName: "not unique",
|
||||||
|
ObjectRef: corev1.TypedLocalObjectReference{
|
||||||
|
APIGroup: pointer.String("idp.supervisor." + env.APIGroupSuffix),
|
||||||
|
Kind: "this is the wrong kind",
|
||||||
|
Name: "also will not be found",
|
||||||
|
},
|
||||||
|
Transforms: v1alpha1.FederationDomainTransforms{
|
||||||
|
Constants: []v1alpha1.FederationDomainTransformsConstant{
|
||||||
|
{Name: "ryan", Type: "string", StringValue: "ryan"},
|
||||||
|
{Name: "unused", Type: "stringList", StringListValue: []string{"foo", "bar"}},
|
||||||
|
{Name: "rejectMe", Type: "string", StringValue: "rejectMeWithDefaultMessage"},
|
||||||
|
},
|
||||||
|
Expressions: []v1alpha1.FederationDomainTransformsExpression{
|
||||||
|
{Type: "policy/v1", Expression: `username == strConst.ryan || username == strConst.rejectMe`, Message: "only special users allowed"},
|
||||||
|
{Type: "policy/v1", Expression: `username != "rejectMeWithDefaultMessage"`}, // no message specified
|
||||||
|
{Type: "username/v1", Expression: `"pre:" + username`},
|
||||||
|
{Type: "groups/v1", Expression: `groups.map(g, "pre:" + g)`},
|
||||||
|
},
|
||||||
|
Examples: []v1alpha1.FederationDomainTransformsExample{
|
||||||
|
{ // this example should pass
|
||||||
|
Username: "ryan",
|
||||||
|
Groups: []string{"a", "b"},
|
||||||
|
Expects: v1alpha1.FederationDomainTransformsExampleExpects{
|
||||||
|
Username: "pre:ryan",
|
||||||
|
Groups: []string{"pre:b", "pre:a", "pre:b", "pre:a"}, // order and repeats don't matter, treated like a set
|
||||||
|
Rejected: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // this example should pass
|
||||||
|
Username: "other",
|
||||||
|
Expects: v1alpha1.FederationDomainTransformsExampleExpects{
|
||||||
|
Rejected: true,
|
||||||
|
Message: "only special users allowed",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // this example should fail because it expects the user to be rejected but the user was actually not rejected
|
||||||
|
Username: "ryan",
|
||||||
|
Groups: []string{"a", "b"},
|
||||||
|
Expects: v1alpha1.FederationDomainTransformsExampleExpects{
|
||||||
|
Rejected: true,
|
||||||
|
Message: "this input is ignored in this case",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // this example should fail because it expects the user not to be rejected but they were actually rejected
|
||||||
|
Username: "other",
|
||||||
|
Groups: []string{"a", "b"},
|
||||||
|
Expects: v1alpha1.FederationDomainTransformsExampleExpects{
|
||||||
|
Username: "pre:other",
|
||||||
|
Groups: []string{"pre:a", "pre:b"},
|
||||||
|
Rejected: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // this example should fail because it expects the wrong rejection message
|
||||||
|
Username: "other",
|
||||||
|
Groups: []string{"a", "b"},
|
||||||
|
Expects: v1alpha1.FederationDomainTransformsExampleExpects{
|
||||||
|
Rejected: true,
|
||||||
|
Message: "wrong message",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // this example should pass even though it does not make any assertion about the rejection message
|
||||||
|
// because the message assertions defaults to asserting the default rejection message
|
||||||
|
Username: "rejectMeWithDefaultMessage",
|
||||||
|
Groups: []string{"a", "b"},
|
||||||
|
Expects: v1alpha1.FederationDomainTransformsExampleExpects{
|
||||||
|
Rejected: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // this example should fail because it expects both the wrong username and groups
|
||||||
|
Username: "ryan",
|
||||||
|
Groups: []string{"b", "a"},
|
||||||
|
Expects: v1alpha1.FederationDomainTransformsExampleExpects{
|
||||||
|
Username: "wrong",
|
||||||
|
Groups: []string{},
|
||||||
|
Rejected: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // this example should fail because it expects the wrong username only
|
||||||
|
Username: "ryan",
|
||||||
|
Groups: []string{"a", "b"},
|
||||||
|
Expects: v1alpha1.FederationDomainTransformsExampleExpects{
|
||||||
|
Username: "wrong",
|
||||||
|
Groups: []string{"pre:b", "pre:a"},
|
||||||
|
Rejected: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // this example should fail because it expects the wrong groups only
|
||||||
|
Username: "ryan",
|
||||||
|
Groups: []string{"b", "a"},
|
||||||
|
Expects: v1alpha1.FederationDomainTransformsExampleExpects{
|
||||||
|
Username: "pre:ryan",
|
||||||
|
Groups: []string{"wrong2", "wrong1"},
|
||||||
|
Rejected: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // this example should fail because it does not expect anything but the auth actually was successful
|
||||||
|
Username: "ryan",
|
||||||
|
Groups: []string{"b", "a"},
|
||||||
|
Expects: v1alpha1.FederationDomainTransformsExampleExpects{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, v1alpha1.FederationDomainPhaseError)
|
||||||
|
testlib.WaitForFederationDomainStatusConditions(ctx, t, fd.Name, replaceSomeConditions(
|
||||||
|
allSuccessfulFederationDomainConditions(fd.Spec),
|
||||||
|
[]v1alpha1.Condition{
|
||||||
|
{
|
||||||
|
Type: "IdentityProvidersDisplayNamesUnique", Status: "False", Reason: "DuplicateDisplayNames",
|
||||||
|
Message: `the names specified by .spec.identityProviders[].displayName contain duplicates: "not unique"`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "IdentityProvidersFound", Status: "False", Reason: "IdentityProvidersObjectRefsNotFound",
|
||||||
|
Message: here.Doc(
|
||||||
|
`cannot find resource specified by .spec.identityProviders[0].objectRef (with name "will not be found")
|
||||||
|
|
||||||
|
cannot find resource specified by .spec.identityProviders[2].objectRef (with name "also will not be found")`,
|
||||||
|
)},
|
||||||
|
{
|
||||||
|
Type: "IdentityProvidersObjectRefAPIGroupSuffixValid", Status: "False", Reason: "APIGroupUnrecognized",
|
||||||
|
Message: `some API groups specified by .spec.identityProviders[].objectRef.apiGroup are not recognized ` +
|
||||||
|
`(should be "idp.supervisor.pinniped.dev"): "this is the wrong api group"`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "IdentityProvidersObjectRefKindValid", Status: "False", Reason: "KindUnrecognized",
|
||||||
|
Message: `some kinds specified by .spec.identityProviders[].objectRef.kind are not recognized ` +
|
||||||
|
`(should be one of "ActiveDirectoryIdentityProvider", "LDAPIdentityProvider", "OIDCIdentityProvider"): "this is the wrong kind"`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "Ready", Status: "False", Reason: "NotReady",
|
||||||
|
Message: "the FederationDomain is not ready: see other conditions for details",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "TransformsExamplesPassed", Status: "False", Reason: "TransformsExamplesFailed",
|
||||||
|
Message: here.Doc(
|
||||||
|
`unable to check if the examples specified by .spec.identityProviders[0].transforms.examples[] had errors because an expression was invalid
|
||||||
|
|
||||||
|
.spec.identityProviders[2].transforms.examples[2] example failed:
|
||||||
|
expected: authentication to be rejected
|
||||||
|
actual: authentication was not rejected
|
||||||
|
|
||||||
|
.spec.identityProviders[2].transforms.examples[3] example failed:
|
||||||
|
expected: authentication not to be rejected
|
||||||
|
actual: authentication was rejected with message "only special users allowed"
|
||||||
|
|
||||||
|
.spec.identityProviders[2].transforms.examples[4] example failed:
|
||||||
|
expected: authentication rejection message "wrong message"
|
||||||
|
actual: authentication rejection message "only special users allowed"
|
||||||
|
|
||||||
|
.spec.identityProviders[2].transforms.examples[6] example failed:
|
||||||
|
expected: username "wrong"
|
||||||
|
actual: username "pre:ryan"
|
||||||
|
|
||||||
|
.spec.identityProviders[2].transforms.examples[6] example failed:
|
||||||
|
expected: groups []
|
||||||
|
actual: groups ["pre:a", "pre:b"]
|
||||||
|
|
||||||
|
.spec.identityProviders[2].transforms.examples[7] example failed:
|
||||||
|
expected: username "wrong"
|
||||||
|
actual: username "pre:ryan"
|
||||||
|
|
||||||
|
.spec.identityProviders[2].transforms.examples[8] example failed:
|
||||||
|
expected: groups ["wrong1", "wrong2"]
|
||||||
|
actual: groups ["pre:a", "pre:b"]
|
||||||
|
|
||||||
|
.spec.identityProviders[2].transforms.examples[9] example failed:
|
||||||
|
expected: username ""
|
||||||
|
actual: username "pre:ryan"
|
||||||
|
|
||||||
|
.spec.identityProviders[2].transforms.examples[9] example failed:
|
||||||
|
expected: groups []
|
||||||
|
actual: groups ["pre:a", "pre:b"]`,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "TransformsExpressionsValid", Status: "False", Reason: "InvalidTransformsExpressions",
|
||||||
|
Message: here.Doc(
|
||||||
|
`spec.identityProvider[0].transforms.expressions[0].expression was invalid:
|
||||||
|
CEL expression compile error: ERROR: <input>:1:6: Syntax error: mismatched input 'is' expecting <EOF>
|
||||||
|
| this is not a valid cel expression
|
||||||
|
| .....^
|
||||||
|
|
||||||
|
spec.identityProvider[0].transforms.expressions[1].expression was invalid:
|
||||||
|
CEL expression compile error: ERROR: <input>:1:6: Syntax error: mismatched input 'is' expecting <EOF>
|
||||||
|
| this is also not a valid cel expression
|
||||||
|
| .....^
|
||||||
|
|
||||||
|
spec.identityProvider[0].transforms.expressions[3].expression was invalid:
|
||||||
|
CEL expression compile error: ERROR: <input>:1:7: Syntax error: mismatched input 'not' expecting <EOF>
|
||||||
|
| still not a valid cel expression
|
||||||
|
| ......^`,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
|
|
||||||
|
// Updating the FederationDomain to fix some of the problems should make some of the errors go away.
|
||||||
|
federationDomainsClient := testlib.NewSupervisorClientset(t).ConfigV1alpha1().FederationDomains(env.SupervisorNamespace)
|
||||||
|
fd, err := federationDomainsClient.Get(ctx, fd.Name, metav1.GetOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
fd.Spec.IdentityProviders[0] = v1alpha1.FederationDomainIdentityProvider{
|
||||||
|
// Fix the display name.
|
||||||
|
DisplayName: "now made unique",
|
||||||
|
// Fix the objectRef.
|
||||||
|
ObjectRef: corev1.TypedLocalObjectReference{
|
||||||
|
APIGroup: pointer.String("idp.supervisor." + env.APIGroupSuffix),
|
||||||
|
Kind: "OIDCIdentityProvider",
|
||||||
|
Name: oidcIdentityProvider.Name,
|
||||||
|
},
|
||||||
|
Transforms: v1alpha1.FederationDomainTransforms{
|
||||||
|
Constants: []v1alpha1.FederationDomainTransformsConstant{
|
||||||
|
{Name: "foo", Type: "string", StringValue: "bar"},
|
||||||
|
},
|
||||||
|
Expressions: []v1alpha1.FederationDomainTransformsExpression{
|
||||||
|
// Fix the compile errors.
|
||||||
|
{Type: "username/v1", Expression: `"pre:" + username`},
|
||||||
|
},
|
||||||
|
Examples: []v1alpha1.FederationDomainTransformsExample{
|
||||||
|
{ // this example should fail because it expects both the wrong username and groups
|
||||||
|
Username: "ryan",
|
||||||
|
Groups: []string{"b", "a"},
|
||||||
|
Expects: v1alpha1.FederationDomainTransformsExampleExpects{
|
||||||
|
Username: "wrong",
|
||||||
|
Groups: []string{},
|
||||||
|
Rejected: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fd.Spec.IdentityProviders[2].Transforms.Examples = []v1alpha1.FederationDomainTransformsExample{
|
||||||
|
{ // this example should pass
|
||||||
|
Username: "other",
|
||||||
|
Expects: v1alpha1.FederationDomainTransformsExampleExpects{
|
||||||
|
Rejected: true,
|
||||||
|
Message: "only special users allowed",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fd, err = federationDomainsClient.Update(ctx, fd, metav1.UpdateOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
testlib.WaitForFederationDomainStatusConditions(ctx, t, fd.Name, replaceSomeConditions(
|
||||||
|
allSuccessfulFederationDomainConditions(fd.Spec),
|
||||||
|
[]v1alpha1.Condition{
|
||||||
|
{
|
||||||
|
Type: "IdentityProvidersFound", Status: "False", Reason: "IdentityProvidersObjectRefsNotFound",
|
||||||
|
Message: `cannot find resource specified by .spec.identityProviders[2].objectRef (with name "also will not be found")`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "IdentityProvidersObjectRefKindValid", Status: "False", Reason: "KindUnrecognized",
|
||||||
|
Message: `some kinds specified by .spec.identityProviders[].objectRef.kind are not recognized ` +
|
||||||
|
`(should be one of "ActiveDirectoryIdentityProvider", "LDAPIdentityProvider", "OIDCIdentityProvider"): "this is the wrong kind"`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "Ready", Status: "False", Reason: "NotReady",
|
||||||
|
Message: "the FederationDomain is not ready: see other conditions for details",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "TransformsExamplesPassed", Status: "False", Reason: "TransformsExamplesFailed",
|
||||||
|
Message: here.Doc(
|
||||||
|
`.spec.identityProviders[0].transforms.examples[0] example failed:
|
||||||
|
expected: username "wrong"
|
||||||
|
actual: username "pre:ryan"
|
||||||
|
|
||||||
|
.spec.identityProviders[0].transforms.examples[0] example failed:
|
||||||
|
expected: groups []
|
||||||
|
actual: groups ["a", "b"]`,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
|
|
||||||
|
// Updating the FederationDomain to fix the rest of the problems should make all the errors go away.
|
||||||
|
fd, err = federationDomainsClient.Get(ctx, fd.Name, metav1.GetOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
fd.Spec.IdentityProviders[2].ObjectRef = corev1.TypedLocalObjectReference{
|
||||||
|
APIGroup: pointer.String("idp.supervisor." + env.APIGroupSuffix),
|
||||||
|
Kind: "OIDCIdentityProvider",
|
||||||
|
Name: oidcIdentityProvider.Name,
|
||||||
|
}
|
||||||
|
fd.Spec.IdentityProviders[0].Transforms.Examples = []v1alpha1.FederationDomainTransformsExample{
|
||||||
|
{ // this example should pass
|
||||||
|
Username: "ryan",
|
||||||
|
Groups: []string{"b", "a"},
|
||||||
|
Expects: v1alpha1.FederationDomainTransformsExampleExpects{
|
||||||
|
Username: "pre:ryan",
|
||||||
|
Groups: []string{"a", "b"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fd, err = federationDomainsClient.Update(ctx, fd, metav1.UpdateOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
testlib.WaitForFederationDomainStatusPhase(ctx, t, fd.Name, v1alpha1.FederationDomainPhaseReady)
|
||||||
|
testlib.WaitForFederationDomainStatusConditions(ctx, t, fd.Name, allSuccessfulFederationDomainConditions(fd.Spec))
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
@ -193,6 +536,315 @@ func TestSupervisorFederationDomainStatus_Disruptive(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSupervisorFederationDomainCRDValidations_Parallel(t *testing.T) {
|
||||||
|
env := testlib.IntegrationEnv(t)
|
||||||
|
fdClient := testlib.NewSupervisorClientset(t).ConfigV1alpha1().FederationDomains(env.SupervisorNamespace)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
||||||
|
t.Cleanup(cancel)
|
||||||
|
|
||||||
|
objectMeta := testlib.ObjectMetaWithRandomName(t, "federation-domain")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fd *v1alpha1.FederationDomain
|
||||||
|
wantErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "issuer cannot be empty",
|
||||||
|
fd: &v1alpha1.FederationDomain{
|
||||||
|
ObjectMeta: objectMeta,
|
||||||
|
Spec: v1alpha1.FederationDomainSpec{
|
||||||
|
Issuer: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: fmt.Sprintf("FederationDomain.config.supervisor.%s %q is invalid: "+
|
||||||
|
`spec.issuer: Invalid value: "": spec.issuer in body should be at least 1 chars long`,
|
||||||
|
env.APIGroupSuffix, objectMeta.Name),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "IDP display names cannot be empty",
|
||||||
|
fd: &v1alpha1.FederationDomain{
|
||||||
|
ObjectMeta: objectMeta,
|
||||||
|
Spec: v1alpha1.FederationDomainSpec{
|
||||||
|
Issuer: "https://example.com",
|
||||||
|
IdentityProviders: []v1alpha1.FederationDomainIdentityProvider{{DisplayName: ""}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: fmt.Sprintf("FederationDomain.config.supervisor.%s %q is invalid: "+
|
||||||
|
`spec.identityProviders[0].displayName: Invalid value: "": `+
|
||||||
|
"spec.identityProviders[0].displayName in body should be at least 1 chars long",
|
||||||
|
env.APIGroupSuffix, objectMeta.Name),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "IDP transform constants must have unique names",
|
||||||
|
fd: &v1alpha1.FederationDomain{
|
||||||
|
ObjectMeta: objectMeta,
|
||||||
|
Spec: v1alpha1.FederationDomainSpec{
|
||||||
|
Issuer: "https://example.com",
|
||||||
|
IdentityProviders: []v1alpha1.FederationDomainIdentityProvider{
|
||||||
|
{
|
||||||
|
DisplayName: "foo",
|
||||||
|
Transforms: v1alpha1.FederationDomainTransforms{
|
||||||
|
Constants: []v1alpha1.FederationDomainTransformsConstant{
|
||||||
|
{Name: "notUnique", Type: "string", StringValue: "foo"},
|
||||||
|
{Name: "notUnique", Type: "string", StringValue: "bar"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: fmt.Sprintf("FederationDomain.config.supervisor.%s %q is invalid: "+
|
||||||
|
`spec.identityProviders[0].transforms.constants[1]: Duplicate value: map[string]interface {}{"name":"notUnique"}`,
|
||||||
|
env.APIGroupSuffix, objectMeta.Name),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "IDP transform constant names cannot be empty",
|
||||||
|
fd: &v1alpha1.FederationDomain{
|
||||||
|
ObjectMeta: objectMeta,
|
||||||
|
Spec: v1alpha1.FederationDomainSpec{
|
||||||
|
Issuer: "https://example.com",
|
||||||
|
IdentityProviders: []v1alpha1.FederationDomainIdentityProvider{
|
||||||
|
{
|
||||||
|
DisplayName: "foo",
|
||||||
|
Transforms: v1alpha1.FederationDomainTransforms{
|
||||||
|
Constants: []v1alpha1.FederationDomainTransformsConstant{
|
||||||
|
{Name: "", Type: "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: fmt.Sprintf("FederationDomain.config.supervisor.%s %q is invalid: "+
|
||||||
|
`spec.identityProviders[0].transforms.constants[0].name: Invalid value: "": `+
|
||||||
|
`spec.identityProviders[0].transforms.constants[0].name in body should be at least 1 chars long`,
|
||||||
|
env.APIGroupSuffix, objectMeta.Name),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "IDP transform constant names cannot be more than 64 characters",
|
||||||
|
fd: &v1alpha1.FederationDomain{
|
||||||
|
ObjectMeta: objectMeta,
|
||||||
|
Spec: v1alpha1.FederationDomainSpec{
|
||||||
|
Issuer: "https://example.com",
|
||||||
|
IdentityProviders: []v1alpha1.FederationDomainIdentityProvider{
|
||||||
|
{
|
||||||
|
DisplayName: "foo",
|
||||||
|
Transforms: v1alpha1.FederationDomainTransforms{
|
||||||
|
Constants: []v1alpha1.FederationDomainTransformsConstant{
|
||||||
|
{Name: "12345678901234567890123456789012345678901234567890123456789012345", Type: "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: fmt.Sprintf("FederationDomain.config.supervisor.%s %q is invalid: "+
|
||||||
|
`spec.identityProviders[0].transforms.constants[0].name: Too long: may not be longer than 64`,
|
||||||
|
env.APIGroupSuffix, objectMeta.Name),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "IDP transform constant names must be a legal CEL variable name",
|
||||||
|
fd: &v1alpha1.FederationDomain{
|
||||||
|
ObjectMeta: objectMeta,
|
||||||
|
Spec: v1alpha1.FederationDomainSpec{
|
||||||
|
Issuer: "https://example.com",
|
||||||
|
IdentityProviders: []v1alpha1.FederationDomainIdentityProvider{
|
||||||
|
{
|
||||||
|
DisplayName: "foo",
|
||||||
|
Transforms: v1alpha1.FederationDomainTransforms{
|
||||||
|
Constants: []v1alpha1.FederationDomainTransformsConstant{
|
||||||
|
{Name: "cannot have spaces", Type: "string"},
|
||||||
|
{Name: "1mustStartWithLetter", Type: "string"},
|
||||||
|
{Name: "_mustStartWithLetter", Type: "string"},
|
||||||
|
{Name: "canOnlyIncludeLettersAndNumbersAnd_", Type: "string"},
|
||||||
|
{Name: "CanStart1_withUpperCase", Type: "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: fmt.Sprintf("FederationDomain.config.supervisor.%s %q is invalid: "+
|
||||||
|
`[spec.identityProviders[0].transforms.constants[0].name: Invalid value: "cannot have spaces": `+
|
||||||
|
`spec.identityProviders[0].transforms.constants[0].name in body should match '^[a-zA-Z][_a-zA-Z0-9]*$', `+
|
||||||
|
`spec.identityProviders[0].transforms.constants[1].name: Invalid value: "1mustStartWithLetter": `+
|
||||||
|
`spec.identityProviders[0].transforms.constants[1].name in body should match '^[a-zA-Z][_a-zA-Z0-9]*$', `+
|
||||||
|
`spec.identityProviders[0].transforms.constants[2].name: Invalid value: "_mustStartWithLetter": `+
|
||||||
|
`spec.identityProviders[0].transforms.constants[2].name in body should match '^[a-zA-Z][_a-zA-Z0-9]*$']`,
|
||||||
|
env.APIGroupSuffix, objectMeta.Name),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "IDP transform constant types must be one of the allowed enum strings",
|
||||||
|
fd: &v1alpha1.FederationDomain{
|
||||||
|
ObjectMeta: objectMeta,
|
||||||
|
Spec: v1alpha1.FederationDomainSpec{
|
||||||
|
Issuer: "https://example.com",
|
||||||
|
IdentityProviders: []v1alpha1.FederationDomainIdentityProvider{
|
||||||
|
{
|
||||||
|
DisplayName: "foo",
|
||||||
|
Transforms: v1alpha1.FederationDomainTransforms{
|
||||||
|
Constants: []v1alpha1.FederationDomainTransformsConstant{
|
||||||
|
{Name: "a", Type: "this is invalid"},
|
||||||
|
{Name: "b", Type: "string"},
|
||||||
|
{Name: "c", Type: "stringList"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: fmt.Sprintf("FederationDomain.config.supervisor.%s %q is invalid: "+
|
||||||
|
`spec.identityProviders[0].transforms.constants[0].type: Unsupported value: "this is invalid": `+
|
||||||
|
`supported values: "string", "stringList"`,
|
||||||
|
env.APIGroupSuffix, objectMeta.Name),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "IDP transform expression types must be one of the allowed enum strings",
|
||||||
|
fd: &v1alpha1.FederationDomain{
|
||||||
|
ObjectMeta: objectMeta,
|
||||||
|
Spec: v1alpha1.FederationDomainSpec{
|
||||||
|
Issuer: "https://example.com",
|
||||||
|
IdentityProviders: []v1alpha1.FederationDomainIdentityProvider{
|
||||||
|
{
|
||||||
|
DisplayName: "foo",
|
||||||
|
Transforms: v1alpha1.FederationDomainTransforms{
|
||||||
|
Expressions: []v1alpha1.FederationDomainTransformsExpression{
|
||||||
|
{Type: "this is invalid", Expression: "foo"},
|
||||||
|
{Type: "policy/v1", Expression: "foo"},
|
||||||
|
{Type: "username/v1", Expression: "foo"},
|
||||||
|
{Type: "groups/v1", Expression: "foo"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: fmt.Sprintf("FederationDomain.config.supervisor.%s %q is invalid: "+
|
||||||
|
`spec.identityProviders[0].transforms.expressions[0].type: Unsupported value: "this is invalid": `+
|
||||||
|
`supported values: "policy/v1", "username/v1", "groups/v1"`,
|
||||||
|
env.APIGroupSuffix, objectMeta.Name),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "IDP transform expressions cannot be empty",
|
||||||
|
fd: &v1alpha1.FederationDomain{
|
||||||
|
ObjectMeta: objectMeta,
|
||||||
|
Spec: v1alpha1.FederationDomainSpec{
|
||||||
|
Issuer: "https://example.com",
|
||||||
|
IdentityProviders: []v1alpha1.FederationDomainIdentityProvider{
|
||||||
|
{
|
||||||
|
DisplayName: "foo",
|
||||||
|
Transforms: v1alpha1.FederationDomainTransforms{
|
||||||
|
Expressions: []v1alpha1.FederationDomainTransformsExpression{
|
||||||
|
{Type: "username/v1", Expression: ""},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: fmt.Sprintf("FederationDomain.config.supervisor.%s %q is invalid: "+
|
||||||
|
`spec.identityProviders[0].transforms.expressions[0].expression: Invalid value: "": `+
|
||||||
|
`spec.identityProviders[0].transforms.expressions[0].expression in body should be at least 1 chars long`,
|
||||||
|
env.APIGroupSuffix, objectMeta.Name),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "IDP transform example usernames cannot be empty",
|
||||||
|
fd: &v1alpha1.FederationDomain{
|
||||||
|
ObjectMeta: objectMeta,
|
||||||
|
Spec: v1alpha1.FederationDomainSpec{
|
||||||
|
Issuer: "https://example.com",
|
||||||
|
IdentityProviders: []v1alpha1.FederationDomainIdentityProvider{
|
||||||
|
{
|
||||||
|
DisplayName: "foo",
|
||||||
|
Transforms: v1alpha1.FederationDomainTransforms{
|
||||||
|
Examples: []v1alpha1.FederationDomainTransformsExample{
|
||||||
|
{Username: ""},
|
||||||
|
{Username: "non-empty"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: fmt.Sprintf("FederationDomain.config.supervisor.%s %q is invalid: "+
|
||||||
|
`spec.identityProviders[0].transforms.examples[0].username: Invalid value: "": `+
|
||||||
|
`spec.identityProviders[0].transforms.examples[0].username in body should be at least 1 chars long`,
|
||||||
|
env.APIGroupSuffix, objectMeta.Name),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "minimum valid",
|
||||||
|
fd: &v1alpha1.FederationDomain{
|
||||||
|
ObjectMeta: testlib.ObjectMetaWithRandomName(t, "fd"),
|
||||||
|
Spec: v1alpha1.FederationDomainSpec{
|
||||||
|
Issuer: "https://example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "minimum valid when IDPs are included",
|
||||||
|
fd: &v1alpha1.FederationDomain{
|
||||||
|
ObjectMeta: testlib.ObjectMetaWithRandomName(t, "fd"),
|
||||||
|
Spec: v1alpha1.FederationDomainSpec{
|
||||||
|
Issuer: "https://example.com",
|
||||||
|
IdentityProviders: []v1alpha1.FederationDomainIdentityProvider{
|
||||||
|
{DisplayName: "foo"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "minimum valid when IDP has transform constants, expressions, and examples",
|
||||||
|
fd: &v1alpha1.FederationDomain{
|
||||||
|
ObjectMeta: testlib.ObjectMetaWithRandomName(t, "fd"),
|
||||||
|
Spec: v1alpha1.FederationDomainSpec{
|
||||||
|
Issuer: "https://example.com",
|
||||||
|
IdentityProviders: []v1alpha1.FederationDomainIdentityProvider{
|
||||||
|
{
|
||||||
|
DisplayName: "foo",
|
||||||
|
Transforms: v1alpha1.FederationDomainTransforms{
|
||||||
|
Constants: []v1alpha1.FederationDomainTransformsConstant{
|
||||||
|
{Name: "foo", Type: "string"},
|
||||||
|
},
|
||||||
|
Expressions: []v1alpha1.FederationDomainTransformsExpression{
|
||||||
|
{Type: "username/v1", Expression: "foo"},
|
||||||
|
},
|
||||||
|
Examples: []v1alpha1.FederationDomainTransformsExample{
|
||||||
|
{Username: "foo"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
tt := test
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
_, createErr := fdClient.Create(ctx, tt.fd, metav1.CreateOptions{})
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
// Delete it if it exists.
|
||||||
|
delErr := fdClient.Delete(ctx, tt.fd.Name, metav1.DeleteOptions{})
|
||||||
|
if !k8serrors.IsNotFound(delErr) {
|
||||||
|
require.NoError(t, delErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if tt.wantErr == "" {
|
||||||
|
require.NoError(t, createErr)
|
||||||
|
} else {
|
||||||
|
require.EqualError(t, createErr, tt.wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func replaceSomeConditions(conditions []v1alpha1.Condition, replaceWithTheseConditions []v1alpha1.Condition) []v1alpha1.Condition {
|
func replaceSomeConditions(conditions []v1alpha1.Condition, replaceWithTheseConditions []v1alpha1.Condition) []v1alpha1.Condition {
|
||||||
cp := make([]v1alpha1.Condition, len(conditions))
|
cp := make([]v1alpha1.Condition, len(conditions))
|
||||||
copy(cp, conditions)
|
copy(cp, conditions)
|
||||||
@ -257,10 +909,6 @@ func allSuccessfulFederationDomainConditions(federationDomainSpec v1alpha1.Feder
|
|||||||
Message: fmt.Sprintf("the FederationDomain is ready and its endpoints are available: "+
|
Message: fmt.Sprintf("the FederationDomain is ready and its endpoints are available: "+
|
||||||
"the discovery endpoint is %s/.well-known/openid-configuration", federationDomainSpec.Issuer),
|
"the discovery endpoint is %s/.well-known/openid-configuration", federationDomainSpec.Issuer),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Type: "TransformsConstantsNamesUnique", Status: "True", Reason: "Success",
|
|
||||||
Message: "the names specified by .spec.identityProviders[].transforms.constants[].name are unique",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Type: "TransformsExamplesPassed", Status: "True", Reason: "Success",
|
Type: "TransformsExamplesPassed", Status: "True", Reason: "Success",
|
||||||
Message: "the examples specified by .spec.identityProviders[].transforms.examples[] had no errors",
|
Message: "the examples specified by .spec.identityProviders[].transforms.examples[] had no errors",
|
||||||
|
@ -354,7 +354,7 @@ func WaitForFederationDomainStatusConditions(ctx context.Context, t *testing.T,
|
|||||||
"wanted status conditions: %#v\nactual status conditions were: %#v\nnot equal at index %d",
|
"wanted status conditions: %#v\nactual status conditions were: %#v\nnot equal at index %d",
|
||||||
expectConditions, fd.Status.Conditions, i)
|
expectConditions, fd.Status.Conditions, i)
|
||||||
}
|
}
|
||||||
}, 5*time.Second, 1*time.Second, "wanted FederationDomain conditions")
|
}, 60*time.Second, 1*time.Second, "wanted FederationDomain conditions")
|
||||||
}
|
}
|
||||||
|
|
||||||
func RandBytes(t *testing.T, numBytes int) []byte {
|
func RandBytes(t *testing.T, numBytes int) []byte {
|
||||||
|
Loading…
Reference in New Issue
Block a user