Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Comments

CNTRLPLANE-2550: Add support for CEL expression claim mappings for username and groups#2719

Open
ShazaAldawamneh wants to merge 6 commits intoopenshift:master from
ShazaAldawamneh:CNTRLPLANE-2550
Open

CNTRLPLANE-2550: Add support for CEL expression claim mappings for username and groups #2719
ShazaAldawamneh wants to merge 6 commits intoopenshift:master from
ShazaAldawamneh:CNTRLPLANE-2550

Conversation

@ShazaAldawamneh
Copy link
Contributor

@ShazaAldawamneh ShazaAldawamneh commented Feb 19, 2026

  • Updated UsernameClaimMapping.Claim to be optional when ExternalOIDCWithUpstreamParity is enabled.
  • Added XValidation rule to require claim when PrefixPolicy is 'Prefix'.
  • Updated Expression field to be fully gated behind ExternalOIDCWithUpstreamParity.
  • Ensures minimal Authentication CRs pass validation and preserve expected claim in tests.
  • Aligned PrefixedClaimMapping for groups with same optional/validation behavior.

Signed-off-by: Shaza Aldawamneh <shaza.aldawamneh@hotmail.com>
Copy link

Pipeline controller notification
This repo is configured to use the pipeline controller. Second-stage tests will be triggered either automatically or after lgtm label is added, depending on the repository configuration. The pipeline controller will automatically detect which contexts are required and will utilize /test Prow commands to trigger the second stage.

For optional jobs, comment /test ? to see a list of all defined jobs. To trigger manually all jobs from second stage use /pipeline required command.

This repository is configured in: LGTM mode

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Feb 19, 2026
@openshift-ci openshift-ci bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Feb 19, 2026
Copy link

openshift-ci-robot commented Feb 19, 2026
edited by openshift-ci bot
Loading

@ShazaAldawamneh: This pull request references CNTRLPLANE-2550 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

  • Updated UsernameClaimMapping.Claim to be optional when ExternalOIDCWithUpstreamParity is enabled.
  • Added XValidation rule to require claim when PrefixPolicy is 'Prefix'.
  • Updated Expression field to be fully gated behind ExternalOIDCWithUpstreamParity.
  • Ensures minimal Authentication CRs pass validation and preserve expected claim in tests.
  • Aligned PrefixedClaimMapping for groups with same optional/validation behavior.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Copy link
Contributor

openshift-ci bot commented Feb 19, 2026

Hello @ShazaAldawamneh! Some important instructions when contributing to openshift/api:
API design plays an important part in the user experience of OpenShift and as such API PRs are subject to a high level of scrutiny to ensure they follow our best practices. If you haven't already done so, please review the OpenShift API Conventions and ensure that your proposed changes are compliant. Following these conventions will help expedite the api review process for your PR.

@openshift-ci openshift-ci bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Feb 19, 2026
Copy link
Contributor

openshift-ci bot commented Feb 19, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign joelspeed for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Signed-off-by: Shaza Aldawamneh <shaza.aldawamneh@hotmail.com>
@openshift-ci openshift-ci bot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Feb 19, 2026
Signed-off-by: Shaza Aldawamneh <shaza.aldawamneh@hotmail.com>
Copy link

coderabbitai bot commented Feb 23, 2026
edited
Loading

📝 Walkthrough

Walkthrough

Optional expression fields were added to TokenClaimMapping and UsernameClaimMapping; their claim fields were made optional. Feature-gate-aware XValidation rules (notably for ExternalOIDCWithUpstreamParity) enforce mutual-exclusion / precisely-one-of semantics between claim and expression. Min/max length constraints were added for claim and expression. Generated Swagger and multiple CRD payload manifests were updated to reflect schema and validations. Tests were modified: one invalid-expression test removed and four tests added covering expression-only and mutually-exclusive claim+expression scenarios. Lines changed: +99/-1 across code, CRDs, and tests.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding CEL expression support for claim mappings in username and groups, which aligns with the substantial updates to UsernameClaimMapping and related types documented in the changeset.
Description check ✅ Passed The description is directly related to the changeset, detailing key implementation aspects such as making Claim optional, adding XValidation rules, and gating the Expression field behind ExternalOIDCWithUpstreamParity, all of which correspond to changes in the modified files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.5.0)

Error: build linters: unable to load custom analyzer "kubeapilinter": tools/_output/bin/kube-api-linter.so, plugin: not implemented
The command is terminated due to an error: build linters: unable to load custom analyzer "kubeapilinter": tools/_output/bin/kube-api-linter.so, plugin: not implemented


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
payload-manifests/crds/0000_10_config-operator_01_authentications-DevPreviewNoUpgrade.crd.yaml (1)

189-218: ⚠️ Potential issue | 🟠 Major

Groups: required claim blocks CEL-only mapping.

claim is still required while claim+expression is forbidden, so expression can’t be used alone. If CEL mapping is intended here, drop the unconditional requirement and enforce a one‐of rule.

Suggested fix
- required:
- - claim
 type: object
 x-kubernetes-validations:
- - message: claim and expression cannot both be set
- rule: '!(has(self.claim) && has(self.expression))'
+ - message: precisely one of claim or expression must be set
+ rule: 'has(self.claim) ? !has(self.expression) : has(self.expression)'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@payload-manifests/crds/0000_10_config-operator_01_authentications-DevPreviewNoUpgrade.crd.yaml`
around lines 189 - 218, The schema currently mandates claim in the required list
while also forbidding claim+expression via x-kubernetes-validations, which
prevents using expression alone; update the CRD so that claim is not
unconditionally required and replace the required: - claim rule with a oneOf (or
an equivalent x-kubernetes-validation) that enforces either claim is set XOR
expression is set (e.g., a oneOf referencing presence of self.claim or
self.expression) and keep the existing mutually-exclusive validation
(x-kubernetes-validations rule: '!(has(self.claim) && has(self.expression))') to
ensure only one is provided; modify the block around the claim/expression/prefix
properties and the required/type definitions to reflect this change.
payload-manifests/crds/0000_10_config-operator_01_authentications-CustomNoUpgrade.crd.yaml (1)

189-218: ⚠️ Potential issue | 🟠 Major

Groups: required claim blocks CEL-only mapping.

claim is still required while claim+expression is forbidden, so expression can’t be used alone. If CEL mapping is intended here, drop the unconditional requirement and enforce a one‐of rule.

Suggested fix
- required:
- - claim
 type: object
 x-kubernetes-validations:
- - message: claim and expression cannot both be set
- rule: '!(has(self.claim) && has(self.expression))'
+ - message: precisely one of claim or expression must be set
+ rule: 'has(self.claim) ? !has(self.expression) : has(self.expression)'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@payload-manifests/crds/0000_10_config-operator_01_authentications-CustomNoUpgrade.crd.yaml`
around lines 189 - 218, Remove the unconditional required: - claim for the
mapping object and instead enforce mutual exclusivity and presence by adding a
oneOf that requires either claim or expression; specifically, replace the
top-level required: [claim] with a oneOf containing two subschemas (one with
required: [claim], the other with required: [expression]) so that exactly one is
provided, keep the existing x-kubernetes-validations rule or remove it (it
becomes redundant) and leave prefix as-is; this targets the schema that defines
claim, expression and prefix so look for the object containing those properties
and update its required/oneOf accordingly.
payload-manifests/crds/0000_10_config-operator_01_authentications-TechPreviewNoUpgrade.crd.yaml (1)

189-218: ⚠️ Potential issue | 🟠 Major

Groups: required claim blocks CEL-only mapping.

claim is still required while claim+expression is forbidden, so expression can’t be used alone. If CEL mapping is intended here, drop the unconditional requirement and enforce a one‐of rule.

Suggested fix
- required:
- - claim
 type: object
 x-kubernetes-validations:
- - message: claim and expression cannot both be set
- rule: '!(has(self.claim) && has(self.expression))'
+ - message: precisely one of claim or expression must be set
+ rule: 'has(self.claim) ? !has(self.expression) : has(self.expression)'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@payload-manifests/crds/0000_10_config-operator_01_authentications-TechPreviewNoUpgrade.crd.yaml`
around lines 189 - 218, Remove the unconditional required: - claim and instead
enforce a mutual-exclusion plus presence rule so CEL-only mapping is possible;
keep the existing x-kubernetes-validations rule that forbids both claim and
expression (rule: '!(has(self.claim) && has(self.expression))') and add a second
validation that requires at least one be set (e.g. message: 'either claim or
expression must be set' with rule: '(has(self.claim) || has(self.expression))'),
referencing the same object schema containing the claim and expression fields so
either can be used alone.
🧹 Nitpick comments (1)
config/v1/tests/authentications.config.openshift.io/ExternalOIDCWithUpstreamParity.yaml (1)

459-553: Add a negative test for prefixPolicy: Prefix with expression-only username mapping.

There’s new validation requiring claim when prefixPolicy is Prefix. A targeted test here will lock in that behavior for the feature gate.

🧪 Suggested test case (adjust expectedError to match validator message)
 - name: Cannot set both claim and expression for username mapping
 initial: |
 apiVersion: config.openshift.io/v1
 kind: Authentication
 spec:
 type: OIDC
 oidcProviders:
 - name: myoidc
 issuer:
 issuerURL: https://meh.tld
 audiences: ['openshift-aud']
 claimMappings:
 username:
 claim: "preferred_username"
 expression: "claims.sub"
 expectedError: "claim must not be set when expression is provided"
+ - name: Cannot set prefixPolicy Prefix with username expression
+ initial: |
+ apiVersion: config.openshift.io/v1
+ kind: Authentication
+ spec:
+ type: OIDC
+ oidcProviders:
+ - name: myoidc
+ issuer:
+ issuerURL: https://meh.tld
+ audiences: ['openshift-aud']
+ claimMappings:
+ username:
+ expression: "claims.sub"
+ prefixPolicy: Prefix
+ prefix:
+ prefixString: "myoidc:"
+ expectedError: "claim must be set when prefixPolicy is 'Prefix'"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@config/v1/tests/authentications.config.openshift.io/ExternalOIDCWithUpstreamParity.yaml`
around lines 459 - 553, Add a negative test case that validates the new rule
requiring a literal claim when prefixPolicy is set to Prefix: create a YAML test
similar to the existing cases but set claimMappings.username.expression (e.g.
expression: "has(claims.upn) ? claims.upn : claims.oid") and set prefixPolicy:
Prefix in the provider spec, and assert an expectedError like "claim must be
provided when prefixPolicy is Prefix" (adjust text to match validator). Place it
alongside the other OIDC tests referencing prefixPolicy, Prefix, and
claimMappings.username.expression so the validator for prefixPolicy +
expression-only username mapping is exercised.
i️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 22c7448 and 6cc13c4.

⛔ Files ignored due to path filters (10)
  • config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-CustomNoUpgrade.crd.yaml is excluded by !**/zz_generated.crd-manifests/*
  • config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-Default.crd.yaml is excluded by !**/zz_generated.crd-manifests/*
  • config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-DevPreviewNoUpgrade.crd.yaml is excluded by !**/zz_generated.crd-manifests/*
  • config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-OKD.crd.yaml is excluded by !**/zz_generated.crd-manifests/*
  • config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-TechPreviewNoUpgrade.crd.yaml is excluded by !**/zz_generated.crd-manifests/*
  • config/v1/zz_generated.featuregated-crd-manifests/authentications.config.openshift.io/ExternalOIDC.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • config/v1/zz_generated.featuregated-crd-manifests/authentications.config.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • config/v1/zz_generated.featuregated-crd-manifests/authentications.config.openshift.io/ExternalOIDCWithUpstreamParity.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • openapi/generated_openapi/zz_generated.openapi.go is excluded by !openapi/**
  • openapi/openapi.json is excluded by !openapi/**
📒 Files selected for processing (8)
  • config/v1/tests/authentications.config.openshift.io/ExternalOIDCWithUpstreamParity.yaml
  • config/v1/types_authentication.go
  • config/v1/zz_generated.swagger_doc_generated.go
  • payload-manifests/crds/0000_10_config-operator_01_authentications-CustomNoUpgrade.crd.yaml
  • payload-manifests/crds/0000_10_config-operator_01_authentications-Default.crd.yaml
  • payload-manifests/crds/0000_10_config-operator_01_authentications-DevPreviewNoUpgrade.crd.yaml
  • payload-manifests/crds/0000_10_config-operator_01_authentications-OKD.crd.yaml
  • payload-manifests/crds/0000_10_config-operator_01_authentications-TechPreviewNoUpgrade.crd.yaml
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@config/v1/types_authentication.go`:
- Around line 352-371: The TokenClaimMapping struct currently makes Claim
required which, combined with the mutual-exclusion rule, prevents Expression
from ever being set; change Claim to optional and make the XNOR rule into an
XOR. Remove the +required annotation and change the JSON tag for Claim to
include omitempty (e.g., json:"claim,omitempty"), and update the
openshift:validation rule to an XOR such as rule="has(self.claim) !=
has(self.expression)" so exactly one of Claim or Expression can be provided
(keep Expression's existing omitempty and feature-gate annotations).
- Around line 605-635: Remove the unconditional field-level XValidation on the
Claim field (delete the
+kubebuilder:validation:XValidation:rule="has(self.claim)" annotation) and
instead enforce exclusive-or at the struct level when the feature gate is
enabled: update the existing FeatureGateAwareXValidation (on type
UsernameClaimMapping) to a rule that requires exactly one of claim or expression
when ExternalOIDCWithUpstreamParity is enabled (e.g. rule="has(self.claim) !=
has(self.expression)" with an appropriate message). Keep the MinLength/MaxLength
tags on Claim and Expression but do not require Claim unconditionally.
In `@config/v1/zz_generated.swagger_doc_generated.go`:
- Around line 468-471: The swagger doc for TokenClaimMapping
(map_TokenClaimMapping) incorrectly marks "claim" as required despite supporting
expression-only mappings; update the source Go type comment for
TokenClaimMapping to indicate that "claim" is optional and that "claim" and
"expression" are mutually exclusive (describe that either claim or expression
may be provided, not both), then re-run the swagger generation script to
regenerate zz_generated.swagger_doc_generated.go so the map_TokenClaimMapping
entry reflects the optional claim and mutual exclusion with expression.
- Around line 549-552: The Swagger comment for UsernameClaimMapping (seen in
map_UsernameClaimMapping) is missing the new validation rules: add documentation
that expression has a max length (match the enforced length), and that when
prefixPolicy is "Prefix" the claim field is required and claim itself has a
non-empty/<=256 char constraint; update the source struct/type comment for
UsernameClaimMapping (the comment above its Go type or fields: claim,
expression, prefixPolicy, prefix) to include these sentences and regenerate the
swagger docs so the generated map_UsernameClaimMapping includes the new length
and dependency text.
In
`@payload-manifests/crds/0000_10_config-operator_01_authentications-CustomNoUpgrade.crd.yaml`:
- Around line 267-293: The property-level validation on the claim field (the
x-kubernetes-validations entry with message "claim must be set") makes
expression-only configs impossible; remove that per-property required validation
from the claim schema and instead enforce mutual exclusivity/requirement at the
object level with a oneOf/anyOf rule that requires exactly one of claim or
expression (reference the schema object that contains the claim and expression
properties and the expression property itself); apply the same change to the
duplicate block mentioned (the other claim/expression pair).
In
`@payload-manifests/crds/0000_10_config-operator_01_authentications-DevPreviewNoUpgrade.crd.yaml`:
- Around line 267-293: The property-level validation on the 'claim' field (the
x-kubernetes-validations rule with message "claim must be set") makes
'expression' unusable; remove that field-level requirement and replace it with a
single object-level validation that enforces exactly one of 'claim' or
'expression' (e.g., a oneOf / CEL rule at the parent object) so configs can
specify either claim-only or expression-only but not both; update the schema
entries that reference 'claim' and 'expression' (the two sibling properties
shown) to drop the has(self.claim) rule and add the new object-level one-of
validation.
In
`@payload-manifests/crds/0000_10_config-operator_01_authentications-TechPreviewNoUpgrade.crd.yaml`:
- Around line 267-293: The current property-level x-kubernetes-validations on
claim ("claim must be set") forces claim and prevents expression-only configs;
remove the claim-level "must be set" validation and instead add an object-level
x-kubernetes-validations rule that enforces exactly one of claim or expression
is present (e.g., use a CEL rule like (has(self.claim) + has(self.expression))
== 1 with an appropriate message), and apply the same change for the duplicate
block that appears later for the other mapping. Ensure the claim and expression
property schemas keep their type, minLength and maxLength constraints but no
longer require claim alone.
---
Outside diff comments:
In
`@payload-manifests/crds/0000_10_config-operator_01_authentications-CustomNoUpgrade.crd.yaml`:
- Around line 189-218: Remove the unconditional required: - claim for the
mapping object and instead enforce mutual exclusivity and presence by adding a
oneOf that requires either claim or expression; specifically, replace the
top-level required: [claim] with a oneOf containing two subschemas (one with
required: [claim], the other with required: [expression]) so that exactly one is
provided, keep the existing x-kubernetes-validations rule or remove it (it
becomes redundant) and leave prefix as-is; this targets the schema that defines
claim, expression and prefix so look for the object containing those properties
and update its required/oneOf accordingly.
In
`@payload-manifests/crds/0000_10_config-operator_01_authentications-DevPreviewNoUpgrade.crd.yaml`:
- Around line 189-218: The schema currently mandates claim in the required list
while also forbidding claim+expression via x-kubernetes-validations, which
prevents using expression alone; update the CRD so that claim is not
unconditionally required and replace the required: - claim rule with a oneOf (or
an equivalent x-kubernetes-validation) that enforces either claim is set XOR
expression is set (e.g., a oneOf referencing presence of self.claim or
self.expression) and keep the existing mutually-exclusive validation
(x-kubernetes-validations rule: '!(has(self.claim) && has(self.expression))') to
ensure only one is provided; modify the block around the claim/expression/prefix
properties and the required/type definitions to reflect this change.
In
`@payload-manifests/crds/0000_10_config-operator_01_authentications-TechPreviewNoUpgrade.crd.yaml`:
- Around line 189-218: Remove the unconditional required: - claim and instead
enforce a mutual-exclusion plus presence rule so CEL-only mapping is possible;
keep the existing x-kubernetes-validations rule that forbids both claim and
expression (rule: '!(has(self.claim) && has(self.expression))') and add a second
validation that requires at least one be set (e.g. message: 'either claim or
expression must be set' with rule: '(has(self.claim) || has(self.expression))'),
referencing the same object schema containing the claim and expression fields so
either can be used alone.
---
Nitpick comments:
In
`@config/v1/tests/authentications.config.openshift.io/ExternalOIDCWithUpstreamParity.yaml`:
- Around line 459-553: Add a negative test case that validates the new rule
requiring a literal claim when prefixPolicy is set to Prefix: create a YAML test
similar to the existing cases but set claimMappings.username.expression (e.g.
expression: "has(claims.upn) ? claims.upn : claims.oid") and set prefixPolicy:
Prefix in the provider spec, and assert an expectedError like "claim must be
provided when prefixPolicy is Prefix" (adjust text to match validator). Place it
alongside the other OIDC tests referencing prefixPolicy, Prefix, and
claimMappings.username.expression so the validator for prefixPolicy +
expression-only username mapping is exercised.

Signed-off-by: Shaza Aldawamneh <shaza.aldawamneh@hotmail.com>
Copy link
Contributor

@ehearne-redhat ehearne-redhat left a comment
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if it will help resolve the error, but it might not be a bad idea to check if self.claim exists.

Maybe we could try this rule:

!(size(self.claim) > 0 && size(self.expression) > 0) instead of !(has(self.claim) && has(self.expression))

It seems to follow how other optional strings are checked.

Comment on lines 358 to 359
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=256
Copy link
Contributor

@everettraven everettraven Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are going to add this constraint, we need to make sure we add ratcheting tests like

- name: Should allow changing other fields when a persisted value is no longer valid
initialCRDPatches:
- op: remove
path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/nonZeroDefault/minimum # Minimum is normally 8
initial: |
apiVersion: example.openshift.io/v1
kind: StableConfigType
spec:
nonZeroDefault: 1
immutableField: foo
updated: |
apiVersion: example.openshift.io/v1
kind: StableConfigType
spec:
nonZeroDefault: 1
immutableField: foo
optionalImmutableField: foo
expected: |
apiVersion: example.openshift.io/v1
kind: StableConfigType
spec:
nonZeroDefault: 1
immutableField: foo
optionalImmutableField: foo
- name: Should allow updating a persisted value that is no longer valid to a valid value
initialCRDPatches:
- op: remove
path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/nonZeroDefault/minimum # Minimum is normally 8
initial: |
apiVersion: example.openshift.io/v1
kind: StableConfigType
spec:
nonZeroDefault: 1
immutableField: foo
updated: |
apiVersion: example.openshift.io/v1
kind: StableConfigType
spec:
nonZeroDefault: 8
immutableField: foo
expected: |
apiVersion: example.openshift.io/v1
kind: StableConfigType
spec:
nonZeroDefault: 8
immutableField: foo
- name: Should not allow updating a persisted value that is no longer valid to a still invalid value
initialCRDPatches:
- op: remove
path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/nonZeroDefault/minimum # Minimum is normally 8
initial: |
apiVersion: example.openshift.io/v1
kind: StableConfigType
spec:
nonZeroDefault: 1
immutableField: foo
updated: |
apiVersion: example.openshift.io/v1
kind: StableConfigType
spec:
nonZeroDefault: 2
immutableField: foo
expectedError: "spec.nonZeroDefault: Invalid value: 2: spec.nonZeroDefault in body should be greater than or equal to 8"
as an example.

This is to test the scenarios where a user may have already configured this with a value that does not satisfy these new constraints.

Copy link
Contributor Author

@ShazaAldawamneh ShazaAldawamneh Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for these ratcheting tests, should we add them in /RedHat/api/config/v1/tests/authentications.config.openshift.io/ExternalOIDCWithUpstreamParity.yaml? Also, I’m wondering why we need to add them here if similar tests already exist in the API repo — wouldn’t those run by default?

Copy link
Contributor

@everettraven everettraven Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config/v1/tests/authentications.config.openshift.io/ExternalOIDCWithUpstreamParity.yaml

They would need to be made in all ExternalOIDC related test files.

Also, I’m wondering why we need to add them here if similar tests already exist in the API repo — wouldn’t those run by default?

The existing ones don't automatically test ratcheting behavior of every changed field. When we ratchet the validations of existing fields we need to add new tests here.

Copy link
Contributor Author

@ShazaAldawamneh ShazaAldawamneh Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clarification on the ratcheting tests, that makes sense.

I have another question regarding the MinLength=1 and MaxLength=256 validations. I looked through the existing ExternalOIDC-related test files, and I don’t see any tests that explicitly cover these constraints, even though we’ve had similar validations on other fields before.

Are these length validations being added now for a specific reason? Or are they already enforced/tested in another way that I may have missed?

I just want to make sure I fully understand whether these are new constraints that require additional ratcheting tests, or if there’s existing coverage that I’m not recognizing.

@ShazaAldawamneh ShazaAldawamneh changed the title (削除) [WIP]: CNTRLPLANE-2550: Add support for CEL expression claim mappings for username and groups (削除ここまで) (追記) CNTRLPLANE-2550: Add support for CEL expression claim mappings for username and groups (追記ここまで) Feb 24, 2026
@openshift-ci openshift-ci bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Feb 24, 2026
Signed-off-by: Shaza Aldawamneh <shaza.aldawamneh@hotmail.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
payload-manifests/crds/0000_10_config-operator_01_authentications-TechPreviewNoUpgrade.crd.yaml (1)

267-337: ⚠️ Potential issue | 🟠 Major

Username mapping validation allows empty object when it requires exactly one of claim or expression

The current validation rule !(has(self.claim) && has(self.expression)) only prevents both fields from being set simultaneously, but allows neither field to be set (empty object {}). This contradicts the description which states "precisely one of claim or expression must be set if the ExternalOIDCWithUpstreamParity feature gate is enabled."

Additionally, the similar uid field above uses the stricter rule has(self.claim) ? !has(self.expression) : has(self.expression) which correctly enforces exactly one field must be set. The username field should use the same validation logic.

Suggested fix
- - message: claim and expression cannot both be set
- rule: '!(has(self.claim) && has(self.expression))'
+ - message: precisely one of claim or expression must be set
+ rule: 'has(self.claim) != has(self.expression)'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@payload-manifests/crds/0000_10_config-operator_01_authentications-TechPreviewNoUpgrade.crd.yaml`
around lines 267 - 337, The username mapping x-kubernetes-validation currently
only forbids both claim and expression being set (rule '!(has(self.claim) &&
has(self.expression))') but allows neither; change the validation to require
exactly one of them (mirror the uid rule) by replacing the rule with logic like
"has(self.claim) ? !has(self.expression) : has(self.expression)" so the username
mapping (fields claim and expression) enforces precisely one is present when
ExternalOIDCWithUpstreamParity applies.
payload-manifests/crds/0000_10_config-operator_01_authentications-CustomNoUpgrade.crd.yaml (1)

267-337: ⚠️ Potential issue | 🟠 Major

Username mapping allows empty config

The current validation rule !(has(self.claim) && has(self.expression)) only forbids both fields being set, allowing an empty {} object to pass despite the description requiring precisely one field to be set.

Suggested fix
- - message: claim and expression cannot both be set
- rule: '!(has(self.claim) && has(self.expression))'
+ - message: precisely one of claim or expression must be set
+ rule: 'has(self.claim) != has(self.expression)'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@payload-manifests/crds/0000_10_config-operator_01_authentications-CustomNoUpgrade.crd.yaml`
around lines 267 - 337, The validation currently only forbids both claim and
expression being set (x-kubernetes-validations rule '!(has(self.claim) &&
has(self.expression))'), which still allows neither to be set; change the
validation to require exactly one be present by replacing the rule with
'has(self.claim) != has(self.expression)' (or an equivalent XOR) and update the
message to reflect "exactly one of claim or expression must be set"; target the
x-kubernetes-validations entry that references claim and expression to make this
change.
♻️ Duplicate comments (4)
payload-manifests/crds/0000_10_config-operator_01_authentications-TechPreviewNoUpgrade.crd.yaml (1)

189-218: ⚠️ Potential issue | 🟠 Major

Groups mapping: claim is still required, so expression can’t be used

required: [claim] plus "cannot both be set" means expression-only configs are invalid even when ExternalOIDCWithUpstreamParity is enabled. That conflicts with the intended expression support.

💡 Suggested fix
- required:
- - claim
 type: object
 x-kubernetes-validations:
- - message: claim and expression cannot both be set
- rule: '!(has(self.claim) && has(self.expression))'
+ - message: precisely one of claim or expression must be set
+ rule: 'has(self.claim) != has(self.expression)'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@payload-manifests/crds/0000_10_config-operator_01_authentications-TechPreviewNoUpgrade.crd.yaml`
around lines 189 - 218, The CRD currently forces claim to be mandatory by
listing it under required, which prevents expression-only mappings; remove the
required: - claim entry (i.e., delete the required array or at least remove
"claim" from it) so that the existing x-kubernetes-validations rule
('!(has(self.claim) && has(self.expression))') can still prevent both being set
while allowing expression-only configs; update the schema section containing the
claim, expression, prefix, required, and x-kubernetes-validations entries
accordingly.
payload-manifests/crds/0000_10_config-operator_01_authentications-CustomNoUpgrade.crd.yaml (1)

189-218: ⚠️ Potential issue | 🟠 Major

Groups mapping still requires claim, so expression can’t be used

required: [claim] plus "cannot both be set" blocks expression-only configs even when enabled.

💡 Suggested fix
- required:
- - claim
 type: object
 x-kubernetes-validations:
- - message: claim and expression cannot both be set
- rule: '!(has(self.claim) && has(self.expression))'
+ - message: precisely one of claim or expression must be set
+ rule: 'has(self.claim) != has(self.expression)'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@payload-manifests/crds/0000_10_config-operator_01_authentications-CustomNoUpgrade.crd.yaml`
around lines 189 - 218, The schema currently forces claim via required: [claim],
which prevents expression-only mappings; remove the unconditional required: -
claim and instead enforce presence rules via x-kubernetes-validations: keep the
mutual exclusion validation (rule: '!(has(self.claim) && has(self.expression))')
and add a new validation requiring at least one be present (e.g. message:
"either claim or expression must be set", rule: 'has(self.claim) ||
has(self.expression)'). Update the object definition around
claim/expression/prefix to drop the required entry and rely on those two
validations to allow expression-only configs.
payload-manifests/crds/0000_10_config-operator_01_authentications-DevPreviewNoUpgrade.crd.yaml (2)

213-218: ⚠️ Potential issue | 🟠 Major

expression for groups remains unreachable — required: [claim] was not removed.

The required: - claim constraint (line 214) forces claim to always be present. The mutual-exclusion rule !(has(self.claim) && has(self.expression)) then prevents expression from ever being set simultaneously, making the newly-added expression field permanently unusable. The PR objective states "Applied the same optional/validation behavior to PrefixedClaimMapping for groups," but the required: - claim was not removed the way it was for username. The fix should mirror the uid mapping (lines 257–260): remove claim from required and replace the mutual-exclusion rule with a "precisely one" rule.

🔧 Proposed fix
- required:
- - claim
 type: object
 x-kubernetes-validations:
- - message: claim and expression cannot both be set
- rule: '!(has(self.claim) && has(self.expression))'
+ - message: precisely one of claim or expression must be set
+ rule: 'has(self.claim) ? !has(self.expression) : has(self.expression)'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@payload-manifests/crds/0000_10_config-operator_01_authentications-DevPreviewNoUpgrade.crd.yaml`
around lines 213 - 218, Remove the mandatory "claim" requirement from the groups
PrefixedClaimMapping and change its validation to require exactly one of claim
or expression; specifically, delete the required: - claim entry and replace the
x-kubernetes-validations mutual-exclusion rule ('!(has(self.claim) &&
has(self.expression))') with a "precisely one" rule such as
'count([has(self.claim), has(self.expression)]) == 1' (apply this change to the
PrefixedClaimMapping for groups where fields "claim" and "expression" are
defined).

335-337: ⚠️ Potential issue | 🟠 Major

Username validation rule allows neither claim nor expression — violates "precisely one" contract.

The rule !(has(self.claim) && has(self.expression)) only blocks having both fields simultaneously. It does not enforce that at least one is present. The field descriptions (lines 271–272 and 286–287) explicitly state:

"Precisely one of claim or expression must be set if the ExternalOIDCWithUpstreamParity feature gate is enabled."

Since this is the DevPreviewNoUpgrade CRD (with that gate active), an object with neither claim nor expression passes validation today. The uid mapping (lines 257–260) uses the correct "exactly one" rule — username should match it.

🔧 Proposed fix
 x-kubernetes-validations:
- - message: claim and expression cannot both be set
- rule: '!(has(self.claim) && has(self.expression))'
+ - message: precisely one of claim or expression must be set
+ rule: 'has(self.claim) ? !has(self.expression) : has(self.expression)'
 - message: prefix must be set if prefixPolicy is 'Prefix',
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@payload-manifests/crds/0000_10_config-operator_01_authentications-DevPreviewNoUpgrade.crd.yaml`
around lines 335 - 337, The x-kubernetes-validations rule for username currently
only forbids both fields via '!(has(self.claim) && has(self.expression))' so it
permits neither; update the validation to enforce exactly-one semantics by
replacing the rule with an expression that requires XOR between self.claim and
self.expression (i.e., has(self.claim) != has(self.expression)) or an equivalent
"one and only one" boolean expression, keeping the message consistent, and
mirror the same exact-one logic already used for the uid mapping to ensure the
username validation enforces precisely one of claim or expression.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@config/v1/types_authentication.go`:
- Around line 609-637: The UsernameClaimMapping validation has inverted CEL
rules and malformed kubebuilder markers: update the FeatureGateAwareXValidation
CEL expressions on the UsernameClaimMapping type so the rules require presence
(change rule="!has(self.claim)" to rule="has(self.claim)" for the claim-required
case and change rule="!has(self.claim) && !has(self.expression)" to
rule="has(self.claim) || has(self.expression)" for the claim-or-expression
case), and fix the kubebuilder markers on the Claim and Expression fields by
replacing MinLength:=1 with MinLength=1 (keep MaxLength values unchanged) so
Claim and Expression enforce non-empty lengths as intended.
- Around line 352-374: The feature-gate CEL annotations on TokenClaimMapping are
inverted and Claim lacks omitempty; update the annotations and JSON tag: in the
struct TokenClaimMapping change the first FeatureGateAwareXValidation rule to
require the claim when that gate is active (use rule="has(self.claim)" with the
same message), change the ExternalOIDCWithUpstreamParity rule to require at
least one of claim or expression (use rule="has(self.claim) ||
has(self.expression)", message="claim or expression must be specified"), and add
`omitempty` to the Claim json tag (change `Claim string `json:"claim"` to `Claim
string `json:"claim,omitempty"` ) so Go clients do not serialize empty claims.
In
`@payload-manifests/crds/0000_10_config-operator_01_authentications-DevPreviewNoUpgrade.crd.yaml`:
- Around line 269-270: Fix the grammatical error in the CRD description text by
changing "a optional field" to "an optional field" in the description string
found in the CRD manifest (the authentications-DevPreviewNoUpgrade CRD content
where the sentence reads 'claim is a optional field that configures the JWT
token claim...'); update that description line in the YAML so it reads "claim is
an optional field that configures the JWT token claim whose value is assigned to
the cluster identity field associated with this mapping."
---
Outside diff comments:
In
`@payload-manifests/crds/0000_10_config-operator_01_authentications-CustomNoUpgrade.crd.yaml`:
- Around line 267-337: The validation currently only forbids both claim and
expression being set (x-kubernetes-validations rule '!(has(self.claim) &&
has(self.expression))'), which still allows neither to be set; change the
validation to require exactly one be present by replacing the rule with
'has(self.claim) != has(self.expression)' (or an equivalent XOR) and update the
message to reflect "exactly one of claim or expression must be set"; target the
x-kubernetes-validations entry that references claim and expression to make this
change.
In
`@payload-manifests/crds/0000_10_config-operator_01_authentications-TechPreviewNoUpgrade.crd.yaml`:
- Around line 267-337: The username mapping x-kubernetes-validation currently
only forbids both claim and expression being set (rule '!(has(self.claim) &&
has(self.expression))') but allows neither; change the validation to require
exactly one of them (mirror the uid rule) by replacing the rule with logic like
"has(self.claim) ? !has(self.expression) : has(self.expression)" so the username
mapping (fields claim and expression) enforces precisely one is present when
ExternalOIDCWithUpstreamParity applies.
---
Duplicate comments:
In
`@payload-manifests/crds/0000_10_config-operator_01_authentications-CustomNoUpgrade.crd.yaml`:
- Around line 189-218: The schema currently forces claim via required: [claim],
which prevents expression-only mappings; remove the unconditional required: -
claim and instead enforce presence rules via x-kubernetes-validations: keep the
mutual exclusion validation (rule: '!(has(self.claim) && has(self.expression))')
and add a new validation requiring at least one be present (e.g. message:
"either claim or expression must be set", rule: 'has(self.claim) ||
has(self.expression)'). Update the object definition around
claim/expression/prefix to drop the required entry and rely on those two
validations to allow expression-only configs.
In
`@payload-manifests/crds/0000_10_config-operator_01_authentications-DevPreviewNoUpgrade.crd.yaml`:
- Around line 213-218: Remove the mandatory "claim" requirement from the groups
PrefixedClaimMapping and change its validation to require exactly one of claim
or expression; specifically, delete the required: - claim entry and replace the
x-kubernetes-validations mutual-exclusion rule ('!(has(self.claim) &&
has(self.expression))') with a "precisely one" rule such as
'count([has(self.claim), has(self.expression)]) == 1' (apply this change to the
PrefixedClaimMapping for groups where fields "claim" and "expression" are
defined).
- Around line 335-337: The x-kubernetes-validations rule for username currently
only forbids both fields via '!(has(self.claim) && has(self.expression))' so it
permits neither; update the validation to enforce exactly-one semantics by
replacing the rule with an expression that requires XOR between self.claim and
self.expression (i.e., has(self.claim) != has(self.expression)) or an equivalent
"one and only one" boolean expression, keeping the message consistent, and
mirror the same exact-one logic already used for the uid mapping to ensure the
username validation enforces precisely one of claim or expression.
In
`@payload-manifests/crds/0000_10_config-operator_01_authentications-TechPreviewNoUpgrade.crd.yaml`:
- Around line 189-218: The CRD currently forces claim to be mandatory by listing
it under required, which prevents expression-only mappings; remove the required:
- claim entry (i.e., delete the required array or at least remove "claim" from
it) so that the existing x-kubernetes-validations rule ('!(has(self.claim) &&
has(self.expression))') can still prevent both being set while allowing
expression-only configs; update the schema section containing the claim,
expression, prefix, required, and x-kubernetes-validations entries accordingly.

i️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 6cc13c4 and 1f17fea.

⛔ Files ignored due to path filters (8)
  • config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-CustomNoUpgrade.crd.yaml is excluded by !**/zz_generated.crd-manifests/*
  • config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-Default.crd.yaml is excluded by !**/zz_generated.crd-manifests/*
  • config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-DevPreviewNoUpgrade.crd.yaml is excluded by !**/zz_generated.crd-manifests/*
  • config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-OKD.crd.yaml is excluded by !**/zz_generated.crd-manifests/*
  • config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-TechPreviewNoUpgrade.crd.yaml is excluded by !**/zz_generated.crd-manifests/*
  • config/v1/zz_generated.featuregated-crd-manifests/authentications.config.openshift.io/ExternalOIDC.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • config/v1/zz_generated.featuregated-crd-manifests/authentications.config.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • config/v1/zz_generated.featuregated-crd-manifests/authentications.config.openshift.io/ExternalOIDCWithUpstreamParity.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
📒 Files selected for processing (7)
  • config/v1/tests/authentications.config.openshift.io/ExternalOIDCWithUpstreamParity.yaml
  • config/v1/types_authentication.go
  • payload-manifests/crds/0000_10_config-operator_01_authentications-CustomNoUpgrade.crd.yaml
  • payload-manifests/crds/0000_10_config-operator_01_authentications-Default.crd.yaml
  • payload-manifests/crds/0000_10_config-operator_01_authentications-DevPreviewNoUpgrade.crd.yaml
  • payload-manifests/crds/0000_10_config-operator_01_authentications-OKD.crd.yaml
  • payload-manifests/crds/0000_10_config-operator_01_authentications-TechPreviewNoUpgrade.crd.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • config/v1/tests/authentications.config.openshift.io/ExternalOIDCWithUpstreamParity.yaml

// +kubebuilder:validation:XValidation:rule="has(self.prefixPolicy) && self.prefixPolicy == 'Prefix' ? (has(self.prefix) && size(self.prefix.prefixString) > 0) : !has(self.prefix)",message="prefix must be set if prefixPolicy is 'Prefix', but must remain unset otherwise"
// +union
// +openshift:validation:FeatureGateAwareXValidation:featureGate="";ExternalOIDC;ExternalOIDCWithUIDAndExtraClaimMappings,rule="!has(self.claim)",message="claim is required";
// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDCWithUpstreamParity,rule="!has(self.claim) && !has(self.expression)",message="claim or expression must be specified"
Copy link
Contributor

@everettraven everettraven Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the expression has(self.claim) ? !has(self.expression) : has(self.expression) is more so what we are looking for because it ensures that precisely one of claim or expression is set.

ShazaAldawamneh reacted with thumbs up emoji

// TokenClaimMapping allows specifying a JWT token claim to be used when mapping claims from an authentication token to cluster identities.
// +openshift:validation:FeatureGateAwareXValidation:featureGate="";ExternalOIDC;ExternalOIDCWithUIDAndExtraClaimMappings,rule="!has(self.claim)",message="claim is required";
// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDCWithUpstreamParity,rule="!has(self.claim) && !has(self.expression)",message="claim or expression must be specified"
Copy link
Contributor

@everettraven everettraven Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this, I think we will want an expression like size(self.?claim.orValue("")) > 0 ? !has(self.expression) : has(self.expression) so that something like:

claimMappings:
 groups:
 claim: ""
 expression: "claims.groups"

Would be considered valid, but something like:

claimMappings:
 groups:
 claim: groups
 expression: "claims.groups"

Would not.

Copy link
Contributor Author

@ShazaAldawamneh ShazaAldawamneh Feb 25, 2026
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the feedback
I'm curious why are we having 2 different validations for Claim in TokenClaimMapping

+openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDCWithUpstreamParity,rule="size(self.?claim.orValue("")) > 0 ? !has(self.expression) : has(self.expression)",message="claim or expression must be specified"

and for Claim in UsernameClaimMapping

+openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDCWithUpstreamParity,rule="has(self.claim) ? !has(self.expression) : has(self.expression)",message="claim or expression must be specified"

why are we not making them both have the same validation for this part ?

Signed-off-by: Shaza Aldawamneh <shaza.aldawamneh@hotmail.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
config/v1/types_authentication.go (2)

610-612: ⚠️ Potential issue | 🔴 Critical

Same inverted !has(self.claim) rules on UsernameClaimMapping — these block all valid configurations.

Identical root cause as TokenClaimMapping lines 353–355: every valid object that supplies claim will fail validation; every object missing claim will pass. This makes the claim field effectively unusable for non-parity feature sets.

🐛 Proposed fix
-// +openshift:validation:FeatureGateAwareXValidation:featureGate="",rule="!has(self.claim)",message="claim is required"
-// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDC,rule="!has(self.claim)",message="claim is required"
-// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDCWithUIDAndExtraClaimMappings,rule="!has(self.claim)",message="claim is required"
+// +openshift:validation:FeatureGateAwareXValidation:featureGate="",rule="has(self.claim)",message="claim is required"
+// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDC,rule="has(self.claim)",message="claim is required"
+// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDCWithUIDAndExtraClaimMappings,rule="has(self.claim)",message="claim is required"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@config/v1/types_authentication.go` around lines 610 - 612, The validation
tags on UsernameClaimMapping are inverted (they use !has(self.claim)) and
therefore accept objects missing claim and reject valid ones; update the three
FeatureGateAwareXValidation rules for UsernameClaimMapping to use
has(self.claim) (not !has(self.claim)) so the presence of claim is required
under the same feature gates as TokenClaimMapping (mirror the behavior of the
rules at TokenClaimMapping lines ~353–355) and keep the same messages and
featureGate values.

623-623: ⚠️ Potential issue | 🟠 Major

Fix malformed kubebuilder marker on line 623 — change MaxLength:=256 to MaxLength=256.

The := syntax is not recognized by controller-gen and the constraint will be silently dropped. Anonymous kubebuilder markers use = as the delimiter (e.g., +kubebuilder:validation:MaxLength=256). Without this fix, the Claim field in UsernameClaimMapping will lack the intended maximum length constraint in the generated CRD, despite the comment correctly stating "must not exceed 256 characters."

Proposed fix
-	// +kubebuilder:validation:MaxLength:=256
+	// +kubebuilder:validation:MaxLength=256
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@config/v1/types_authentication.go` at line 623, The kubebuilder marker on the
UsernameClaimMapping Claim field is malformed—replace the incorrect delimiter
`MaxLength:=256` with `MaxLength=256` so controller-gen recognizes the
constraint; locate the marker near the UsernameClaimMapping type (the comment
referencing "must not exceed 256 characters" / Claim field) in
types_authentication.go and update the annotation to
`+kubebuilder:validation:MaxLength=256`.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@config/v1/types_authentication.go`:
- Line 356: The CEL rule in the
+openshift:validation:FeatureGateAwareXValidation annotation for
TokenClaimMapping contains unescaped double quotes in orValue(""), which breaks
the quoted rule string; update the rule value used by the
FeatureGateAwareXValidation annotation (the annotation name
+openshift:validation:FeatureGateAwareXValidation and the rule referencing
ExternalOIDCWithUpstreamParity) to escape the inner quotes (use orValue(\"\"))
so the outer string remains intact and the validation is emitted.
- Line 613: The CEL validation uses has(self.claim) which is always true because
UsernameClaimMapping.Claim is not omitempty; update the
FeatureGateAwareXValidation rule on UsernameClaimMapping to mirror
TokenClaimMapping by checking non-empty value instead of presence — replace
has(self.claim) with size(self.?claim.orValue("")) > 0 (and keep the existing
expression check logic: size(self.?claim.orValue("")) > 0 ?
!has(self.expression) : has(self.expression)); target the rule string associated
with UsernameClaimMapping in types_authentication.go.
---
Duplicate comments:
In `@config/v1/types_authentication.go`:
- Around line 610-612: The validation tags on UsernameClaimMapping are inverted
(they use !has(self.claim)) and therefore accept objects missing claim and
reject valid ones; update the three FeatureGateAwareXValidation rules for
UsernameClaimMapping to use has(self.claim) (not !has(self.claim)) so the
presence of claim is required under the same feature gates as TokenClaimMapping
(mirror the behavior of the rules at TokenClaimMapping lines ~353–355) and keep
the same messages and featureGate values.
- Line 623: The kubebuilder marker on the UsernameClaimMapping Claim field is
malformed—replace the incorrect delimiter `MaxLength:=256` with `MaxLength=256`
so controller-gen recognizes the constraint; locate the marker near the
UsernameClaimMapping type (the comment referencing "must not exceed 256
characters" / Claim field) in types_authentication.go and update the annotation
to `+kubebuilder:validation:MaxLength=256`.

i️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 1f17fea and 3994322.

📒 Files selected for processing (1)
  • config/v1/types_authentication.go

// +openshift:validation:FeatureGateAwareXValidation:featureGate="",rule="!has(self.claim)",message="claim is required"
// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDC,rule="!has(self.claim)",message="claim is required"
// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDCWithUIDAndExtraClaimMappings,rule="!has(self.claim)",message="claim is required"
// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDCWithUpstreamParity,rule="has(self.claim) ? !has(self.expression) : has(self.expression)",message="claim or expression must be specified"
Copy link

@coderabbitai coderabbitai bot Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

has(self.claim) on a non-omitempty field is always true, making expression permanently inaccessible in UsernameClaimMapping.

Since Claim (line 624) lacks omitempty, Go will always serialise it as "claim": "" in JSON. CEL's has() tests field presence, not non-emptiness — so has(self.claim) is always true, the true-branch !has(self.expression) always fires, and any configuration supplying only expression will always be rejected.

TokenClaimMapping's parity rule already handles this correctly with size(self.?claim.orValue("")) > 0. Apply the same pattern here:

🐛 Proposed fix
-// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDCWithUpstreamParity,rule="has(self.claim) ? !has(self.expression) : has(self.expression)",message="claim or expression must be specified"
+// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDCWithUpstreamParity,rule="size(self.?claim.orValue(\"\")) > 0 ? !has(self.expression) : has(self.expression)",message="claim or expression must be specified"
📝 Committable suggestion

!!️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDCWithUpstreamParity,rule="has(self.claim) ? !has(self.expression) : has(self.expression)",message="claim or expression must be specified"
// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDCWithUpstreamParity,rule="size(self.?claim.orValue(\"\")) > 0 ? !has(self.expression) : has(self.expression)",message="claim or expression must be specified"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@config/v1/types_authentication.go` at line 613, The CEL validation uses
has(self.claim) which is always true because UsernameClaimMapping.Claim is not
omitempty; update the FeatureGateAwareXValidation rule on UsernameClaimMapping
to mirror TokenClaimMapping by checking non-empty value instead of presence —
replace has(self.claim) with size(self.?claim.orValue("")) > 0 (and keep the
existing expression check logic: size(self.?claim.orValue("")) > 0 ?
!has(self.expression) : has(self.expression)); target the rule string associated
with UsernameClaimMapping in types_authentication.go.

Copy link
Contributor

openshift-ci bot commented Feb 25, 2026

@ShazaAldawamneh: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/verify 3994322 link true /test verify
ci/prow/verify-crdify 3994322 link true /test verify-crdify
ci/prow/integration 3994322 link true /test integration

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@everettraven everettraven everettraven left review comments

@coderabbitai coderabbitai[bot] coderabbitai[bot] left review comments

@JoelSpeed JoelSpeed Awaiting requested review from JoelSpeed

+1 more reviewer

@ehearne-redhat ehearne-redhat ehearne-redhat left review comments

Reviewers whose approvals may not affect merge requirements

Assignees

No one assigned

Labels

jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

AltStyle によって変換されたページ (->オリジナル) /