diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/pom.xml b/components/rule-mgt/org.wso2.carbon.identity.rule.management/pom.xml
new file mode 100644
index 000000000000..eb0c64971890
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/pom.xml
@@ -0,0 +1,219 @@
+
+
+
+
+
+ org.wso2.carbon.identity.framework
+ rule-mgt
+ 7.7.48-SNAPSHOT
+ ../pom.xml
+
+
+ 4.0.0
+ org.wso2.carbon.identity.rule.management
+ bundle
+ WSO2 Identity - Rule Management Component
+ Rule management backend component
+ http://wso2.org
+
+
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.core
+
+
+ org.wso2.carbon.utils
+ org.wso2.carbon.database.utils
+
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.central.log.mgt
+
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.rule.metadata
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+
+ org.testng
+ testng
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ org.mockito
+ mockito-testng
+ test
+
+
+ com.h2database
+ h2
+ test
+
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.testutil
+ test
+
+
+
+
+
+
+ org.apache.felix
+ maven-bundle-plugin
+ true
+
+
+
+ ${project.artifactId}
+
+ ${project.artifactId}
+
+ 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.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}",
+
+
+ org.apache.commons.lang; version="${commons-lang.wso2.osgi.version.range}",
+ org.apache.commons.logging; version="${import.package.version.commons.logging}",
+ org.apache.commons.collections; version="${commons-collections.wso2.osgi.version.range}",
+ org.osgi.framework; version="${osgi.framework.imp.pkg.version.range}",
+ org.osgi.service.component; version="${osgi.service.component.imp.pkg.version.range}",
+ org.wso2.carbon.utils; version="${carbon.kernel.package.import.version.range}",
+ org.wso2.carbon.identity.central.log.mgt.utils;
+ version="${carbon.identity.package.import.version.range}",
+ com.fasterxml.jackson.core.*; version="${com.fasterxml.jackson.annotation.version.range}",
+ com.fasterxml.jackson.databind.*;
+ version="${com.fasterxml.jackson.annotation.version.range}",
+ com.fasterxml.jackson.annotation.*;
+ version="${com.fasterxml.jackson.annotation.version.range}",
+ org.wso2.carbon.database.utils.jdbc;
+ version="${org.wso2.carbon.database.utils.version.range}",
+ org.wso2.carbon.database.utils.jdbc.exceptions;
+ 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.rule.metadata.*;
+ version="${carbon.identity.package.import.version.range}",
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven.surefire.plugin.version}
+
+
+ src/test/resources/testng.xml
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ ${jacoco.version}
+
+
+ default-prepare-agent
+
+ prepare-agent
+
+
+
+ default-prepare-agent-integration
+
+ prepare-agent-integration
+
+
+
+ default-report
+
+ report
+
+
+
+ default-report-integration
+
+ report-integration
+
+
+
+ default-check
+
+ check
+
+
+
+
+ BUNDLE
+
+
+ LINE
+ COVEREDRATIO
+ 0.80
+
+
+ COMPLEXITY
+ COVEREDRATIO
+ 0.60
+
+
+
+
+
+
+
+
+
+ com.github.spotbugs
+ spotbugs-maven-plugin
+
+ ../../../spotbugs-exclude.xml
+ Max
+ Low
+ true
+ true
+
+
+
+
+
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/cache/RuleCache.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/cache/RuleCache.java
new file mode 100644
index 000000000000..09c68b822f21
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/cache/RuleCache.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.cache;
+
+import org.wso2.carbon.identity.core.cache.BaseCache;
+import org.wso2.carbon.utils.CarbonUtils;
+
+/**
+ * Cache for Rule Management.
+ */
+public class RuleCache extends BaseCache {
+
+ private static final String CACHE_NAME = "RuleCache";
+ private static final RuleCache INSTANCE = new RuleCache();
+
+ public RuleCache() {
+
+ super(CACHE_NAME);
+ }
+
+ public static RuleCache getInstance() {
+
+ CarbonUtils.checkSecurity();
+ return INSTANCE;
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/cache/RuleCacheEntry.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/cache/RuleCacheEntry.java
new file mode 100644
index 000000000000..8080bf5c2934
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/cache/RuleCacheEntry.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.cache;
+
+import org.wso2.carbon.identity.core.cache.CacheEntry;
+import org.wso2.carbon.identity.rule.management.model.Rule;
+
+/**
+ * Cache entry for Rule Management.
+ * This class is used to store Rule object in cache.
+ */
+public class RuleCacheEntry extends CacheEntry {
+
+ private static final long serialVersionUID = -8205516043545934797L;
+
+ private Rule rule;
+
+ public RuleCacheEntry(Rule rule) {
+
+ this.rule = rule;
+ }
+
+ public Rule getRule() {
+
+ return rule;
+ }
+
+ public void setRule(Rule rule) {
+
+ this.rule = rule;
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/cache/RuleCacheKey.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/cache/RuleCacheKey.java
new file mode 100644
index 000000000000..2ace44abc42c
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/cache/RuleCacheKey.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.cache;
+
+import org.wso2.carbon.identity.core.cache.CacheKey;
+
+/**
+ * Cache key for Rule Management.
+ * This class is used to store Rule ID in cache.
+ */
+public class RuleCacheKey extends CacheKey {
+
+ private static final long serialVersionUID = -6662958252110402724L;
+
+ private final String ruleId;
+
+ public RuleCacheKey(String ruleId) {
+
+ this.ruleId = ruleId;
+ }
+
+ public String getRuleId() {
+
+ return ruleId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+
+ if (!(o instanceof RuleCacheKey)) {
+ return false;
+ }
+ return ruleId.equals(((RuleCacheKey) o).getRuleId());
+ }
+
+ @Override
+ public int hashCode() {
+
+ return ruleId.hashCode();
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/constant/RuleSQLConstants.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/constant/RuleSQLConstants.java
new file mode 100644
index 000000000000..7dca06852288
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/constant/RuleSQLConstants.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.constant;
+
+/**
+ * SQL Constants for Rule Management.
+ * This class is used to store SQL queries and column names.
+ */
+public class RuleSQLConstants {
+
+ private RuleSQLConstants() {
+
+ }
+
+ /**
+ * This class is used to store column names.
+ */
+ public static class Column {
+
+ public static final String RULE_INTERNAL_ID = "ID";
+ public static final String RULE_EXTERNAL_ID = "UUID";
+ public static final String RULE_CONTENT = "CONTENT";
+ public static final String IS_ACTIVE = "IS_ACTIVE";
+ public static final String TENANT_ID = "TENANT_ID";
+ public static final String VERSION = "VERSION";
+ public static final String RULE_REFERENCE_ID = "RULE_ID";
+ public static final String FIELD_NAME = "FIELD_NAME";
+ public static final String FIELD_REFERENCE = "FIELD_REFERENCE";
+
+ private Column() {
+
+ }
+ }
+
+ /**
+ * This class is used to store SQL queries.
+ */
+ public static class Query {
+
+ public static final String ADD_RULE = "INSERT INTO IDN_RULE (UUID, CONTENT, IS_ACTIVE, TENANT_ID, VERSION) " +
+ "VALUES (:UUID;, :CONTENT;, :IS_ACTIVE;, :TENANT_ID;, :VERSION;)";
+ public static final String ADD_RULE_REFERENCES = "INSERT INTO IDN_RULE_REFERENCES (RULE_ID, " +
+ "FIELD_NAME, FIELD_REFERENCE, TENANT_ID) VALUES (:RULE_ID;, :FIELD_NAME;, :FIELD_REFERENCE;, " +
+ ":TENANT_ID;)";
+ public static final String UPDATE_RULE =
+ "UPDATE IDN_RULE SET CONTENT = :CONTENT; WHERE UUID = :UUID; AND TENANT_ID = :TENANT_ID;";
+ public static final String DELETE_RULE_REFERENCES =
+ "DELETE FROM IDN_RULE_REFERENCES WHERE RULE_ID = :RULE_ID; AND TENANT_ID = :TENANT_ID;";
+ public static final String DELETE_RULE = "DELETE FROM IDN_RULE WHERE UUID = :UUID; AND TENANT_ID = :TENANT_ID;";
+ public static final String CHANGE_RULE_STATUS = "UPDATE IDN_RULE SET IS_ACTIVE = :IS_ACTIVE; WHERE UUID = " +
+ ":UUID; AND TENANT_ID = :TENANT_ID;";
+ public static final String GET_RULE_BY_ID =
+ "SELECT CONTENT, IS_ACTIVE FROM IDN_RULE WHERE UUID = :UUID; AND TENANT_ID = :TENANT_ID;";
+ public static final String GET_RULE_INTERNAL_ID_BY_ID =
+ "SELECT ID FROM IDN_RULE WHERE UUID = :UUID; AND TENANT_ID = :TENANT_ID;";
+
+ private Query() {
+
+ }
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/dao/RuleManagementDAO.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/dao/RuleManagementDAO.java
new file mode 100644
index 000000000000..ccb873e7504e
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/dao/RuleManagementDAO.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.dao;
+
+import org.wso2.carbon.identity.rule.management.exception.RuleManagementException;
+import org.wso2.carbon.identity.rule.management.model.Rule;
+
+/**
+ * Rule Management DAO.
+ * This class is used to perform CRUD operations on Rule in the datastore.
+ */
+public interface RuleManagementDAO {
+
+ /**
+ * Add a new Rule.
+ *
+ * @param rule Rule object
+ * @param tenantId Tenant ID
+ * @throws RuleManagementException Rule Management Exception
+ */
+ public void addRule(Rule rule, int tenantId) throws RuleManagementException;
+
+ /**
+ * Update an existing Rule.
+ *
+ * @param rule Rule object
+ * @param tenantId Tenant ID
+ * @throws RuleManagementException Rule Management Exception
+ */
+ public void updateRule(Rule rule, int tenantId) throws RuleManagementException;
+
+ /**
+ * Delete a Rule.
+ *
+ * @param ruleId Rule ID
+ * @param tenantId Tenant ID
+ * @throws RuleManagementException Rule Management Exception
+ */
+ public void deleteRule(String ruleId, int tenantId) throws RuleManagementException;
+
+ /**
+ * Get a Rule by Rule ID.
+ *
+ * @param ruleId Rule ID
+ * @param tenantId Tenant ID
+ * @return Rule object
+ * @throws RuleManagementException Rule Management Exception
+ */
+ public Rule getRuleByRuleId(String ruleId, int tenantId) throws RuleManagementException;
+
+ /**
+ * Activate a Rule.
+ *
+ * @param ruleId Rule ID
+ * @param tenantId Tenant ID
+ * @throws RuleManagementException Rule Management Exception
+ */
+ public void activateRule(String ruleId, int tenantId) throws RuleManagementException;
+
+ /**
+ * Deactivate a Rule.
+ *
+ * @param ruleId Rule ID
+ * @param tenantId Tenant ID
+ * @throws RuleManagementException Rule Management Exception
+ */
+ public void deactivateRule(String ruleId, int tenantId) throws RuleManagementException;
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/dao/impl/CacheBackedRuleManagementDAO.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/dao/impl/CacheBackedRuleManagementDAO.java
new file mode 100644
index 000000000000..e926a34f2965
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/dao/impl/CacheBackedRuleManagementDAO.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.dao.impl;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.wso2.carbon.identity.rule.management.cache.RuleCache;
+import org.wso2.carbon.identity.rule.management.cache.RuleCacheEntry;
+import org.wso2.carbon.identity.rule.management.cache.RuleCacheKey;
+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.model.Rule;
+
+/**
+ * Cache backed Rule Management DAO.
+ * This class is used to implement the caching on top of the data layer operations.
+ * This caches the Rule object.
+ */
+public class CacheBackedRuleManagementDAO implements RuleManagementDAO {
+
+ private static final Log LOG = LogFactory.getLog(CacheBackedRuleManagementDAO.class);
+
+ private final RuleManagementDAO ruleManagementDAO;
+ private final RuleCache ruleCache;
+
+ public CacheBackedRuleManagementDAO(RuleManagementDAO ruleManagementDAO) {
+
+ this.ruleManagementDAO = ruleManagementDAO;
+ ruleCache = RuleCache.getInstance();
+ }
+
+ /**
+ * Add a new Rule.
+ * This method is used directly invokes the data layer operation to add the Rule.
+ *
+ * @param rule Rule object
+ * @param tenantId Tenant ID
+ * @throws RuleManagementException Rule Management Exception
+ */
+ @Override
+ public void addRule(Rule rule, int tenantId) throws RuleManagementException {
+
+ ruleManagementDAO.addRule(rule, tenantId);
+ }
+
+ /**
+ * Update an existing Rule.
+ * This method clears the cache entry upon rule update.
+ *
+ * @param rule Rule object
+ * @param tenantId Tenant ID
+ * @throws RuleManagementException Rule Management Exception
+ */
+ @Override
+ public void updateRule(Rule rule, int tenantId) throws RuleManagementException {
+
+ ruleCache.clearCacheEntry(new RuleCacheKey(rule.getId()), tenantId);
+ LOG.debug("Rule cache entry is cleared for rule id: " + rule.getId() + " for rule update.");
+ ruleManagementDAO.updateRule(rule, tenantId);
+ }
+
+ /**
+ * Delete a Rule.
+ * This method clears the cache entry upon rule deletion.
+ *
+ * @param ruleId Rule ID
+ * @param tenantId Tenant ID
+ * @throws RuleManagementException Rule Management Exception
+ */
+ @Override
+ public void deleteRule(String ruleId, int tenantId) throws RuleManagementException {
+
+ ruleCache.clearCacheEntry(new RuleCacheKey(ruleId), tenantId);
+ LOG.debug("Rule cache entry is cleared for rule id: " + ruleId + " for rule deletion.");
+ ruleManagementDAO.deleteRule(ruleId, tenantId);
+ }
+
+ /**
+ * Get a Rule by Rule ID.
+ * This method first checks the cache for the Rule object.
+ * If the Rule object is not found in the cache, it invokes the data layer operation to get the Rule.
+ *
+ * @param ruleId Rule ID
+ * @param tenantId Tenant ID
+ * @return Rule object
+ * @throws RuleManagementException Rule Management Exception
+ */
+ @Override
+ public Rule getRuleByRuleId(String ruleId, int tenantId) throws RuleManagementException {
+
+ RuleCacheEntry ruleCacheEntry = ruleCache.getValueFromCache(new RuleCacheKey(ruleId), tenantId);
+ if (ruleCacheEntry != null && ruleCacheEntry.getRule() != null) {
+ LOG.debug("Rule cache hit for rule id: " + ruleId + ". Returning from cache.");
+ return ruleCacheEntry.getRule();
+ }
+
+ Rule rule = ruleManagementDAO.getRuleByRuleId(ruleId, tenantId);
+ if (rule != null) {
+ LOG.debug("Rule cache miss for rule id: " + ruleId + ". Adding to cache.");
+ ruleCache.addToCache(new RuleCacheKey(ruleId), new RuleCacheEntry(rule), tenantId);
+ }
+ return rule;
+ }
+
+ /**
+ * Activate a Rule.
+ * This method clears the cache entry upon rule activation.
+ *
+ * @param ruleId Rule ID
+ * @param tenantId Tenant ID
+ * @throws RuleManagementException Rule Management Exception
+ */
+ @Override
+ public void activateRule(String ruleId, int tenantId) throws RuleManagementException {
+
+ ruleCache.clearCacheEntry(new RuleCacheKey(ruleId), tenantId);
+ LOG.debug("Rule cache entry is cleared for rule id: " + ruleId + " for rule activation.");
+ ruleManagementDAO.activateRule(ruleId, tenantId);
+ }
+
+ /**
+ * Deactivate a Rule.
+ * This method clears the cache entry upon rule deactivation.
+ *
+ * @param ruleId Rule ID
+ * @param tenantId Tenant ID
+ * @throws RuleManagementException Rule Management Exception
+ */
+ @Override
+ public void deactivateRule(String ruleId, int tenantId) throws RuleManagementException {
+
+ ruleCache.clearCacheEntry(new RuleCacheKey(ruleId), tenantId);
+ LOG.debug("Rule cache entry is cleared for rule id: " + ruleId + " for rule deactivation.");
+ ruleManagementDAO.deactivateRule(ruleId, tenantId);
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/dao/impl/RuleManagementDAOImpl.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/dao/impl/RuleManagementDAOImpl.java
new file mode 100644
index 000000000000..d87dab7cca1c
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/dao/impl/RuleManagementDAOImpl.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.dao.impl;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.wso2.carbon.database.utils.jdbc.NamedJdbcTemplate;
+import org.wso2.carbon.database.utils.jdbc.exceptions.TransactionException;
+import org.wso2.carbon.identity.core.util.IdentityDatabaseUtil;
+import org.wso2.carbon.identity.rule.management.constant.RuleSQLConstants;
+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.Expression;
+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;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Rule Management DAO Implementation.
+ * This class is used to perform CRUD operations on Rule in the database.
+ */
+public class RuleManagementDAOImpl implements RuleManagementDAO {
+
+ private static final String V1 = "1.0.0";
+
+ /**
+ * This method will add the Rule to the database and add the Rule Value References to the database.
+ *
+ * @param rule Rule object
+ * @param tenantId Tenant ID
+ * @throws RuleManagementException If an error occurs while adding the rule to the database.
+ */
+ @Override
+ public void addRule(Rule rule, int tenantId) throws RuleManagementException {
+
+ NamedJdbcTemplate jdbcTemplate = new NamedJdbcTemplate(IdentityDatabaseUtil.getDataSource());
+ try {
+ jdbcTemplate.withTransaction(template -> {
+ int internalId = addRuleToDB(rule, tenantId);
+ addRuleValueReferencesToDB(internalId, rule, tenantId);
+ return null;
+ });
+ } catch (TransactionException e) {
+ throw new RuleManagementServerException("Error while creating the rule in the system.", e);
+ }
+ }
+
+ /**
+ * This method will update the Rule in the database and update the Rule Value References in the database,
+ * by deleting all and adding back reference values for the updated rule.
+ *
+ * @param rule Rule object
+ * @param tenantId Tenant ID
+ * @throws RuleManagementException If an error occurs while updating the rule in the database.
+ */
+ @Override
+ public void updateRule(Rule rule, int tenantId) throws RuleManagementException {
+
+ NamedJdbcTemplate jdbcTemplate = new NamedJdbcTemplate(IdentityDatabaseUtil.getDataSource());
+ try {
+ jdbcTemplate.withTransaction(template -> {
+ updateRuleInDB(rule, tenantId);
+ int internalRuleId = getInternalRuleIdByRuleId(rule.getId(), tenantId);
+ deleteRuleReferencesInDB(internalRuleId, tenantId);
+ addRuleValueReferencesToDB(internalRuleId, rule, tenantId);
+ return null;
+ });
+ } catch (TransactionException e) {
+ throw new RuleManagementServerException("Error while updating the rule in the system.", e);
+ }
+ }
+
+ /**
+ * This method will delete the Rule from the database.
+ *
+ * @param ruleId Rule ID
+ * @param tenantId Tenant ID
+ * @throws RuleManagementException If an error occurs while deleting the rule from the database.
+ */
+ @Override
+ public void deleteRule(String ruleId, int tenantId) throws RuleManagementException {
+
+ NamedJdbcTemplate jdbcTemplate = new NamedJdbcTemplate(IdentityDatabaseUtil.getDataSource());
+ try {
+ jdbcTemplate.withTransaction(template -> {
+ template.executeUpdate(RuleSQLConstants.Query.DELETE_RULE,
+ statement -> {
+ statement.setString(RuleSQLConstants.Column.RULE_EXTERNAL_ID, ruleId);
+ statement.setInt(RuleSQLConstants.Column.TENANT_ID, tenantId);
+ });
+
+ return null;
+ });
+ } catch (TransactionException e) {
+ throw new RuleManagementServerException("Error while deleting the rule in the system.", e);
+ }
+ }
+
+ /**
+ * This method will retrieve the Rule from the database.
+ *
+ * @param ruleId Rule ID
+ * @param tenantId Tenant ID
+ * @return Rule object
+ * @throws RuleManagementException If an error occurs while retrieving the rule from the database.
+ */
+ @Override
+ public Rule getRuleByRuleId(String ruleId, int tenantId) throws RuleManagementException {
+
+ NamedJdbcTemplate jdbcTemplate = new NamedJdbcTemplate(IdentityDatabaseUtil.getDataSource());
+ RuleData ruleData = new RuleData();
+ try {
+ jdbcTemplate.withTransaction(
+ template -> template.fetchSingleRecord(RuleSQLConstants.Query.GET_RULE_BY_ID,
+ (resultSet, rowNumber) -> {
+ ruleData.setRuleJson(resultSet.getString(RuleSQLConstants.Column.RULE_CONTENT));
+ ruleData.setActive(resultSet.getBoolean(RuleSQLConstants.Column.IS_ACTIVE));
+ return null;
+ },
+ statement -> {
+ statement.setString(RuleSQLConstants.Column.RULE_EXTERNAL_ID, ruleId);
+ statement.setInt(RuleSQLConstants.Column.TENANT_ID, tenantId);
+ }));
+
+ if (ruleData.getRuleJson() == null) {
+ return null;
+ }
+
+ return new ORCombinedRule.Builder(convertJsonToRule(ruleData.getRuleJson()))
+ .setId(ruleId)
+ .setActive(ruleData.isActive())
+ .build();
+ } catch (TransactionException e) {
+ throw new RuleManagementServerException("Error while retrieving the rule from the system.", e);
+ }
+ }
+
+ /**
+ * This method will activate the Rule in the database.
+ *
+ * @param ruleId Rule ID
+ * @param tenantId Tenant ID
+ * @throws RuleManagementException If an error occurs while activating the rule in the database.
+ */
+ @Override
+ public void activateRule(String ruleId, int tenantId) throws RuleManagementException {
+
+ NamedJdbcTemplate jdbcTemplate = new NamedJdbcTemplate(IdentityDatabaseUtil.getDataSource());
+ try {
+ jdbcTemplate.withTransaction(template -> {
+ template.executeUpdate(RuleSQLConstants.Query.CHANGE_RULE_STATUS,
+ statement -> {
+ statement.setBoolean(RuleSQLConstants.Column.IS_ACTIVE, true);
+ statement.setString(RuleSQLConstants.Column.RULE_EXTERNAL_ID, ruleId);
+ statement.setInt(RuleSQLConstants.Column.TENANT_ID, tenantId);
+ });
+ return null;
+ });
+ } catch (TransactionException e) {
+ throw new RuleManagementServerException("Error while activating the rule in the system.", e);
+ }
+ }
+
+ /**
+ * This method will deactivate the Rule in the database.
+ *
+ * @param ruleId Rule ID
+ * @param tenantId Tenant ID
+ * @throws RuleManagementException If an error occurs while deactivating the rule in the database.
+ */
+ @Override
+ public void deactivateRule(String ruleId, int tenantId) throws RuleManagementException {
+
+ NamedJdbcTemplate jdbcTemplate = new NamedJdbcTemplate(IdentityDatabaseUtil.getDataSource());
+ try {
+ jdbcTemplate.withTransaction(template -> {
+ template.executeUpdate(RuleSQLConstants.Query.CHANGE_RULE_STATUS,
+ statement -> {
+ statement.setBoolean(RuleSQLConstants.Column.IS_ACTIVE, false);
+ statement.setString(RuleSQLConstants.Column.RULE_EXTERNAL_ID, ruleId);
+ statement.setInt(RuleSQLConstants.Column.TENANT_ID, tenantId);
+ });
+ return null;
+ });
+ } catch (TransactionException e) {
+ throw new RuleManagementServerException("Error while deactivating the rule in the system.", e);
+ }
+ }
+
+ private int addRuleToDB(Rule rule, int tenantId)
+ throws TransactionException, IOException, RuleManagementException {
+
+ InputStream ruleJsonAsInputStream = convertRuleToJson(rule);
+ int ruleJsonStreamLength = ruleJsonAsInputStream.available();
+ NamedJdbcTemplate jdbcTemplate = new NamedJdbcTemplate(IdentityDatabaseUtil.getDataSource());
+ int internalRuleId =
+ jdbcTemplate.withTransaction(template -> template.executeInsert(RuleSQLConstants.Query.ADD_RULE,
+ statement -> {
+ statement.setString(RuleSQLConstants.Column.RULE_EXTERNAL_ID, rule.getId());
+ statement.setBinaryStream(RuleSQLConstants.Column.RULE_CONTENT, ruleJsonAsInputStream,
+ ruleJsonStreamLength);
+ statement.setBoolean(RuleSQLConstants.Column.IS_ACTIVE, rule.isActive());
+ statement.setInt(RuleSQLConstants.Column.TENANT_ID, tenantId);
+ statement.setString(RuleSQLConstants.Column.VERSION, V1);
+ }, rule, true));
+ // Not all JDBC drivers support getting the auto generated database ID.
+ // So if the ID is not returned, get the ID by querying the database.
+ if (internalRuleId == 0) {
+ internalRuleId = getInternalRuleIdByRuleId(rule.getId(), tenantId);
+ }
+
+ return internalRuleId;
+ }
+
+ private int getInternalRuleIdByRuleId(String ruleId, int tenantId) throws RuleManagementException {
+
+ NamedJdbcTemplate jdbcTemplate = new NamedJdbcTemplate(IdentityDatabaseUtil.getDataSource());
+ try {
+ return jdbcTemplate.withTransaction(
+ template -> template.fetchSingleRecord(RuleSQLConstants.Query.GET_RULE_INTERNAL_ID_BY_ID,
+ (resultSet, rowNumber) -> resultSet.getInt(RuleSQLConstants.Column.RULE_INTERNAL_ID),
+ statement -> {
+ statement.setString(RuleSQLConstants.Column.RULE_EXTERNAL_ID, ruleId);
+ statement.setInt(RuleSQLConstants.Column.TENANT_ID, tenantId);
+ }));
+ } catch (TransactionException e) {
+ throw new RuleManagementServerException("Error while retrieving the rule from the system.", e);
+ }
+ }
+
+ private void addRuleValueReferencesToDB(int internalRuleId, Rule rule, int tenantId) throws TransactionException {
+
+ ORCombinedRule orCombinedRule = (ORCombinedRule) rule;
+ NamedJdbcTemplate jdbcTemplate = new NamedJdbcTemplate(IdentityDatabaseUtil.getDataSource());
+ jdbcTemplate.withTransaction(template -> template.executeBatchInsert(RuleSQLConstants.Query.ADD_RULE_REFERENCES,
+ statement -> {
+ for (ANDCombinedRule rule1 : orCombinedRule.getRules()) {
+ for (Expression expression : rule1.getExpressions()) {
+ if (expression.getValue().getType() == Value.Type.REFERENCE) {
+ statement.setInt(RuleSQLConstants.Column.RULE_REFERENCE_ID, internalRuleId);
+ statement.setString(RuleSQLConstants.Column.FIELD_NAME, expression.getField());
+ statement.setString(RuleSQLConstants.Column.FIELD_REFERENCE,
+ expression.getValue().getFieldValue());
+ statement.setInt(RuleSQLConstants.Column.TENANT_ID, tenantId);
+ statement.addBatch();
+ }
+ }
+ }
+ }, null));
+ }
+
+ private void updateRuleInDB(Rule rule, int tenantId)
+ throws IOException, TransactionException, RuleManagementServerException {
+
+ InputStream ruleJsonAsInputStream = convertRuleToJson(rule);
+ int ruleJsonStreamLength = ruleJsonAsInputStream.available();
+ NamedJdbcTemplate jdbcTemplate = new NamedJdbcTemplate(IdentityDatabaseUtil.getDataSource());
+ jdbcTemplate.withTransaction(template -> {
+ template.executeUpdate(RuleSQLConstants.Query.UPDATE_RULE,
+ statement -> {
+ statement.setBinaryStream(RuleSQLConstants.Column.RULE_CONTENT, ruleJsonAsInputStream,
+ ruleJsonStreamLength);
+ statement.setString(RuleSQLConstants.Column.RULE_EXTERNAL_ID, rule.getId());
+ statement.setInt(RuleSQLConstants.Column.TENANT_ID, tenantId);
+ });
+ return null;
+ });
+ }
+
+ private void deleteRuleReferencesInDB(int internalRuleId, int tenantId) throws TransactionException {
+
+ NamedJdbcTemplate jdbcTemplate = new NamedJdbcTemplate(IdentityDatabaseUtil.getDataSource());
+ jdbcTemplate.withTransaction(template -> {
+ template.executeUpdate(RuleSQLConstants.Query.DELETE_RULE_REFERENCES,
+ statement -> {
+ statement.setInt(RuleSQLConstants.Column.RULE_REFERENCE_ID, internalRuleId);
+ statement.setInt(RuleSQLConstants.Column.TENANT_ID, tenantId);
+ });
+ return null;
+ });
+ }
+
+ private InputStream convertRuleToJson(Rule rule) throws RuleManagementServerException {
+
+ try {
+ ObjectMapper objectMapper = new ObjectMapper();
+ return new ByteArrayInputStream(objectMapper.writeValueAsString(rule).getBytes(StandardCharsets.UTF_8));
+ } catch (JsonProcessingException e) {
+ throw new RuleManagementServerException("Failed to convert rule to JSON.", e);
+ }
+ }
+
+ private ORCombinedRule convertJsonToRule(String ruleJson) throws RuleManagementServerException {
+
+ try {
+ ObjectMapper objectMapper = new ObjectMapper();
+ return objectMapper.readValue(ruleJson, ORCombinedRule.class);
+ } catch (JsonProcessingException e) {
+ throw new RuleManagementServerException("Failed to convert JSON to rule.", e);
+ }
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/exception/RuleManagementClientException.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/exception/RuleManagementClientException.java
new file mode 100644
index 000000000000..2375fcef1fba
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/exception/RuleManagementClientException.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.exception;
+
+/**
+ * Rule Management Client Exception.
+ * This class is used to handle client exceptions in Rule Management.
+ */
+public class RuleManagementClientException extends RuleManagementException {
+
+ public RuleManagementClientException(String message) {
+
+ super(message);
+ }
+
+ public RuleManagementClientException(String message, Throwable cause) {
+
+ super(message, cause);
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/exception/RuleManagementException.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/exception/RuleManagementException.java
new file mode 100644
index 000000000000..f821bf7db653
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/exception/RuleManagementException.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.exception;
+
+/**
+ * Rule Management Exception.
+ * This class is used to handle exceptions in Rule Management.
+ */
+public class RuleManagementException extends Exception {
+
+ public RuleManagementException(String message) {
+
+ super(message);
+ }
+
+ public RuleManagementException(String message, Throwable cause) {
+
+ super(message, cause);
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/exception/RuleManagementServerException.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/exception/RuleManagementServerException.java
new file mode 100644
index 000000000000..fe214941bb6e
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/exception/RuleManagementServerException.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.exception;
+
+/**
+ * Rule Management Server Exception.
+ * This class is used to handle server exceptions in Rule Management.
+ */
+public class RuleManagementServerException extends RuleManagementException {
+
+ public RuleManagementServerException(String message) {
+
+ super(message);
+ }
+
+ public RuleManagementServerException(String message, Throwable cause) {
+
+ super(message, cause);
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/internal/RuleManagementComponentServiceHolder.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/internal/RuleManagementComponentServiceHolder.java
new file mode 100644
index 000000000000..f64827a3eeec
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/internal/RuleManagementComponentServiceHolder.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.internal;
+
+import org.wso2.carbon.identity.rule.metadata.service.RuleMetadataService;
+
+/**
+ * Rule Management Component Service Holder.
+ */
+public class RuleManagementComponentServiceHolder {
+
+ private static final RuleManagementComponentServiceHolder INSTANCE = new RuleManagementComponentServiceHolder();
+
+ private RuleMetadataService ruleMetadataService;
+
+ private RuleManagementComponentServiceHolder() {
+
+ }
+
+ public static RuleManagementComponentServiceHolder getInstance() {
+
+ return INSTANCE;
+ }
+
+ public RuleMetadataService getRuleMetadataService() {
+
+ return ruleMetadataService;
+ }
+
+ public void setRuleMetadataService(RuleMetadataService ruleMetadataService) {
+
+ this.ruleMetadataService = ruleMetadataService;
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/internal/RuleManagementServiceComponent.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/internal/RuleManagementServiceComponent.java
new file mode 100644
index 000000000000..5d42ce9675de
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/internal/RuleManagementServiceComponent.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.internal;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
+import org.wso2.carbon.identity.rule.management.service.RuleManagementService;
+import org.wso2.carbon.identity.rule.management.service.impl.RuleManagementServiceImpl;
+import org.wso2.carbon.identity.rule.metadata.service.RuleMetadataService;
+
+/**
+ * Rule Management Service Component.
+ */
+@Component(
+ name = "rule.management.service.component",
+ immediate = true
+)
+public class RuleManagementServiceComponent {
+
+ private static final Log LOG = LogFactory.getLog(RuleManagementServiceComponent.class);
+
+ @Activate
+ protected void activate(ComponentContext context) {
+
+ try {
+ BundleContext bundleCtx = context.getBundleContext();
+
+ bundleCtx.registerService(RuleManagementService.class.getName(),
+ RuleManagementServiceImpl.getInstance(), null);
+ LOG.debug("Rule management bundle is activated.");
+ } catch (Throwable e) {
+ LOG.error("Error while initializing rule management service component.", e);
+ }
+ }
+
+ @Deactivate
+ protected void deactivate(ComponentContext context) {
+
+ try {
+ BundleContext bundleCtx = context.getBundleContext();
+ bundleCtx.ungetService(bundleCtx.getServiceReference(RuleManagementService.class));
+ LOG.debug("Rule management bundle is deactivated.");
+ } catch (Throwable e) {
+ LOG.error("Error while deactivating rule management service component.", e);
+ }
+ }
+
+ @Reference(
+ name = "rule.metadata.service.component",
+ service = RuleMetadataService.class,
+ cardinality = ReferenceCardinality.MANDATORY,
+ policy = ReferencePolicy.DYNAMIC,
+ unbind = "unsetRuleMetadataService"
+ )
+ protected void setRuleMetadataService(RuleMetadataService ruleMetadataService) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Registering a reference for RuleMetadataService in the rule management service component.");
+ }
+
+ RuleManagementComponentServiceHolder.getInstance().setRuleMetadataService(ruleMetadataService);
+ }
+
+ protected void unsetRuleMetadataService(RuleMetadataService ruleMetadataService) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Unregistering a reference for RuleMetadataService in the rule management service component.");
+ }
+
+ RuleManagementComponentServiceHolder.getInstance().setRuleMetadataService(null);
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/Condition.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/Condition.java
new file mode 100644
index 000000000000..21b27ea43915
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/Condition.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.model;
+
+/**
+ * This class is used to define the conditions in Rule Management.
+ */
+public enum Condition {
+ AND, OR
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/Expression.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/Expression.java
new file mode 100644
index 000000000000..d68cd2372a5c
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/Expression.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.model;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+
+/**
+ * Represents an expression in Rule Management.
+ * This class has a field, an operator and a value.
+ */
+@JsonDeserialize(builder = Expression.Builder.class)
+public class Expression {
+
+ private final String field;
+ private final String operator;
+ private final Value value;
+
+ private Expression(Builder builder) {
+
+ this.field = builder.field;
+ this.operator = builder.operator;
+ this.value = builder.value;
+ }
+
+ public String getField() {
+
+ return field;
+ }
+
+ public String getOperator() {
+
+ return operator;
+ }
+
+ public Value getValue() {
+
+ return value;
+ }
+
+ /**
+ * Builder for the Expression.
+ */
+ @JsonPOJOBuilder(withPrefix = "")
+ public static class Builder {
+
+ private String field;
+ private String operator;
+ private Value value;
+
+ public Builder field(String field) {
+
+ this.field = field;
+ return this;
+ }
+
+ public Builder operator(String operator) {
+
+ this.operator = operator;
+ return this;
+ }
+
+ public Builder value(Value value) {
+
+ this.value = value;
+ return this;
+ }
+
+ public Expression build() {
+
+ return new Expression(this);
+ }
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/FlowType.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/FlowType.java
new file mode 100644
index 000000000000..5f8d469f8b8e
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/FlowType.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.model;
+
+/**
+ * This class is used to define the flow types in Rule Management.
+ */
+public enum FlowType {
+
+ PRE_ISSUE_ACCESS_TOKEN;
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/Rule.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/Rule.java
new file mode 100644
index 000000000000..b752a68834b9
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/Rule.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+/**
+ * Represents a rule in Rule Management.
+ * This class has an id, a condition and a status.
+ */
+public abstract class Rule {
+
+ protected String id;
+ protected Condition condition;
+ protected boolean isActive;
+
+ /**
+ * @JsonIgnore annotation is used to ignore the id field when serializing and deserializing the object,
+ * to and from JSON in order to store in the database.
+ */
+ @JsonIgnore
+ public String getId() {
+
+ return 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.
+ */
+ @JsonIgnore
+ public boolean isActive() {
+
+ return isActive;
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/Value.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/Value.java
new file mode 100644
index 000000000000..0549ee2bcc60
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/Value.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.model;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Represents a value in Rule Management.
+ * This class has a type and a value.
+ */
+public class Value {
+
+ private final Type type;
+ private final String fieldValue;
+
+ @JsonCreator
+ public Value(@JsonProperty("type") Type type, @JsonProperty("value") String fieldValue) {
+
+ this.type = type;
+ this.fieldValue = fieldValue;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public String getFieldValue() {
+ return fieldValue;
+ }
+
+ /**
+ * Represents the type of the value.
+ */
+ public enum Type {
+ STRING, NUMBER, BOOLEAN, DATE_TIME, REFERENCE
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/internal/ANDCombinedRule.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/internal/ANDCombinedRule.java
new file mode 100644
index 000000000000..9bdc57381cfb
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/internal/ANDCombinedRule.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.model.internal;
+
+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;
+import java.util.UUID;
+
+/**
+ * Represents an AND combined rule.
+ * This class extends the Rule class and has a list of expressions.
+ */
+@JsonDeserialize(builder = ANDCombinedRule.Builder.class)
+public class ANDCombinedRule extends Rule {
+
+ private final List expressions;
+
+ private ANDCombinedRule(Builder builder) {
+
+ this.id = builder.id;
+ this.expressions = builder.expressions;
+ this.condition = Condition.AND;
+ this.isActive = true;
+ }
+
+ public Condition getCondition() {
+
+ return condition;
+ }
+
+ public List getExpressions() {
+
+ return expressions;
+ }
+
+ /**
+ * Builder for the ANDCombinedRule.
+ */
+ @JsonPOJOBuilder(withPrefix = "set")
+ public static class Builder {
+
+ private String id;
+ private List expressions = new ArrayList<>();
+
+ public Builder addExpression(Expression expression) {
+
+ expressions.add(expression);
+ return this;
+ }
+
+ public Builder setCondition(Condition condition) {
+
+ if (condition != Condition.AND) {
+ throw new IllegalArgumentException("Condition must be AND for ANDCombinedRule");
+ }
+
+ return this;
+ }
+
+ public Builder setExpressions(List expressions) {
+
+ this.expressions = expressions;
+ return this;
+ }
+
+ public ANDCombinedRule build() {
+
+ this.id = UUID.randomUUID().toString();
+ return new ANDCombinedRule(this);
+ }
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/internal/ORCombinedRule.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/internal/ORCombinedRule.java
new file mode 100644
index 000000000000..85124b6a5606
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/internal/ORCombinedRule.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.model.internal;
+
+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;
+import java.util.UUID;
+
+/**
+ * Represents an OR combined rule.
+ * This class extends the Rule class and has a list of ANDCombinedRules.
+ */
+@JsonDeserialize(builder = ORCombinedRule.Builder.class)
+public class ORCombinedRule extends Rule {
+
+ private List rules;
+
+ /**
+ * Builder for the ORCombinedRule.
+ */
+ private ORCombinedRule(Builder builder) {
+
+ this.id = builder.id;
+ this.isActive = builder.isActive;
+ this.rules = builder.rules;
+ this.condition = Condition.OR;
+ }
+
+ public Condition getCondition() {
+
+ return condition;
+ }
+
+ public List getRules() {
+
+ return rules;
+ }
+
+ /**
+ * Builder for the ORCombinedRule.
+ */
+ @JsonPOJOBuilder(withPrefix = "set")
+ public static class Builder {
+
+ private String id;
+ private boolean isActive = true;
+ private List rules = new ArrayList<>();
+
+ public Builder() {
+
+ }
+
+ public Builder(ORCombinedRule orCombinedRule) {
+
+ this.id = orCombinedRule.id;
+ this.rules = orCombinedRule.rules;
+ }
+
+ public Builder addRule(ANDCombinedRule andCombinedRule) {
+
+ rules.add(andCombinedRule);
+ return this;
+ }
+
+ public Builder setId(String id) {
+
+ this.id = id;
+ return this;
+ }
+
+ public Builder setActive(boolean isActive) {
+
+ this.isActive = isActive;
+ return this;
+ }
+
+ public Builder setCondition(Condition condition) {
+
+ if (condition != Condition.OR) {
+ throw new IllegalArgumentException("Condition must be OR for ORCombinedRule");
+ }
+
+ return this;
+ }
+
+ public Builder setRules(List rules) {
+
+ this.rules = rules;
+ return this;
+ }
+
+ public ORCombinedRule build() {
+
+ this.id = (this.id == null) ? UUID.randomUUID().toString() : this.id;
+ return new ORCombinedRule(this);
+ }
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/internal/RuleData.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/internal/RuleData.java
new file mode 100644
index 000000000000..f3e592259c72
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/model/internal/RuleData.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.model.internal;
+
+/**
+ * Represents a rule in the data layer.
+ * This class has the rule JSON and the active status of the rule which is stored in the database.
+ */
+public class RuleData {
+
+ private String ruleJson;
+ private boolean isActive;
+
+ public String getRuleJson() {
+
+ return ruleJson;
+ }
+
+ public void setRuleJson(String ruleJson) {
+
+ this.ruleJson = ruleJson;
+ }
+
+ public boolean isActive() {
+
+ return isActive;
+ }
+
+ public void setActive(boolean active) {
+
+ isActive = active;
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/service/RuleManagementService.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/service/RuleManagementService.java
new file mode 100644
index 000000000000..57f1b9678bdf
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/service/RuleManagementService.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.service;
+
+import org.wso2.carbon.identity.rule.management.exception.RuleManagementException;
+import org.wso2.carbon.identity.rule.management.model.Rule;
+
+/**
+ * This interface is used to define the Rule Management Service.
+ * This interface has the methods to add, update, delete, get and deactivate rules.
+ */
+public interface RuleManagementService {
+
+ /**
+ * Adds a new rule.
+ *
+ * @param rule Rule to be added.
+ * @param tenantDomain Tenant domain.
+ * @return Added rule.
+ * @throws RuleManagementException If an error occurs while adding the rule.
+ */
+ public Rule addRule(Rule rule, String tenantDomain) throws RuleManagementException;
+
+ /**
+ * Updates an existing rule.
+ *
+ * @param rule Rule to be updated.
+ * @param tenantDomain Tenant domain.
+ * @return Updated rule.
+ * @throws RuleManagementException If an error occurs while updating the rule.
+ */
+ public Rule updateRule(Rule rule, String tenantDomain) throws RuleManagementException;
+
+ /**
+ * Deletes a rule.
+ *
+ * @param ruleId Rule ID.
+ * @param tenantDomain Tenant domain.
+ * @throws RuleManagementException If an error occurs while deleting the rule.
+ */
+ public void deleteRule(String ruleId, String tenantDomain) throws RuleManagementException;
+
+ /**
+ * Retrieves a rule by rule ID.
+ *
+ * @param ruleId Rule ID.
+ * @param tenantDomain Tenant domain.
+ * @return Rule.
+ * @throws RuleManagementException If an error occurs while retrieving the rule.
+ */
+ public Rule getRuleByRuleId(String ruleId, String tenantDomain) throws RuleManagementException;
+
+ /**
+ * Deactivates a rule.
+ *
+ * @param ruleId Rule ID.
+ * @param tenantDomain Tenant domain.
+ * @return Deactivated rule.
+ * @throws RuleManagementException If an error occurs while deactivating the rule.
+ */
+ public Rule deactivateRule(String ruleId, String tenantDomain) throws RuleManagementException;
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/service/impl/RuleManagementServiceImpl.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/service/impl/RuleManagementServiceImpl.java
new file mode 100644
index 000000000000..f60a40d38356
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/service/impl/RuleManagementServiceImpl.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.service.impl;
+
+import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
+import org.wso2.carbon.identity.rule.management.dao.RuleManagementDAO;
+import org.wso2.carbon.identity.rule.management.dao.impl.CacheBackedRuleManagementDAO;
+import org.wso2.carbon.identity.rule.management.dao.impl.RuleManagementDAOImpl;
+import org.wso2.carbon.identity.rule.management.exception.RuleManagementClientException;
+import org.wso2.carbon.identity.rule.management.exception.RuleManagementException;
+import org.wso2.carbon.identity.rule.management.model.Rule;
+import org.wso2.carbon.identity.rule.management.service.RuleManagementService;
+
+/**
+ * Implementation of Rule Management Service.
+ */
+public class RuleManagementServiceImpl implements RuleManagementService {
+
+ private static final RuleManagementServiceImpl ruleManagementService = new RuleManagementServiceImpl();
+ private final RuleManagementDAO ruleManagementDAO;
+
+ private RuleManagementServiceImpl() {
+
+ ruleManagementDAO = new CacheBackedRuleManagementDAO(new RuleManagementDAOImpl());
+ }
+
+ public static RuleManagementServiceImpl getInstance() {
+
+ return ruleManagementService;
+ }
+
+ /**
+ * Add a new rule.
+ *
+ * @param rule Rule to be added.
+ * @param tenantDomain Tenant domain.
+ * @return Added rule.
+ * @throws RuleManagementException If an error occurs while adding the rule.
+ */
+ @Override
+ public Rule addRule(Rule rule, String tenantDomain) throws RuleManagementException {
+
+ int tenantId = IdentityTenantUtil.getTenantId(tenantDomain);
+ ruleManagementDAO.addRule(rule, tenantId);
+ return ruleManagementDAO.getRuleByRuleId(rule.getId(), tenantId);
+ }
+
+ /**
+ * Update an existing rule.
+ *
+ * @param rule Rule to be updated.
+ * @param tenantDomain Tenant domain.
+ * @return Updated rule.
+ * @throws RuleManagementException If an error occurs while updating the rule.
+ */
+ @Override
+ public Rule updateRule(Rule rule, String tenantDomain) throws RuleManagementException {
+
+ validateIfRuleExists(rule.getId(), tenantDomain);
+
+ ruleManagementDAO.updateRule(rule, IdentityTenantUtil.getTenantId(tenantDomain));
+ return ruleManagementDAO.getRuleByRuleId(rule.getId(), IdentityTenantUtil.getTenantId(tenantDomain));
+ }
+
+ /**
+ * Delete a rule.
+ *
+ * @param ruleId Rule ID.
+ * @param tenantDomain Tenant domain.
+ * @throws RuleManagementException If an error occurs while deleting the rule.
+ */
+ @Override
+ public void deleteRule(String ruleId, String tenantDomain) throws RuleManagementException {
+
+ if (isRuleExists(ruleId, tenantDomain)) {
+ ruleManagementDAO.deleteRule(ruleId, IdentityTenantUtil.getTenantId(tenantDomain));
+ }
+ }
+
+ /**
+ * Retrieve a rule by rule ID.
+ *
+ * @param ruleId Rule ID.
+ * @param tenantDomain Tenant domain.
+ * @return Rule.
+ * @throws RuleManagementException If an error occurs while retrieving the rule.
+ */
+ @Override
+ public Rule getRuleByRuleId(String ruleId, String tenantDomain) throws RuleManagementException {
+
+ return ruleManagementDAO.getRuleByRuleId(ruleId, IdentityTenantUtil.getTenantId(tenantDomain));
+ }
+
+ /**
+ * Deactivate a rule.
+ *
+ * @param ruleId Rule ID.
+ * @param tenantDomain Tenant domain.
+ * @return Deactivated rule.
+ * @throws RuleManagementException If an error occurs while deactivating the rule.
+ */
+ @Override
+ public Rule deactivateRule(String ruleId, String tenantDomain) throws RuleManagementException {
+
+ validateIfRuleExists(ruleId, tenantDomain);
+
+ ruleManagementDAO.deactivateRule(ruleId, IdentityTenantUtil.getTenantId(tenantDomain));
+ return ruleManagementDAO.getRuleByRuleId(ruleId, IdentityTenantUtil.getTenantId(tenantDomain));
+ }
+
+ private void validateIfRuleExists(String ruleId, String tenantDomain) throws RuleManagementException {
+
+ if (!isRuleExists(ruleId, tenantDomain)) {
+ throw new RuleManagementClientException("Rule not found for the given rule id: " + ruleId);
+ }
+ }
+
+ private boolean isRuleExists(String ruleId, String tenantDomain) throws RuleManagementException {
+
+ Rule existingRule =
+ ruleManagementDAO.getRuleByRuleId(ruleId, IdentityTenantUtil.getTenantId(tenantDomain));
+ return existingRule != null;
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/util/RuleBuilder.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/util/RuleBuilder.java
new file mode 100644
index 000000000000..e970988d53cd
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/main/java/org/wso2/carbon/identity/rule/management/util/RuleBuilder.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.util;
+
+import org.wso2.carbon.identity.rule.management.exception.RuleManagementClientException;
+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.Expression;
+import org.wso2.carbon.identity.rule.management.model.FlowType;
+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.metadata.exception.RuleMetadataException;
+import org.wso2.carbon.identity.rule.metadata.model.FieldDefinition;
+import org.wso2.carbon.identity.rule.metadata.model.OptionsInputValue;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * RuleBuilder class is used to build a rule.
+ * The RuleBuilder instance can be created using the create method for a given flow type and tenant domain.
+ * This class provides methods to add expressions and conditions to the rule and to build the rule.
+ * Validations are done while adding expressions and conditions to the rule.
+ */
+public class RuleBuilder {
+
+ private static final int MAX_EXPRESSIONS_COMBINED_WITH_AND = 5;
+ private static final int MAX_RULES_COMBINED_WITH_OR = 10;
+
+ private final ORCombinedRule.Builder orCombinedRuleBuilder = new ORCombinedRule.Builder();
+ private ANDCombinedRule.Builder andCombinedRuleBuilder = new ANDCombinedRule.Builder();
+ private final Map expressionMetadataFieldsMap;
+
+ private boolean isError = false;
+ private String errorMessage;
+
+ private int andRuleCount = 0;
+ private int orRuleCount = 0;
+
+ private RuleBuilder(List expressionMetadataFields) {
+
+ this.expressionMetadataFieldsMap = expressionMetadataFields.stream()
+ .collect(java.util.stream.Collectors.toMap(fieldDefinition -> fieldDefinition.getField().getName(),
+ fieldDefinition -> fieldDefinition));
+ }
+
+ /**
+ * Add an expression to the rule.
+ *
+ * @param expression Expression to be added.
+ * @return RuleBuilder
+ */
+ public RuleBuilder addAndExpression(Expression expression) {
+
+ validateExpression(expression);
+ addExpressionForANDCombinedRule(expression);
+ validateMaxAllowedANDCombinedExpressions();
+ return this;
+ }
+
+ /**
+ * Add an OR condition to the rule.
+ *
+ * @return RuleBuilder
+ */
+ public RuleBuilder addOrCondition() {
+
+ addORCombinedRule();
+ validateMaxAllowedORCombinedRules();
+ initANDCombinedRule();
+ return this;
+ }
+
+ /**
+ * Build the rule.
+ *
+ * @return Rule
+ * @throws RuleManagementClientException If an error occurs while building the rule.
+ */
+ 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);
+ }
+
+ orCombinedRuleBuilder.addRule(andCombinedRuleBuilder.build());
+ return orCombinedRuleBuilder.build();
+ }
+
+ /**
+ * Create a RuleBuilder instance.
+ *
+ * @param flowType Flow type.
+ * @param tenantDomain Tenant domain.
+ * @return RuleBuilder
+ * @throws RuleManagementException If an error occurs while creating the RuleBuilder instance.
+ */
+ public static RuleBuilder create(FlowType flowType, String tenantDomain) throws RuleManagementException {
+
+ try {
+ List fieldDefinitionList =
+ RuleManagementComponentServiceHolder.getInstance().getRuleMetadataService()
+ .getExpressionMeta(
+ org.wso2.carbon.identity.rule.metadata.model.FlowType.valueOf(flowType.name()),
+ tenantDomain);
+
+ if (fieldDefinitionList == null || fieldDefinitionList.isEmpty()) {
+ throw new RuleManagementClientException(
+ "Expression metadata from RuleMetadataService is null or empty.");
+ }
+
+ return new RuleBuilder(fieldDefinitionList);
+ } catch (RuleMetadataException e) {
+ throw new RuleManagementServerException(
+ "Error while retrieving expression metadata from RuleMetadataService.", e);
+ }
+ }
+
+ private void addExpressionForANDCombinedRule(Expression expression) {
+
+ andCombinedRuleBuilder.addExpression(expression);
+ andRuleCount++;
+ }
+
+ private void initANDCombinedRule() {
+
+ andCombinedRuleBuilder = new ANDCombinedRule.Builder();
+ andRuleCount = 0;
+ }
+
+ private void addORCombinedRule() {
+
+ orCombinedRuleBuilder.addRule(andCombinedRuleBuilder.build());
+ orRuleCount++;
+ }
+
+ private void validateExpression(Expression expression) {
+
+ FieldDefinition fieldDefinition = expressionMetadataFieldsMap.get(expression.getField());
+
+ if (isError) {
+ return;
+ }
+
+ if (fieldDefinition == null) {
+ setValidationError("Field " + expression.getField() + " is not supported");
+ return;
+ }
+
+ if (fieldDefinition.getOperators().stream()
+ .noneMatch(operator -> operator.getName().equals(expression.getOperator()))) {
+ setValidationError("Operator " + expression.getOperator() + " is not supported for field " +
+ expression.getField());
+ return;
+ }
+
+ if (!fieldDefinition.getValue().getValueType().name().equals(expression.getValue().getType().name())) {
+ setValidationError("Value type " + expression.getValue().getType().name() + " is not supported for field "
+ + expression.getField());
+ }
+
+ 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());
+ }
+ }
+
+ private void validateMaxAllowedANDCombinedExpressions() {
+
+ if (isError) {
+ return;
+ }
+
+ if (andRuleCount > MAX_EXPRESSIONS_COMBINED_WITH_AND) {
+ setValidationError("Maximum number of expressions combined with AND exceeded. Maximum allowed: " +
+ MAX_EXPRESSIONS_COMBINED_WITH_AND + " Provided: " + andRuleCount);
+ }
+ }
+
+ private void validateMaxAllowedORCombinedRules() {
+
+ if (isError) {
+ return;
+ }
+
+ if (orRuleCount > MAX_RULES_COMBINED_WITH_OR) {
+ setValidationError("Maximum number of rules combined with OR exceeded. Maximum allowed: " +
+ MAX_RULES_COMBINED_WITH_OR + " Provided: " + orRuleCount);
+ }
+ }
+
+ private void setValidationError(String message) {
+
+ isError = true;
+ errorMessage = message;
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/java/org/wso2/carbon/identity/rule/management/dao/CacheBackedRuleManagementDAOTest.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/java/org/wso2/carbon/identity/rule/management/dao/CacheBackedRuleManagementDAOTest.java
new file mode 100644
index 000000000000..fbe7f6661041
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/java/org/wso2/carbon/identity/rule/management/dao/CacheBackedRuleManagementDAOTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.dao;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.wso2.carbon.identity.common.testng.WithCarbonHome;
+import org.wso2.carbon.identity.common.testng.WithRealmService;
+import org.wso2.carbon.identity.core.internal.IdentityCoreServiceDataHolder;
+import org.wso2.carbon.identity.rule.management.cache.RuleCache;
+import org.wso2.carbon.identity.rule.management.cache.RuleCacheEntry;
+import org.wso2.carbon.identity.rule.management.cache.RuleCacheKey;
+import org.wso2.carbon.identity.rule.management.dao.impl.CacheBackedRuleManagementDAO;
+import org.wso2.carbon.identity.rule.management.exception.RuleManagementException;
+import org.wso2.carbon.identity.rule.management.model.Rule;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+@WithCarbonHome
+@WithRealmService(injectToSingletons = {IdentityCoreServiceDataHolder.class})
+public class CacheBackedRuleManagementDAOTest {
+
+ private RuleManagementDAO ruleManagementDAO;
+ private CacheBackedRuleManagementDAO cacheBackedRuleManagementDAO;
+ private RuleCache ruleCache;
+
+ public static final String RULE_ID = "ruleId";
+ public static final int TENANT_ID = 1;
+
+ @BeforeClass
+ public void setUpClass() {
+
+ ruleCache = RuleCache.getInstance();
+ }
+
+ @BeforeMethod
+ public void setUp() {
+
+ ruleManagementDAO = mock(RuleManagementDAO.class);
+ cacheBackedRuleManagementDAO = new CacheBackedRuleManagementDAO(ruleManagementDAO);
+ }
+
+ @Test
+ public void testAddRule() throws RuleManagementException {
+
+ Rule rule = mock(Rule.class);
+
+ cacheBackedRuleManagementDAO.addRule(rule, TENANT_ID);
+
+ verify(ruleManagementDAO).addRule(rule, TENANT_ID);
+ }
+
+ @Test
+ public void testUpdateRule() throws RuleManagementException {
+
+ Rule rule = mock(Rule.class);
+ when(rule.getId()).thenReturn(RULE_ID);
+
+ cacheBackedRuleManagementDAO.updateRule(rule, TENANT_ID);
+
+ verify(ruleManagementDAO).updateRule(rule, TENANT_ID);
+ assertNull(ruleCache.getValueFromCache(new RuleCacheKey(RULE_ID), TENANT_ID));
+ }
+
+ @Test
+ public void testUpdateRuleWhenCacheIsPopulated() throws RuleManagementException {
+
+ Rule rule = mock(Rule.class);
+ when(rule.getId()).thenReturn(RULE_ID);
+ RuleCacheEntry cacheEntry = new RuleCacheEntry(rule);
+ ruleCache.addToCache(new RuleCacheKey(RULE_ID), cacheEntry, TENANT_ID);
+
+ cacheBackedRuleManagementDAO.updateRule(rule, TENANT_ID);
+
+ verify(ruleManagementDAO).updateRule(rule, TENANT_ID);
+ assertNull(ruleCache.getValueFromCache(new RuleCacheKey(RULE_ID), TENANT_ID));
+ }
+
+ @Test
+ public void testDeleteRule() throws RuleManagementException {
+
+ cacheBackedRuleManagementDAO.deleteRule(RULE_ID, TENANT_ID);
+
+ verify(ruleManagementDAO).deleteRule(RULE_ID, TENANT_ID);
+ assertNull(ruleCache.getValueFromCache(new RuleCacheKey(RULE_ID), TENANT_ID));
+ }
+
+ @Test
+ public void testDeleteRuleWhenCacheIsPopulated() throws RuleManagementException {
+
+ Rule rule = mock(Rule.class);
+ when(rule.getId()).thenReturn(RULE_ID);
+ RuleCacheEntry cacheEntry = new RuleCacheEntry(rule);
+ ruleCache.addToCache(new RuleCacheKey(RULE_ID), cacheEntry, TENANT_ID);
+
+ cacheBackedRuleManagementDAO.deleteRule(RULE_ID, TENANT_ID);
+
+ verify(ruleManagementDAO).deleteRule(RULE_ID, TENANT_ID);
+ assertNull(ruleCache.getValueFromCache(new RuleCacheKey(RULE_ID), TENANT_ID));
+ }
+
+ @Test
+ public void testGetRuleByRuleIdCacheHit() throws RuleManagementException {
+
+ Rule rule = mock(Rule.class);
+ RuleCacheEntry cacheEntry = new RuleCacheEntry(rule);
+ ruleCache.addToCache(new RuleCacheKey(RULE_ID), cacheEntry, TENANT_ID);
+
+ Rule result = cacheBackedRuleManagementDAO.getRuleByRuleId(RULE_ID, TENANT_ID);
+
+ assertEquals(result, rule);
+ verify(ruleManagementDAO, never()).getRuleByRuleId(RULE_ID, TENANT_ID);
+ }
+
+ @Test
+ public void testGetRuleByRuleIdCacheMiss() throws RuleManagementException {
+
+ ruleCache.clear(TENANT_ID);
+
+ Rule rule = mock(Rule.class);
+ when(rule.getId()).thenReturn(RULE_ID);
+ when(ruleManagementDAO.getRuleByRuleId(RULE_ID, TENANT_ID)).thenReturn(rule);
+
+ Rule result = cacheBackedRuleManagementDAO.getRuleByRuleId(RULE_ID, TENANT_ID);
+
+ assertEquals(result, rule);
+ verify(ruleManagementDAO).getRuleByRuleId(RULE_ID, TENANT_ID);
+ // Verify that the rule is added to the cache
+ assertEquals(rule, ruleCache.getValueFromCache(new RuleCacheKey(RULE_ID), TENANT_ID).getRule());
+ }
+
+ @Test
+ public void testActivateRule() throws RuleManagementException {
+
+ cacheBackedRuleManagementDAO.activateRule(RULE_ID, TENANT_ID);
+
+ verify(ruleManagementDAO).activateRule(RULE_ID, TENANT_ID);
+ assertNull(ruleCache.getValueFromCache(new RuleCacheKey(RULE_ID), TENANT_ID));
+ }
+
+ @Test
+ public void testActivateRuleWhenCacheIsPopulated() throws RuleManagementException {
+
+ Rule rule = mock(Rule.class);
+ when(rule.getId()).thenReturn(RULE_ID);
+ RuleCacheEntry cacheEntry = new RuleCacheEntry(rule);
+ ruleCache.addToCache(new RuleCacheKey(RULE_ID), cacheEntry, TENANT_ID);
+
+ cacheBackedRuleManagementDAO.activateRule(RULE_ID, TENANT_ID);
+
+ verify(ruleManagementDAO).activateRule(RULE_ID, TENANT_ID);
+ assertNull(ruleCache.getValueFromCache(new RuleCacheKey(RULE_ID), TENANT_ID));
+ }
+
+ @Test
+ public void testDeactivateRule() throws RuleManagementException {
+
+ cacheBackedRuleManagementDAO.deactivateRule(RULE_ID, TENANT_ID);
+
+ verify(ruleManagementDAO).deactivateRule(RULE_ID, TENANT_ID);
+ assertNull(ruleCache.getValueFromCache(new RuleCacheKey(RULE_ID), TENANT_ID));
+ }
+
+ @Test
+ public void testDeactivateRuleWhenCacheIsPopulated() throws RuleManagementException {
+
+ Rule rule = mock(Rule.class);
+ when(rule.getId()).thenReturn(RULE_ID);
+ RuleCacheEntry cacheEntry = new RuleCacheEntry(rule);
+ ruleCache.addToCache(new RuleCacheKey(RULE_ID), cacheEntry, TENANT_ID);
+
+ cacheBackedRuleManagementDAO.deactivateRule(RULE_ID, TENANT_ID);
+
+ verify(ruleManagementDAO).deactivateRule(RULE_ID, TENANT_ID);
+ assertNull(ruleCache.getValueFromCache(new RuleCacheKey(RULE_ID), TENANT_ID));
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/java/org/wso2/carbon/identity/rule/management/dao/RuleManagementDAOImplTest.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/java/org/wso2/carbon/identity/rule/management/dao/RuleManagementDAOImplTest.java
new file mode 100644
index 000000000000..63a54980ec53
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/java/org/wso2/carbon/identity/rule/management/dao/RuleManagementDAOImplTest.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.dao;
+
+import org.testng.annotations.Test;
+import org.wso2.carbon.identity.common.testng.WithCarbonHome;
+import org.wso2.carbon.identity.common.testng.WithH2Database;
+import org.wso2.carbon.identity.rule.management.dao.impl.RuleManagementDAOImpl;
+import org.wso2.carbon.identity.rule.management.exception.RuleManagementException;
+import org.wso2.carbon.identity.rule.management.model.Expression;
+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 static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+@WithH2Database(files = {"dbscripts/h2.sql"})
+@WithCarbonHome
+public class RuleManagementDAOImplTest {
+
+ public static final int TENANT_ID = 1;
+ private Rule createdRule;
+ RuleManagementDAOImpl ruleManagementDAOImpl = new RuleManagementDAOImpl();
+
+ @Test
+ public void testAddRule() throws RuleManagementException {
+
+ Expression expression1 = new Expression.Builder().field("application").operator("equals")
+ .value(new Value(Value.Type.REFERENCE, "testapp1")).build();
+
+ Expression expression2 = new Expression.Builder().field("grantType").operator("equals")
+ .value(new Value(Value.Type.STRING, "authorization_code")).build();
+ ANDCombinedRule andCombinedRule1 =
+ new ANDCombinedRule.Builder().addExpression(expression1).addExpression(expression2).build();
+
+ Expression expression3 = new Expression.Builder().field("application").operator("equals")
+ .value(new Value(Value.Type.REFERENCE, "testapp2")).build();
+ ANDCombinedRule andCombinedRule2 =
+ new ANDCombinedRule.Builder().addExpression(expression3).build();
+
+ ORCombinedRule orCombinedRule =
+ new ORCombinedRule.Builder().addRule(andCombinedRule1).addRule(andCombinedRule2).build();
+
+ ruleManagementDAOImpl.addRule(orCombinedRule, TENANT_ID);
+
+ createdRule = ruleManagementDAOImpl.getRuleByRuleId(orCombinedRule.getId(), TENANT_ID);
+ assertNotNull(createdRule);
+ assertEquals(orCombinedRule.getId(), createdRule.getId());
+ assertTrue(createdRule.isActive());
+
+ ORCombinedRule retrievedORCombinedRule = assertOrCombinedRule(createdRule, 2);
+ ANDCombinedRule retrievedAndCombinedRule1 = assertAndCombinedRule(retrievedORCombinedRule.getRules().get(0), 2);
+ assertExpressions(retrievedAndCombinedRule1, expression1, expression2);
+ ANDCombinedRule retrievedAndCombinedRule2 = assertAndCombinedRule(retrievedORCombinedRule.getRules().get(1), 1);
+ assertExpressions(retrievedAndCombinedRule2, expression3);
+ }
+
+ @Test(dependsOnMethods = {"testAddRule"})
+ public void testAddRuleWithMultipleExpressionsUsingSameFieldReferenceAndOR() throws RuleManagementException {
+
+ Expression expression1 = new Expression.Builder().field("application").operator("equals")
+ .value(new Value(Value.Type.REFERENCE, "testapp1")).build();
+
+ Expression expression2 = new Expression.Builder().field("grantType").operator("equals")
+ .value(new Value(Value.Type.STRING, "authorization_code")).build();
+
+ ANDCombinedRule andCombinedRule1 =
+ new ANDCombinedRule.Builder().addExpression(expression1).addExpression(expression2).build();
+
+ Expression expression3 = new Expression.Builder().field("application").operator("equals")
+ .value(new Value(Value.Type.REFERENCE, "testapp1")).build();
+
+ Expression expression4 = new Expression.Builder().field("grantType").operator("equals")
+ .value(new Value(Value.Type.STRING, "client_credentials")).build();
+
+ ANDCombinedRule andCombinedRule2 =
+ new ANDCombinedRule.Builder().addExpression(expression3).addExpression(expression4).build();
+
+ Expression expression5 = new Expression.Builder().field("application").operator("equals")
+ .value(new Value(Value.Type.REFERENCE, "testapp2")).build();
+ ANDCombinedRule andCombinedRule3 =
+ new ANDCombinedRule.Builder().addExpression(expression5).build();
+
+ ORCombinedRule orCombinedRule =
+ new ORCombinedRule.Builder().addRule(andCombinedRule1).addRule(andCombinedRule2)
+ .addRule(andCombinedRule3).build();
+
+ ruleManagementDAOImpl.addRule(orCombinedRule, TENANT_ID);
+
+ createdRule = ruleManagementDAOImpl.getRuleByRuleId(orCombinedRule.getId(), TENANT_ID);
+ assertNotNull(createdRule);
+ assertEquals(orCombinedRule.getId(), createdRule.getId());
+ assertTrue(createdRule.isActive());
+
+ ORCombinedRule retrievedORCombinedRule = assertOrCombinedRule(createdRule, 3);
+ ANDCombinedRule retrievedAndCombinedRule1 = assertAndCombinedRule(retrievedORCombinedRule.getRules().get(0), 2);
+ assertExpressions(retrievedAndCombinedRule1, expression1, expression2);
+ ANDCombinedRule retrievedAndCombinedRule2 = assertAndCombinedRule(retrievedORCombinedRule.getRules().get(1), 2);
+ assertExpressions(retrievedAndCombinedRule2, expression3, expression4);
+ ANDCombinedRule retrievedAndCombinedRule3 = assertAndCombinedRule(retrievedORCombinedRule.getRules().get(2), 1);
+ assertExpressions(retrievedAndCombinedRule3, expression5);
+ }
+
+ @Test(dependsOnMethods = {"testAddRuleWithMultipleExpressionsUsingSameFieldReferenceAndOR"})
+ public void testUpdateRule() throws RuleManagementException {
+
+ Expression expression1 = new Expression.Builder().field("application").operator("equals")
+ .value(new Value(Value.Type.REFERENCE, "testapp1")).build();
+
+ Expression expression2 = new Expression.Builder().field("grantType").operator("equals")
+ .value(new Value(Value.Type.STRING, "password")).build();
+ ANDCombinedRule andCombinedRule1 =
+ new ANDCombinedRule.Builder().addExpression(expression1).addExpression(expression2).build();
+
+ Expression expression3 = new Expression.Builder().field("application").operator("equals")
+ .value(new Value(Value.Type.REFERENCE, "testapp2")).build();
+ ANDCombinedRule andCombinedRule2 =
+ new ANDCombinedRule.Builder().addExpression(expression3).build();
+
+ Expression expression4 = new Expression.Builder().field("application").operator("equals")
+ .value(new Value(Value.Type.REFERENCE, "testapp4")).build();
+ ANDCombinedRule andCombinedRule3 =
+ new ANDCombinedRule.Builder().addExpression(expression4).build();
+
+ ORCombinedRule orCombinedRule =
+ new ORCombinedRule.Builder().setId(createdRule.getId()).addRule(andCombinedRule1)
+ .addRule(andCombinedRule2)
+ .addRule(andCombinedRule3).build();
+
+ ruleManagementDAOImpl.updateRule(orCombinedRule, TENANT_ID);
+
+ Rule updatedRule = ruleManagementDAOImpl.getRuleByRuleId(createdRule.getId(), TENANT_ID);
+ assertNotNull(updatedRule);
+ assertEquals(createdRule.getId(), updatedRule.getId());
+ assertTrue(updatedRule.isActive());
+
+ ORCombinedRule retrievedORCombinedRule = assertOrCombinedRule(updatedRule, 3);
+ ANDCombinedRule retrievedAndCombinedRule1 = assertAndCombinedRule(retrievedORCombinedRule.getRules().get(0), 2);
+ assertExpressions(retrievedAndCombinedRule1, expression1, expression2);
+ ANDCombinedRule retrievedAndCombinedRule2 = assertAndCombinedRule(retrievedORCombinedRule.getRules().get(1), 1);
+ assertExpressions(retrievedAndCombinedRule2, expression3);
+ ANDCombinedRule retrievedAndCombinedRule3 = assertAndCombinedRule(retrievedORCombinedRule.getRules().get(2), 1);
+ assertExpressions(retrievedAndCombinedRule3, expression4);
+ }
+
+ @Test(dependsOnMethods = {"testUpdateRule"})
+ public void testDeactivateRule() throws RuleManagementException {
+
+ ruleManagementDAOImpl.deactivateRule(createdRule.getId(), TENANT_ID);
+ Rule deactivatedRule = ruleManagementDAOImpl.getRuleByRuleId(createdRule.getId(), TENANT_ID);
+ assertNotNull(deactivatedRule);
+ assertEquals(deactivatedRule.getId(), createdRule.getId());
+ assertFalse(deactivatedRule.isActive());
+ }
+
+ @Test(dependsOnMethods = {"testDeactivateRule"})
+ public void testActivateRule() throws RuleManagementException {
+
+ ruleManagementDAOImpl.activateRule(createdRule.getId(), TENANT_ID);
+ Rule activatedRule = ruleManagementDAOImpl.getRuleByRuleId(createdRule.getId(), TENANT_ID);
+ assertNotNull(activatedRule);
+ assertEquals(activatedRule.getId(), createdRule.getId());
+ assertTrue(activatedRule.isActive());
+ }
+
+ @Test(dependsOnMethods = {"testActivateRule"})
+ public void testDeleteRule() throws RuleManagementException {
+
+ ruleManagementDAOImpl.deleteRule(createdRule.getId(), TENANT_ID);
+ Rule deletedRule = ruleManagementDAOImpl.getRuleByRuleId(createdRule.getId(), TENANT_ID);
+ assertNull(deletedRule);
+ }
+
+ private static void assertExpressions(ANDCombinedRule andCombinedRule, Expression... expressions) {
+
+ assertEquals(andCombinedRule.getExpressions().size(), expressions.length);
+ for (int i = 0; i < expressions.length; i++) {
+ assertEquals(andCombinedRule.getExpressions().get(i).getField(), expressions[i].getField());
+ assertEquals(andCombinedRule.getExpressions().get(i).getOperator(), expressions[i].getOperator());
+ assertEquals(andCombinedRule.getExpressions().get(i).getValue().getType(),
+ expressions[i].getValue().getType());
+ assertEquals(andCombinedRule.getExpressions().get(i).getValue().getFieldValue(),
+ expressions[i].getValue().getFieldValue());
+ }
+ }
+
+ private static ANDCombinedRule assertAndCombinedRule(Rule andRule, int expectedExpressionsSize) {
+
+ assertTrue(andRule instanceof ANDCombinedRule);
+
+ ANDCombinedRule andCombinedRule = (ANDCombinedRule) andRule;
+ assertNotNull(andCombinedRule.getId());
+ assertTrue(andCombinedRule.isActive());
+ assertNotNull(andCombinedRule.getExpressions());
+ assertEquals(andCombinedRule.getExpressions().size(), expectedExpressionsSize);
+ return andCombinedRule;
+ }
+
+ private static ORCombinedRule assertOrCombinedRule(Rule rule, int expectedRulesSize) {
+
+ assertTrue(rule instanceof ORCombinedRule);
+ ORCombinedRule orCombinedRule = (ORCombinedRule) rule;
+ assertNotNull(orCombinedRule.getRules());
+ assertEquals(orCombinedRule.getRules().size(), expectedRulesSize);
+ return orCombinedRule;
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/java/org/wso2/carbon/identity/rule/management/service/RuleManagementServiceImplTest.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/java/org/wso2/carbon/identity/rule/management/service/RuleManagementServiceImplTest.java
new file mode 100644
index 000000000000..f32f420ff919
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/java/org/wso2/carbon/identity/rule/management/service/RuleManagementServiceImplTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.service;
+
+import org.mockito.MockedStatic;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.wso2.carbon.identity.common.testng.WithCarbonHome;
+import org.wso2.carbon.identity.common.testng.WithRealmService;
+import org.wso2.carbon.identity.core.internal.IdentityCoreServiceDataHolder;
+import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
+import org.wso2.carbon.identity.rule.management.dao.RuleManagementDAO;
+import org.wso2.carbon.identity.rule.management.exception.RuleManagementClientException;
+import org.wso2.carbon.identity.rule.management.exception.RuleManagementException;
+import org.wso2.carbon.identity.rule.management.model.Rule;
+import org.wso2.carbon.identity.rule.management.service.impl.RuleManagementServiceImpl;
+
+import java.lang.reflect.Field;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
+
+@WithCarbonHome
+@WithRealmService(injectToSingletons = {IdentityCoreServiceDataHolder.class})
+public class RuleManagementServiceImplTest {
+
+ private RuleManagementServiceImpl ruleManagementService;
+ private RuleManagementDAO ruleManagementDAO;
+
+ private MockedStatic identityTenantUtilMockedStatic;
+
+ public static final String RULE_ID = "ruleId";
+ public static final String TENANT_DOMAIN = "test.com";
+ public static final int TENANT_ID = 1;
+
+ @BeforeClass
+ public void setUpClass() {
+
+ ruleManagementService = RuleManagementServiceImpl.getInstance();
+
+ identityTenantUtilMockedStatic = mockStatic(IdentityTenantUtil.class);
+ when(IdentityTenantUtil.getTenantId(TENANT_DOMAIN)).thenReturn(TENANT_ID);
+ }
+
+ @AfterClass
+ public void tearDownClass() {
+
+ identityTenantUtilMockedStatic.close();
+ }
+
+ @BeforeMethod
+ public void setUp() throws Exception {
+
+ ruleManagementDAO = mock(RuleManagementDAO.class);
+ Field daoField = RuleManagementServiceImpl.class.getDeclaredField("ruleManagementDAO");
+ daoField.setAccessible(true);
+ daoField.set(ruleManagementService, ruleManagementDAO);
+ }
+
+ @Test
+ public void testAddRule() throws RuleManagementException {
+
+ Rule rule = mock(Rule.class);
+ when(rule.getId()).thenReturn(RULE_ID);
+
+ when(ruleManagementDAO.getRuleByRuleId(RULE_ID, TENANT_ID)).thenReturn(rule);
+
+ Rule result = ruleManagementService.addRule(rule, TENANT_DOMAIN);
+
+ verify(ruleManagementDAO).addRule(rule, TENANT_ID);
+ verify(ruleManagementDAO).getRuleByRuleId(RULE_ID, TENANT_ID);
+
+ assertEquals(result, rule);
+ }
+
+ @Test
+ public void testUpdateRule() throws RuleManagementException {
+
+ Rule rule = mock(Rule.class);
+ when(rule.getId()).thenReturn(RULE_ID);
+ when(ruleManagementDAO.getRuleByRuleId(RULE_ID, TENANT_ID)).thenReturn(rule);
+
+ Rule result = ruleManagementService.updateRule(rule, TENANT_DOMAIN);
+
+ verify(ruleManagementDAO).updateRule(rule, TENANT_ID);
+ verify(ruleManagementDAO, times(2)).getRuleByRuleId(RULE_ID, TENANT_ID);
+ assertEquals(result, rule);
+ }
+
+ @Test
+ public void testDeleteRule() throws RuleManagementException {
+
+ Rule rule = mock(Rule.class);
+ when(ruleManagementDAO.getRuleByRuleId(RULE_ID, TENANT_ID)).thenReturn(rule);
+
+ ruleManagementService.deleteRule(RULE_ID, TENANT_DOMAIN);
+
+ verify(ruleManagementDAO).deleteRule(RULE_ID, TENANT_ID);
+ }
+
+ @Test
+ public void testGetRuleByRuleId() throws RuleManagementException {
+
+ Rule rule = mock(Rule.class);
+ when(ruleManagementDAO.getRuleByRuleId(RULE_ID, TENANT_ID)).thenReturn(rule);
+
+ Rule result = ruleManagementService.getRuleByRuleId(RULE_ID, TENANT_DOMAIN);
+
+ verify(ruleManagementDAO).getRuleByRuleId(RULE_ID, TENANT_ID);
+ assertEquals(result, rule);
+ }
+
+ @Test
+ public void testDeactivateRule() throws RuleManagementException {
+
+ Rule rule = mock(Rule.class);
+ when(ruleManagementDAO.getRuleByRuleId(RULE_ID, TENANT_ID)).thenReturn(rule);
+
+ Rule result = ruleManagementService.deactivateRule(RULE_ID, TENANT_DOMAIN);
+
+ verify(ruleManagementDAO).deactivateRule(RULE_ID, TENANT_ID);
+ verify(ruleManagementDAO, times(2)).getRuleByRuleId(RULE_ID, TENANT_ID);
+ assertEquals(result, rule);
+ }
+
+ @Test(expectedExceptions = RuleManagementClientException.class, expectedExceptionsMessageRegExp =
+ "Rule not found for the given rule id: " + RULE_ID)
+ public void testUpdateIfRuleNotExist() throws RuleManagementException {
+
+ when(ruleManagementDAO.getRuleByRuleId(RULE_ID, TENANT_ID)).thenReturn(null);
+
+ Rule rule = mock(Rule.class);
+ when(rule.getId()).thenReturn(RULE_ID);
+ ruleManagementService.updateRule(rule, TENANT_DOMAIN);
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/java/org/wso2/carbon/identity/rule/management/util/RuleBuilderTest.java b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/java/org/wso2/carbon/identity/rule/management/util/RuleBuilderTest.java
new file mode 100644
index 000000000000..dc8751dc2d91
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/java/org/wso2/carbon/identity/rule/management/util/RuleBuilderTest.java
@@ -0,0 +1,476 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.wso2.carbon.identity.rule.management.util;
+
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.MockitoAnnotations;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.wso2.carbon.identity.rule.management.exception.RuleManagementClientException;
+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.Expression;
+import org.wso2.carbon.identity.rule.management.model.FlowType;
+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.metadata.config.OperatorConfig;
+import org.wso2.carbon.identity.rule.metadata.config.RuleMetadataConfigFactory;
+import org.wso2.carbon.identity.rule.metadata.exception.RuleMetadataException;
+import org.wso2.carbon.identity.rule.metadata.model.Field;
+import org.wso2.carbon.identity.rule.metadata.model.FieldDefinition;
+import org.wso2.carbon.identity.rule.metadata.model.Link;
+import org.wso2.carbon.identity.rule.metadata.model.Operator;
+import org.wso2.carbon.identity.rule.metadata.model.OptionsInputValue;
+import org.wso2.carbon.identity.rule.metadata.model.OptionsReferenceValue;
+import org.wso2.carbon.identity.rule.metadata.model.OptionsValue;
+import org.wso2.carbon.identity.rule.metadata.service.RuleMetadataService;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+public class RuleBuilderTest {
+
+ @Mock
+ RuleMetadataService ruleMetadataService;
+ private MockedStatic ruleMetadataConfigFactoryMockedStatic;
+ OperatorConfig operatorConfig;
+
+ @BeforeClass
+ public void setUpClass() throws Exception {
+
+ String filePath = Objects.requireNonNull(getClass().getClassLoader().getResource(
+ "configs/valid-operators.json")).getFile();
+ operatorConfig = OperatorConfig.load(new File(filePath));
+ }
+
+ @BeforeMethod
+ public void setUpMethod() {
+
+ ruleMetadataConfigFactoryMockedStatic = mockStatic(RuleMetadataConfigFactory.class);
+ ruleMetadataConfigFactoryMockedStatic.when(RuleMetadataConfigFactory::getOperatorConfig)
+ .thenReturn(operatorConfig);
+
+ MockitoAnnotations.openMocks(this);
+ RuleManagementComponentServiceHolder.getInstance().setRuleMetadataService(ruleMetadataService);
+ }
+
+ @AfterMethod
+ public void tearDownMethod() {
+
+ ruleMetadataConfigFactoryMockedStatic.close();
+ }
+
+ @Test(expectedExceptions = RuleManagementClientException.class,
+ expectedExceptionsMessageRegExp = "Expression metadata from RuleMetadataService is null or empty.")
+ public void testCreateRuleBuilderWhenExpressionMetaReturnedFromMetadataServiceIsNull()
+ throws Exception {
+
+ when(ruleMetadataService.getExpressionMeta(
+ org.wso2.carbon.identity.rule.metadata.model.FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1")).thenReturn(
+ null);
+
+ RuleBuilder.create(FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1");
+ }
+
+ @Test(expectedExceptions = RuleManagementClientException.class,
+ expectedExceptionsMessageRegExp = "Expression metadata from RuleMetadataService is null or empty.")
+ public void testCreateRuleBuilderWhenExpressionMetaReturnedFromMetadataServiceIsEmpty()
+ throws Exception {
+
+ when(ruleMetadataService.getExpressionMeta(
+ org.wso2.carbon.identity.rule.metadata.model.FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1")).thenReturn(
+ Collections.emptyList());
+
+ RuleBuilder.create(FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1");
+ }
+
+ @Test(expectedExceptions = RuleManagementServerException.class,
+ expectedExceptionsMessageRegExp = "Error while retrieving expression metadata from RuleMetadataService.")
+ public void testCreateRuleBuilderWhenMetadataServiceThrowsException()
+ throws Exception {
+
+ when(ruleMetadataService.getExpressionMeta(
+ org.wso2.carbon.identity.rule.metadata.model.FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1")).thenThrow(
+ new RuleMetadataException("RULEMETA-60005", "Error while retrieving expression metadata.",
+ "Failed to load data from configurations."));
+
+ RuleBuilder.create(FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1");
+ }
+
+ @Test
+ public void testCreateRuleBuilder() throws Exception {
+
+ List mockedFieldDefinitions = getMockedFieldDefinitions();
+ when(ruleMetadataService.getExpressionMeta(
+ org.wso2.carbon.identity.rule.metadata.model.FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1")).thenReturn(
+ mockedFieldDefinitions);
+
+ RuleBuilder ruleBuilder = RuleBuilder.create(FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1");
+ assertNotNull(ruleBuilder);
+ }
+
+ @Test
+ public void testCreateRuleWithValidExpressions() throws Exception {
+
+ List mockedFieldDefinitions = getMockedFieldDefinitions();
+ when(ruleMetadataService.getExpressionMeta(
+ org.wso2.carbon.identity.rule.metadata.model.FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1")).thenReturn(
+ mockedFieldDefinitions);
+
+ RuleBuilder ruleBuilder = RuleBuilder.create(FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1");
+
+ Expression expression1 = new Expression.Builder().field("application").operator("equals")
+ .value(new Value(Value.Type.REFERENCE, "testapp")).build();
+ ruleBuilder.addAndExpression(expression1);
+
+ Expression expression2 = new Expression.Builder().field("grantType").operator("equals")
+ .value(new Value(Value.Type.STRING, "authorization_code")).build();
+ ruleBuilder.addAndExpression(expression2);
+ Rule rule = ruleBuilder.build();
+
+ assertNotNull(rule);
+ assertNotNull(rule.getId());
+ assertTrue(rule.isActive());
+
+ ORCombinedRule orCombinedRule = assertOrCombinedRule(rule, 1);
+
+ orCombinedRule.getRules()
+ .forEach(andRule -> assertExpressions(assertAndCombinedRule(andRule, 2), expression1, expression2));
+ }
+
+ @Test(expectedExceptions = RuleManagementClientException.class,
+ expectedExceptionsMessageRegExp = "Building rule failed due to validation errors. " +
+ "Error: Field invalid is not supported")
+ public void testCreateRuleWithAnExpressionWithInvalidField() throws Exception {
+
+ List mockedFieldDefinitions = getMockedFieldDefinitions();
+ when(ruleMetadataService.getExpressionMeta(
+ org.wso2.carbon.identity.rule.metadata.model.FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1")).thenReturn(
+ mockedFieldDefinitions);
+
+ RuleBuilder ruleBuilder = RuleBuilder.create(FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1");
+
+ Expression expression = new Expression.Builder().field("invalid").operator("equals")
+ .value(new Value(Value.Type.STRING, "value")).build();
+ ruleBuilder.addAndExpression(expression);
+
+ ruleBuilder.build();
+ }
+
+ @Test(expectedExceptions = RuleManagementClientException.class,
+ expectedExceptionsMessageRegExp = "Building rule failed due to validation errors. " +
+ "Error: Operator invalid is not supported for field application")
+ public void testCreateRuleWithAnExpressionWithInvalidOperator() throws Exception {
+
+ List mockedFieldDefinitions = getMockedFieldDefinitions();
+ when(ruleMetadataService.getExpressionMeta(
+ org.wso2.carbon.identity.rule.metadata.model.FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1"))
+ .thenReturn(mockedFieldDefinitions);
+
+ RuleBuilder ruleBuilder = RuleBuilder.create(FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1");
+
+ Expression expression = new Expression.Builder().field("application").operator("invalid")
+ .value(new Value(Value.Type.STRING, "value")).build();
+ ruleBuilder.addAndExpression(expression);
+
+ ruleBuilder.build();
+ }
+
+ @Test(expectedExceptions = RuleManagementClientException.class,
+ expectedExceptionsMessageRegExp = "Building rule failed due to validation errors. " +
+ "Error: Value invalid is not supported for field grantType")
+ public void testCreateRuleWithAnExpressionWithInvalidValue() throws Exception {
+
+ List mockedFieldDefinitions = getMockedFieldDefinitions();
+ when(ruleMetadataService.getExpressionMeta(
+ org.wso2.carbon.identity.rule.metadata.model.FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1"))
+ .thenReturn(mockedFieldDefinitions);
+
+ RuleBuilder ruleBuilder = RuleBuilder.create(FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1");
+
+ Expression expression = new Expression.Builder().field("grantType").operator("equals")
+ .value(new Value(Value.Type.STRING, "invalid")).build();
+ ruleBuilder.addAndExpression(expression);
+
+ ruleBuilder.build();
+ }
+
+ @Test(expectedExceptions = RuleManagementClientException.class,
+ expectedExceptionsMessageRegExp = "Building rule failed due to validation errors. " +
+ "Error: Value type STRING is not supported for field application")
+ public void testCreateRuleWithAnExpressionWithInvalidValueType() throws Exception {
+
+ List mockedFieldDefinitions = getMockedFieldDefinitions();
+ when(ruleMetadataService.getExpressionMeta(
+ org.wso2.carbon.identity.rule.metadata.model.FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1"))
+ .thenReturn(mockedFieldDefinitions);
+
+ RuleBuilder ruleBuilder = RuleBuilder.create(FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1");
+
+ Expression expression = new Expression.Builder().field("application").operator("equals")
+ .value(new Value(Value.Type.STRING, "testapp")).build();
+ ruleBuilder.addAndExpression(expression);
+
+ ruleBuilder.build();
+ }
+
+ /**
+ * This test case is to test the scenario where multiple validation failures occur when building a rule.
+ * In this case, only the very first validation failure is expected to be thrown.
+ *
+ * @throws Exception Client exception when building the rule
+ */
+ @Test(expectedExceptions = RuleManagementClientException.class,
+ expectedExceptionsMessageRegExp = "Building rule failed due to validation errors. " +
+ "Error: Field invalid is not supported")
+ public void testCreateRuleWithMultipleValidationFailures() throws Exception {
+
+ List mockedFieldDefinitions = getMockedFieldDefinitions();
+ when(ruleMetadataService.getExpressionMeta(
+ org.wso2.carbon.identity.rule.metadata.model.FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1"))
+ .thenReturn(mockedFieldDefinitions);
+
+ RuleBuilder ruleBuilder = RuleBuilder.create(FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1");
+
+ // Expression with invalid field
+ Expression expression1 = new Expression.Builder().field("invalid").operator("equals")
+ .value(new Value(Value.Type.STRING, "value")).build();
+ ruleBuilder.addAndExpression(expression1);
+
+ // Expression with invalid operator
+ Expression expression2 = new Expression.Builder().field("grantType").operator("equals")
+ .value(new Value(Value.Type.STRING, "invalid")).build();
+ ruleBuilder.addAndExpression(expression2);
+
+ ruleBuilder.addOrCondition();
+
+ // Expression with invalid value type
+ Expression expression3 = new Expression.Builder().field("application").operator("equals")
+ .value(new Value(Value.Type.STRING, "testapp")).build();
+ ruleBuilder.addAndExpression(expression3);
+
+ ruleBuilder.build();
+ }
+
+ @Test(expectedExceptions = RuleManagementClientException.class,
+ expectedExceptionsMessageRegExp = "Building rule failed due to validation errors. " +
+ "Error: Maximum number of expressions combined with AND exceeded. Maximum allowed: 5 Provided: 6")
+ public void testCreateRuleWithMaxAllowedExpressionsCombinedWithAND() throws Exception {
+
+ List mockedFieldDefinitions = getMockedFieldDefinitions();
+ when(ruleMetadataService.getExpressionMeta(
+ org.wso2.carbon.identity.rule.metadata.model.FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1")).thenReturn(
+ mockedFieldDefinitions);
+
+ RuleBuilder ruleBuilder = RuleBuilder.create(FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1");
+
+ for (int i = 0; i < 6; i++) {
+ Expression expression = new Expression.Builder().field("application").operator("equals")
+ .value(new Value(Value.Type.REFERENCE, "testapp" + i)).build();
+ ruleBuilder.addAndExpression(expression);
+ }
+
+ ruleBuilder.build();
+ }
+
+ @Test(expectedExceptions = RuleManagementClientException.class,
+ expectedExceptionsMessageRegExp = "Building rule failed due to validation errors. " +
+ "Error: Maximum number of rules combined with OR exceeded. Maximum allowed: 10 Provided: 11")
+ public void testCreateRuleWithMaxAllowedRulesCombinedWithOR() throws Exception {
+
+ List mockedFieldDefinitions = getMockedFieldDefinitions();
+ when(ruleMetadataService.getExpressionMeta(
+ org.wso2.carbon.identity.rule.metadata.model.FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1")).thenReturn(
+ mockedFieldDefinitions);
+
+ RuleBuilder ruleBuilder = RuleBuilder.create(FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1");
+
+ for (int i = 0; i < 11; i++) {
+ Expression expression = new Expression.Builder().field("application").operator("equals")
+ .value(new Value(Value.Type.REFERENCE, "testapp" + i)).build();
+ ruleBuilder.addAndExpression(expression);
+ ruleBuilder.addOrCondition();
+ }
+
+ ruleBuilder.build();
+ }
+
+ @Test
+ public void testCreateRuleWithValidExpressionsAndOR() throws Exception {
+
+ List mockedFieldDefinitions = getMockedFieldDefinitions();
+ when(ruleMetadataService.getExpressionMeta(
+ org.wso2.carbon.identity.rule.metadata.model.FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1")).thenReturn(
+ mockedFieldDefinitions);
+
+ RuleBuilder ruleBuilder = RuleBuilder.create(FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1");
+
+ Expression expression1 = new Expression.Builder().field("application").operator("equals")
+ .value(new Value(Value.Type.REFERENCE, "testapp1")).build();
+ ruleBuilder.addAndExpression(expression1);
+
+ Expression expression2 = new Expression.Builder().field("grantType").operator("equals")
+ .value(new Value(Value.Type.STRING, "authorization_code")).build();
+ ruleBuilder.addAndExpression(expression2);
+
+ ruleBuilder.addOrCondition();
+
+ Expression expression3 = new Expression.Builder().field("application").operator("equals")
+ .value(new Value(Value.Type.REFERENCE, "testapp2")).build();
+ ruleBuilder.addAndExpression(expression3);
+
+ Rule rule = ruleBuilder.build();
+
+ assertNotNull(rule);
+ assertNotNull(rule.getId());
+ assertTrue(rule.isActive());
+
+ ORCombinedRule orCombinedRule = assertOrCombinedRule(rule, 2);
+ ANDCombinedRule andCombinedRule = assertAndCombinedRule(orCombinedRule.getRules().get(0), 2);
+ assertExpressions(andCombinedRule, expression1, expression2);
+
+ andCombinedRule = assertAndCombinedRule(orCombinedRule.getRules().get(1), 1);
+ assertExpressions(andCombinedRule, expression3);
+ }
+
+ @Test
+ public void testCreateRuleWithMultipleExpressionsUsingSameFieldReferenceAndOR() throws Exception {
+
+ List mockedFieldDefinitions = getMockedFieldDefinitions();
+ when(ruleMetadataService.getExpressionMeta(
+ org.wso2.carbon.identity.rule.metadata.model.FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1")).thenReturn(
+ mockedFieldDefinitions);
+
+ RuleBuilder ruleBuilder = RuleBuilder.create(FlowType.PRE_ISSUE_ACCESS_TOKEN, "tenant1");
+
+ Expression expression1 = new Expression.Builder().field("application").operator("equals")
+ .value(new Value(Value.Type.REFERENCE, "testapp1")).build();
+ ruleBuilder.addAndExpression(expression1);
+
+ Expression expression2 = new Expression.Builder().field("grantType").operator("equals")
+ .value(new Value(Value.Type.STRING, "authorization_code")).build();
+ ruleBuilder.addAndExpression(expression2);
+
+ ruleBuilder.addOrCondition();
+
+ Expression expression3 = new Expression.Builder().field("application").operator("equals")
+ .value(new Value(Value.Type.REFERENCE, "testapp1")).build();
+ ruleBuilder.addAndExpression(expression3);
+
+ Expression expression4 = new Expression.Builder().field("grantType").operator("equals")
+ .value(new Value(Value.Type.STRING, "client_credentials")).build();
+ ruleBuilder.addAndExpression(expression4);
+
+ ruleBuilder.addOrCondition();
+
+ Expression expression5 = new Expression.Builder().field("application").operator("equals")
+ .value(new Value(Value.Type.REFERENCE, "testapp2")).build();
+ ruleBuilder.addAndExpression(expression5);
+
+ Rule rule = ruleBuilder.build();
+
+ assertNotNull(rule);
+ assertNotNull(rule.getId());
+ assertTrue(rule.isActive());
+
+ ORCombinedRule orCombinedRule = assertOrCombinedRule(rule, 3);
+ ANDCombinedRule andCombinedRule = assertAndCombinedRule(orCombinedRule.getRules().get(0), 2);
+ assertExpressions(andCombinedRule, expression1, expression2);
+
+ andCombinedRule = assertAndCombinedRule(orCombinedRule.getRules().get(1), 2);
+ assertExpressions(andCombinedRule, expression3, expression4);
+
+ andCombinedRule = assertAndCombinedRule(orCombinedRule.getRules().get(2), 1);
+ assertExpressions(andCombinedRule, expression5);
+ }
+
+ private List getMockedFieldDefinitions() {
+
+ List fieldDefinitionList = new ArrayList<>();
+
+ Field applicationField = new Field("application", "application");
+ List operators = Arrays.asList(new Operator("equals", "equals"),
+ new Operator("notEquals", "not equals"));
+
+ List links = Arrays.asList(new Link("/applications?offset=0&limit=10", "GET", "values"),
+ new Link("/applications?filter=name+eq+*&limit=10", "GET", "filter"));
+ org.wso2.carbon.identity.rule.metadata.model.Value
+ applicationValue = new OptionsReferenceValue.Builder().valueReferenceAttribute("id")
+ .valueDisplayAttribute("name").valueType(
+ org.wso2.carbon.identity.rule.metadata.model.Value.ValueType.REFERENCE).links(links).build();
+ fieldDefinitionList.add(new FieldDefinition(applicationField, operators, applicationValue));
+
+ Field grantTypeField = new Field("grantType", "grantType");
+ List optionsValues = Arrays.asList(new OptionsValue("authorization_code", "authorization code"),
+ new OptionsValue("password", "password"), new OptionsValue("refresh_token", "refresh token"),
+ new OptionsValue("client_credentials", "client credentials"),
+ new OptionsValue("urn:ietf:params:oauth:grant-type:token-exchange", "token exchange"));
+ org.wso2.carbon.identity.rule.metadata.model.Value
+ grantTypeValue =
+ new OptionsInputValue(org.wso2.carbon.identity.rule.metadata.model.Value.ValueType.STRING,
+ optionsValues);
+ fieldDefinitionList.add(new FieldDefinition(grantTypeField, operators, grantTypeValue));
+
+ return fieldDefinitionList;
+ }
+
+ private static void assertExpressions(ANDCombinedRule andCombinedRule, Expression... expressions) {
+
+ assertEquals(andCombinedRule.getExpressions().size(), expressions.length);
+ for (int i = 0; i < expressions.length; i++) {
+ assertEquals(andCombinedRule.getExpressions().get(i), expressions[i]);
+ }
+ }
+
+ private static ANDCombinedRule assertAndCombinedRule(Rule andRule, int expectedExpressionsSize) {
+
+ assertTrue(andRule instanceof ANDCombinedRule);
+
+ ANDCombinedRule andCombinedRule = (ANDCombinedRule) andRule;
+ assertNotNull(andCombinedRule.getId());
+ assertTrue(andCombinedRule.isActive());
+ assertNotNull(andCombinedRule.getExpressions());
+ assertEquals(andCombinedRule.getExpressions().size(), expectedExpressionsSize);
+ return andCombinedRule;
+ }
+
+ private static ORCombinedRule assertOrCombinedRule(Rule rule, int expectedRulesSize) {
+
+ assertTrue(rule instanceof ORCombinedRule);
+ ORCombinedRule orCombinedRule = (ORCombinedRule) rule;
+ assertNotNull(orCombinedRule.getRules());
+ assertEquals(orCombinedRule.getRules().size(), expectedRulesSize);
+ return orCombinedRule;
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/resources/configs/valid-operators.json b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/resources/configs/valid-operators.json
new file mode 100644
index 000000000000..440832b8f460
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/resources/configs/valid-operators.json
@@ -0,0 +1,14 @@
+{
+ "equals": {
+ "name": "equals",
+ "displayName": "equals"
+ },
+ "notEquals": {
+ "name": "notEquals",
+ "displayName": "not equals"
+ },
+ "contains": {
+ "name": "contains",
+ "displayName": "contains"
+ }
+}
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/resources/dbscripts/h2.sql b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/resources/dbscripts/h2.sql
new file mode 100644
index 000000000000..6e6236d38fa6
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/resources/dbscripts/h2.sql
@@ -0,0 +1,20 @@
+CREATE TABLE IF NOT EXISTS IDN_RULE (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ UUID CHAR(36) NOT NULL,
+ CONTENT BLOB NOT NULL,
+ IS_ACTIVE BOOLEAN DEFAULT TRUE,
+ TENANT_ID INTEGER NOT NULL,
+ VERSION VARCHAR(15) NOT NULL,
+ PRIMARY KEY (ID),
+ UNIQUE (UUID)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_RULE_REFERENCES (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ RULE_ID INTEGER NOT NULL,
+ FIELD_NAME VARCHAR(100) NOT NULL,
+ FIELD_REFERENCE VARCHAR(255) NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ PRIMARY KEY (ID),
+ FOREIGN KEY (RULE_ID) REFERENCES IDN_RULE(ID) ON DELETE CASCADE
+);
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/resources/repository/conf/carbon.xml b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/resources/repository/conf/carbon.xml
new file mode 100644
index 000000000000..a5a1a6470cbc
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/resources/repository/conf/carbon.xml
@@ -0,0 +1,686 @@
+
+
+
+
+
+
+
+ WSO2 Identity Server
+
+
+ IS
+
+
+ 5.3.0
+
+
+ localhost
+
+
+ localhost
+
+
+ local:/${carbon.context}/services/
+
+
+
+
+
+
+ IdentityServer
+
+
+
+
+
+
+ org.wso2.carbon
+
+
+ /
+
+
+
+
+
+
+
+
+ 15
+
+
+
+
+
+
+
+
+ 0
+
+
+
+
+ 9999
+
+ 11111
+
+
+
+
+
+ 10389
+
+ 8000
+
+
+
+
+
+ 10500
+
+
+
+
+
+
+
+
+ org.wso2.carbon.tomcat.jndi.CarbonJavaURLContextFactory
+
+
+
+
+
+
+
+
+ java
+
+
+
+
+
+
+
+
+
+ false
+
+
+ false
+
+
+ 600
+
+
+
+ false
+
+
+
+
+
+
+
+ 30
+
+
+
+
+
+
+
+
+ 15
+
+
+
+
+
+ ${carbon.home}/repository/deployment/server/
+
+
+ 15
+
+
+ ${carbon.home}/repository/conf/axis2/axis2.xml
+
+
+ 30000
+
+
+ ${carbon.home}/repository/deployment/client/
+
+ ${carbon.home}/repository/conf/axis2/axis2_client.xml
+
+ true
+
+
+
+
+
+
+
+
+
+ admin
+ Default Administrator Role
+
+
+ user
+ Default User Role
+
+
+
+
+
+
+
+
+
+
+
+ ${carbon.home}/repository/resources/security/wso2carbon.jks
+
+ JKS
+
+ wso2carbon
+
+ wso2carbon
+
+ wso2carbon
+
+
+
+
+
+ ${carbon.home}/repository/resources/security/client-truststore.jks
+
+ JKS
+
+ wso2carbon
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserManager
+
+
+ false
+
+ org.wso2.carbon.identity.provider.AttributeCallbackHandler
+
+
+ org.wso2.carbon.identity.sts.store.DBTokenStore
+
+
+ true
+ allow
+
+
+
+
+
+
+ claim_mgt_menu
+ identity_mgt_emailtemplate_menu
+ identity_security_questions_menu
+
+
+
+ ${carbon.home}/tmp/work
+
+
+
+
+
+ true
+
+
+ 10
+
+
+ 30
+
+
+
+
+
+ 100
+
+
+
+ keystore
+ certificate
+ *
+
+ org.wso2.carbon.ui.transports.fileupload.AnyFileUploadExecutor
+
+
+
+
+ jarZip
+
+ org.wso2.carbon.ui.transports.fileupload.JarZipUploadExecutor
+
+
+
+ dbs
+
+ org.wso2.carbon.ui.transports.fileupload.DBSFileUploadExecutor
+
+
+
+ tools
+
+ org.wso2.carbon.ui.transports.fileupload.ToolsFileUploadExecutor
+
+
+
+ toolsAny
+
+ org.wso2.carbon.ui.transports.fileupload.ToolsAnyFileUploadExecutor
+
+
+
+
+
+
+
+
+
+ - info
+ org.wso2.carbon.core.transports.util.InfoProcessor
+
+
+ - wsdl
+ org.wso2.carbon.core.transports.util.Wsdl11Processor
+
+
+ - wsdl2
+ org.wso2.carbon.core.transports.util.Wsdl20Processor
+
+
+ - xsd
+ org.wso2.carbon.core.transports.util.XsdProcessor
+
+
+
+
+
+ false
+ false
+ true
+ svn
+ http://svnrepo.example.com/repos/
+ username
+ password
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${require.carbon.servlet}
+
+
+
+
+ true
+
+
+
+
+
+
+ default repository
+ http://product-dist.wso2.com/p2/carbon/releases/wilkes/
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+ true
+
+
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/resources/repository/conf/identity/identity.xml b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/resources/repository/conf/identity/identity.xml
new file mode 100644
index 000000000000..07de6831dbf4
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/resources/repository/conf/identity/identity.xml
@@ -0,0 +1,743 @@
+
+
+
+
+
+
+
+
+ jdbc/WSO2IdentityDB
+
+
+
+
+ true
+ true
+ 0
+
+ true
+ 20160
+ 1140
+
+
+ true
+ 720
+
+
+
+
+
+
+ 15
+ 20160
+
+
+
+
+
+ ${carbon.home}/conf/keystores
+ SunX509
+ SunX509
+
+
+
+ SelfAndManaged
+ CertValidate
+
+
+
+
+
+
+
+
+
+
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/openidserver
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/openid
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/openid_login.do
+
+
+ false
+
+ 7200
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -1
+ -1
+ -1
+ -1
+
+
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth/request-token
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth/authorize-url
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth/access-token
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/authorize
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/token
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/revoke
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/introspect
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/userinfo
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oidc/checksession
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oidc/logout
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/oauth2_authz.do
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/oauth2_error.do
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/oauth2_consent.do
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/oauth2_logout_consent.do
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/oauth2_logout.do
+
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/.well-known/webfinger
+
+
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/identity/connect/register
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/jwks
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/oidcdiscovery
+
+
+ 300
+
+ 3600
+
+ 3600
+
+ 84600
+
+ 300
+
+ false
+
+ true
+
+ org.wso2.carbon.identity.oauth.tokenprocessor.PlainTextPersistenceProcessor
+
+
+
+ false
+
+
+
+
+
+ token
+ org.wso2.carbon.identity.oauth2.authz.handlers.AccessTokenResponseTypeHandler
+
+
+ code
+ org.wso2.carbon.identity.oauth2.authz.handlers.CodeResponseTypeHandler
+
+
+ id_token
+ org.wso2.carbon.identity.oauth2.authz.handlers.IDTokenResponseTypeHandler
+
+
+ id_token token
+ org.wso2.carbon.identity.oauth2.authz.handlers.IDTokenTokenResponseTypeHandler
+
+
+
+
+
+ authorization_code
+ org.wso2.carbon.identity.oauth2.token.handlers.grant.AuthorizationCodeGrantHandler
+
+
+ password
+ org.wso2.carbon.identity.oauth2.token.handlers.grant.PasswordGrantHandler
+
+
+ refresh_token
+ org.wso2.carbon.identity.oauth2.token.handlers.grant.RefreshGrantHandler
+
+
+ client_credentials
+ org.wso2.carbon.identity.oauth2.token.handlers.grant.ClientCredentialsGrantHandler
+
+
+ urn:ietf:params:oauth:grant-type:saml2-bearer
+ org.wso2.carbon.identity.oauth2.token.handlers.grant.saml.SAML2BearerGrantHandler
+
+
+ iwa:ntlm
+ org.wso2.carbon.identity.oauth2.token.handlers.grant.iwa.ntlm.NTLMAuthenticationGrantHandler
+
+
+ idTokenNotAllowedGrantType
+ org.wso2.carbon.identity.oauth2.token.handlers.grant.idTokenNotAllowedGrantHandler
+ false
+
+
+
+
+
+
+
+
+ false
+
+
+
+ false
+
+
+
+ false
+ org.wso2.carbon.identity.oauth2.authcontext.JWTTokenGenerator
+ org.wso2.carbon.identity.oauth2.authcontext.DefaultClaimsRetriever
+ http://wso2.org/claims
+ SHA256withRSA
+ 10
+
+
+
+
+
+ org.wso2.carbon.identity.openidconnect.DefaultIDTokenBuilder
+ SHA256withRSA
+
+
+
+
+
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/token
+ org.wso2.carbon.identity.openidconnect.DefaultOIDCClaimsCallbackHandler
+ 3600
+ org.wso2.carbon.identity.oauth.endpoint.user.impl.UserInfoUserStoreClaimRetriever
+ org.wso2.carbon.identity.oauth.endpoint.user.impl.UserInforRequestDefaultValidator
+ org.wso2.carbon.identity.oauth.endpoint.user.impl.UserInfoISAccessTokenValidator
+ org.wso2.carbon.identity.oauth.endpoint.user.impl.UserInfoJSONResponseBuilder
+ false
+
+
+
+
+
+
+
+ gtalk
+ talk.google.com
+ 5222
+ gmail.com
+ multifactor1@gmail.com
+ wso2carbon
+
+
+
+
+
+ 157680000
+ 157680000
+ ${carbon.host}
+
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/samlsso
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/samlsso_logout.do
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/samlsso_notification.do
+ 5
+ 60000
+
+ false
+ http://wso2.org/claims
+ org.wso2.carbon.identity.sso.saml.builders.assertion.ExtendedDefaultAssertionBuilder
+
+ org.wso2.carbon.identity.sso.saml.builders.encryption.DefaultSSOEncrypter
+ org.wso2.carbon.identity.sso.saml.builders.signature.DefaultSSOSigner
+ org.wso2.carbon.identity.sso.saml.validators.SAML2HTTPRedirectDeflateSignatureValidator
+
+
+
+ 5
+ false
+ http://www.w3.org/2000/09/xmldsig#rsa-sha1
+ http://www.w3.org/2000/09/xmldsig#sha1
+ true
+
+
+
+
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/services/wso2carbon-sts
+
+
+
+
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/passivests
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/retry.do
+ org.wso2.carbon.identity.sts.passive.utils.NoPersistenceTokenStore
+ true
+
+
+
+
+ false
+ ${Ports.ThriftEntitlementReceivePort}
+ 10000
+
+ ${carbon.home}/repository/resources/security/wso2carbon.jks
+ wso2carbon
+
+
+ ${carbon.host}
+
+
+
+
+
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/wso2/scim/Users
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/wso2/scim/Groups
+
+
+ 5
+
+
+ 10
+ local://services
+
+
+
+
+
+
+
+
+
+
+
+
+ org.wso2.carbon.identity.governance.store.JDBCIdentityDataStore
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /permission/admin/manage/identity/identitymgt
+
+
+
+
+
+ /permission/admin/manage/identity/usermgt/view
+
+
+ /permission/admin/manage/identity/usermgt/view
+
+
+
+ /permission/admin/manage/identity/configmgt/list
+
+
+
+ /permission/admin/manage/identity/configmgt/add
+
+
+ /permission/admin/manage/identity/configmgt/update
+
+
+
+ /permission/admin/manage/identity/configmgt/delete
+
+
+
+ /permission/admin/manage/identity/configmgt/add
+
+
+ /permission/admin/manage/identity/configmgt/update
+
+
+
+ /permission/admin/manage/identity/configmgt/delete
+
+
+
+ /permission/admin/manage/identity/configmgt/add
+
+
+ /permission/admin/manage/identity/configmgt/update
+
+
+
+ /permission/admin/manage/identity/configmgt/delete
+
+
+
+
+
+
+ /permission/admin/manage/identity/consentmgt/add
+
+
+
+ /permission/admin/manage/identity/consentmgt/delete
+
+
+
+ /permission/admin/manage/identity/consentmgt/add
+
+
+
+ /permission/admin/manage/identity/consentmgt/delete
+
+
+
+ /permission/admin/manage/identity/consentmgt/add
+
+
+
+ /permission/admin/manage/identity/consentmgt/delete
+
+
+
+ /permission/admin/manage/identity/identitymgt
+
+
+
+ /permission/admin/manage/identity/applicationmgt/create
+
+
+ /permission/admin/manage/identity/applicationmgt/delete
+
+
+ /permission/admin/manage/identity/applicationmgt/update
+
+
+ /permission/admin/manage/identity/applicationmgt/view
+
+
+ /permission/admin/manage/identity/applicationmgt/delete
+
+
+ /permission/admin/manage/identity/applicationmgt/create
+
+
+ /permission/admin/manage/identity/applicationmgt/view
+
+
+ /permission/admin/manage/identity/pep
+
+
+ /permission/admin/manage/identity/usermgt/create
+
+
+ /permission/admin/manage/identity/usermgt/list
+
+
+ /permission/admin/manage/identity/rolemgt/create
+
+
+ /permission/admin/manage/identity/rolemgt/view
+
+
+ /permission/admin/manage/identity/usermgt/view
+
+
+ /permission/admin/manage/identity/usermgt/update
+
+
+ /permission/admin/manage/identity/usermgt/update
+
+
+ /permission/admin/manage/identity/usermgt/delete
+
+
+ /permission/admin/manage/identity/rolemgt/view
+
+
+ /permission/admin/manage/identity/rolemgt/update
+
+
+ /permission/admin/manage/identity/rolemgt/update
+
+
+ /permission/admin/manage/identity/rolemgt/delete
+
+
+ /permission/admin/login
+
+
+ /permission/admin/manage/identity/usermgt/delete
+
+
+ /permission/admin/login
+
+
+ /permission/admin/login
+
+
+ /permission/admin/manage/identity/usermgt/create
+
+
+
+
+
+
+
+
+ /permission/admin/manage/identity/usermgt
+
+
+ /permission/admin/manage/identity/applicationmgt
+
+
+
+
+
+
+ /permission/admin/manage/identity/usermgt/update
+
+
+
+
+
+ /permission/admin/manage/humantask/viewtasks
+
+
+ /permission/admin/login
+
+
+ /permission/admin/manage/identity/usermgt
+
+
+ /permission/admin/manage/identity/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /api/identity/user/v0.9
+ /api/identity/recovery/v0.9
+ /oauth2
+ /api/identity/entitlement
+
+
+ /identity/(.*)
+
+
+
+
+
+ applications,connections
+
+
+
+ 300
+
diff --git a/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/resources/testng.xml b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/resources/testng.xml
new file mode 100644
index 000000000000..90613c3d2d42
--- /dev/null
+++ b/components/rule-mgt/org.wso2.carbon.identity.rule.management/src/test/resources/testng.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/db2.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/db2.sql
index e75cc05b5f8b..b029ed8dd200 100644
--- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/db2.sql
+++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/db2.sql
@@ -2119,6 +2119,43 @@ REFERENCING NEW AS NEW FOR EACH ROW MODE DB2SQL
= (NEXTVAL FOR IDN_OAUTH2_TOKEN_CLAIMS_SEQ);
END
/
+CREATE TABLE IDN_RULE (
+ ID INTEGER NOT NULL,
+ UUID CHAR(36) NOT NULL,
+ CONTENT BLOB NOT NULL,
+ IS_ACTIVE BOOLEAN DEFAULT TRUE,
+ TENANT_ID INTEGER NOT NULL,
+ VERSION VARCHAR(15) NOT NULL,
+ PRIMARY KEY (ID),
+ UNIQUE (UUID)
+)
+/
+CREATE SEQUENCE IDN_RULE_SEQ START WITH 1 INCREMENT BY 1 NOCACHE
+/
+CREATE TRIGGER IDN_RULE_TRIG NO CASCADE BEFORE INSERT ON IDN_RULE_REFERENCES
+REFERENCING NEW AS NEW FOR EACH ROW MODE DB2SQL
+ BEGIN ATOMIC
+ SET (NEW.ID) = (NEXTVAL FOR IDN_RULE_SEQ);
+ END
+/
+CREATE TABLE IDN_RULE_REFERENCES (
+ ID INTEGER NOT NULL,
+ RULE_ID INTEGER NOT NULL,
+ FIELD_NAME VARCHAR(100) NOT NULL,
+ FIELD_REFERENCE VARCHAR(255) NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ PRIMARY KEY (ID),
+ FOREIGN KEY (RULE_ID) REFERENCES IDN_RULE(ID) ON DELETE CASCADE
+)
+/
+CREATE SEQUENCE IDN_RULE_REFERENCES_SEQ START WITH 1 INCREMENT BY 1 NOCACHE
+/
+CREATE TRIGGER IDN_RULE_REFERENCES_TRIG NO CASCADE BEFORE INSERT ON IDN_RULE_REFERENCES
+REFERENCING NEW AS NEW FOR EACH ROW MODE DB2SQL
+ BEGIN ATOMIC
+ SET (NEW.ID) = (NEXTVAL FOR IDN_RULE_REFERENCES_SEQ);
+ END
+/
-- --------------------------- INDEX CREATION -----------------------------
-- IDN_OAUTH2_ACCESS_TOKEN --
@@ -2284,3 +2321,9 @@ CREATE INDEX IDX_IDN_CERTIFICATE_ID_TID ON IDN_CERTIFICATE (ID, TENANT_ID)
/
CREATE INDEX IDX_IDN_CERTIFICATE_UUID_TID ON IDN_CERTIFICATE (UUID, TENANT_ID)
/
+
+-- RULES --
+CREATE INDEX IDX_IDN_RULE_UUID_TID ON IDN_RULE (UUID, TENANT_ID)
+/
+CREATE INDEX IDX_IDN_RULE_REF_RID_TID ON IDN_RULE_REFERENCES (RULE_ID, TENANT_ID)
+/
diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/h2.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/h2.sql
index 437ba45ddbef..56f35ea09d30 100644
--- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/h2.sql
+++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/h2.sql
@@ -1389,6 +1389,27 @@ CREATE TABLE IF NOT EXISTS IDN_OAUTH2_TOKEN_CLAIMS (
FOREIGN KEY (APP_ID) REFERENCES IDN_OAUTH_CONSUMER_APPS(ID) ON DELETE CASCADE
);
+CREATE TABLE IF NOT EXISTS IDN_RULE (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ UUID CHAR(36) NOT NULL,
+ CONTENT BLOB NOT NULL,
+ IS_ACTIVE BOOLEAN DEFAULT TRUE,
+ TENANT_ID INTEGER NOT NULL,
+ VERSION VARCHAR(15) NOT NULL,
+ PRIMARY KEY (ID),
+ UNIQUE (UUID)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_RULE_REFERENCES (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ RULE_ID INTEGER NOT NULL,
+ FIELD_NAME VARCHAR(100) NOT NULL,
+ FIELD_REFERENCE VARCHAR(255) NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ PRIMARY KEY (ID),
+ FOREIGN KEY (RULE_ID) REFERENCES IDN_RULE(ID) ON DELETE CASCADE
+);
+
-- --------------------------- INDEX CREATION -----------------------------
-- IDN_OAUTH2_ACCESS_TOKEN --
CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED);
@@ -1501,3 +1522,7 @@ CREATE INDEX IDX_IDN_ACTION_PROPERTIES_AU_TI ON IDN_ACTION_PROPERTIES (ACTION_UU
-- CERTIFICATE --
CREATE INDEX IDX_IDN_CERTIFICATE_ID_TID ON IDN_CERTIFICATE (ID, TENANT_ID);
CREATE INDEX IDX_IDN_CERTIFICATE_UUID_TID ON IDN_CERTIFICATE (UUID, TENANT_ID);
+
+-- RULES --
+CREATE INDEX IDX_IDN_RULE_UUID_TID ON IDN_RULE (UUID, TENANT_ID);
+CREATE INDEX IDX_IDN_RULE_REF_RID_TID ON IDN_RULE_REFERENCES (RULE_ID, TENANT_ID);
diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mssql.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mssql.sql
index 04f2181996a8..199d5d9d2404 100644
--- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mssql.sql
+++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mssql.sql
@@ -1539,6 +1539,29 @@ CREATE TABLE IDN_OAUTH2_TOKEN_CLAIMS (
FOREIGN KEY (APP_ID) REFERENCES IDN_OAUTH_CONSUMER_APPS(ID) ON DELETE CASCADE
);
+IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[IDN_RULE]') AND TYPE IN (N'U'))
+CREATE TABLE IDN_RULE (
+ ID INTEGER IDENTITY,
+ UUID CHAR(36) NOT NULL,
+ CONTENT VARBINARY(MAX) NOT NULL,
+ IS_ACTIVE BIT DEFAULT 1,
+ TENANT_ID INTEGER NOT NULL,
+ VERSION VARCHAR(15) NOT NULL,
+ PRIMARY KEY (ID),
+ UNIQUE (UUID)
+);
+
+IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[IDN_RULE_REFERENCES]') AND TYPE IN (N'U'))
+CREATE TABLE IDN_RULE_REFERENCES (
+ ID INTEGER IDENTITY,
+ RULE_ID INTEGER NOT NULL,
+ FIELD_NAME VARCHAR(100) NOT NULL,
+ FIELD_REFERENCE VARCHAR(255) NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ PRIMARY KEY (ID),
+ FOREIGN KEY (RULE_ID) REFERENCES IDN_RULE(ID) ON DELETE CASCADE
+);
+
-- --------------------------- INDEX CREATION -----------------------------
-- IDN_OAUTH2_ACCESS_TOKEN --
CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED);
@@ -1653,6 +1676,10 @@ CREATE INDEX IDX_IDN_ACTION_PROPERTIES_AU_TI ON IDN_ACTION_PROPERTIES (ACTION_UU
CREATE INDEX IDX_IDN_CERTIFICATE_ID_TID ON IDN_CERTIFICATE (ID, TENANT_ID);
CREATE INDEX IDX_IDN_CERTIFICATE_UUID_TID ON IDN_CERTIFICATE (UUID, TENANT_ID);
+-- RULES --
+CREATE INDEX IDX_IDN_RULE_UUID_TID ON IDN_RULE (UUID, TENANT_ID);
+CREATE INDEX IDX_IDN_RULE_REF_RID_TID ON IDN_RULE_REFERENCES (RULE_ID, TENANT_ID);
+
GO
-- Trigger IDN_CLAIM delete by dialect on IDN_CLAIM_DIALECT deletion --
diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql-cluster.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql-cluster.sql
index a08513b13e90..e53f3c0f542f 100644
--- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql-cluster.sql
+++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql-cluster.sql
@@ -1552,6 +1552,27 @@ CREATE TABLE IF NOT EXISTS IDN_OAUTH2_TOKEN_CLAIMS (
FOREIGN KEY (APP_ID) REFERENCES IDN_OAUTH_CONSUMER_APPS(ID) ON DELETE CASCADE
)ENGINE NDB;
+CREATE TABLE IF NOT EXISTS IDN_RULE (
+ ID INTEGER AUTO_INCREMENT,
+ UUID CHAR(36) NOT NULL,
+ CONTENT BLOB NOT NULL,
+ IS_ACTIVE BOOLEAN DEFAULT TRUE,
+ TENANT_ID INTEGER NOT NULL,
+ VERSION VARCHAR(15) NOT NULL,
+ PRIMARY KEY (ID),
+ UNIQUE (UUID)
+)ENGINE NDB;
+
+CREATE TABLE IF NOT EXISTS IDN_RULE_REFERENCES (
+ ID INTEGER AUTO_INCREMENT,
+ RULE_ID INTEGER NOT NULL,
+ FIELD_NAME VARCHAR(100) NOT NULL,
+ FIELD_REFERENCE VARCHAR(255) NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ PRIMARY KEY (ID),
+ FOREIGN KEY (RULE_ID) REFERENCES IDN_RULE(ID) ON DELETE CASCADE
+)ENGINE NDB;
+
-- --------------------------- INDEX CREATION -----------------------------
-- IDN_OAUTH2_ACCESS_TOKEN --
CREATE INDEX IDX_TC
@@ -1693,3 +1714,7 @@ CREATE INDEX IDX_IDN_ACTION_PROPERTIES_AU_TI ON IDN_ACTION_PROPERTIES (ACTION_UU
-- CERTIFICATE --
CREATE INDEX IDX_IDN_CERTIFICATE_ID_TID ON IDN_CERTIFICATE (ID, TENANT_ID);
CREATE INDEX IDX_IDN_CERTIFICATE_UUID_TID ON IDN_CERTIFICATE (UUID, TENANT_ID);
+
+-- RULES --
+CREATE INDEX IDX_IDN_RULE_UUID_TID ON IDN_RULE (UUID, TENANT_ID);
+CREATE INDEX IDX_IDN_RULE_REF_RID_TID ON IDN_RULE_REFERENCES (RULE_ID, TENANT_ID);
diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql.sql
index 34ec2b9e5918..6a1eb37a6492 100644
--- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql.sql
+++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql.sql
@@ -1420,6 +1420,27 @@ CREATE TABLE IF NOT EXISTS IDN_OAUTH2_TOKEN_CLAIMS (
FOREIGN KEY (APP_ID) REFERENCES IDN_OAUTH_CONSUMER_APPS(ID) ON DELETE CASCADE
)DEFAULT CHARACTER SET latin1 ENGINE INNODB;
+CREATE TABLE IF NOT EXISTS IDN_RULE (
+ ID INTEGER AUTO_INCREMENT,
+ UUID CHAR(36) NOT NULL,
+ CONTENT BLOB NOT NULL,
+ IS_ACTIVE BOOLEAN DEFAULT TRUE,
+ TENANT_ID INTEGER NOT NULL,
+ VERSION VARCHAR(15) NOT NULL,
+ PRIMARY KEY (ID),
+ UNIQUE (UUID)
+)DEFAULT CHARACTER SET latin1 ENGINE INNODB;
+
+CREATE TABLE IF NOT EXISTS IDN_RULE_REFERENCES (
+ ID INTEGER AUTO_INCREMENT,
+ RULE_ID INTEGER NOT NULL,
+ FIELD_NAME VARCHAR(100) NOT NULL,
+ FIELD_REFERENCE VARCHAR(255) NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ PRIMARY KEY (ID),
+ FOREIGN KEY (RULE_ID) REFERENCES IDN_RULE(ID) ON DELETE CASCADE
+)DEFAULT CHARACTER SET latin1 ENGINE INNODB;
+
-- --------------------------- INDEX CREATION -----------------------------
-- IDN_OAUTH2_ACCESS_TOKEN --
CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED);
@@ -1529,3 +1550,7 @@ CREATE INDEX IDX_IDN_ACTION_PROPERTIES_AU_TI ON IDN_ACTION_PROPERTIES (ACTION_UU
-- CERTIFICATE --
CREATE INDEX IDX_IDN_CERTIFICATE_ID_TID ON IDN_CERTIFICATE (ID, TENANT_ID);
CREATE INDEX IDX_IDN_CERTIFICATE_UUID_TID ON IDN_CERTIFICATE (UUID, TENANT_ID);
+
+-- RULES --
+CREATE INDEX IDX_IDN_RULE_UUID_TID ON IDN_RULE (UUID, TENANT_ID);
+CREATE INDEX IDX_IDN_RULE_REF_RID_TID ON IDN_RULE_REFERENCES (RULE_ID, TENANT_ID);
diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle.sql
index 5c94891b5a4d..9e127808dceb 100644
--- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle.sql
+++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle.sql
@@ -2184,6 +2184,45 @@ CREATE OR REPLACE TRIGGER IDN_OAUTH2_TOKEN_CLAIMS_TRIG
SELECT IDN_OAUTH2_TOKEN_CLAIMS_SEQ.nextval INTO :NEW.ID FROM dual;
END;
/
+CREATE TABLE IDN_RULE (
+ ID INTEGER,
+ UUID CHAR(36) NOT NULL,
+ CONTENT BLOB NOT NULL,
+ IS_ACTIVE CHAR (1) DEFAULT '1',
+ TENANT_ID INTEGER NOT NULL,
+ VERSION VARCHAR(15) NOT NULL,
+ PRIMARY KEY (ID),
+ UNIQUE (UUID)
+)
+/
+CREATE SEQUENCE IDN_RULE_SEQ START WITH 1 INCREMENT BY 1 NOCACHE
+/
+CREATE OR REPLACE TRIGGER IDN_RULE_TRIGGER
+ BEFORE INSERT ON IDN_RULE
+ REFERENCING NEW AS NEW FOR EACH ROW
+ BEGIN
+ SELECT IDN_RULE_SEQ.nextval INTO :NEW.ID FROM dual;
+ END;
+/
+CREATE TABLE IDN_RULE_REFERENCES (
+ ID INTEGER,
+ RULE_ID INTEGER NOT NULL,
+ FIELD_NAME VARCHAR(100) NOT NULL,
+ FIELD_REFERENCE VARCHAR(255) NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ PRIMARY KEY (ID),
+ FOREIGN KEY (RULE_ID) REFERENCES IDN_RULE(ID) ON DELETE CASCADE
+)
+/
+CREATE SEQUENCE IDN_RULE_REFERENCES_SEQ START WITH 1 INCREMENT BY 1 NOCACHE
+/
+CREATE OR REPLACE TRIGGER IDN_RULE_REFERENCES_TRIGGER
+ BEFORE INSERT ON IDN_RULE_REFERENCES
+ REFERENCING NEW AS NEW FOR EACH ROW
+ BEGIN
+ SELECT IDN_RULE_REFERENCES_SEQ.nextval INTO :NEW.ID FROM dual;
+ END;
+/
-- --------------------------- INDEX CREATION -----------------------------
-- IDN_OAUTH2_ACCESS_TOKEN --
@@ -2341,3 +2380,9 @@ CREATE INDEX IDX_IDN_CERTIFICATE_ID_TID ON IDN_CERTIFICATE (ID, TENANT_ID)
/
CREATE INDEX IDX_IDN_CERTIFICATE_UUID_TID ON IDN_CERTIFICATE (UUID, TENANT_ID)
/
+
+-- RULES --
+CREATE INDEX IDX_IDN_RULE_UUID_TID ON IDN_RULE (UUID, TENANT_ID)
+/
+CREATE INDEX IDX_IDN_RULE_REF_RID_TID ON IDN_RULE_REFERENCES (RULE_ID, TENANT_ID)
+/
diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle_rac.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle_rac.sql
index d8b1eb15c5a8..9b90807829d4 100644
--- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle_rac.sql
+++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle_rac.sql
@@ -2117,7 +2117,45 @@ CREATE OR REPLACE TRIGGER IDN_OAUTH2_TOKEN_CLAIMS_TRIG
SELECT IDN_OAUTH2_TOKEN_CLAIMS_SEQ.nextval INTO :NEW.ID FROM dual;
END;
/
-
+CREATE TABLE IDN_RULE (
+ ID INTEGER,
+ UUID CHAR(36) NOT NULL,
+ CONTENT BLOB NOT NULL,
+ IS_ACTIVE CHAR (1) DEFAULT '1',
+ TENANT_ID INTEGER NOT NULL,
+ VERSION VARCHAR(15) NOT NULL,
+ PRIMARY KEY (ID),
+ UNIQUE (UUID)
+)
+/
+CREATE SEQUENCE IDN_RULE_SEQ START WITH 1 INCREMENT BY 1 NOCACHE
+/
+CREATE OR REPLACE TRIGGER IDN_RULE_TRIGGER
+ BEFORE INSERT ON IDN_RULE
+ REFERENCING NEW AS NEW FOR EACH ROW
+ BEGIN
+ SELECT IDN_RULE_SEQ.nextval INTO :NEW.ID FROM dual;
+ END;
+/
+CREATE TABLE IDN_RULE_REFERENCES (
+ ID INTEGER,
+ RULE_ID INTEGER NOT NULL,
+ FIELD_NAME VARCHAR(100) NOT NULL,
+ FIELD_REFERENCE VARCHAR(255) NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ PRIMARY KEY (ID),
+ FOREIGN KEY (RULE_ID) REFERENCES IDN_RULE(ID) ON DELETE CASCADE
+)
+/
+CREATE SEQUENCE IDN_RULE_REFERENCES_SEQ START WITH 1 INCREMENT BY 1 NOCACHE
+/
+CREATE OR REPLACE TRIGGER IDN_RULE_REFERENCES_TRIGGER
+ BEFORE INSERT ON IDN_RULE_REFERENCES
+ REFERENCING NEW AS NEW FOR EACH ROW
+ BEGIN
+ SELECT IDN_RULE_REFERENCES_SEQ.nextval INTO :NEW.ID FROM dual;
+ END;
+/
-- --------------------------- INDEX CREATION -----------------------------
-- IDN_OAUTH2_ACCESS_TOKEN --
CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED)
@@ -2246,3 +2284,9 @@ CREATE INDEX IDX_IDN_CERTIFICATE_ID_TID ON IDN_CERTIFICATE (ID, TENANT_ID)
/
CREATE INDEX IDX_IDN_CERTIFICATE_UUID_TID ON IDN_CERTIFICATE (UUID, TENANT_ID)
/
+
+-- RULES --
+CREATE INDEX IDX_IDN_RULE_UUID_TID ON IDN_RULE (UUID, TENANT_ID)
+/
+CREATE INDEX IDX_IDN_RULE_REF_RID_TID ON IDN_RULE_REFERENCES (RULE_ID, TENANT_ID)
+/
diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/postgresql.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/postgresql.sql
index 30fc69db8e60..185115350c3b 100644
--- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/postgresql.sql
+++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/postgresql.sql
@@ -1661,6 +1661,33 @@ CREATE TABLE IDN_OAUTH2_TOKEN_CLAIMS (
FOREIGN KEY (APP_ID) REFERENCES IDN_OAUTH_CONSUMER_APPS(ID) ON DELETE CASCADE
);
+DROP TABLE IF EXISTS IDN_RULE;
+DROP SEQUENCE IF EXISTS IDN_RULE_SEQ;
+CREATE SEQUENCE IDN_RULE_SEQ;
+CREATE TABLE IDN_RULE (
+ ID INTEGER NOT NULL DEFAULT NEXTVAL('IDN_RULE_SEQ'),
+ UUID CHAR(36) NOT NULL,
+ CONTENT BYTEA NOT NULL,
+ IS_ACTIVE BOOLEAN DEFAULT TRUE,
+ TENANT_ID INTEGER NOT NULL,
+ VERSION VARCHAR(15) NOT NULL,
+ PRIMARY KEY (ID)
+ UNIQUE (UUID)
+);
+
+DROP TABLE IF EXISTS IDN_RULE_REFERENCES;
+DROP SEQUENCE IF EXISTS IDN_RULE_REFERENCES_SEQ;
+CREATE SEQUENCE IDN_RULE_REFERENCES_SEQ;
+CREATE TABLE IDN_RULE_REFERENCES (
+ ID INTEGER NOT NULL DEFAULT NEXTVAL('IDN_RULE_REFERENCES_SEQ'),
+ RULE_ID INTEGER NOT NULL,
+ FIELD_NAME VARCHAR(100) NOT NULL,
+ FIELD_REFERENCE VARCHAR(255) NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ PRIMARY KEY (ID),
+ FOREIGN KEY (RULE_ID) REFERENCES IDN_RULE(ID) ON DELETE CASCADE
+);
+
-- --------------------------- INDEX CREATION -----------------------------
-- IDN_OAUTH2_ACCESS_TOKEN --
CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED);
@@ -1776,3 +1803,7 @@ CREATE INDEX IDX_IDN_ACTION_PROPERTIES_AU_TI ON IDN_ACTION_PROPERTIES (ACTION_UU
-- CERTIFICATE --
CREATE INDEX IDX_IDN_CERTIFICATE_ID_TID ON IDN_CERTIFICATE (ID, TENANT_ID);
CREATE INDEX IDX_IDN_CERTIFICATE_UUID_TID ON IDN_CERTIFICATE (UUID, TENANT_ID);
+
+-- RULES --
+CREATE INDEX IDX_IDN_RULE_UUID_TID ON IDN_RULE (UUID, TENANT_ID);
+CREATE INDEX IDX_IDN_RULE_REF_RID_TID ON IDN_RULE_REFERENCES (RULE_ID, TENANT_ID);
diff --git a/pom.xml b/pom.xml
index a50c576aeced..ac9ebe7063e1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -335,6 +335,11 @@
org.wso2.carbon.identity.rule.metadata
${project.version}
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.rule.management
+ ${project.version}
+
org.wso2.carbon.identity.framework
@@ -1843,7 +1848,7 @@
4.7.39
[4.7.2, 5.0.0)
- 2.1.7
+ 2.2.2
[2.0.0,3.0.0)