diff --git a/internal/controller/supervisorconfig/federation_domain_watcher.go b/internal/controller/supervisorconfig/federation_domain_watcher.go index e83b3f6e8..1546f9128 100644 --- a/internal/controller/supervisorconfig/federation_domain_watcher.go +++ b/internal/controller/supervisorconfig/federation_domain_watcher.go @@ -44,6 +44,7 @@ const ( typeIdentityProvidersAPIGroupSuffixValid = "IdentityProvidersObjectRefAPIGroupSuffixValid" typeIdentityProvidersObjectRefKindValid = "IdentityProvidersObjectRefKindValid" typeTransformsConstantsNamesUnique = "TransformsConstantsNamesUnique" + typeTransformsExpressionsValid = "TransformsExpressionsValid" reasonSuccess = "Success" reasonNotReady = "NotReady" @@ -59,6 +60,7 @@ const ( reasonAPIGroupNameUnrecognized = "APIGroupUnrecognized" reasonKindUnrecognized = "KindUnrecognized" reasonDuplicateConstantsNames = "DuplicateConstantsNames" + reasonInvalidTransformsExpressions = "InvalidTransformsExpressions" kindLDAPIdentityProvider = "LDAPIdentityProvider" kindOIDCIdentityProvider = "OIDCIdentityProvider" @@ -162,7 +164,7 @@ func (c *federationDomainWatcherController) Sync(ctx controllerlib.Context) erro } // Process each FederationDomain to validate its spec and to turn it into a FederationDomainIssuer. - federationDomainIssuers, fdToConditionsMap, err := c.processAllFederationDomains(federationDomains) + federationDomainIssuers, fdToConditionsMap, err := c.processAllFederationDomains(ctx.Context, federationDomains) if err != nil { return err } @@ -185,6 +187,7 @@ func (c *federationDomainWatcherController) Sync(ctx controllerlib.Context) erro } func (c *federationDomainWatcherController) processAllFederationDomains( + ctx context.Context, federationDomains []*configv1alpha1.FederationDomain, ) ([]*federationdomainproviders.FederationDomainIssuer, map[*configv1alpha1.FederationDomain][]*configv1alpha1.Condition, error) { federationDomainIssuers := make([]*federationdomainproviders.FederationDomainIssuer, 0) @@ -196,7 +199,7 @@ func (c *federationDomainWatcherController) processAllFederationDomains( conditions = crossDomainConfigValidator.Validate(federationDomain, conditions) - federationDomainIssuer, conditions, err := c.makeFederationDomainIssuer(federationDomain, conditions) + federationDomainIssuer, conditions, err := c.makeFederationDomainIssuer(ctx, federationDomain, conditions) if err != nil { return nil, nil, err } @@ -216,6 +219,7 @@ func (c *federationDomainWatcherController) processAllFederationDomains( } func (c *federationDomainWatcherController) makeFederationDomainIssuer( + ctx context.Context, federationDomain *configv1alpha1.FederationDomain, conditions []*configv1alpha1.Condition, ) (*federationdomainproviders.FederationDomainIssuer, []*configv1alpha1.Condition, error) { @@ -230,7 +234,7 @@ func (c *federationDomainWatcherController) makeFederationDomainIssuer( return nil, nil, err } } else { - federationDomainIssuer, conditions, err = c.makeFederationDomainIssuerWithExplicitIDPs(federationDomain, conditions) + federationDomainIssuer, conditions, err = c.makeFederationDomainIssuerWithExplicitIDPs(ctx, federationDomain, conditions) if err != nil { return nil, nil, err } @@ -313,19 +317,23 @@ func (c *federationDomainWatcherController) makeLegacyFederationDomainIssuer( }) } - // This is the constructor for the backwards compatibility mode. + // This is the constructor for the legacy backwards compatibility mode. federationDomainIssuer, err := federationdomainproviders.NewFederationDomainIssuerWithDefaultIDP(federationDomain.Spec.Issuer, defaultFederationDomainIdentityProvider) conditions = appendIssuerURLValidCondition(err, conditions) + // These conditions can only have errors when the list of IDPs is explicitly configured, + // and in this case there are no IDPs explicitly configured, so set these conditions all to have no errors. conditions = appendIdentityProviderDuplicateDisplayNamesCondition(sets.Set[string]{}, conditions) conditions = appendIdentityProviderObjectRefAPIGroupSuffixCondition(c.apiGroup, []string{}, conditions) conditions = appendIdentityProviderObjectRefKindCondition(c.sortedAllowedKinds(), []string{}, conditions) conditions = appendTransformsConstantsNamesUniqueCondition(sets.Set[string]{}, conditions) + conditions = appendTransformsExpressionsValidCondition([]string{}, conditions) return federationDomainIssuer, conditions, nil } func (c *federationDomainWatcherController) makeFederationDomainIssuerWithExplicitIDPs( + ctx context.Context, federationDomain *configv1alpha1.FederationDomain, conditions []*configv1alpha1.Condition, ) (*federationdomainproviders.FederationDomainIssuer, []*configv1alpha1.Condition, error) { @@ -337,10 +345,13 @@ func (c *federationDomainWatcherController) makeFederationDomainIssuerWithExplic badKinds := []string{} for index, idp := range federationDomain.Spec.IdentityProviders { + idpIsValid := true + // The CRD requires the displayName field, and validates that it has at least one character, // so here we only need to validate that they are unique. if displayNames.Has(idp.DisplayName) { duplicateDisplayNames.Insert(idp.DisplayName) + idpIsValid = false } displayNames.Insert(idp.DisplayName) @@ -361,6 +372,7 @@ func (c *federationDomainWatcherController) makeFederationDomainIssuerWithExplic canTryToFindIDP = false } + // When the apiGroup and kind are valid, try to find the IDP CR. var idpResourceUID types.UID idpWasFound := false if canTryToFindIDP { @@ -375,26 +387,31 @@ func (c *federationDomainWatcherController) makeFederationDomainIssuerWithExplic } if !canTryToFindIDP || !idpWasFound { idpNotFoundIndices = append(idpNotFoundIndices, index) + idpIsValid = false } var err error var pipeline *idtransform.TransformationPipeline - pipeline, conditions, err = c.makeTransformationPipelineForIdentityProvider(idp, federationDomain.Name, conditions) + var allExamplesPassed bool + pipeline, allExamplesPassed, conditions, err = c.makeTransformationPipelineAndEvaluateExamplesForIdentityProvider(ctx, idp, index, conditions) if err != nil { return nil, nil, err } + if !allExamplesPassed { + idpIsValid = false + } - // For each valid IDP (unique displayName, valid objectRef + valid transforms), add it to the list. + if !idpIsValid { + // Something about the IDP was not valid. Don't add it. + continue + } + + // For a valid IDP (unique displayName, valid objectRef, valid transforms), add it to the list. federationDomainIdentityProviders = append(federationDomainIdentityProviders, &federationdomainproviders.FederationDomainIdentityProvider{ DisplayName: idp.DisplayName, UID: idpResourceUID, Transforms: pipeline, }) - plog.Debug("loaded FederationDomain identity provider", - "federationDomain", federationDomain.Name, - "identityProviderDisplayName", idp.DisplayName, - "identityProviderResourceUID", idpResourceUID, - ) } if len(idpNotFoundIndices) != 0 { @@ -457,12 +474,31 @@ func (c *federationDomainWatcherController) findIDPsUIDByObjectRef(objectRef cor return idpResourceUID, true, nil } -func (c *federationDomainWatcherController) makeTransformationPipelineForIdentityProvider( +func (c *federationDomainWatcherController) makeTransformationPipelineAndEvaluateExamplesForIdentityProvider( + ctx context.Context, idp configv1alpha1.FederationDomainIdentityProvider, - federationDomainName string, + idpIndex int, conditions []*configv1alpha1.Condition, -) (*idtransform.TransformationPipeline, []*configv1alpha1.Condition, error) { - pipeline := idtransform.NewTransformationPipeline() +) (*idtransform.TransformationPipeline, bool, []*configv1alpha1.Condition, error) { + consts, conditions, err := c.makeTransformsConstants(idp, conditions) + if err != nil { + return nil, false, nil, err + } + + pipeline, conditions, err := c.makeTransformationPipeline(idp, idpIndex, consts, conditions) + if err != nil { + return nil, false, nil, err + } + + allExamplesPassed, conditions := c.evaluateExamples(ctx, idp, pipeline, conditions) + + return pipeline, allExamplesPassed, conditions, nil +} + +func (c *federationDomainWatcherController) makeTransformsConstants( + idp configv1alpha1.FederationDomainIdentityProvider, + conditions []*configv1alpha1.Condition, +) (*celtransformer.TransformationConstants, []*configv1alpha1.Condition, error) { consts := &celtransformer.TransformationConstants{ StringConstants: map[string]string{}, StringListConstants: map[string][]string{}, @@ -488,10 +524,23 @@ func (c *federationDomainWatcherController) makeTransformationPipelineForIdentit return nil, nil, fmt.Errorf("one of spec.identityProvider[].transforms.constants[].type is invalid: %q", constant.Type) } } + conditions = appendTransformsConstantsNamesUniqueCondition(duplicateConstNames, conditions) + return consts, conditions, nil +} + +func (c *federationDomainWatcherController) makeTransformationPipeline( + idp configv1alpha1.FederationDomainIdentityProvider, + idpIndex int, + consts *celtransformer.TransformationConstants, + conditions []*configv1alpha1.Condition, +) (*idtransform.TransformationPipeline, []*configv1alpha1.Condition, error) { + pipeline := idtransform.NewTransformationPipeline() + expressionsCompileErrors := []string{} + // Compile all the expressions and add them to the pipeline. - for idx, expr := range idp.Transforms.Expressions { + for exprIndex, expr := range idp.Transforms.Expressions { var rawTransform celtransformer.CELTransformation switch expr.Type { case "username/v1": @@ -507,37 +556,50 @@ func (c *federationDomainWatcherController) makeTransformationPipelineForIdentit // This shouldn't really happen since the CRD validates it, but handle it as an error. return nil, nil, fmt.Errorf("one of spec.identityProvider[].transforms.expressions[].type is invalid: %q", expr.Type) } + compiledTransform, err := c.celTransformer.CompileTransformation(rawTransform, consts) if err != nil { - // TODO: handle compile err - plog.Error("error compiling identity transformation", err, - "federationDomain", federationDomainName, - "idpDisplayName", idp.DisplayName, - "transformationIndex", idx, - "transformationType", expr.Type, - "transformationExpression", expr.Expression, - ) + expressionsCompileErrors = append(expressionsCompileErrors, + fmt.Sprintf("spec.identityProvider[%d].transforms.expressions[%d].expression was invalid:\n%s", + idpIndex, exprIndex, err.Error())) } + pipeline.AppendTransformation(compiledTransform) - plog.Debug("successfully compiled identity transformation expression", - "type", expr.Type, - "expr", expr.Expression, - "policyMessage", expr.Message, - ) + } + + conditions = appendTransformsExpressionsValidCondition(expressionsCompileErrors, conditions) + + if len(expressionsCompileErrors) > 0 { + // One or more of the expressions did not compile, so we don't have a useful pipeline to return. + return nil, conditions, nil + } + + return pipeline, conditions, nil +} + +func (c *federationDomainWatcherController) evaluateExamples( + ctx context.Context, + idp configv1alpha1.FederationDomainIdentityProvider, + pipeline *idtransform.TransformationPipeline, + conditions []*configv1alpha1.Condition, +) (bool, []*configv1alpha1.Condition) { + if pipeline == nil { + // TODO cannot evaluate examples, but still need to write a condition for it + return false, conditions } // Run all the provided transform examples. If any fail, put errors on the FederationDomain status. - for idx, e := range idp.Transforms.Examples { - // TODO: use a real context param below - result, _ := pipeline.Evaluate(context.TODO(), e.Username, e.Groups) + examplesErrors := []string{} + for examplesIndex, e := range idp.Transforms.Examples { + result, _ := pipeline.Evaluate(ctx, e.Username, e.Groups) // TODO: handle err resultWasAuthRejected := !result.AuthenticationAllowed if e.Expects.Rejected && !resultWasAuthRejected { //nolint:gocritic,nestif // TODO: handle this failed example + examplesErrors = append(examplesErrors, "TODO") plog.Warning("FederationDomain identity provider transformations example failed: expected authentication to be rejected but it was not", - "federationDomain", federationDomainName, "idpDisplayName", idp.DisplayName, - "exampleIndex", idx, + "exampleIndex", examplesIndex, "expectedRejected", e.Expects.Rejected, "actualRejectedResult", resultWasAuthRejected, "expectedMessage", e.Expects.Message, @@ -545,10 +607,10 @@ func (c *federationDomainWatcherController) makeTransformationPipelineForIdentit ) } else if !e.Expects.Rejected && resultWasAuthRejected { // TODO: handle this failed example + examplesErrors = append(examplesErrors, "TODO") plog.Warning("FederationDomain identity provider transformations example failed: expected authentication not to be rejected but it was rejected", - "federationDomain", federationDomainName, "idpDisplayName", idp.DisplayName, - "exampleIndex", idx, + "exampleIndex", examplesIndex, "expectedRejected", e.Expects.Rejected, "actualRejectedResult", resultWasAuthRejected, "expectedMessage", e.Expects.Message, @@ -557,10 +619,10 @@ func (c *federationDomainWatcherController) makeTransformationPipelineForIdentit } else if e.Expects.Rejected && resultWasAuthRejected && e.Expects.Message != result.RejectedAuthenticationMessage { // TODO: when expected message is blank, then treat it like it expects the default message // TODO: handle this failed example + examplesErrors = append(examplesErrors, "TODO") plog.Warning("FederationDomain identity provider transformations example failed: expected a different authentication rejection message", - "federationDomain", federationDomainName, "idpDisplayName", idp.DisplayName, - "exampleIndex", idx, + "exampleIndex", examplesIndex, "expectedRejected", e.Expects.Rejected, "actualRejectedResult", resultWasAuthRejected, "expectedMessage", e.Expects.Message, @@ -572,10 +634,10 @@ func (c *federationDomainWatcherController) makeTransformationPipelineForIdentit // TODO: when both of these fail, put both errors onto the status (not just the first one) if e.Expects.Username != result.Username { // TODO: handle this failed example + examplesErrors = append(examplesErrors, "TODO") plog.Warning("FederationDomain identity provider transformations example failed: expected a different transformed username", - "federationDomain", federationDomainName, "idpDisplayName", idp.DisplayName, - "exampleIndex", idx, + "exampleIndex", examplesIndex, "expectedUsername", e.Expects.Username, "actualUsernameResult", result.Username, ) @@ -584,10 +646,10 @@ func (c *federationDomainWatcherController) makeTransformationPipelineForIdentit // TODO: Do we need to make this insensitive to ordering, or should the transformations evaluator be changed to always return sorted group names at the end of the pipeline? // TODO: What happens if the user did not write any group expectation? Treat it like expecting an empty list of groups? // TODO: handle this failed example + examplesErrors = append(examplesErrors, "TODO") plog.Warning("FederationDomain identity provider transformations example failed: expected a different transformed groups list", - "federationDomain", federationDomainName, "idpDisplayName", idp.DisplayName, - "exampleIndex", idx, + "exampleIndex", examplesIndex, "expectedGroups", e.Expects.Groups, "actualGroupsResult", result.Groups, ) @@ -595,7 +657,7 @@ func (c *federationDomainWatcherController) makeTransformationPipelineForIdentit } } - return pipeline, conditions, nil + return len(examplesErrors) == 0, conditions } func (c *federationDomainWatcherController) sortedAllowedKinds() []string { @@ -642,6 +704,26 @@ func appendIdentityProviderObjectRefAPIGroupSuffixCondition(expectedSuffixName s return conditions } +func appendTransformsExpressionsValidCondition(errors []string, conditions []*configv1alpha1.Condition) []*configv1alpha1.Condition { + if len(errors) > 0 { + conditions = append(conditions, &configv1alpha1.Condition{ + Type: typeTransformsExpressionsValid, + Status: configv1alpha1.ConditionFalse, + Reason: reasonInvalidTransformsExpressions, + Message: fmt.Sprintf("the expressions specified by .spec.identityProviders[].transforms.expressions[] were invalid:\n\n%s", + strings.Join(errors, "\n\n")), + }) + } else { + conditions = append(conditions, &configv1alpha1.Condition{ + Type: typeTransformsExpressionsValid, + Status: configv1alpha1.ConditionTrue, + Reason: reasonSuccess, + Message: "the expressions specified by .spec.identityProviders[].transforms.expressions[] are valid", + }) + } + return conditions +} + func appendIdentityProviderDuplicateDisplayNamesCondition(duplicateDisplayNames sets.Set[string], conditions []*configv1alpha1.Condition) []*configv1alpha1.Condition { if duplicateDisplayNames.Len() > 0 { conditions = append(conditions, &configv1alpha1.Condition{ diff --git a/internal/controller/supervisorconfig/federation_domain_watcher_test.go b/internal/controller/supervisorconfig/federation_domain_watcher_test.go index 0fbf7fa5c..15031ba2c 100644 --- a/internal/controller/supervisorconfig/federation_domain_watcher_test.go +++ b/internal/controller/supervisorconfig/federation_domain_watcher_test.go @@ -27,6 +27,7 @@ import ( pinnipedinformers "go.pinniped.dev/generated/latest/client/supervisor/informers/externalversions" "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/federationdomain/federationdomainproviders" + "go.pinniped.dev/internal/here" "go.pinniped.dev/internal/idtransform" "go.pinniped.dev/internal/testutil" ) @@ -419,6 +420,28 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) { } } + happyTransformationExpressionsCondition := func(time metav1.Time, observedGeneration int64) configv1alpha1.Condition { + return configv1alpha1.Condition{ + Type: "TransformsExpressionsValid", + Status: "True", + ObservedGeneration: observedGeneration, + LastTransitionTime: time, + Reason: "Success", + Message: "the expressions specified by .spec.identityProviders[].transforms.expressions[] are valid", + } + } + + sadTransformationExpressionsCondition := func(errorMessages string, time metav1.Time, observedGeneration int64) configv1alpha1.Condition { + return configv1alpha1.Condition{ + Type: "TransformsExpressionsValid", + Status: "False", + ObservedGeneration: observedGeneration, + LastTransitionTime: time, + Reason: "InvalidTransformsExpressions", + Message: fmt.Sprintf("the expressions specified by .spec.identityProviders[].transforms.expressions[] were invalid:\n\n%s", errorMessages), + } + } + happyAPIGroupSuffixCondition := func(time metav1.Time, observedGeneration int64) configv1alpha1.Condition { return configv1alpha1.Condition{ Type: "IdentityProvidersObjectRefAPIGroupSuffixValid", @@ -474,22 +497,21 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) { return cp } - allHappyConditionsLegacyConfigurationSuccess := func(issuer string, idpName string, time metav1.Time, observedGeneration int64) []configv1alpha1.Condition { - return sortConditionsByType([]configv1alpha1.Condition{ - happyConstNamesUniqueCondition(frozenMetav1Now, 123), - happyKindCondition(frozenMetav1Now, 123), - happyAPIGroupSuffixCondition(frozenMetav1Now, 123), - happyDisplayNamesUniqueCondition(frozenMetav1Now, 123), - happyIdentityProvidersFoundConditionLegacyConfigurationSuccess(idpName, time, observedGeneration), - happyIssuerIsUniqueCondition(time, observedGeneration), - happyIssuerURLValidCondition(time, observedGeneration), - happyOneTLSSecretPerIssuerHostnameCondition(time, observedGeneration), - happyReadyCondition(issuer, time, observedGeneration), - }) + replaceConditions := func(conditions []configv1alpha1.Condition, sadConditions []configv1alpha1.Condition) []configv1alpha1.Condition { + for _, sadReplaceCondition := range sadConditions { + for origIndex, origCondition := range conditions { + if origCondition.Type == sadReplaceCondition.Type { + conditions[origIndex] = sadReplaceCondition + break + } + } + } + return conditions } allHappyConditionsSuccess := func(issuer string, time metav1.Time, observedGeneration int64) []configv1alpha1.Condition { return sortConditionsByType([]configv1alpha1.Condition{ + happyTransformationExpressionsCondition(frozenMetav1Now, 123), happyConstNamesUniqueCondition(frozenMetav1Now, 123), happyKindCondition(frozenMetav1Now, 123), happyAPIGroupSuffixCondition(frozenMetav1Now, 123), @@ -502,16 +524,13 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) { }) } - replaceConditions := func(conditions []configv1alpha1.Condition, sadConditions []configv1alpha1.Condition) []configv1alpha1.Condition { - for _, sadReplaceCondition := range sadConditions { - for origIndex, origCondition := range conditions { - if origCondition.Type == sadReplaceCondition.Type { - conditions[origIndex] = sadReplaceCondition - break - } - } - } - return conditions + allHappyConditionsLegacyConfigurationSuccess := func(issuer string, idpName string, time metav1.Time, observedGeneration int64) []configv1alpha1.Condition { + return replaceConditions( + allHappyConditionsSuccess(issuer, time, observedGeneration), + []configv1alpha1.Condition{ + happyIdentityProvidersFoundConditionLegacyConfigurationSuccess(idpName, time, observedGeneration), + }, + ) } invalidIssuerURL := ":/host//path" @@ -1228,36 +1247,12 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) { }, Transforms: configv1alpha1.FederationDomainTransforms{ Constants: []configv1alpha1.FederationDomainTransformsConstant{ - { - Name: "duplicate1", - Type: "string", - StringValue: "abc", - }, - { - Name: "duplicate1", - Type: "string", - StringValue: "def", - }, - { - Name: "duplicate1", - Type: "string", - StringValue: "efg", - }, - { - Name: "duplicate2", - Type: "string", - StringValue: "123", - }, - { - Name: "duplicate2", - Type: "string", - StringValue: "456", - }, - { - Name: "unique", - Type: "string", - StringValue: "hij", - }, + {Name: "duplicate1", Type: "string", StringValue: "abc"}, + {Name: "duplicate1", Type: "string", StringValue: "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"}, }, }, }, @@ -1281,6 +1276,67 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) { ), }, }, + { + name: "the federation domain has transformation expressions which don't compile", + 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{ + Expressions: []configv1alpha1.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"}, + }, + }, + }, + }, + }, + }, + }, + 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{ + sadTransformationExpressionsCondition( + here.Doc( + `spec.identityProvider[0].transforms.expressions[0].expression was invalid: + CEL expression compile error: ERROR: :1:6: Syntax error: mismatched input 'is' expecting + | this is not a valid cel expression + | .....^ + + spec.identityProvider[0].transforms.expressions[1].expression was invalid: + CEL expression compile error: ERROR: :1:6: Syntax error: mismatched input 'is' expecting + | this is also not a valid cel expression + | .....^ + + spec.identityProvider[0].transforms.expressions[3].expression was invalid: + CEL expression compile error: ERROR: :1:7: Syntax error: mismatched input 'not' expecting + | still not a valid cel expression + | ......^`), + frozenMetav1Now, 123), + sadReadyCondition(frozenMetav1Now, 123), + }), + ), + }, + }, { name: "the federation domain specifies illegal const type, which shouldn't really happen since the CRD validates it", inputObjects: []runtime.Object{ diff --git a/test/integration/supervisor_discovery_test.go b/test/integration/supervisor_discovery_test.go index 5f7a93adb..ed2e2e204 100644 --- a/test/integration/supervisor_discovery_test.go +++ b/test/integration/supervisor_discovery_test.go @@ -142,6 +142,7 @@ func TestSupervisorOIDCDiscovery_Disruptive(t *testing.T) { "IdentityProvidersObjectRefAPIGroupSuffixValid": v1alpha1.ConditionTrue, "IdentityProvidersDisplayNamesUnique": v1alpha1.ConditionTrue, "TransformsConstantsNamesUnique": v1alpha1.ConditionTrue, + "TransformsExpressionsValid": v1alpha1.ConditionTrue, }) requireStatus(t, client, ns, config6Duplicate2.Name, v1alpha1.FederationDomainPhaseError, map[string]v1alpha1.ConditionStatus{ "Ready": v1alpha1.ConditionFalse, @@ -153,6 +154,7 @@ func TestSupervisorOIDCDiscovery_Disruptive(t *testing.T) { "IdentityProvidersObjectRefAPIGroupSuffixValid": v1alpha1.ConditionTrue, "IdentityProvidersDisplayNamesUnique": v1alpha1.ConditionTrue, "TransformsConstantsNamesUnique": v1alpha1.ConditionTrue, + "TransformsExpressionsValid": v1alpha1.ConditionTrue, }) requireDiscoveryEndpointsAreNotFound(t, scheme, addr, caBundle, issuer6) @@ -181,6 +183,7 @@ func TestSupervisorOIDCDiscovery_Disruptive(t *testing.T) { "IdentityProvidersObjectRefAPIGroupSuffixValid": v1alpha1.ConditionTrue, "IdentityProvidersDisplayNamesUnique": v1alpha1.ConditionTrue, "TransformsConstantsNamesUnique": v1alpha1.ConditionTrue, + "TransformsExpressionsValid": v1alpha1.ConditionTrue, }) requireDiscoveryEndpointsAreNotFound(t, scheme, addr, caBundle, badIssuer) requireDeletingFederationDomainCausesDiscoveryEndpointsToDisappear(t, badConfig, client, ns, scheme, addr, caBundle, badIssuer) @@ -698,6 +701,7 @@ func requireFullySuccessfulStatus(t *testing.T, client pinnipedclientset.Interfa "IdentityProvidersObjectRefAPIGroupSuffixValid": v1alpha1.ConditionTrue, "IdentityProvidersDisplayNamesUnique": v1alpha1.ConditionTrue, "TransformsConstantsNamesUnique": v1alpha1.ConditionTrue, + "TransformsExpressionsValid": v1alpha1.ConditionTrue, }) }