Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bundle and refactor rule management service #6265

Merged
merged 5 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<parent>
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>rule-mgt</artifactId>
<version>7.7.48-SNAPSHOT</version>
<version>7.7.80-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down Expand Up @@ -99,14 +99,12 @@
org.wso2.carbon.identity.rule.management.internal,
org.wso2.carbon.identity.rule.management.constant,
org.wso2.carbon.identity.rule.management.dao.*,
org.wso2.carbon.identity.rule.management.model.internal,
org.wso2.carbon.identity.rule.management.service.impl,
</Private-Package>
<Export-Package>
!org.wso2.carbon.identity.rule.management.internal,
!org.wso2.carbon.identity.rule.management.constant,
!org.wso2.carbon.identity.rule.management.dao.*,
!org.wso2.carbon.identity.rule.management.model.internal,
!org.wso2.carbon.identity.rule.management.service.impl,
org.wso2.carbon.identity.rule.management.*;
version="${carbon.identity.package.export.version}",
Expand All @@ -131,6 +129,8 @@
version="${org.wso2.carbon.database.utils.version.range}",
org.wso2.carbon.identity.core.cache;
version="${carbon.identity.package.import.version.range}",
org.wso2.carbon.identity.core.util;
ashanthamara marked this conversation as resolved.
Show resolved Hide resolved
version="${carbon.identity.package.import.version.range}",
org.wso2.carbon.identity.rule.metadata.*;
version="${carbon.identity.package.import.version.range}",
</Import-Package>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* under the License.
*/

package org.wso2.carbon.identity.rule.management.model.internal;
package org.wso2.carbon.identity.rule.management.dao.impl;

/**
* Represents a rule in the data layer.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,11 @@
import org.wso2.carbon.identity.rule.management.dao.RuleManagementDAO;
import org.wso2.carbon.identity.rule.management.exception.RuleManagementException;
import org.wso2.carbon.identity.rule.management.exception.RuleManagementServerException;
import org.wso2.carbon.identity.rule.management.model.ANDCombinedRule;
import org.wso2.carbon.identity.rule.management.model.Expression;
import org.wso2.carbon.identity.rule.management.model.ORCombinedRule;
import org.wso2.carbon.identity.rule.management.model.Rule;
import org.wso2.carbon.identity.rule.management.model.Value;
import org.wso2.carbon.identity.rule.management.model.internal.ANDCombinedRule;
import org.wso2.carbon.identity.rule.management.model.internal.ORCombinedRule;
import org.wso2.carbon.identity.rule.management.model.internal.RuleData;

import java.io.ByteArrayInputStream;
import java.io.IOException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,10 @@
* under the License.
*/

package org.wso2.carbon.identity.rule.management.model.internal;
package org.wso2.carbon.identity.rule.management.model;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import org.wso2.carbon.identity.rule.management.model.Condition;
import org.wso2.carbon.identity.rule.management.model.Expression;
import org.wso2.carbon.identity.rule.management.model.Rule;

import java.util.ArrayList;
import java.util.List;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

package org.wso2.carbon.identity.rule.management.model;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;

Expand All @@ -36,7 +38,7 @@ private Expression(Builder builder) {

this.field = builder.field;
this.operator = builder.operator;
this.value = builder.value;
this.value = resolveValue(builder);
}

public String getField() {
Expand All @@ -54,6 +56,15 @@ public Value getValue() {
return value;
}

private Value resolveValue(Builder builder) {

if (builder.rawValue != null) {
return new Value(Value.Type.RAW, builder.rawValue);
} else {
return builder.value;
}
}

/**
* Builder for the Expression.
*/
Expand All @@ -63,6 +74,7 @@ public static class Builder {
private String field;
private String operator;
private Value value;
private String rawValue;

public Builder field(String field) {

Expand All @@ -76,14 +88,39 @@ public Builder operator(String operator) {
return this;
}

@JsonProperty("value")
public Builder value(Value value) {

this.value = value;
return this;
}

/**
* '@JsonIgnore' annotation is used to ignore the invocation of this method when deserializing the object,
* from JSON from the database.
* {@link #value(Value)} method is expected to be invoked at deserialization.
*/
@JsonIgnore
public Builder value(String value) {

this.rawValue = value;
return this;
}

public Expression build() {

if (field == null || field.isEmpty()) {
throw new IllegalArgumentException("Field must be provided.");
}

if (operator == null || operator.isEmpty()) {
throw new IllegalArgumentException("Operator must be provided.");
}

if (value == null && rawValue == null) {
throw new IllegalArgumentException("Either primitive value or Value with type must be provided.");
}

return new Expression(this);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@
* under the License.
*/

package org.wso2.carbon.identity.rule.management.model.internal;
package org.wso2.carbon.identity.rule.management.model;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import org.wso2.carbon.identity.rule.management.model.Condition;
import org.wso2.carbon.identity.rule.management.model.Rule;

import java.util.ArrayList;
import java.util.List;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ public String getId() {
return id;
}

public void setId(String id) {

this.id = id;
}

/**
* @JsonIgnore annotation is used to ignore the active field when serializing and deserializing the object,
* to and from JSON in order to store in the database.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public Type getType() {
return type;
}

@JsonProperty("value")
public String getFieldValue() {
return fieldValue;
}
Expand All @@ -49,6 +50,6 @@ public String getFieldValue() {
* Represents the type of the value.
*/
public enum Type {
STRING, NUMBER, BOOLEAN, DATE_TIME, REFERENCE
STRING, NUMBER, BOOLEAN, DATE_TIME, REFERENCE, RAW
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@
import org.wso2.carbon.identity.rule.management.exception.RuleManagementException;
import org.wso2.carbon.identity.rule.management.exception.RuleManagementServerException;
import org.wso2.carbon.identity.rule.management.internal.RuleManagementComponentServiceHolder;
import org.wso2.carbon.identity.rule.management.model.ANDCombinedRule;
import org.wso2.carbon.identity.rule.management.model.Expression;
import org.wso2.carbon.identity.rule.management.model.FlowType;
import org.wso2.carbon.identity.rule.management.model.ORCombinedRule;
import org.wso2.carbon.identity.rule.management.model.Rule;
import org.wso2.carbon.identity.rule.management.model.internal.ANDCombinedRule;
import org.wso2.carbon.identity.rule.management.model.internal.ORCombinedRule;
import org.wso2.carbon.identity.rule.management.model.Value;
import org.wso2.carbon.identity.rule.metadata.exception.RuleMetadataException;
import org.wso2.carbon.identity.rule.metadata.model.FieldDefinition;
import org.wso2.carbon.identity.rule.metadata.model.OptionsInputValue;
Expand Down Expand Up @@ -70,8 +71,8 @@ private RuleBuilder(List<FieldDefinition> expressionMetadataFields) {
*/
public RuleBuilder addAndExpression(Expression expression) {

validateExpression(expression);
addExpressionForANDCombinedRule(expression);
Expression validatedExpression = validateExpressionAndResolveValue(expression);
addExpressionForANDCombinedRule(validatedExpression);
validateMaxAllowedANDCombinedExpressions();
return this;
}
Expand Down Expand Up @@ -100,7 +101,7 @@ public Rule build() throws RuleManagementClientException {
if (isError) {
// The very first validation error will be thrown as an exception.
throw new RuleManagementClientException(
"Building rule failed due to validation errors. Error: " + errorMessage);
"Rule validation failed: " + errorMessage);
}

orCombinedRuleBuilder.addRule(andCombinedRuleBuilder.build());
Expand Down Expand Up @@ -154,37 +155,52 @@ private void addORCombinedRule() {
orRuleCount++;
}

private void validateExpression(Expression expression) {
private Expression validateExpressionAndResolveValue(Expression expression) {

FieldDefinition fieldDefinition = expressionMetadataFieldsMap.get(expression.getField());

if (isError) {
return;
return expression;
}

if (fieldDefinition == null) {
setValidationError("Field " + expression.getField() + " is not supported");
return;
if (!isValidFieldDefinition(fieldDefinition, expression.getField())) {
return expression;
}

if (fieldDefinition.getOperators().stream()
.noneMatch(operator -> operator.getName().equals(expression.getOperator()))) {
setValidationError("Operator " + expression.getOperator() + " is not supported for field " +
expression.getField());
return;
if (!isValidOperator(fieldDefinition, expression.getOperator())) {
return expression;
}

if (!fieldDefinition.getValue().getValueType().name().equals(expression.getValue().getType().name())) {
setValidationError("Value type " + expression.getValue().getType().name() + " is not supported for field "
+ expression.getField());
Value resolvedValue = validateAndResolveValue(fieldDefinition, expression.getValue());
if (isError) {
return expression;
}

if (fieldDefinition.getValue() instanceof OptionsInputValue &&
((OptionsInputValue) fieldDefinition.getValue()).getValues().stream().noneMatch(
optionsValue -> optionsValue.getName().equals(expression.getValue().getFieldValue()))) {
setValidationError("Value " + expression.getValue().getFieldValue() + " is not supported for field " +
expression.getField());
return new Expression.Builder()
.field(expression.getField())
.operator(expression.getOperator())
.value(resolvedValue)
.build();
}

private boolean isValidFieldDefinition(FieldDefinition fieldDefinition, String field) {

if (fieldDefinition == null) {
setValidationError("Field " + field + " is not supported");
return false;
}
return true;
}

private boolean isValidOperator(FieldDefinition fieldDefinition, String operator) {

if (fieldDefinition.getOperators().stream()
.noneMatch(op -> op.getName().equals(operator))) {
setValidationError(
"Operator " + operator + " is not supported for field " + fieldDefinition.getField().getName());
return false;
}
return true;
}

private void validateMaxAllowedANDCombinedExpressions() {
Expand Down Expand Up @@ -216,4 +232,91 @@ private void setValidationError(String message) {
isError = true;
errorMessage = message;
}

private Value validateAndResolveValue(FieldDefinition fieldDefinition, Value value) {

String rawValue = value.getFieldValue();

if (!isValidValueType(fieldDefinition, value)) {
return value;
}

if (!isValidOptionsInputValue(fieldDefinition, rawValue)) {
return value;
}

try {
return resolveValue(fieldDefinition, rawValue);
} catch (RuleManagementClientException e) {
setValidationError(e.getMessage());
return value;
}
}

private Value resolveValue(FieldDefinition fieldDefinition,
String rawValue) throws RuleManagementClientException {

org.wso2.carbon.identity.rule.metadata.model.Value.ValueType
fieldDefinitionValueType = fieldDefinition.getValue().getValueType();

switch (fieldDefinitionValueType) {
case STRING:
return new Value(Value.Type.STRING, rawValue);
case NUMBER:
return validateNumberValue(rawValue);
case BOOLEAN:
return validateBooleanValue(rawValue);
case REFERENCE:
return new Value(Value.Type.REFERENCE, rawValue);
default:
throw new RuleManagementClientException(
ashanthamara marked this conversation as resolved.
Show resolved Hide resolved
"Unsupported value type: " + fieldDefinitionValueType + " for field: " +
fieldDefinition.getField().getName());
}
}

private boolean isValidValueType(FieldDefinition fieldDefinition, Value value) {

org.wso2.carbon.identity.rule.metadata.model.Value.ValueType fieldDefinitionValueType =
fieldDefinition.getValue().getValueType();
Value.Type valueType = value.getType();

if (valueType != Value.Type.RAW && !valueType.name().equals(fieldDefinitionValueType.name())) {
setValidationError(
"Value type " + valueType + " is not supported for field " + fieldDefinition.getField().getName());
return false;
}
return true;
}

private boolean isValidOptionsInputValue(FieldDefinition fieldDefinition, String fieldValue) {

if (fieldDefinition.getValue() instanceof OptionsInputValue &&
((OptionsInputValue) fieldDefinition.getValue()).getValues().stream()
.noneMatch(optionsValue -> optionsValue.getName().equals(fieldValue))) {
setValidationError(
"Value " + fieldValue + " is not supported for field " + fieldDefinition.getField().getName());
return false;
}

return true;
}

private Value validateNumberValue(String rawValue) throws RuleManagementClientException {
ashanthamara marked this conversation as resolved.
Show resolved Hide resolved

try {
Double.parseDouble(rawValue);
return new Value(Value.Type.NUMBER, rawValue);
} catch (NumberFormatException e) {
throw new RuleManagementClientException("Value " + rawValue + " is not a valid NUMBER.");
malithie marked this conversation as resolved.
Show resolved Hide resolved
}
}

private Value validateBooleanValue(String rawValue) throws RuleManagementClientException {
malithie marked this conversation as resolved.
Show resolved Hide resolved

if (!rawValue.equalsIgnoreCase("true") && !rawValue.equalsIgnoreCase("false")) {
throw new RuleManagementClientException("Value " + rawValue + " is not a valid BOOLEAN.");
}
return new Value(Value.Type.BOOLEAN, rawValue);
}
}
Loading
Loading