Validate transforms expressions in federation_domain_watcher.go
This commit is contained in:
parent
013030041a
commit
52925a2a46
@ -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{
|
||||
|
@ -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: <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
|
||||
| ......^`),
|
||||
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{
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user