Skip to content

Add value-less operators and value input to rule operators - #8346

Merged
ashanthamara merged 4 commits into
wso2:masterfrom
ashanthamara:feature/rule-mgt-field-comparison
Oct 14, 2026
Merged

ashanthamara merged 4 commits into
wso2:masterfrom
ashanthamara:feature/rule-mgt-field-comparison

Conversation

@ashanthamara

@ashanthamara ashanthamara commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Proposed changes in this pull request

Why

A rule can compare a field with a value (country equals Sri Lanka), but cannot ask whether a field has a value at all, for example "the user has no groups" or "the phone number was left blank". Some conditions also failed quietly: a malformed stored condition, an unknown operator or a referenced field that was never resolved evaluated to false (or threw a NullPointerException) instead of reporting an error.

This PR follows up on #8334 with:

  • four operators that check a field alone, with no value;
  • valueInput on every operator, saying what value a condition using it takes;
  • in / notIn limited to multi-valued fields;
  • validation that closes the gaps found while testing edge cases.

What changes

1. Operators that check the field alone

Operator Holds when the field is
isBlank / isNotBlank null, a whitespace-only string, or a list whose entries are all blank (or the opposite)
isEmpty / isNotEmpty null or an empty list (or the opposite); multi-valued fields only

A condition using one of these has no value:

{ "field": "user.groups", "operator": "isEmpty" }

These checks run before the "a missing value never holds" rule, so isEmpty holds on a field with no value. isEmpty / isNotEmpty on a single-valued field is an error; isBlank covers that case.

2. Operators that need a multi-valued field

in, notIn, isEmpty and isNotEmpty check the entries of a multi-valued field, so they apply to multi-valued fields only:

  • groups in [admins, staff] holds when the field and the list share an entry;
  • country in [LK, IN] on a single-valued field is an error at evaluation (use equals for one value);
  • groups in <another field> still intersects the two, and a single-valued field on the right counts as a set of one.

The field metadata has no multi-valued type, so this is checked at evaluation; a product offering these operators (such as flows) restricts them to multi-valued fields in its own metadata and validation.

3. valueInput on operators (rule.metadata)

Each operator in operators.json declares the value a condition using it takes, which is also what a client renders after the operator is picked:

valueInput Operators Input
none isBlank, isNotBlank, isEmpty, isNotEmpty no value
single (default) equals, notEquals, contains, … one value
list in, notIn a list of values
  • An operator without valueInput takes a single value, so existing configs load unchanged. An unknown value fails the config load.
  • A field definition takes each operator's valueInput from the operator configuration; a field cannot declare otherwise.
  • The operator registry rejects a configuration that disagrees with an operator's implementation, for example in configured as single.
  • The builder and the evaluator decide list handling from valueInput instead of the operator names in / notIn.
  • The operator registry defines each operator once, with its implementation, value input and whether it needs a multi-valued field.

4. Validation when a rule is built (RuleBuilder)

  • An operator with valueInput: none must have no value; any other operator needs a value of a known type.
  • A list value only with a list operator, and a list operator needs a list or another field.
  • A null value, or a list with a null or blank entry, is rejected.
  • A blank qualifier, on the expression or on a field reference, is stored as no qualifier.

5. Evaluation errors instead of a quiet false (RuleEvaluator)

A stored condition that the builder would have rejected now raises RuleEvaluationException:

  • an unknown operator or value type;
  • a value on a value-less operator, or no value on any other operator;
  • a list value with a single operator, or a single value with a list operator;
  • in, notIn, isEmpty or isNotEmpty on a single-valued field;
  • a null list entry, or a number or boolean value that does not parse;
  • a referenced field that was not provided.

A missing (null) value on either side of a comparison still makes the condition not hold.

6. Other fixes

  • A qualifier in fields.json is ignored rather than failing to load the file.
  • The FieldDefinition error message for an unknown operator no longer throws a NullPointerException.
  • The audit log describes conditions without a value, field references and lists.
  • Reference rows are written only for conditions that have a reference value.

Tests

  • RuleBuilderTest: each rejected condition, value-less conditions built without a value, blank qualifiers stored as none.
  • RuleEvaluatorTest: each value-less operator over text, number, boolean and list fields, in / notIn on single-valued fields as errors, set comparisons between fields, mismatched and malformed stored values as errors, blank qualifiers.
  • OperatorRegistryTest: the value input of each operator, the operators that need a multi-valued field, and configurations that disagree with the implementation.
  • OperatorConfigTest, FieldDefinitionConfigTest: valueInput from configuration, an unknown value input, a qualifier in fields.json.
  • AuditLogBuilderForRuleTest, RuleSerializationCompatibilityTest: conditions without a value.

When should this PR be merged

Any time. Existing operator configurations and stored rules keep working. in / notIn were added in #8334 and are not released yet, so limiting them to multi-valued fields affects no released rule.

Follow up actions

  • Flow rule evaluation (Add rule evaluation step to orchestration flows #8335): offer in, notIn, isEmpty and isNotEmpty only on multi-valued fields (multi-valued claims, groups, roles) and validate them per claim.
  • Rule metadata REST API: an optional value in conditions and valueInput in the operator metadata (identity-api-server).
  • Console rule editor: render the value input from valueInput (identity-apps).

Related Issues

Developer Checklist (Mandatory)

  • Complete the Developer Checklist in the related product-is issue to track any behavioral change or migration impact.

@coderabbitai

coderabbitai Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: wso2/carbon-identity-framework/.coderabbit.yml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 7306f512-46ab-4b3d-b058-f9ee6e582bf7
📥 Commits

Reviewing files that changed from the base of the PR and between c9beb37 and 6b2ede9.

📒 Files selected for processing: 2
  • components/rule-mgt/org.wso2.carbon.identity.rule.metadata/src/main/java/org/wso2/carbon/identity/rule/metadata/api/model/FieldDefinition.java
  • components/rule-mgt/org.wso2.carbon.identity.rule.metadata/src/test/java/org/wso2/carbon/identity/rule/metadata/config/FieldDefinitionConfigTest.java

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 0 remain after this review.

📝 Walkthrough

The change adds operator input modes and blank and empty operators. Rule management builds and validates expressions using these modes. Rule evaluation validates expression values and applies registered operators. Metadata loading deserializes value subtypes and validates configured field options.

Changes: Operator input modes and rule evaluation

Layer / File(s) Summary
Operator metadata and configuration
components/rule-mgt/.../metadata/api/model/*, components/rule-mgt/.../metadata/internal/config/*, components/rule-mgt/.../metadata/internal/deserializer/*, components/rule-mgt/.../metadata/config/*, components/rule-mgt/.../metadata/service/RuleMetadataManagerTest.java, components/rule-mgt/.../metadata/configs/*, features/.../rulemeta/operators.json
Operator definitions expose NONE, SINGLE, and LIST value-input modes. Field configuration deserializes value subtypes, loads value-field options, and validates referenced fields. Operator configurations add blank and empty operators and declare list inputs for membership operators.
Rule construction and value handling
components/rule-mgt/.../management/api/model/Expression.java, components/rule-mgt/.../management/api/util/*, components/rule-mgt/.../management/internal/dao/impl/RuleManagementDAOImpl.java, components/rule-mgt/.../management/src/test/*, components/rule-mgt/.../management/src/test/resources/configs/valid-operators.json, components/user-mgt/.../PreUpdatePasswordActionValidator.java
Rule construction validates values against operator input modes and normalizes blank qualifiers. Value-less expressions are supported. Persistence and password-action validation handle null values. Audit formatting handles null, list, and field-reference values. Tests cover these cases and invalid expression inputs.
Operator evaluation and validation
components/rule-mgt/.../evaluation/api/model/Operator.java, components/rule-mgt/.../evaluation/internal/service/impl/*, components/rule-mgt/.../evaluation/core/*, components/rule-mgt/.../evaluation/src/test/resources/configs/valid-operators.json
The registry associates predicates with input modes and multi-valued-field requirements. The evaluator validates expression shapes and typed operands, resolves field references, and evaluates blank, empty, and membership operators. Tests cover operator behavior and validation errors.

Priority: ➖ Normal

Change: Feature

Merge Risk: ⚪ Minimal · up to 6b2ed

No actionable merge-blocking issue remains from the reviewed changes; merge after normal checks.

Security architecture risk: 🔵 Low · up to 6b2ed

The change strengthens rule validation without an observed access-control bypass. The main risk is compatibility: retaining older configuration can interrupt membership-based approval rules, and newly created value-less rules are not compatible with the previous version.

Retained concerns

  • Medium · reliability · inferred: If an upgrade retains pre-PR operators.json, in/notIn default to SINGLE and remain available to metadata consumers, but the new evaluator excludes them because their implementations require LIST. Rule admission and execution can therefore disagree, and affected approval-workflow requests terminate with evaluation errors. This is a conditional rule-enforcement availability and configuration-drift concern, not a demonstrated approval bypass. The updated packaged configuration is the strongest counterevidence; actual deployment retention of older configuration is unknown.
Security review details

Security Blast Radius

  • inferred — Operator availability is process-wide through a static runtime registry. A configuration mismatch can consequently affect rules across tenants sharing that process and its configuration, including approval-workflow rules. Inspected persistence reads remain tenant-scoped; the mismatch does not itself establish cross-tenant data access or additional privileges.

Trust Boundaries and Controls

  • observed — Caller-created Operator objects cannot redefine a FieldDefinition's value-input capability: the constructor replaces their modes from central configuration and returns an unmodifiable operator list. RuleBuilder then checks the expression's field, permitted operator, qualifier, and value requirement against that metadata.

Resilience and Maintainability Implications

  • observed — The runtime registry builds a local compatible-operator map and publishes an immutable map through a volatile reference, avoiding incremental publication. Operators omitted for incompatible metadata fail explicitly when evaluated. At the inspected workflow-executor boundary, those errors are thrown rather than converted into permission to continue without a matching workflow.

Hardening Proposals

  • proposed — Gate rollout on agreement between advertised operator modes and installed evaluator capabilities, including retained or customized configuration. Assess existing scalar membership conditions before enabling the stricter evaluator, and define a rollback policy for value-less rules created after upgrade. These are transition safeguards, not evidence of a deployed vulnerability.
Pre-merge checks: Passed 3 · Failed 2

Failed Failed checks: 2 warnings

Check name Status Explanation Resolution
Description check Warning The description gives detailed purpose, goals, implementation approach, behavior, tests, and follow-up work, but it does not follow the required template and omits several required sections, including… Update the description to include every required template section. Mark non-applicable sections as “N/A” with a brief explanation, and provide the required security-check responses, test-environment details, documentation status, release no…
Docstring Coverage Warning Docstring coverage is 41.96% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 112 functions across 26 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 3 passed
Check name Status Explanation
Title check Passed The title clearly summarizes the main changes: value-less operators and operator value-input metadata.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description gives detailed purpose, goals, implementation approach, behavior, tests, and follow-up work, but it does not follow the required template and omits several required sections, including user stories, release note, documentation, training, certification, marketing, security checks, samples, related PRs, migrations, test environment, and learning.

Resolution

Update the description to include every required template section. Mark non-applicable sections as “N/A” with a brief explanation, and provide the required security-check responses, test-environment details, documentation status, release note, and user-story summary.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@codecov

codecov Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.09302% with 17 lines in your changes missing coverage. Please review.
✅ Project coverage is 54.69%. Comparing base (594c643) to head (50e5e1a).
⚠️ Report is 13 commits behind head on master.

Files with missing lines Patch % Lines
...valuation/internal/service/impl/RuleEvaluator.java 87.75% 3 Missing and 3 partials ⚠️
...n/identity/rule/evaluation/api/model/Operator.java 60.00% 2 Missing ⚠️
...uation/internal/service/impl/OperatorRegistry.java 95.83% 0 Missing and 2 partials ⚠️
...le/management/api/util/AuditLogBuilderForRule.java 80.00% 0 Missing and 2 partials ⚠️
...adata/internal/deserializer/ValueDeserializer.java 87.50% 1 Missing and 1 partial ⚠️
...ement/internal/dao/impl/RuleManagementDAOImpl.java 50.00% 0 Missing and 1 partial ⚠️
...ntity/rule/metadata/api/model/FieldDefinition.java 91.66% 0 Missing and 1 partial ⚠️
...l/management/PreUpdatePasswordActionValidator.java 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #8346      +/-   ##
============================================
- Coverage     54.72%   54.69%   -0.03%     
+ Complexity    22521    22507      -14     
============================================
  Files          2267     2268       +1     
  Lines        133008   133482     +474     
  Branches      21870    21940      +70     
============================================
+ Hits          72785    73012     +227     
- Misses        51465    51707     +242     
- Partials       8758     8763       +5     
Flag Coverage Δ
unit 39.96% <92.45%> (+0.09%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@jenkins-is-staging

Copy link
Copy Markdown

PR builder started
Link: https://github.com/wso2/product-is/actions/runs/38354054898

@jenkins-is-staging

Copy link
Copy Markdown

PR builder completed
Link: https://github.com/wso2/product-is/actions/runs/38354054898
Status: success

@jenkins-is-staging jenkins-is-staging left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Approving the pull request based on the successful pr build https://github.com/wso2/product-is/actions/runs/38354054898

@ashanthamara
ashanthamara force-pushed the feature/rule-mgt-field-comparison branch from ff13925 to 19e46ca Compare October 13, 2026 09:53
@jenkins-is-staging

Copy link
Copy Markdown

PR builder started
Link: https://github.com/wso2/product-is/actions/runs/38380034286

@jenkins-is-staging

Copy link
Copy Markdown

PR builder completed
Link: https://github.com/wso2/product-is/actions/runs/38380034286
Status: success

@jenkins-is-staging jenkins-is-staging left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Approving the pull request based on the successful pr build https://github.com/wso2/product-is/actions/runs/38380034286

Map<String, Operator> loaded = new HashMap<>();
for (org.wso2.carbon.identity.rule.metadata.api.model.Operator operator
: ruleMetadataService.getApplicableOperatorsInExpressions()) {
String name = operator.getName();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

can operator be null

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

In order for the operator to be null in here, the operator should be defined in the operators.json file like below,

{
  "equals": {
    "name": "equals",
    "displayName": "equals"
  },
  "notEquals": null
}

So we can consider this as a configuration issue of the server. However updated the logic to ignore null operators to reduce the impact for the other operators.

private boolean applyOperatorForList(Operator operator, Object fieldValue, Object expressionValue) {
private static Boolean parseBoolean(String value) throws RuleEvaluationException {

if (!"true".equalsIgnoreCase(value) && !"false".equalsIgnoreCase(value)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

shall we use constants

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed

@ashanthamara
ashanthamara force-pushed the feature/rule-mgt-field-comparison branch from 19e46ca to c9beb37 Compare October 13, 2026 11:24
@coderabbitai
coderabbitai Bot requested a review from pamodaaw October 13, 2026 11:27
@ashanthamara
ashanthamara force-pushed the feature/rule-mgt-field-comparison branch from c9beb37 to 6b2ede9 Compare October 14, 2026 11:06
@ashanthamara
ashanthamara force-pushed the feature/rule-mgt-field-comparison branch from 6b2ede9 to 50e5e1a Compare October 14, 2026 11:23
@jenkins-is-staging

Copy link
Copy Markdown

PR builder started
Link: https://github.com/wso2/product-is/actions/runs/38556541810

@sonarqubecloud

Copy link
Copy Markdown

@jenkins-is-staging

Copy link
Copy Markdown

PR builder completed
Link: https://github.com/wso2/product-is/actions/runs/38556541810
Status: success

@jenkins-is-staging jenkins-is-staging left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Approving the pull request based on the successful pr build https://github.com/wso2/product-is/actions/runs/38556541810

@ashanthamara
ashanthamara merged commit e94299a into wso2:master Oct 14, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants