diff --git a/modules/api-resources/api-resources-full/pom.xml b/modules/api-resources/api-resources-full/pom.xml
index 34680fe753..4b27440339 100644
--- a/modules/api-resources/api-resources-full/pom.xml
+++ b/modules/api-resources/api-resources-full/pom.xml
@@ -500,5 +500,13 @@
org.wso2.carbon.identity.server.api
org.wso2.carbon.identity.api.server.action.management.common
+
+ org.wso2.carbon.identity.server.api
+ org.wso2.carbon.identity.api.server.rule.metadata.v1
+
+
+ org.wso2.carbon.identity.server.api
+ org.wso2.carbon.identity.api.server.rule.metadata.common
+
diff --git a/modules/api-resources/api-resources-full/src/main/webapp/WEB-INF/beans.xml b/modules/api-resources/api-resources-full/src/main/webapp/WEB-INF/beans.xml
index 476a6c5b88..3fa00cc159 100644
--- a/modules/api-resources/api-resources-full/src/main/webapp/WEB-INF/beans.xml
+++ b/modules/api-resources/api-resources-full/src/main/webapp/WEB-INF/beans.xml
@@ -80,6 +80,7 @@
+
@@ -147,6 +148,7 @@
+
diff --git a/modules/api-resources/pom.xml b/modules/api-resources/pom.xml
index c24179881d..6a2fac4e2e 100644
--- a/modules/api-resources/pom.xml
+++ b/modules/api-resources/pom.xml
@@ -515,6 +515,16 @@
org.wso2.carbon.identity.api.server.action.management.common
${identity.server.api.version}
+
+ org.wso2.carbon.identity.server.api
+ org.wso2.carbon.identity.api.server.rule.metadata.v1
+ ${identity.server.api.version}
+
+
+ org.wso2.carbon.identity.server.api
+ org.wso2.carbon.identity.api.server.rule.metadata.common
+ ${identity.server.api.version}
+
diff --git a/modules/distribution/src/assembly/bin.xml b/modules/distribution/src/assembly/bin.xml
index a09594ff09..3edd2fc81f 100644
--- a/modules/distribution/src/assembly/bin.xml
+++ b/modules/distribution/src/assembly/bin.xml
@@ -181,6 +181,17 @@
**/
+
+
+
+ ../p2-profile-gen/target/wso2carbon-core-${carbon.kernel.version}/repository/resources/identity/rulemeta
+
+ wso2is-${pom.version}/repository/resources/identity/rulemeta
+
+ **/*.json
+
+
+
src/repository/resources/conf/templates
wso2is-${pom.version}/repository/resources/conf/templates
diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/rules/metadata/v1/RulesMetadataFailureTest.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/rules/metadata/v1/RulesMetadataFailureTest.java
new file mode 100644
index 0000000000..13219d2fa8
--- /dev/null
+++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/rules/metadata/v1/RulesMetadataFailureTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.identity.integration.test.rest.api.server.rules.metadata.v1;
+
+import io.restassured.RestAssured;
+import io.restassured.response.Response;
+import org.apache.http.HttpStatus;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Factory;
+import org.testng.annotations.Test;
+import org.wso2.carbon.automation.engine.context.TestUserMode;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+
+public class RulesMetadataFailureTest extends RulesMetadataTestBase {
+
+ @DataProvider(name = "testExecutionContextProvider")
+ public static Object[][] getTestExecutionContext() {
+
+ return new Object[][]{
+ {TestUserMode.SUPER_TENANT_ADMIN},
+ {TestUserMode.TENANT_ADMIN}
+ };
+ }
+
+ @Factory(dataProvider = "testExecutionContextProvider")
+ public RulesMetadataFailureTest(TestUserMode userMode) throws Exception {
+
+ super.init(userMode);
+ this.context = isServer;
+ this.authenticatingUserName = context.getContextTenant().getTenantAdmin().getUserName();
+ this.authenticatingCredential = context.getContextTenant().getTenantAdmin().getPassword();
+ this.tenant = context.getContextTenant().getDomain();
+ }
+
+ @BeforeClass(alwaysRun = true)
+ public void init() throws Exception {
+
+ super.initTestClass(tenant);
+ }
+
+ @AfterClass(alwaysRun = true)
+ public void conclude() {
+
+ super.conclude();
+ }
+
+ @BeforeMethod(alwaysRun = true)
+ public void testInit() {
+
+ RestAssured.basePath = basePath;
+ }
+
+ @Test
+ public void testGetRuleMetadataForNotImplementedFlow() throws Exception {
+
+ Response responseOfGet = getResponseOfGet(getAPIRequestForFlow("preLogin"));
+ responseOfGet.then()
+ .log().ifValidationFails()
+ .assertThat()
+ .statusCode(HttpStatus.SC_BAD_REQUEST)
+ .body("code", equalTo("RULEMETA-60001"))
+ .body("message", equalTo("Invalid flow."))
+ .body("description", equalTo("Provided flow type is invalid or not implemented."));
+ }
+}
diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/rules/metadata/v1/RulesMetadataSuccessTest.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/rules/metadata/v1/RulesMetadataSuccessTest.java
new file mode 100644
index 0000000000..88633f57f7
--- /dev/null
+++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/rules/metadata/v1/RulesMetadataSuccessTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.identity.integration.test.rest.api.server.rules.metadata.v1;
+
+import io.restassured.RestAssured;
+import io.restassured.response.Response;
+import org.apache.http.HttpStatus;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Factory;
+import org.testng.annotations.Test;
+import org.wso2.carbon.automation.engine.context.TestUserMode;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.Matchers.hasItems;
+
+public class RulesMetadataSuccessTest extends RulesMetadataTestBase {
+
+ @DataProvider(name = "testExecutionContextProvider")
+ public static Object[][] getTestExecutionContext() {
+
+ return new Object[][]{
+ {TestUserMode.SUPER_TENANT_ADMIN},
+ {TestUserMode.TENANT_ADMIN}
+ };
+ }
+
+ @Factory(dataProvider = "testExecutionContextProvider")
+ public RulesMetadataSuccessTest(TestUserMode userMode) throws Exception {
+
+ super.init(userMode);
+ this.context = isServer;
+ this.authenticatingUserName = context.getContextTenant().getTenantAdmin().getUserName();
+ this.authenticatingCredential = context.getContextTenant().getTenantAdmin().getPassword();
+ this.tenant = context.getContextTenant().getDomain();
+ }
+
+ @BeforeClass(alwaysRun = true)
+ public void init() throws Exception {
+
+ super.initTestClass(tenant);
+ }
+
+ @AfterClass(alwaysRun = true)
+ public void conclude() {
+
+ super.conclude();
+ }
+
+ @BeforeMethod(alwaysRun = true)
+ public void testInit() {
+
+ RestAssured.basePath = basePath;
+ }
+
+ @DataProvider(name = "flowProvider")
+ public static Object[][] getFlows() {
+
+ return new Object[][]{
+ {"preIssueAccessToken"},
+ };
+ }
+
+ @Test(dataProvider = "flowProvider")
+ public void testGetRuleMetadata(String flow) throws Exception {
+
+ Response responseOfGet = getResponseOfGet(getAPIRequestForValidFlow(flow));
+ validateResponse(flow, responseOfGet);
+ }
+
+ private static void validateResponse(String flow, Response response) {
+
+ if (flow.equals("preIssueAccessToken")) {
+ validateResponseForPreIssueAccessTokenFlow(response);
+ } else {
+ throw new IllegalArgumentException("Invalid flow: " + flow);
+ }
+ }
+
+ private static void validateResponseForPreIssueAccessTokenFlow(Response response) {
+
+ response.then()
+ .log().ifValidationFails()
+ .assertThat()
+ .statusCode(HttpStatus.SC_OK)
+ .body("[0].field.name", equalTo("application"))
+ .body("[0].field.displayName", equalTo("application"))
+ .body("[0].operators.name", hasItems("equals", "notEquals"))
+ .body("[0].operators.displayName", hasItems("equals", "not equals"))
+ .body("[0].value.inputType", equalTo("options"))
+ .body("[0].value.valueType", equalTo("reference"))
+ .body("[0].value.valueReferenceAttribute", equalTo("id"))
+ .body("[0].value.valueDisplayAttribute", equalTo("name"))
+ .body("[0].value.links.href",
+ hasItems("/applications?offset=0&limit=10", "/applications?filter=&limit=10"))
+ .body("[0].value.links.method", hasItems("GET"))
+ .body("[0].value.links.rel", hasItems("values", "filter"))
+ .body("[1].field.name", equalTo("grantType"))
+ .body("[1].field.displayName", equalTo("grant type"))
+ .body("[1].operators.name", hasItems("equals", "notEquals"))
+ .body("[1].operators.displayName", hasItems("equals", "not equals"))
+ .body("[1].value.inputType", equalTo("options"))
+ .body("[1].value.valueType", equalTo("string"))
+ .body("[1].value.values.name",
+ hasItems("authorization_code", "password", "refresh_token", "client_credentials"))
+ .body("[1].value.values.displayName",
+ hasItems("authorization code", "password", "refresh token", "client credentials"));
+ }
+}
diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/rules/metadata/v1/RulesMetadataTestBase.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/rules/metadata/v1/RulesMetadataTestBase.java
new file mode 100644
index 0000000000..6dad1077fd
--- /dev/null
+++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/rules/metadata/v1/RulesMetadataTestBase.java
@@ -0,0 +1,70 @@
+/*
+ * 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.identity.integration.test.rest.api.server.rules.metadata.v1;
+
+import org.testng.Assert;
+import org.wso2.identity.integration.test.rest.api.server.common.RESTAPIServerTestBase;
+
+import java.io.IOException;
+
+public class RulesMetadataTestBase extends RESTAPIServerTestBase {
+
+ private static final String API_DEFINITION_NAME = "rule-metadata.yaml";
+ private static final String API_VERSION = "v1";
+
+ private static String swaggerDefinition;
+
+ static {
+ String API_PACKAGE_NAME = "org.wso2.carbon.identity.api.server.rule.metadata.v1";
+ try {
+ swaggerDefinition = getAPISwaggerDefinition(API_PACKAGE_NAME, API_DEFINITION_NAME);
+ } catch (IOException e) {
+ Assert.fail(String.format("Unable to read the swagger definition %s from %s", API_DEFINITION_NAME,
+ API_PACKAGE_NAME), e);
+ }
+ }
+
+ protected void initTestClass(String tenantDomain) throws IOException {
+
+ super.testInit(API_VERSION, swaggerDefinition, tenantDomain);
+ }
+
+ protected String getAPIRequestForValidFlow(String flow) {
+
+ validateFlow(flow);
+ return getAPIEndpoint() + "?flow=" + flow;
+ }
+
+ protected String getAPIRequestForFlow(String flow) {
+
+ return getAPIEndpoint() + "?flow=" + flow;
+ }
+
+ protected String getAPIEndpoint() {
+
+ return "/rules/metadata";
+ }
+
+ private void validateFlow(String flow) {
+
+ if (!"preIssueAccessToken".equals(flow)) {
+ throw new IllegalArgumentException("Invalid flow: " + flow);
+ }
+ }
+}
diff --git a/modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml b/modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml
index 4e236d6586..630b531007 100644
--- a/modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml
+++ b/modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml
@@ -240,6 +240,8 @@
+
+
diff --git a/modules/p2-profile-gen/pom.xml b/modules/p2-profile-gen/pom.xml
index 544ad99569..caa6a56ac8 100644
--- a/modules/p2-profile-gen/pom.xml
+++ b/modules/p2-profile-gen/pom.xml
@@ -362,6 +362,11 @@
org.wso2.carbon.identity.framework:org.wso2.carbon.identity.certificate.management.server.feature:${carbon.identity.framework.version}
+
+
+ org.wso2.carbon.identity.framework:org.wso2.carbon.identity.rule.management.server.feature:${carbon.identity.framework.version}
+
+
org.wso2.carbon.identity.tool.validator.sso.saml2:org.wso2.carbon.identity.tools.saml.validator.feature:${identity.tool.samlsso.validator.version}
@@ -856,6 +861,10 @@
org.wso2.carbon.identity.certificate.management.server.feature.group
${carbon.identity.framework.version}
+
+ org.wso2.carbon.identity.rule.management.server.feature.group
+ ${carbon.identity.framework.version}
+
org.wso2.carbon.identity.unique.claim.mgt.server.feature.group