diff --git a/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/main/java/org/wso2/carbon/identity/central/log/mgt/utils/LogConstants.java b/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/main/java/org/wso2/carbon/identity/central/log/mgt/utils/LogConstants.java index 5fa200d58289..d20006f00a31 100644 --- a/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/main/java/org/wso2/carbon/identity/central/log/mgt/utils/LogConstants.java +++ b/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/main/java/org/wso2/carbon/identity/central/log/mgt/utils/LogConstants.java @@ -161,4 +161,16 @@ public static class UserManagement { public static final String DELETED_PERMISSIONS_FIELD = "DeletedPermissions"; public static final String DELETED_IDP_GROUPS_FIELD = "DeletedIdpGroups"; } + + /** + * Registration flow management related log constants. + */ + public static class RegistrationFlowManagement { + + public static final String UPDATE_REGISTRATION_FLOW = "update-registration-flow"; + + private RegistrationFlowManagement() { + + } + } } diff --git a/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/main/java/org/wso2/carbon/identity/central/log/mgt/utils/LoggerUtils.java b/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/main/java/org/wso2/carbon/identity/central/log/mgt/utils/LoggerUtils.java index fb468e28c256..ab8d99d86958 100644 --- a/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/main/java/org/wso2/carbon/identity/central/log/mgt/utils/LoggerUtils.java +++ b/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/main/java/org/wso2/carbon/identity/central/log/mgt/utils/LoggerUtils.java @@ -80,7 +80,7 @@ public enum Initiator { * Defines the Targets of the logs. */ public enum Target { - User, Role, Group, Application, Action + User, Role, Group, Application, Action, Flow } /** diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/pom.xml b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/pom.xml new file mode 100644 index 000000000000..ab2fdf54a4dd --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/pom.xml @@ -0,0 +1,165 @@ + + + + + + org.wso2.carbon.identity.framework + identity-framework + 7.8.28-SNAPSHOT + ../../../pom.xml + + + 4.0.0 + org.wso2.carbon.identity.user.registration.mgt + bundle + WSO2 Carbon - Identity User Self Registration Flow Manager + WSO2 user self registration flow management + http://www.wso2.com + + + + com.fasterxml.jackson.core + jackson-databind + provided + + + com.fasterxml.jackson.core + jackson-core + provided + + + org.testng + testng + test + + + org.ops4j.pax.logging + pax-logging-api + + + org.wso2.carbon.utils + org.wso2.carbon.database.utils + + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.core + + + org.slf4j + slf4j-api + + + org.slf4j + jcl-over-slf4j + + + org.slf4j + log4j-over-slf4j + + + + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.central.log.mgt + + + org.wso2.carbon + org.wso2.carbon.utils + + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.testutil + test + + + org.mockito + mockito-core + test + + + com.h2database + h2 + test + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 8 + 8 + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + + + + org.codehaus.mojo + findbugs-maven-plugin + + true + + + + org.apache.felix + maven-bundle-plugin + true + + + ${project.artifactId} + ${project.artifactId} + + org.wso2.carbon.identity.user.registration.mgt.internal + + + javax.xml.parsers; version="${javax.xml.parsers.import.pkg.version}", + org.xml.sax, + org.w3c.dom, + org.osgi.framework; version="${osgi.framework.imp.pkg.version.range}", + org.apache.xerces.util.*; resolution:=optional, + org.apache.commons.logging; version="${import.package.version.commons.logging}", + 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.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.util; + version="${carbon.identity.package.import.version.range}", + org.wso2.carbon.identity.central.log.mgt.utils; + version="${carbon.identity.package.import.version.range}", + javax.servlet.http; version="${imp.pkg.version.javax.servlet}" + + + !org.wso2.carbon.identity.user.registration.mgt.internal, + org.wso2.carbon.identity.user.registration.mgt.*; + version="${carbon.identity.package.export.version}" + + + + + + + diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/Constants.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/Constants.java new file mode 100644 index 000000000000..4ff88efda771 --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/Constants.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * 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.user.registration.mgt; + +/** + * Constants for the self registration flow. + */ +public class Constants { + + public static final String COMPLETE = "COMPLETE"; + public static final String DEFAULT_FLOW_NAME = "defaultFlow"; + + private Constants() { + + } + + public enum ErrorMessages { + + // Server errors. + ERROR_CODE_ADD_DEFAULT_FLOW("65001", "Error while updating the flow.", + "Unexpected server error while updating the default flow in tenant, %s"), + + ERROR_CODE_GET_DEFAULT_FLOW("65002", "Error while retrieving the flow.", + "Unexpected server error while retrieving the default flow from database in " + + "tenant, %s"), + + ERROR_CODE_SERIALIZE_PAGE_CONTENT("65003", "Error while serializing the page content.", + "Unexpected server error while serializing the page content for step %s" + + " in tenant, %s"), + ERROR_CODE_INVALID_NODE("65004", "Node id not found.", "Could not resolve a valid node with id, %s to create " + + "a link."), + ERROR_CODE_DESERIALIZE_PAGE_CONTENT("65005", "Error while deserializing the page content.", + "Unexpected server error while deserializing the page content for step %s" + + " in tenant, %s"), + ERROR_CODE_GET_REG_GRAPH_FAILED("65006", "Error while retrieving the registration graph.", + "Unexpected server error while retrieving the registration graph for tenant, " + + "%s"), + + // Client errors. + ERROR_CODE_UNSUPPORTED_STEP_TYPE("60001", "Unsupported step type.", + "The step type, %s is not supported."), + ERROR_CODE_STEP_DATA_NOT_FOUND("60002", "Step data not found.", + "The step data for step, %s is not found."), + ERROR_CODE_COMPONENT_DATA_NOT_FOUND("60003", "Component data not found.", + "The step, %s of type view must have components defined."), + ERROR_CODE_INVALID_ACTION_FOR_BUTTON("60004", "Invalid action configuration.", + "The component, %s of type button must have an action defined."), + ERROR_CODE_NEXT_ACTION_NOT_FOUND("60005", "Invalid action configuration.", + "Next step is not defined for the action in component, %s."), + ERROR_CODE_EXECUTOR_INFO_NOT_FOUND("60006", "Invalid action configuration.", + "Executor data is not defined for the action type EXECUTOR in component, " + + "%s."), + ERROR_CODE_MULTIPLE_STEP_EXECUTORS("60007", "Multiple executors defined for the step.", + "Multiple executors are defined for the step, %s."), + ERROR_CODE_UNSUPPORTED_ACTION_TYPE("60008", "Unsupported action type.", + "The action type, %s defined for component %s is not supported."), + ERROR_CODE_INVALID_NEXT_STEP("600009", "Invalid next step configuration.", "Cannot resolve a step for the " + + "next id, %s."), + ERROR_CODE_ACTION_DATA_NOT_FOUND("60010", "Action data not found.", + "The step, %s of type redirection must have action defined"), + ERROR_CODE_INVALID_ACTION_TYPE("60011", "Invalid action type.", + "The action type, %s is not supported for step, %s of type redirection."), + ; + + private static final String ERROR_PREFIX = "RFM"; + private final String code; + private final String message; + private final String description; + + ErrorMessages(String code, String message, String description) { + + this.code = ERROR_PREFIX + "-" + code; + this.message = message; + this.description = description; + } + + public String getCode() { + + return code; + } + + public String getMessage() { + + return message; + } + + public String getDescription() { + + return description; + } + + @Override + public String toString() { + + return code + ":" + message; + } + } + + /** + * Constants for the node types. + */ + public static class NodeTypes { + + public static final String DECISION = "DECISION"; + public static final String TASK_EXECUTION = "TASK_EXECUTION"; + public static final String PROMPT_ONLY = "PROMPT_ONLY"; + + private NodeTypes() { + + } + } + + /** + * Constants for the step types. + */ + public static class StepTypes { + + public static final String VIEW = "VIEW"; + public static final String REDIRECTION = "REDIRECTION"; + + private StepTypes() { + + } + } + + public static class ComponentTypes { + + public static final String FORM = "FORM"; + public static final String BUTTON = "BUTTON"; + + private ComponentTypes() { + + } + } + + /** + * Constants for the action types. + */ + public static class ActionTypes { + + public static final String EXECUTOR = "EXECUTOR"; + public static final String NEXT = "NEXT"; + + private ActionTypes() { + + } + } + + /** + * Constants for defined executor types. + */ + public static class ExecutorTypes { + + public static final String USER_ONBOARDING = "UserOnboardingExecutor"; + + private ExecutorTypes() { + + } + } +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/RegistrationFlowMgtService.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/RegistrationFlowMgtService.java new file mode 100644 index 000000000000..8595f7bb8fbd --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/RegistrationFlowMgtService.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * 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.user.registration.mgt; + +import static org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils.triggerAuditLogEvent; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.DEFAULT_FLOW_NAME; +import static org.wso2.carbon.identity.user.registration.mgt.utils.RegistrationMgtUtils.getInitiatorId; +import static org.wso2.carbon.identity.user.registration.mgt.utils.RegistrationMgtUtils.isEnableV2AuditLogs; +import org.wso2.carbon.identity.central.log.mgt.utils.LogConstants; +import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils; +import org.wso2.carbon.identity.user.registration.mgt.dao.RegistrationFlowDAO; +import org.wso2.carbon.identity.user.registration.mgt.dao.RegistrationFlowDAOImpl; +import org.wso2.carbon.identity.user.registration.mgt.exception.RegistrationFrameworkException; +import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationFlowDTO; +import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationGraphConfig; +import org.wso2.carbon.identity.user.registration.mgt.utils.GraphBuilder; +import org.wso2.carbon.utils.AuditLog; + +/** + * This class is responsible for managing the registration flow. + */ +public class RegistrationFlowMgtService { + + private static final RegistrationFlowMgtService instance = new RegistrationFlowMgtService(); + private static final RegistrationFlowDAO registrationFlowDAO = new RegistrationFlowDAOImpl(); + + private RegistrationFlowMgtService() { + + } + + public static RegistrationFlowMgtService getInstance() { + + return instance; + } + + /** + * Update the default registration flow of the given tenant. + * + * @param flowDTO The registration flow. + * @param tenantID The tenant ID. + */ + public void updateDefaultRegistrationFlow(RegistrationFlowDTO flowDTO, int tenantID) + throws RegistrationFrameworkException { + + RegistrationGraphConfig flowConfig = GraphBuilder.convert(flowDTO); + registrationFlowDAO.updateDefaultRegistrationFlowByTenant(flowConfig, tenantID, DEFAULT_FLOW_NAME); + if (isEnableV2AuditLogs()) { + AuditLog.AuditLogBuilder auditLogBuilder = + new AuditLog.AuditLogBuilder(getInitiatorId(), LoggerUtils.getInitiatorType(getInitiatorId()), + flowConfig.getId(), + LoggerUtils.Target.Flow.name(), + LogConstants.RegistrationFlowManagement.UPDATE_REGISTRATION_FLOW); + triggerAuditLogEvent(auditLogBuilder, true); + } + } + + /** + * Get the default registration flow of the given tenant. + * + * @param tenantID The tenant ID. + * @return The registration flow. + * @throws RegistrationFrameworkException If an error occurs while retrieving the default flow. + */ + public RegistrationFlowDTO getRegistrationFlow(int tenantID) throws RegistrationFrameworkException { + + return registrationFlowDAO.getDefaultRegistrationFlowByTenant(tenantID); + } + + /** + * Get the registration flow by tenant ID. + * + * @param tenantID The tenant ID. + */ + public RegistrationGraphConfig getRegistrationFlowConfig(int tenantID) { + + // TODO: Implement this method. + return null; + } +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/dao/RegistrationFlowDAO.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/dao/RegistrationFlowDAO.java new file mode 100644 index 000000000000..326cb4e98369 --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/dao/RegistrationFlowDAO.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * 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.user.registration.mgt.dao; + +import org.wso2.carbon.identity.user.registration.mgt.exception.RegistrationFrameworkException; +import org.wso2.carbon.identity.user.registration.mgt.exception.RegistrationServerException; +import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationFlowDTO; +import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationGraphConfig; + +/** + * DAO interface for registration flow management. + */ +public interface RegistrationFlowDAO { + + /** + * Update the default registration flow of the given tenant. + * + * @param regFlowConfig The registration flow. + * @param tenantId The tenant ID. + * @param flowName The flow name. + * @throws RegistrationFrameworkException If an error occurs while updating the default flow. + */ + void updateDefaultRegistrationFlowByTenant(RegistrationGraphConfig regFlowConfig, int tenantId, String flowName) + throws RegistrationFrameworkException; + + /** + * Get the default registration flow of the given tenant. + * + * @param tenantId The tenant ID. + * @return The registration flow. + * @throws RegistrationServerException If an error occurs while retrieving the default flow. + */ + RegistrationFlowDTO getDefaultRegistrationFlowByTenant(int tenantId) throws RegistrationServerException; + + /** + * Get the default registration graph of the given tenant. + * + * @param tenantId The tenant ID. + * @return The registration graph. + * @throws RegistrationServerException If an error occurs while retrieving the flow. + */ + RegistrationGraphConfig getDefaultRegistrationGraphByTenant(int tenantId) throws RegistrationFrameworkException; +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/dao/RegistrationFlowDAOImpl.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/dao/RegistrationFlowDAOImpl.java new file mode 100644 index 000000000000..ff1ba9bda278 --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/dao/RegistrationFlowDAOImpl.java @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * 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.user.registration.mgt.dao; + +import static org.wso2.carbon.identity.user.registration.mgt.Constants.StepTypes.REDIRECTION; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.StepTypes.VIEW; +import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.DELETE_FLOW; +import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.GET_FLOW; +import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.INSERT_FLOW_INTO_IDN_FLOW; +import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.INSERT_FLOW_NODE_INFO; +import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.INSERT_FLOW_PAGE_INFO; +import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.INSERT_FLOW_PAGE_META; +import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.INSERT_NODE_EDGES; +import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.INSERT_NODE_EXECUTOR_INFO; +import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.SQLPlaceholders.COORDINATE_X; +import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.SQLPlaceholders.COORDINATE_Y; +import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.SQLPlaceholders.HEIGHT; +import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.SQLPlaceholders.PAGE_CONTENT; +import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.SQLPlaceholders.PAGE_TYPE; +import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.SQLPlaceholders.REGISTRATION_FLOW; +import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.SQLPlaceholders.STEP_ID; +import static org.wso2.carbon.identity.user.registration.mgt.dao.SQLConstants.SQLPlaceholders.WIDTH; +import static org.wso2.carbon.identity.user.registration.mgt.utils.RegistrationMgtUtils.handleClientException; +import static org.wso2.carbon.identity.user.registration.mgt.utils.RegistrationMgtUtils.handleServerException; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.database.utils.jdbc.JdbcTemplate; +import org.wso2.carbon.database.utils.jdbc.exceptions.DataAccessException; +import org.wso2.carbon.database.utils.jdbc.exceptions.TransactionException; +import org.wso2.carbon.identity.core.util.JdbcUtils; +import org.wso2.carbon.identity.core.util.LambdaExceptionUtils; +import org.wso2.carbon.identity.user.registration.mgt.Constants; +import org.wso2.carbon.identity.user.registration.mgt.exception.RegistrationFrameworkException; +import org.wso2.carbon.identity.user.registration.mgt.exception.RegistrationServerException; +import org.wso2.carbon.identity.user.registration.mgt.model.ActionDTO; +import org.wso2.carbon.identity.user.registration.mgt.model.ComponentDTO; +import org.wso2.carbon.identity.user.registration.mgt.model.DataDTO; +import org.wso2.carbon.identity.user.registration.mgt.model.ExecutorDTO; +import org.wso2.carbon.identity.user.registration.mgt.model.NodeConfig; +import org.wso2.carbon.identity.user.registration.mgt.model.NodeEdge; +import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationFlowDTO; +import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationGraphConfig; +import org.wso2.carbon.identity.user.registration.mgt.model.StepDTO; + +/** + * The DAO class for the registration flow. + */ +public class RegistrationFlowDAOImpl implements RegistrationFlowDAO { + + private static final Log LOG = LogFactory.getLog(RegistrationFlowDAOImpl.class); + + private static byte[] serializeObject(Object obj) throws IOException { + + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos)) { + oos.writeObject(obj); + oos.flush(); + return baos.toByteArray(); + } + } + + + private static byte[] serializeStepData(StepDTO stepDTO, int tenantId) + throws RegistrationFrameworkException { + + try { + if (VIEW.equals(stepDTO.getType())) { + List components = stepDTO.getData().getComponents(); + return serializeObject(components); + } else if (REDIRECTION.equals(stepDTO.getType())) { + ActionDTO action = stepDTO.getData().getAction(); + return serializeObject(action); + } else { + throw handleClientException(Constants.ErrorMessages.ERROR_CODE_UNSUPPORTED_STEP_TYPE, + stepDTO.getType()); + } + } catch (IOException e) { + throw handleServerException(Constants.ErrorMessages.ERROR_CODE_SERIALIZE_PAGE_CONTENT, e, + stepDTO.getId(), tenantId); + } + } + + @Override + public void updateDefaultRegistrationFlowByTenant(RegistrationGraphConfig regFlowConfig, int tenantId, + String flowName) throws RegistrationFrameworkException { + + String flowId = regFlowConfig.getId(); + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + try { + jdbcTemplate.withTransaction(template -> { + // Delete the existing flow for the tenant. + template.executeUpdate(DELETE_FLOW, + preparedStatement -> { + preparedStatement.setInt(1, tenantId); + preparedStatement.setString(2, REGISTRATION_FLOW); + }); + + // Insert into IDN_FLOW. + template.executeInsert(INSERT_FLOW_INTO_IDN_FLOW, + preparedStatement -> { + preparedStatement.setString(1, flowId); + preparedStatement.setInt(2, tenantId); + preparedStatement.setString(3, flowName); + preparedStatement.setString(4, REGISTRATION_FLOW); + preparedStatement.setBoolean(5, true); + }, regFlowConfig, false); + + // Insert into IDN_FLOW_NODE. + Map nodeIdToRegNodeIdMap = new HashMap<>(); + for (Map.Entry entry : regFlowConfig.getNodeConfigs().entrySet()) { + NodeConfig node = entry.getValue(); + int regNodeId = template.executeInsert(INSERT_FLOW_NODE_INFO, + preparedStatement -> { + preparedStatement.setString(1, node.getId()); + preparedStatement.setString(2, flowId); + preparedStatement.setString(3, node.getType()); + preparedStatement.setBoolean(4, node.isFirstNode()); + }, entry, true); + + nodeIdToRegNodeIdMap.put(node.getId(), regNodeId); + + // Insert into IDN_FLOW_NODE_EXECUTOR. + ExecutorDTO executorConfig = node.getExecutorConfig(); + if (executorConfig != null) { + template.executeInsert(INSERT_NODE_EXECUTOR_INFO, + preparedStatement -> { + preparedStatement.setInt(1, + nodeIdToRegNodeIdMap.get(node.getId())); + preparedStatement.setString(2, executorConfig.getName()); + preparedStatement.setString(3, executorConfig.getIdpName()); + }, null, false); + } + } + // Insert graph edges into IDN_FLOW_NODE_MAPPING. + for (Map.Entry entry : regFlowConfig.getNodeConfigs().entrySet()) { + NodeConfig node = entry.getValue(); + if (node.getEdges() != null) { + for (NodeEdge edge : node.getEdges()) { + template.executeInsert(INSERT_NODE_EDGES, + preparedStatement -> { + preparedStatement.setInt(1, nodeIdToRegNodeIdMap.get( + edge.getSourceNodeId())); + preparedStatement.setInt(2, nodeIdToRegNodeIdMap.get( + edge.getTargetNodeId())); + preparedStatement.setString(3, edge.getTriggeringActionId()); + }, null, false); + } + } + } + + // Insert into IDN_FLOW_PAGE. + for (Map.Entry entry : regFlowConfig.getNodePageMappings().entrySet()) { + + StepDTO stepDTO = entry.getValue(); + byte[] pageContent = serializeStepData(stepDTO, tenantId); + int regNodeId = nodeIdToRegNodeIdMap.get(entry.getKey()); + + int pageAutoIncId = template.executeInsert(INSERT_FLOW_PAGE_INFO, + preparedStatement -> { + preparedStatement.setString(1, flowId); + preparedStatement.setInt(2, regNodeId); + preparedStatement.setString(3, stepDTO.getId()); + preparedStatement.setBinaryStream(4, + new ByteArrayInputStream( + pageContent)); + preparedStatement.setString(5, stepDTO.getType()); + }, entry, true); + + // Insert into IDN_FLOW_PAGE_META. + template.executeInsert(INSERT_FLOW_PAGE_META, + preparedStatement -> { + preparedStatement.setInt(1, pageAutoIncId); + preparedStatement.setDouble(2, stepDTO.getCoordinateX()); + preparedStatement.setDouble(3, stepDTO.getCoordinateY()); + preparedStatement.setDouble(4, stepDTO.getCoordinateX()); + preparedStatement.setDouble(5, stepDTO.getCoordinateY()); + }, null, false); + } + return null; + }); + } catch (TransactionException e) { + throw handleServerException(Constants.ErrorMessages.ERROR_CODE_ADD_DEFAULT_FLOW, e, tenantId); + } + } + + @Override + public RegistrationFlowDTO getDefaultRegistrationFlowByTenant(int tenantId) throws RegistrationServerException { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + + try { + List steps = jdbcTemplate + .executeQuery(GET_FLOW, (LambdaExceptionUtils.rethrowRowMapper((resultSet, rowNumber) -> { + StepDTO stepDTO = new StepDTO.Builder() + .id(resultSet.getString(STEP_ID)) + .type(resultSet.getString(PAGE_TYPE)) + .coordinateX(resultSet.getDouble(COORDINATE_X)) + .coordinateY(resultSet.getDouble(COORDINATE_Y)) + .height(resultSet.getDouble(HEIGHT)) + .width(resultSet.getDouble(WIDTH)) + .build(); + + resolvePageContent(stepDTO, resultSet.getBinaryStream(PAGE_CONTENT), tenantId); + return stepDTO; + })), preparedStatement -> preparedStatement.setInt(1, tenantId)); + + RegistrationFlowDTO registrationFlowDTO = new RegistrationFlowDTO(); + if (steps.isEmpty()) { + LOG.debug("No steps are found in the default flow of tenant " + tenantId); + return registrationFlowDTO; + } + registrationFlowDTO.getSteps().addAll(steps); + return registrationFlowDTO; + } catch (DataAccessException e) { + throw handleServerException(Constants.ErrorMessages.ERROR_CODE_GET_DEFAULT_FLOW, e, tenantId); + } + } + + private void resolvePageContent(StepDTO stepDTO, InputStream pageContent, int tenantId) + throws DataAccessException, RegistrationServerException { + + try (ObjectInputStream ois = new ObjectInputStream(pageContent)) { + Object obj = ois.readObject(); + if (VIEW.equals(stepDTO.getType()) && obj instanceof List) { + List tempList = (List) obj; + if (!tempList.isEmpty() && tempList.get(0) instanceof ComponentDTO) { + List components = tempList.stream() + .map(ComponentDTO.class::cast) + .collect(Collectors.toList()); + stepDTO.setData(new DataDTO.Builder().components(components).build()); + } else { + throw handleServerException(Constants.ErrorMessages.ERROR_CODE_DESERIALIZE_PAGE_CONTENT, + stepDTO.getId(), tenantId); + } + } else if (REDIRECTION.equals(stepDTO.getType())) { + if (obj instanceof ActionDTO) { + ActionDTO action = (ActionDTO) obj; + stepDTO.setData(new DataDTO.Builder().action(action).build()); + } + } + } catch (IOException | ClassNotFoundException e) { + throw handleServerException(Constants.ErrorMessages.ERROR_CODE_DESERIALIZE_PAGE_CONTENT, e, stepDTO.getId(), + tenantId); + } + } + + @Override + public RegistrationGraphConfig getDefaultRegistrationGraphByTenant(int tenantId) + throws RegistrationFrameworkException { + + return null; + } +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/dao/SQLConstants.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/dao/SQLConstants.java new file mode 100644 index 000000000000..f38fd038c772 --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/dao/SQLConstants.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * 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.user.registration.mgt.dao; + +/** + * This class holds the database queries and constants for the flow management DAO layer. + */ +public class SQLConstants { + + public static final String DELETE_FLOW = + "DELETE FROM IDN_FLOW WHERE TENANT_ID = ? AND IS_DEFAULT = TRUE AND TYPE = ?"; + public static final String INSERT_FLOW_INTO_IDN_FLOW = + "INSERT INTO IDN_FLOW (ID, TENANT_ID, FLOW_NAME, TYPE, IS_DEFAULT) VALUES (?, ?, ?, ?, ?)"; + public static final String INSERT_FLOW_NODE_INFO = + "INSERT INTO IDN_FLOW_NODE (NODE_ID, FLOW_ID, NODE_TYPE, IS_FIRST_NODE) VALUES (?, ?, ?, ?)"; + public static final String INSERT_NODE_EXECUTOR_INFO = + "INSERT INTO IDN_FLOW_NODE_EXECUTOR (FLOW_NODE_ID, EXECUTOR_NAME, IDP_NAME) VALUES (?, ?, ?)"; + public static final String INSERT_NODE_EDGES = + "INSERT INTO IDN_FLOW_NODE_MAPPING (FLOW_NODE_ID, NEXT_NODE_ID, TRIGGERING_ELEMENT) VALUES (?, ?, ?)"; + public static final String INSERT_FLOW_PAGE_INFO = + "INSERT INTO IDN_FLOW_PAGE (FLOW_ID, FLOW_NODE_ID, STEP_ID, PAGE_CONTENT, TYPE) VALUES (?, ?, ?, ?, ?)"; + public static final String INSERT_FLOW_PAGE_META = + "INSERT INTO IDN_FLOW_PAGE_META (PAGE_ID, COORDINATE_X, COORDINATE_Y, HEIGHT, WIDTH) VALUES (?, ?, ?, ?, " + + "?)"; + public static final String GET_FLOW = + "SELECT P.ID AS PAGE_ID, P.STEP_ID, P.PAGE_CONTENT, P.TYPE AS PAGE_TYPE, M.COORDINATE_X, M" + + ".COORDINATE_Y, M.HEIGHT, M.WIDTH FROM IDN_FLOW F JOIN IDN_FLOW_PAGE P ON F.ID = P.FLOW_ID " + + "LEFT JOIN IDN_FLOW_PAGE_META M ON P.ID = M.PAGE_ID WHERE F.TENANT_ID = ? AND F.IS_DEFAULT = " + + "TRUE AND F.TYPE = 'REGISTRATION';"; + + private SQLConstants() { + + } + + /** + * SQL Placeholders. + */ + public static final class SQLPlaceholders { + + public static final String REGISTRATION_FLOW = "REGISTRATION"; + public static final String STEP_ID = "STEP_ID"; + public static final String PAGE_CONTENT = "PAGE_CONTENT"; + public static final String PAGE_TYPE = "PAGE_TYPE"; + public static final String COORDINATE_X = "COORDINATE_X"; + public static final String COORDINATE_Y = "COORDINATE_Y"; + public static final String HEIGHT = "HEIGHT"; + public static final String WIDTH = "WIDTH"; + + private SQLPlaceholders() { + + } + } +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/exception/RegistrationClientException.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/exception/RegistrationClientException.java new file mode 100644 index 000000000000..eee93d9be70c --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/exception/RegistrationClientException.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * 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.user.registration.mgt.exception; + +/** + * Represents the client exception that occurs in the registration framework. + */ +public class RegistrationClientException extends RegistrationFrameworkException { + + public RegistrationClientException(String message) { + + super(message); + } + + public RegistrationClientException(String errorCode, String message, String description, Throwable cause) { + + super(errorCode, message, description, cause); + } + + public RegistrationClientException(String errorCode, String message, String description) { + + super(errorCode, message, description); + } +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/exception/RegistrationFrameworkException.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/exception/RegistrationFrameworkException.java new file mode 100644 index 000000000000..26006c345c6d --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/exception/RegistrationFrameworkException.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * 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.user.registration.mgt.exception; + +/** + * Represents the exception that occurs in the registration framework. + */ +public class RegistrationFrameworkException extends Exception { + + private String errorCode; + private String description; + + public RegistrationFrameworkException(String message) { + + super(message); + } + + public RegistrationFrameworkException(String errorCode, String message, String description, Throwable cause) { + + super(message, cause); + this.errorCode = errorCode; + this.description = description; + } + + public RegistrationFrameworkException(String errorCode, String message, String description) { + + super(message); + this.errorCode = errorCode; + this.description = description; + } + + public String getErrorCode() { + + return errorCode; + } + + public void setErrorCode(String errorCode) { + + this.errorCode = errorCode; + } + + public String getDescription() { + + return description; + } + + public void setDescription(String description) { + + this.description = description; + } +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/exception/RegistrationServerException.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/exception/RegistrationServerException.java new file mode 100644 index 000000000000..ada15a4b7739 --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/exception/RegistrationServerException.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * 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.user.registration.mgt.exception; + +/** + * Represents the server exception that occurs in the registration framework. + */ +public class RegistrationServerException extends RegistrationFrameworkException { + + public RegistrationServerException(String message) { + + super(message); + } + + public RegistrationServerException(String errorCode, String message, String description, Throwable cause) { + + super(errorCode, message, description, cause); + } + + public RegistrationServerException(String errorCode, String message, String description) { + + super(errorCode, message, description); + } +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/internal/RegistrationFlowMgtServiceComponent.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/internal/RegistrationFlowMgtServiceComponent.java new file mode 100644 index 000000000000..55b9a62ff2b2 --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/internal/RegistrationFlowMgtServiceComponent.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * 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.user.registration.mgt.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.wso2.carbon.identity.user.registration.mgt.RegistrationFlowMgtService; + +@Component( + name = "user.registration.flow.mgt.component", + immediate = true) +public class RegistrationFlowMgtServiceComponent { + + private static final Log LOG = LogFactory.getLog(RegistrationFlowMgtServiceComponent.class); + + @Activate + protected void activate(ComponentContext context) { + + BundleContext bundleContext = context.getBundleContext(); + bundleContext.registerService(RegistrationFlowMgtService.class.getName(), + RegistrationFlowMgtService.getInstance(), null); + LOG.debug("RegistrationFlowMgt bundle is activated."); + } + + @Deactivate + protected void deactivate(ComponentContext context) { + + BundleContext bundleCtx = context.getBundleContext(); + bundleCtx.ungetService(bundleCtx.getServiceReference(RegistrationFlowMgtService.class)); + LOG.debug("RegistrationFlowMgt bundle is deactivated."); + } +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ActionDTO.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ActionDTO.java new file mode 100644 index 000000000000..5ae3aa03e581 --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ActionDTO.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * 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.user.registration.mgt.model; + +import java.io.Serializable; + +/** + * DTO class for Action. + */ +public class ActionDTO implements Serializable { + + private static final long serialVersionUID = 1L; + private String type; + private ExecutorDTO executor; + private String nextId; + + public ActionDTO() { + + } + + private ActionDTO(Builder builder) { + + this.type = builder.type; + this.executor = builder.executor; + this.nextId = builder.nextId; + } + + public String getType() { + + return type; + } + + public void setType(String type) { + + this.type = type; + } + + public ExecutorDTO getExecutor() { + + return executor; + } + + public void setExecutor(ExecutorDTO executor) { + + this.executor = executor; + } + + public String getNextId() { + + return nextId; + } + + public void setNextId(String nextId) { + + this.nextId = nextId; + } + + /** + * Builder class to build {@link ActionDTO} objects. + */ + public static class Builder { + + private String type; + private ExecutorDTO executor; + private String nextId; + + public Builder type(String type) { + + this.type = type; + return this; + } + + public Builder executor(ExecutorDTO executor) { + + this.executor = executor; + return this; + } + + public Builder nextId(String nextId) { + + this.nextId = nextId; + return this; + } + + public ActionDTO build() { + + return new ActionDTO(this); + } + } +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ComponentDTO.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ComponentDTO.java new file mode 100644 index 000000000000..ba1567366766 --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ComponentDTO.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * 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.user.registration.mgt.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * DTO class for component in the step. + */ +public class ComponentDTO implements Serializable { + + private static final long serialVersionUID = 1L; + private String id; + private String category; + private String type; + private String identifier; + private ActionDTO action; + private Map configs = new HashMap<>(); + private List components = new ArrayList<>(); + + public ComponentDTO() { + + } + + private ComponentDTO(Builder builder) { + + this.id = builder.id; + this.category = builder.category; + this.type = builder.type; + this.identifier = builder.identifier; + this.configs = builder.configs; + this.components = builder.components; + this.action = builder.action; + } + + public String getId() { + + return id; + } + + public void setId(String id) { + + this.id = id; + } + + public String getType() { + + return type; + } + + public void setType(String type) { + + this.type = type; + } + + public Map getConfigs() { + + return configs; + } + + public void addConfig(String key, Object value) { + + this.configs.put(key, value); + } + + public ActionDTO getAction() { + + return action; + } + + public void setAction(ActionDTO action) { + + this.action = action; + } + + public String getIdentifier() { + + return identifier; + } + + public void setIdentifier(String identifier) { + + this.identifier = identifier; + } + + public String getCategory() { + + return category; + } + + public void setCategory(String category) { + + this.category = category; + } + + public List getComponents() { + + return components; + } + + public void setComponents(List components) { + + this.components = components; + } + + /** + * Builder class to build {@link ComponentDTO} objects. + */ + public static class Builder { + + private Map configs = new HashMap<>(); + private List components = new ArrayList<>(); + private String id; + private String category; + private String type; + private String identifier; + private ActionDTO action; + + public Builder id(String id) { + + this.id = id; + return this; + } + + public Builder category(String category) { + + this.category = category; + return this; + } + + public Builder type(String type) { + + this.type = type; + return this; + } + + public Builder identifier(String identifier) { + + this.identifier = identifier; + return this; + } + + public Builder configs(Map configs) { + + if (configs != null && !configs.isEmpty()) { + this.configs.putAll(configs); + } + this.configs = configs; + return this; + } + + public Builder components(List components) { + + if (components != null && !components.isEmpty()) { + this.components.addAll(components); + } + this.components = components; + return this; + } + + public Builder action(ActionDTO action) { + + this.action = action; + return this; + } + + public ComponentDTO build() { + + return new ComponentDTO(this); + } + } +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/DataDTO.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/DataDTO.java new file mode 100644 index 000000000000..83328a121865 --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/DataDTO.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * 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.user.registration.mgt.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * DTO class for component and step data. + */ +public class DataDTO implements Serializable { + + private static final long serialVersionUID = 1L; + private ActionDTO action; + private String url; + private List components = new ArrayList<>(); + + public DataDTO() { + + } + + private DataDTO(Builder builder) { + + this.components = builder.components; + this.action = builder.action; + this.url = builder.url; + } + + public List getComponents() { + + return components; + } + + public void addComponent(ComponentDTO component) { + + this.components.add(component); + } + + public ActionDTO getAction() { + + return action; + } + + public void setAction(ActionDTO action) { + + this.action = action; + } + + public String getUrl() { + + return url; + } + + public void setUrl(String url) { + + this.url = url; + } + + /** + * Builder class to build {@link DataDTO} objects. + */ + public static class Builder { + + private final List components = new ArrayList<>(); + private ActionDTO action; + private String url; + + public Builder components(List components) { + + if (components != null && !components.isEmpty()) { + this.components.addAll(components); + } + return this; + } + + public Builder action(ActionDTO action) { + + this.action = action; + return this; + } + + public Builder url(String url) { + + this.url = url; + return this; + } + + public DataDTO build() { + + return new DataDTO(this); + } + } +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ExecutorDTO.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ExecutorDTO.java new file mode 100644 index 000000000000..941c6942737d --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ExecutorDTO.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * 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.user.registration.mgt.model; + +import java.io.Serializable; + +/** + * DTO class for Executor. + */ +public class ExecutorDTO implements Serializable { + + private static final long serialVersionUID = 1L; + private String name; + private String idpName; + + public ExecutorDTO() { + + } + + private ExecutorDTO(Builder builder) { + + this.name = builder.name; + this.idpName = builder.idpName; + } + + public ExecutorDTO(String name) { + + this.name = name; + } + + public ExecutorDTO(String name, String idpName) { + + this.name = name; + this.idpName = idpName; + } + + public String getName() { + + return name; + } + + public void setName(String name) { + + this.name = name; + } + + public String getIdpName() { + + return idpName; + } + + public void setIdpName(String idpName) { + + this.idpName = idpName; + } + + /** + * Builder class to build {@link ExecutorDTO}. + */ + public static class Builder { + + private String name; + private String idpName; + + public Builder name(String name) { + + this.name = name; + return this; + } + + public Builder idpName(String idpName) { + + this.idpName = idpName; + return this; + } + + public ExecutorDTO build() { + + return new ExecutorDTO(this); + } + } +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/NodeConfig.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/NodeConfig.java new file mode 100644 index 000000000000..2df9f1a39144 --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/NodeConfig.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * 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.user.registration.mgt.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * This class is responsible for holding the node configuration. + */ +public class NodeConfig implements Serializable { + + private static final long serialVersionUID = 1L; + private final List edges = new ArrayList<>(); + private String id; + private String type; + private boolean isFirstNode; + private String nextNodeId; + private String previousNodeId; + private ExecutorDTO executorConfig; + + public NodeConfig(Builder builder) { + + this.id = builder.id; + this.type = builder.type; + this.isFirstNode = builder.isFirstNode; + this.nextNodeId = builder.nextNodeId; + this.previousNodeId = builder.previousNodeId; + this.executorConfig = builder.executorConfig; + this.edges.addAll(builder.edges); + } + + public String getId() { + + return id; + } + + public void setId(String id) { + + this.id = id; + } + + public String getType() { + + return type; + } + + public void setType(String type) { + + this.type = type; + } + + public boolean isFirstNode() { + + return isFirstNode; + } + + public void setFirstNode(boolean firstNode) { + + isFirstNode = firstNode; + } + + public ExecutorDTO getExecutorConfig() { + + return executorConfig; + } + + public void setExecutorConfig(ExecutorDTO executorConfig) { + + this.executorConfig = executorConfig; + } + + public String getNextNodeId() { + + return nextNodeId; + } + + public void setNextNodeId(String nextNodeId) { + + this.nextNodeId = nextNodeId; + } + + public String getPreviousNodeId() { + + return previousNodeId; + } + + public void setPreviousNodeId(String previousNodeId) { + + this.previousNodeId = previousNodeId; + } + + public List getEdges() { + + return edges; + } + + public void addEdge(NodeEdge edge) { + + this.edges.add(edge); + } + + public static class Builder { + + private final List edges = new ArrayList<>(); + private String id; + private String type; + private boolean isFirstNode; + private String nextNodeId = null; + private String previousNodeId = null; + private ExecutorDTO executorConfig = null; + + public Builder id(String id) { + + this.id = id; + return this; + } + + public Builder type(String type) { + + this.type = type; + return this; + } + + public Builder isFirstNode(boolean isFirstNode) { + + this.isFirstNode = isFirstNode; + return this; + } + + public Builder nextNodeId(String nextNodeId) { + + this.nextNodeId = nextNodeId; + return this; + } + + public Builder previousNodeId(String previousNodeId) { + + this.previousNodeId = previousNodeId; + return this; + } + + public Builder executorConfig(ExecutorDTO executorConfig) { + + this.executorConfig = executorConfig; + return this; + } + + public Builder edges(List edges) { + + this.edges.addAll(edges); + return this; + } + + public NodeConfig build() { + + return new NodeConfig(this); + } + } +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/NodeEdge.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/NodeEdge.java new file mode 100644 index 000000000000..4fdecc7f03ed --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/NodeEdge.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * 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.user.registration.mgt.model; + +import java.io.Serializable; + +/** + * DTO class for NodeEdge in the registration graph. + */ +public class NodeEdge implements Serializable { + + private static final long serialVersionUID = 1L; + String sourceNodeId; + String targetNodeId; + String triggeringActionId; + + public NodeEdge(String sourceNodeId, String targetNodeId, String triggeringActionId) { + + this.sourceNodeId = sourceNodeId; + this.targetNodeId = targetNodeId; + this.triggeringActionId = triggeringActionId; + } + + public String getSourceNodeId() { + + return sourceNodeId; + } + + public void setSourceNodeId(String sourceNodeId) { + + this.sourceNodeId = sourceNodeId; + } + + public String getTargetNodeId() { + + return targetNodeId; + } + + public void setTargetNodeId(String targetNodeId) { + + this.targetNodeId = targetNodeId; + } + + public String getTriggeringActionId() { + + return triggeringActionId; + } + + public void setTriggeringActionId(String triggeringActionId) { + + this.triggeringActionId = triggeringActionId; + } +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/RegistrationFlowDTO.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/RegistrationFlowDTO.java new file mode 100644 index 000000000000..5dc3404b1009 --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/RegistrationFlowDTO.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * 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.user.registration.mgt.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * DTO class for RegistrationFlow. + */ +public class RegistrationFlowDTO { + + private List steps = new ArrayList<>(); + + public List getSteps() { + + return steps; + } + + public void setSteps(List steps) { + + this.steps = steps; + } +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/RegistrationGraphConfig.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/RegistrationGraphConfig.java new file mode 100644 index 000000000000..55dce7cd58e0 --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/RegistrationGraphConfig.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * 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.user.registration.mgt.model; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * This class is responsible for holding the registration flow configuration. + */ +public class RegistrationGraphConfig implements Serializable { + + private static final long serialVersionUID = 1L; + private final Map nodeConfigs = new HashMap<>(); + private final Map nodePageMappings = new HashMap<>(); + private String id; + private String firstNodeId; + + public String getId() { + + return id; + } + + public void setId(String id) { + + this.id = id; + } + + public String getFirstNodeId() { + + return firstNodeId; + } + + public void setFirstNodeId(String firstNodeId) { + + this.firstNodeId = firstNodeId; + } + + public Map getNodeConfigs() { + + return nodeConfigs; + } + + public void setNodeConfigs(Map nodeConfigs) { + + this.nodeConfigs.putAll(nodeConfigs); + } + + public void addNodeConfig(NodeConfig node) { + + this.nodeConfigs.put(node.getId(), node); + } + + public Map getNodePageMappings() { + + return nodePageMappings; + } + + public void setNodePageMappings(Map stepContent) { + + this.nodePageMappings.putAll(stepContent); + } + + public void addNodePageMapping(String nodeId, StepDTO stepDTO) { + + this.nodePageMappings.put(nodeId, stepDTO); + } +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/StepDTO.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/StepDTO.java new file mode 100644 index 000000000000..137e38083a2a --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/StepDTO.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * 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.user.registration.mgt.model; + +import java.io.Serializable; + +/** + * DTO class for Step. + */ +public class StepDTO implements Serializable { + + private static final long serialVersionUID = 1L; + private String id; + private String type; + private double coordinateX; + private double coordinateY; + private double width; + private double height; + private DataDTO data; + + public StepDTO() { + + } + + private StepDTO(Builder builder) { + + this.id = builder.id; + this.type = builder.type; + this.coordinateX = builder.coordinateX; + this.coordinateY = builder.coordinateY; + this.width = builder.width; + this.height = builder.height; + this.data = builder.data; + } + + public String getId() { + + return id; + } + + public void setId(String id) { + + this.id = id; + } + + public String getType() { + + return type; + } + + public void setType(String type) { + + this.type = type; + } + + public double getCoordinateX() { + + return coordinateX; + } + + public void setCoordinateX(double coordinateX) { + + this.coordinateX = coordinateX; + } + + public double getCoordinateY() { + + return coordinateY; + } + + public void setCoordinateY(double coordinateY) { + + this.coordinateY = coordinateY; + } + + public double getWidth() { + + return width; + } + + public void setWidth(double width) { + + this.width = width; + } + + public double getHeight() { + + return height; + } + + public void setHeight(double height) { + + this.height = height; + } + + public DataDTO getData() { + + return data; + } + + public void setData(DataDTO data) { + + this.data = data; + } + + /** + * Builder class to build {@link StepDTO}. + */ + public static class Builder { + + private String id; + private String type; + private double coordinateX; + private double coordinateY; + private double width; + private double height; + private DataDTO data; + + public Builder id(String id) { + + this.id = id; + return this; + } + + public Builder type(String type) { + + this.type = type; + return this; + } + + public Builder coordinateX(double coordinateX) { + + this.coordinateX = coordinateX; + return this; + } + + public Builder coordinateY(double coordinateY) { + + this.coordinateY = coordinateY; + return this; + } + + public Builder width(double width) { + + this.width = width; + return this; + } + + public Builder height(double height) { + + this.height = height; + return this; + } + + public Builder data(DataDTO data) { + + this.data = data; + return this; + } + + public StepDTO build() { + + return new StepDTO(this); + } + } +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ValidationDTO.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ValidationDTO.java new file mode 100644 index 000000000000..6ab666abb7bc --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/model/ValidationDTO.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * 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.user.registration.mgt.model; + +/* + * DTO class for Validation. + */ +public class ValidationDTO { + +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/utils/GraphBuilder.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/utils/GraphBuilder.java new file mode 100644 index 000000000000..1417f276d8c1 --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/utils/GraphBuilder.java @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * 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.user.registration.mgt.utils; + +import static org.wso2.carbon.identity.user.registration.mgt.Constants.ActionTypes.EXECUTOR; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.ActionTypes.NEXT; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.COMPLETE; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.ComponentTypes.BUTTON; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.ComponentTypes.FORM; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages.ERROR_CODE_ACTION_DATA_NOT_FOUND; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages.ERROR_CODE_COMPONENT_DATA_NOT_FOUND; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages.ERROR_CODE_EXECUTOR_INFO_NOT_FOUND; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages.ERROR_CODE_INVALID_ACTION_FOR_BUTTON; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages.ERROR_CODE_INVALID_ACTION_TYPE; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages.ERROR_CODE_INVALID_NEXT_STEP; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages.ERROR_CODE_INVALID_NODE; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages.ERROR_CODE_MULTIPLE_STEP_EXECUTORS; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages.ERROR_CODE_NEXT_ACTION_NOT_FOUND; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages.ERROR_CODE_UNSUPPORTED_ACTION_TYPE; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.ExecutorTypes.USER_ONBOARDING; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.NodeTypes.DECISION; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.NodeTypes.PROMPT_ONLY; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.NodeTypes.TASK_EXECUTION; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.StepTypes.REDIRECTION; +import static org.wso2.carbon.identity.user.registration.mgt.Constants.StepTypes.VIEW; +import static org.wso2.carbon.identity.user.registration.mgt.utils.RegistrationMgtUtils.handleClientException; +import static org.wso2.carbon.identity.user.registration.mgt.utils.RegistrationMgtUtils.handleServerException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.identity.user.registration.mgt.Constants; +import org.wso2.carbon.identity.user.registration.mgt.exception.RegistrationClientException; +import org.wso2.carbon.identity.user.registration.mgt.exception.RegistrationFrameworkException; +import org.wso2.carbon.identity.user.registration.mgt.model.ActionDTO; +import org.wso2.carbon.identity.user.registration.mgt.model.ComponentDTO; +import org.wso2.carbon.identity.user.registration.mgt.model.ExecutorDTO; +import org.wso2.carbon.identity.user.registration.mgt.model.NodeConfig; +import org.wso2.carbon.identity.user.registration.mgt.model.NodeEdge; +import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationFlowDTO; +import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationGraphConfig; +import org.wso2.carbon.identity.user.registration.mgt.model.StepDTO; + +/** + * This class is responsible for building the registration flow graph. + */ +public class GraphBuilder { + + private static final Log LOG = LogFactory.getLog(GraphBuilder.class); + + private GraphBuilder() { + + } + + /** + * Converts the registration flow DTO to a registration graph configuration. + * + * @param flowDTO The registration flow DTO. + * @return The registration graph configuration. + * @throws RegistrationFrameworkException If an error occurs while converting the flow. + */ + public static RegistrationGraphConfig convert(RegistrationFlowDTO flowDTO) + throws RegistrationFrameworkException { + + RegistrationGraphConfig flowConfig = new RegistrationGraphConfig(); + flowConfig.setId(UUID.randomUUID().toString()); + + Map nodeMap = new HashMap<>(); + Map stepContentMap = new HashMap<>(); + List nodeEdges = new ArrayList<>(); + + String endNodeId = handleEndNode(nodeMap); + + for (StepDTO step : flowDTO.getSteps()) { + if (step.getData() == null) { + throw handleClientException(Constants.ErrorMessages.ERROR_CODE_STEP_DATA_NOT_FOUND, step.getId()); + } + NodeConfig stepNode; + if (VIEW.equals(step.getType())) { + stepNode = processViewStep(step, nodeMap, nodeEdges, endNodeId); + } else if (REDIRECTION.equals(step.getType())) { + stepNode = processRedirectionStep(step, nodeMap, nodeEdges, endNodeId); + } else { + throw handleClientException(Constants.ErrorMessages.ERROR_CODE_UNSUPPORTED_STEP_TYPE, step.getType()); + } + stepContentMap.put(step.getId(), step); + setFirstNodeIfNeeded(flowConfig, stepNode); + } + + updateNodeMappings(nodeMap, nodeEdges); + flowConfig.setNodeConfigs(nodeMap); + flowConfig.setNodePageMappings(stepContentMap); + return flowConfig; + } + + private static String handleEndNode(Map nodeMap) { + + NodeConfig userOnboardingNode = createUserOnboardingNode(); + nodeMap.put(userOnboardingNode.getId(), userOnboardingNode); + return userOnboardingNode.getId(); + } + + private static NodeConfig processRedirectionStep(StepDTO step, Map nodeMap, + List nodeMappings, String endNodeId) + throws RegistrationFrameworkException { + + ActionDTO action = step.getData().getAction(); + if (action == null) { + throw handleClientException(ERROR_CODE_ACTION_DATA_NOT_FOUND, step.getId()); + } + if (!EXECUTOR.equals(action.getType())) { + throw handleClientException(ERROR_CODE_INVALID_ACTION_TYPE, action.getType(), step.getId()); + } + + NodeConfig redirectionNode = createTaskExecutionNode(step.getId(), action.getExecutor()); + String nextNodeId = COMPLETE.equals(action.getNextId()) ? endNodeId : action.getNextId(); + nodeMap.put(redirectionNode.getId(), redirectionNode); + nodeMappings.add(new NodeEdge(redirectionNode.getId(), nextNodeId, null)); + return redirectionNode; + } + + private static NodeConfig processViewStep(StepDTO step, Map nodeMap, List nodeEdges, + String endNodeId) throws RegistrationFrameworkException { + + List stepNodes = new ArrayList<>(); + List components = step.getData().getComponents(); + if (components == null || components.isEmpty()) { + throw handleClientException(ERROR_CODE_COMPONENT_DATA_NOT_FOUND, step.getId()); + } + for (ComponentDTO component : components) { + processComponent(component, stepNodes, step.getId()); + } + return handleTempNodesInStep(stepNodes, step, nodeMap, nodeEdges, endNodeId); + } + + private static void processComponent(ComponentDTO component, List stepNodes, String stepId) + throws RegistrationFrameworkException { + + if (FORM.equals(component.getType())) { + if (LOG.isDebugEnabled()) { + LOG.debug("Processing form component: " + component.getId()); + } + for (ComponentDTO subComponent : component.getComponents()) { + processComponent(subComponent, stepNodes, stepId); + } + } else if (BUTTON.equals(component.getType())) { + validateStepActions(component.getAction(), stepNodes, component.getId(), stepId); + stepNodes.add(createNodeFromAction(component.getAction(), component.getId())); + } + } + + private static void validateStepActions(ActionDTO action, List stepNodes, String id, String stepId) + throws RegistrationFrameworkException { + + if (action == null) { + throw handleClientException(ERROR_CODE_INVALID_ACTION_FOR_BUTTON, id); + } + if (action.getNextId() == null) { + throw handleClientException(ERROR_CODE_NEXT_ACTION_NOT_FOUND, id); + } + if (EXECUTOR.equals(action.getType())) { + if (action.getExecutor() == null) { + throw handleClientException(ERROR_CODE_EXECUTOR_INFO_NOT_FOUND, id); + } + if (stepNodes.stream().anyMatch(nodeConfig -> (TASK_EXECUTION.equals(nodeConfig.getType())))) { + throw handleClientException(ERROR_CODE_MULTIPLE_STEP_EXECUTORS, stepId); + } + } + } + + private static NodeConfig createNodeFromAction(ActionDTO action, String componentId) + throws RegistrationClientException { + + NodeConfig tempNodeInComponent; + if (NEXT.equals(action.getType())) { + tempNodeInComponent = createPagePromptNode(componentId); + } else if (EXECUTOR.equals(action.getType())) { + tempNodeInComponent = createTaskExecutionNode(componentId, action.getExecutor()); + } else { + throw handleClientException(ERROR_CODE_UNSUPPORTED_ACTION_TYPE, action.getType(), componentId); + } + tempNodeInComponent.setNextNodeId(action.getNextId()); + return tempNodeInComponent; + } + + private static NodeConfig handleTempNodesInStep(List tempNodesInStep, StepDTO step, + Map nodeMap, List nodeMappings, + String endNodeId) { + + NodeConfig stepNode = null; + if (tempNodesInStep.size() > 1) { + if (LOG.isDebugEnabled()) { + LOG.debug("Multiple nodes are derived from the step: " + step.getId() + ". Creating a decision node."); + } + NodeConfig decisionNode = createDecisionNode(step.getId()); + for (NodeConfig nodeConfig : tempNodesInStep) { + String nextNodeId = + COMPLETE.equals(nodeConfig.getNextNodeId()) ? endNodeId : nodeConfig.getNextNodeId(); + if (TASK_EXECUTION.equals(nodeConfig.getType())) { + if (LOG.isDebugEnabled()) { + LOG.debug("A node with an execution found in the step. Therefore adding it to the node list " + + "with id, " + nodeConfig.getId()); + } + nodeConfig.setNextNodeId(null); + nodeMap.put(nodeConfig.getId(), nodeConfig); + nodeMappings.add(new NodeEdge(nodeConfig.getId(), nextNodeId, null)); + nodeMappings.add(new NodeEdge(decisionNode.getId(), nodeConfig.getId(), nodeConfig.getId())); + } else { + // Edge from decision node to the next node derived from NEXT actions. + nodeMappings.add(new NodeEdge(decisionNode.getId(), nextNodeId, nodeConfig.getId())); + } + } + stepNode = decisionNode; + } else if (tempNodesInStep.size() == 1) { + if (LOG.isDebugEnabled()) { + LOG.debug("Only one node derived from the step: " + step.getId() + ". Adding it to the node list."); + } + + NodeConfig tempNode = tempNodesInStep.get(0); + + String nextNodeId = COMPLETE.equals(tempNode.getNextNodeId()) ? endNodeId : tempNode.getNextNodeId(); + stepNode = new NodeConfig.Builder() + .id(step.getId()) + .type(tempNode.getType()) + .executorConfig(tempNode.getExecutorConfig()) + .build(); + nodeMappings.add(new NodeEdge(stepNode.getId(), nextNodeId, tempNode.getId())); + } + nodeMap.put(step.getId(), stepNode); + return stepNode; + } + + private static void setFirstNodeIfNeeded(RegistrationGraphConfig flowConfig, NodeConfig nodeConfig) { + + if (flowConfig.getFirstNodeId() == null) { + if (LOG.isDebugEnabled()) { + LOG.debug( + "Setting node: " + nodeConfig.getId() + " as the first node in the flow " + flowConfig.getId()); + } + flowConfig.setFirstNodeId(nodeConfig.getId()); + nodeConfig.setFirstNode(true); + } + } + + private static void updateNodeMappings(Map nodeMap, List nodeMappings) + throws RegistrationFrameworkException { + + for (NodeEdge edge : nodeMappings) { + String nodeId = edge.getSourceNodeId(); + String nextNodeId = edge.getTargetNodeId(); + + if (!nodeMap.containsKey(nodeId)) { + throw handleServerException(ERROR_CODE_INVALID_NODE, nodeId); + } + if (!nodeMap.containsKey(nextNodeId)) { + throw handleClientException(ERROR_CODE_INVALID_NEXT_STEP, nextNodeId); + } + nodeMap.get(nodeId).addEdge(edge); + } + } + + private static NodeConfig createTaskExecutionNode(String id, ExecutorDTO executorDTO) { + + NodeConfig node = new NodeConfig.Builder() + .id(id) + .type(TASK_EXECUTION) + .executorConfig(executorDTO) + .build(); + if (LOG.isDebugEnabled()) { + LOG.debug("Created a task execution node " + id + " with executor " + executorDTO.getName() + "."); + } + return node; + } + + private static NodeConfig createDecisionNode(String id) { + + NodeConfig nodeConfig = new NodeConfig.Builder() + .id(id) + .type(DECISION) + .build(); + if (LOG.isDebugEnabled()) { + LOG.debug("Created a decision node " + id + "."); + } + return nodeConfig; + } + + private static NodeConfig createUserOnboardingNode() { + + NodeConfig nodeConfig = new NodeConfig.Builder() + .id(UUID.randomUUID().toString()) + .type(TASK_EXECUTION) + .executorConfig(new ExecutorDTO(USER_ONBOARDING)) + .build(); + if (LOG.isDebugEnabled()) { + LOG.debug("Created a node with id " + nodeConfig.getId() + " for user onboarding."); + } + return nodeConfig; + } + + private static NodeConfig createPagePromptNode(String id) { + + NodeConfig nodeConfig = new NodeConfig.Builder() + .id(id) + .type(PROMPT_ONLY) + .build(); + if (LOG.isDebugEnabled()) { + LOG.debug("Created a node with id " + nodeConfig.getId() + " to prompt a page."); + } + return nodeConfig; + } +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/utils/RegistrationMgtUtils.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/utils/RegistrationMgtUtils.java new file mode 100644 index 000000000000..2b9430f1f4e1 --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/main/java/org/wso2/carbon/identity/user/registration/mgt/utils/RegistrationMgtUtils.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * + * 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.user.registration.mgt.utils; + +import static org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils.ENABLE_V2_AUDIT_LOGS; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.wso2.carbon.CarbonConstants; +import org.wso2.carbon.context.CarbonContext; +import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils; +import org.wso2.carbon.identity.core.util.IdentityUtil; +import org.wso2.carbon.identity.user.registration.mgt.Constants.ErrorMessages; +import org.wso2.carbon.identity.user.registration.mgt.exception.RegistrationClientException; +import org.wso2.carbon.identity.user.registration.mgt.exception.RegistrationServerException; +import org.wso2.carbon.user.core.util.UserCoreUtil; +import org.wso2.carbon.utils.multitenancy.MultitenantUtils; + +public class RegistrationMgtUtils { + + private RegistrationMgtUtils() { + + } + + /** + * Handle the registration flow management server exceptions. + * + * @param error Error message. + * @param e Throwable. + * @param data The error message data. + * @return RegistrationServerException. + */ + public static RegistrationServerException handleServerException(ErrorMessages error, Throwable e, Object... data) { + + String description = error.getDescription(); + if (ArrayUtils.isNotEmpty(data)) { + description = String.format(description, data); + } + return new RegistrationServerException(error.getCode(), error.getMessage(), description, e); + } + + /** + * Handle the registration flow management server exceptions. + * + * @param error Error message. + * @param data The error message data. + * @return RegistrationServerException. + */ + public static RegistrationServerException handleServerException(ErrorMessages error, Object... data) { + + String description = error.getDescription(); + if (ArrayUtils.isNotEmpty(data)) { + description = String.format(description, data); + } + return new RegistrationServerException(error.getCode(), error.getMessage(), description); + } + + /** + * Handle the registration flow management client exceptions. + * + * @param error Error message. + * @param e Throwable. + * @param data The error message data. + * @return RegistrationClientException. + */ + public static RegistrationClientException handleClientException(ErrorMessages error, Throwable e, Object... data) { + + String description = error.getDescription(); + if (ArrayUtils.isNotEmpty(data)) { + description = String.format(description, data); + } + return new RegistrationClientException(error.getCode(), error.getMessage(), description, e); + } + + /** + * Handle the registration flow management client exceptions. + * + * @param error Error message. + * @param data The error message data. + * @return RegistrationClientException. + */ + public static RegistrationClientException handleClientException(ErrorMessages error, Object... data) { + + String description = error.getDescription(); + if (ArrayUtils.isNotEmpty(data)) { + description = String.format(description, data); + } + return new RegistrationClientException(error.getCode(), error.getMessage(), description); + } + + /** + * Check whether the v2 audit logs are enabled. + * + * @return true if v2 audit logs are enabled. + */ + public static boolean isEnableV2AuditLogs() { + + return Boolean.parseBoolean(System.getProperty(ENABLE_V2_AUDIT_LOGS)); + } + + /** + * Get the initiator for audit logs. + * + * @return Initiator id despite masking. + */ + public static String getInitiatorId() { + + String initiator = null; + String username = MultitenantUtils.getTenantAwareUsername(getUser()); + String tenantDomain = MultitenantUtils.getTenantDomain(getUser()); + if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(tenantDomain)) { + initiator = IdentityUtil.getInitiatorId(username, tenantDomain); + } + if (StringUtils.isBlank(initiator)) { + if (username.equals(CarbonConstants.REGISTRY_SYSTEM_USERNAME)) { + // If the initiator is wso2.system, we need not to mask the username. + return LoggerUtils.Initiator.System.name(); + } + initiator = LoggerUtils.getMaskedContent(getUser()); + } + return initiator; + } + + /** + * To get the current user, who is doing the current task. + * + * @return current logged-in user. + */ + private static String getUser() { + + String user = CarbonContext.getThreadLocalCarbonContext().getUsername(); + if (StringUtils.isNotEmpty(user)) { + user = UserCoreUtil + .addTenantDomainToEntry(user, CarbonContext.getThreadLocalCarbonContext().getTenantDomain()); + } else { + user = CarbonConstants.REGISTRY_SYSTEM_USERNAME; + } + return user; + } +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/java/org/wso2/carbon/identity/user/registration/mgt/dao/RegistrationFlowDAOImplTest.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/java/org/wso2/carbon/identity/user/registration/mgt/dao/RegistrationFlowDAOImplTest.java new file mode 100644 index 000000000000..2a8481f5c49e --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/java/org/wso2/carbon/identity/user/registration/mgt/dao/RegistrationFlowDAOImplTest.java @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2025, 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.user.registration.mgt.dao; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.dbcp.BasicDataSource; +import org.apache.commons.lang.StringUtils; +import org.mockito.MockedStatic; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.wso2.carbon.identity.core.util.IdentityDatabaseUtil; +import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationFlowDTO; +import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationGraphConfig; +import org.wso2.carbon.identity.user.registration.mgt.utils.GraphBuilder; + +import java.io.File; +import java.nio.file.Paths; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +import static org.mockito.Mockito.mockStatic; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +/** + * Test class for RegistrationFlowDAOImpl. + */ +public class RegistrationFlowDAOImplTest { + + private static final String DB_NAME = "registration_flow_mgt_dao_db"; + private RegistrationFlowDAOImpl daoImpl; + private static final Map dataSourceMap = new HashMap<>(); + + @BeforeClass + public void setUp() throws Exception { + + daoImpl = new RegistrationFlowDAOImpl(); + initiateH2Database(getFilePath("identity.sql")); + } + + @AfterClass + public void tearDown() throws Exception { + + closeH2Database(); + } + + @DataProvider + public Object[][] registrationFlowData() { + return new Object[][]{ + {createSampleRegistrationGraphConfig(), 1, "SampleFlow"} + }; + } + + @Test(dataProvider = "registrationFlowData") + public void testUpdateDefaultRegistrationFlowByTenant(RegistrationGraphConfig regFlowConfig, int tenantId, + String flowName) throws Exception { + + try (MockedStatic identityDatabaseUtil = mockStatic(IdentityDatabaseUtil.class)) { + identityDatabaseUtil.when(IdentityDatabaseUtil::getDataSource) + .thenReturn(dataSourceMap.get(DB_NAME)); + daoImpl.updateDefaultRegistrationFlowByTenant(regFlowConfig, tenantId, flowName); + RegistrationFlowDTO flowDTO = daoImpl.getDefaultRegistrationFlowByTenant(tenantId); + assertNotNull(flowDTO); + } + } + + @Test(dataProvider = "registrationFlowData") + public void testGetDefaultRegistrationFlowByTenant(RegistrationGraphConfig regFlowConfig, int tenantId, + String flowName) throws Exception { + + try (MockedStatic identityDatabaseUtil = mockStatic(IdentityDatabaseUtil.class)) { + identityDatabaseUtil.when(IdentityDatabaseUtil::getDataSource) + .thenReturn(dataSourceMap.get(DB_NAME)); + daoImpl.updateDefaultRegistrationFlowByTenant(regFlowConfig, tenantId, flowName); + RegistrationFlowDTO flowDTO = daoImpl.getDefaultRegistrationFlowByTenant(tenantId); + assertNotNull(flowDTO); + assertEquals(flowDTO.getSteps().size(), 3); + assertEquals(flowDTO.getSteps().get(0).getId(), "step_1"); + } + } + + private static RegistrationGraphConfig createSampleRegistrationGraphConfig() { + + try { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + RegistrationFlowDTO flowDTO = objectMapper.readValue(new File(getFilePath("reg_flow.json")), + RegistrationFlowDTO.class); + return GraphBuilder.convert(flowDTO); + } catch (Exception e) { + throw new RuntimeException("Error while reading the JSON file.", e); + } + } + + private static Connection getConnection() throws SQLException { + + if (dataSourceMap.get(DB_NAME) != null) { + return dataSourceMap.get(DB_NAME).getConnection(); + } + throw new RuntimeException("No datasource initiated for database: " + DB_NAME); + } + + /** + * Initiate H2 database. + * + * @param scriptPath Path to the database script. + * @throws Exception Error when initiating H2 database. + */ + private void initiateH2Database(String scriptPath) throws Exception { + + BasicDataSource dataSource = new BasicDataSource(); + dataSource.setDriverClassName("org.h2.Driver"); + dataSource.setUsername("username"); + dataSource.setPassword("password"); + dataSource.setUrl("jdbc:h2:mem:test" + DB_NAME); + dataSource.setTestOnBorrow(true); + dataSource.setValidationQuery("select 1"); + try (Connection connection = dataSource.getConnection()) { + connection.createStatement().executeUpdate("RUNSCRIPT FROM '" + scriptPath + "'"); + } + dataSourceMap.put(DB_NAME, dataSource); + } + + /** + * Close H2 database. + * + * @throws Exception Error when closing H2 database. + */ + public static void closeH2Database() throws Exception { + + BasicDataSource dataSource = dataSourceMap.get(DB_NAME); + if (dataSource != null) { + dataSource.close(); + } + } + + /** + * Get the path to the database script. + * + * @return Path to the database script. + */ + private static String getFilePath(String filename) { + + if (StringUtils.isNotBlank(filename)) { + return Paths.get(System.getProperty("user.dir"), "src", "test", "resources", filename).toString(); + } + throw new IllegalArgumentException("DB Script file name cannot be empty."); + } +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/java/org/wso2/carbon/identity/user/registration/mgt/utils/GraphBuilderTest.java b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/java/org/wso2/carbon/identity/user/registration/mgt/utils/GraphBuilderTest.java new file mode 100644 index 000000000000..7be005c1903d --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/java/org/wso2/carbon/identity/user/registration/mgt/utils/GraphBuilderTest.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2025, 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.user.registration.mgt.utils; + +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; +import org.wso2.carbon.identity.user.registration.mgt.Constants; +import org.wso2.carbon.identity.user.registration.mgt.exception.RegistrationFrameworkException; +import org.wso2.carbon.identity.user.registration.mgt.model.ActionDTO; +import org.wso2.carbon.identity.user.registration.mgt.model.ComponentDTO; +import org.wso2.carbon.identity.user.registration.mgt.model.DataDTO; +import org.wso2.carbon.identity.user.registration.mgt.model.ExecutorDTO; +import org.wso2.carbon.identity.user.registration.mgt.model.NodeConfig; +import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationFlowDTO; +import org.wso2.carbon.identity.user.registration.mgt.model.RegistrationGraphConfig; +import org.wso2.carbon.identity.user.registration.mgt.model.StepDTO; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +/** + * Test class for GraphBuilder. + */ +public class GraphBuilderTest { + + public static final String STEP_1 = "step_qx3a"; + public static final String STEP_2 = "step_we12"; + public static final String STEP_3 = "step_sd21"; + public static final String GOOGLE_IDP_NAME = "Google1"; + public static final String OIDC_AUTHENTICATOR = "OIDCAuthenticator"; + public static final String OIDC_IDP = "OIDC_IDP"; + public static final String GOOGLE_OIDC_AUTHENTICATOR = "GoogleOIDCAuthenticator"; + public static Map stepIdToNextNodeIdMap = new HashMap<>(); + + @BeforeClass + public void setUpClass() { + } + + @AfterMethod + public void tearDown() { + } + + @Test + public void testConvert() throws RegistrationFrameworkException { + + // Create a sample RegistrationFlowDTO. + RegistrationFlowDTO flowDTO = new RegistrationFlowDTO(); + flowDTO.setSteps(new ArrayList<>()); + + Map configs = new HashMap<>(); + configs.put("key1", "value1"); + configs.put("key2", "value2"); + + ComponentDTO componentDTO1 = new ComponentDTO.Builder() + .id("component_2sa1") + .type("BUTTON") + .configs(configs) + .category("SUBMIT") + .action(new ActionDTO.Builder() + .type(Constants.ActionTypes.NEXT) + .nextId(STEP_2) + .build()) + .build(); + + // Add a sample StepDTO. + StepDTO stepDTO1 = new StepDTO.Builder() + .id(STEP_1) + .type(Constants.StepTypes.VIEW) + .coordinateX(0) + .coordinateY(0) + .height(100.15) + .width(200.25) + .data(new DataDTO.Builder() + .components(Collections.singletonList(componentDTO1)) + .build()) + .build(); + + ComponentDTO componentDTO2 = new ComponentDTO.Builder() + .id("component_2sa1") + .type("BUTTON") + .configs(configs) + .action(new ActionDTO.Builder() + .type(Constants.ActionTypes.EXECUTOR) + .executor(new ExecutorDTO.Builder() + .name(OIDC_AUTHENTICATOR) + .idpName(OIDC_IDP) + .build()) + .nextId(STEP_3) + .build()) + .build(); + + StepDTO stepDTO2 = new StepDTO.Builder() + .id(STEP_2) + .type(Constants.StepTypes.VIEW) + .coordinateX(0) + .coordinateY(0) + .height(100.15) + .width(200.25) + .data(new DataDTO.Builder() + .components(Collections.singletonList(componentDTO2)) + .build()) + .build(); + + StepDTO stepDTO3 = new StepDTO.Builder() + .id(STEP_3) + .type(Constants.StepTypes.REDIRECTION) + .coordinateX(0) + .coordinateY(0) + .height(100.15) + .width(200.25) + .data(new DataDTO.Builder() + .action(new ActionDTO.Builder() + .type(Constants.ActionTypes.EXECUTOR) + .executor(new ExecutorDTO.Builder() + .name(GOOGLE_OIDC_AUTHENTICATOR) + .idpName(GOOGLE_IDP_NAME) + .build()) + .nextId(Constants.COMPLETE) + .build()) + .build()) + .build(); + + flowDTO.setSteps(Arrays.asList(stepDTO1, stepDTO2, stepDTO3)); + + // Convert the flowDTO to a RegistrationGraphConfig. + RegistrationGraphConfig graphConfig = GraphBuilder.convert(flowDTO); + + // Assert the converted graphConfig. + assertNotNull(graphConfig); + assertNotNull(graphConfig.getId()); + assertEquals(graphConfig.getNodeConfigs().size(), 4); + assertEquals(graphConfig.getNodePageMappings().size(), 3); + assertEquals(graphConfig.getFirstNodeId(), STEP_1); + + graphConfig.getNodePageMappings().forEach((nodeId, stepDTO) -> { + stepIdToNextNodeIdMap.put(stepDTO.getId(), graphConfig.getNodeConfigs().get(nodeId).getNextNodeId()); + }); + + graphConfig.getNodePageMappings().forEach((nodeId, stepDTO) -> { + assertNotNull(nodeId); + assertNotNull(stepDTO); + NodeConfig nodeConfig = graphConfig.getNodeConfigs().get(nodeId); + assertNotNull(nodeConfig); + + switch (stepDTO.getId()) { + case STEP_1: + assertEquals(nodeConfig.getType(), Constants.NodeTypes.PROMPT_ONLY); + assertTrue(nodeConfig.isFirstNode()); + assertEquals(nodeConfig.getNextNodeId(), stepIdToNextNodeIdMap.get(STEP_1)); + assertNull(nodeConfig.getExecutorConfig()); + break; + case STEP_2: + assertEquals(nodeConfig.getType(), Constants.NodeTypes.TASK_EXECUTION); + assertEquals(nodeConfig.getExecutorConfig().getName(), OIDC_AUTHENTICATOR); + assertEquals(nodeConfig.getExecutorConfig().getIdpName(), OIDC_IDP); + assertEquals(nodeConfig.getNextNodeId(), stepIdToNextNodeIdMap.get(STEP_2)); + break; + case STEP_3: + assertEquals(nodeConfig.getType(), Constants.NodeTypes.TASK_EXECUTION); + assertEquals(nodeConfig.getExecutorConfig().getName(), GOOGLE_OIDC_AUTHENTICATOR); + assertEquals(nodeConfig.getExecutorConfig().getIdpName(), GOOGLE_IDP_NAME); + assertEquals(nodeConfig.getNextNodeId(), stepIdToNextNodeIdMap.get(STEP_3)); + break; + default: + break; + } + }); + } +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/resources/identity.sql b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/resources/identity.sql new file mode 100644 index 000000000000..8ef3067feca9 --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/resources/identity.sql @@ -0,0 +1,56 @@ +CREATE TABLE IDN_FLOW ( + ID VARCHAR(50) PRIMARY KEY, + TENANT_ID INT, + FLOW_NAME VARCHAR(255), + TYPE VARCHAR(100), + IS_DEFAULT BOOLEAN DEFAULT FALSE, + UNIQUE (TENANT_ID, FLOW_NAME) +); + +CREATE TABLE IDN_FLOW_NODE ( + ID INT PRIMARY KEY AUTO_INCREMENT, + NODE_ID VARCHAR(50), + FLOW_ID VARCHAR(50), + NODE_TYPE VARCHAR(100), + IS_FIRST_NODE BOOLEAN DEFAULT FALSE, + UNIQUE (NODE_ID, FLOW_ID), + FOREIGN KEY (FLOW_ID) REFERENCES IDN_FLOW(ID) ON DELETE CASCADE +); + +CREATE TABLE IDN_FLOW_NODE_MAPPING ( + ID INT PRIMARY KEY AUTO_INCREMENT, + FLOW_NODE_ID INT, + NEXT_NODE_ID INT, + TRIGGERING_ELEMENT VARCHAR(100), + FOREIGN KEY (FLOW_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE, + FOREIGN KEY (NEXT_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE +); + +CREATE TABLE IDN_FLOW_PAGE ( + ID INT PRIMARY KEY AUTO_INCREMENT, + FLOW_ID VARCHAR(50), + FLOW_NODE_ID INT, + STEP_ID VARCHAR(50), + PAGE_CONTENT BLOB, + TYPE VARCHAR(100), + FOREIGN KEY (FLOW_ID) REFERENCES IDN_FLOW(ID) ON DELETE CASCADE, + FOREIGN KEY (FLOW_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE +); + +CREATE TABLE IDN_FLOW_PAGE_META ( + ID INT PRIMARY KEY AUTO_INCREMENT, + PAGE_ID INT, + COORDINATE_X DOUBLE, + COORDINATE_Y DOUBLE, + HEIGHT DOUBLE, + WIDTH DOUBLE, + FOREIGN KEY (PAGE_ID) REFERENCES IDN_FLOW_PAGE(ID) ON DELETE CASCADE +); + +CREATE TABLE IDN_FLOW_NODE_EXECUTOR ( + ID INT PRIMARY KEY AUTO_INCREMENT, + FLOW_NODE_ID INT, + EXECUTOR_NAME VARCHAR(255), + IDP_NAME INT DEFAULT NULL, + FOREIGN KEY (FLOW_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE +); diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/resources/reg_flow.json b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/resources/reg_flow.json new file mode 100644 index 000000000000..a85ab57ad0fe --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/resources/reg_flow.json @@ -0,0 +1,155 @@ +{ + "steps": [ + { + "id": "step_1", + "type": "VIEW", + "width": 120, + "height": 240, + "coordinateX": 0, + "coordinateY": 0, + "data": { + "components": [ + { + "id": "component_232d", + "category": "BLOCK", + "type": "FORM", + "components": [ + { + "id": "element_gd43", + "category": "FIELD", + "type": "INPUT", + "variant": "TEXT", + "config": { + "type": "text", + "hint": "", + "label": "Username", + "required": true, + "placeholder": "Enter your username" + } + }, + { + "id": "element_23dx", + "category": "FIELD", + "type": "INPUT", + "variant": "PASSWORD", + "config": { + "name": "password", + "type": "password", + "hint": "", + "label": "Password", + "required": true, + "placeholder": "Enter your password" + } + }, + { + "id": "element_56jd", + "category": "BUTTON", + "type": "BUTTON", + "variant": "PRIMARY", + "action": { + "type": "EXECUTOR", + "executor": { + "name": "PasswordOnboardExecutor" + }, + "nextId": "step_a5sf" + }, + "config": { + "text": "Continue" + } + } + ] + }, + { + "id": "element_gd43", + "category": "BUTTON", + "type": "BUTTON", + "variant": "PRIMARY", + "action": { + "type": "NEXT", + "nextId": "step_dfr2" + }, + "config": { + "text": "Continue with Google" + } + } + ] + } + }, + { + "id": "step_a5sf", + "type": "VIEW", + "width": 120, + "height": 240, + "coordinateX": 0, + "coordinateY": 0, + "data": { + "components": [ + { + "id": "component_232d", + "category": "BLOCK", + "type": "FORM", + "components": [ + { + "id": "element_gd43", + "category": "FIELD", + "type": "INPUT", + "variant": "TEXT", + "config": { + "type": "text", + "hint": "", + "label": "Email", + "required": true, + "placeholder": "Enter your email" + } + }, + { + "id": "element_23dx", + "category": "FIELD", + "type": "INPUT", + "variant": "TEXT", + "config": { + "type": "text", + "hint": "", + "label": "Phone", + "required": true, + "placeholder": "Enter your phone" + } + }, + { + "id": "element_56jd", + "category": "BUTTON", + "type": "BUTTON", + "variant": "PRIMARY", + "action": { + "type": "NEXT", + "nextId": "COMPLETE" + }, + "config": { + "text": "Complete Registration" + } + } + ] + } + ] + } + }, + { + "id": "step_dfr2", + "type": "REDIRECTION", + "width": 120, + "height": 240, + "coordinateX": 0, + "coordinateY": 0, + "data": { + "action": { + "type": "EXECUTOR", + "executor": { + "name": "GoogleOIDCAuthenticator", + "idp_name": "Google" + }, + "nextId": "COMPLETE" + } + } + } + ] +} diff --git a/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/resources/testng.xml b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/resources/testng.xml new file mode 100644 index 000000000000..afd12216f02d --- /dev/null +++ b/components/registration-framework/org.wso2.carbon.identity.user.registration.mgt/src/test/resources/testng.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + diff --git a/components/registration-framework/pom.xml b/components/registration-framework/pom.xml new file mode 100644 index 000000000000..2181b481bfbf --- /dev/null +++ b/components/registration-framework/pom.xml @@ -0,0 +1,41 @@ + + + + + + org.wso2.carbon.identity.framework + identity-framework + 7.8.28-SNAPSHOT + ../../pom.xml + + + 4.0.0 + registration-framework + pom + WSO2 Carbon - Provisioning Aggregator Module + + This is a Carbon bundle that represents the registration framework. + + http://wso2.org + + + org.wso2.carbon.identity.user.registration.mgt + + + diff --git a/components/user-mgt/org.wso2.carbon.identity.user.registration/src/main/java/org/wso2/carbon/identity/user/registration/internal/UserRegistrationDSComponent.java b/components/user-mgt/org.wso2.carbon.identity.user.registration/src/main/java/org/wso2/carbon/identity/user/registration/internal/UserRegistrationDSComponent.java index d196e1bbd5d9..c3183d5b18f7 100644 --- a/components/user-mgt/org.wso2.carbon.identity.user.registration/src/main/java/org/wso2/carbon/identity/user/registration/internal/UserRegistrationDSComponent.java +++ b/components/user-mgt/org.wso2.carbon.identity.user.registration/src/main/java/org/wso2/carbon/identity/user/registration/internal/UserRegistrationDSComponent.java @@ -45,10 +45,10 @@ public static RegistryService getRegistryService() { } @Reference( - name = "registry.service", - service = org.wso2.carbon.registry.core.service.RegistryService.class, - cardinality = ReferenceCardinality.MANDATORY, - policy = ReferencePolicy.DYNAMIC, + name = "registry.service", + service = org.wso2.carbon.registry.core.service.RegistryService.class, + cardinality = ReferenceCardinality.MANDATORY, + policy = ReferencePolicy.DYNAMIC, unbind = "unsetRegistryService") protected void setRegistryService(RegistryService registryService) { if (log.isDebugEnabled()) { @@ -62,10 +62,10 @@ public static RealmService getRealmService() { } @Reference( - name = "user.realmservice.default", - service = org.wso2.carbon.user.core.service.RealmService.class, - cardinality = ReferenceCardinality.MANDATORY, - policy = ReferencePolicy.DYNAMIC, + name = "user.realmservice.default", + service = org.wso2.carbon.user.core.service.RealmService.class, + cardinality = ReferenceCardinality.MANDATORY, + policy = ReferencePolicy.DYNAMIC, unbind = "unsetRealmService") protected void setRealmService(RealmService realmService) { if (log.isDebugEnabled()) { @@ -97,5 +97,5 @@ protected void unsetRealmService(RealmService realmService) { } UserRegistrationDSComponent.realmService = null; } -} +} 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 d92f8d5d75d2..4b1b96bd61ba 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 @@ -1548,6 +1548,63 @@ CREATE TABLE IF NOT EXISTS IDN_PUSH_DEVICE_STORE ( UNIQUE (USER_ID) ); +CREATE TABLE IDN_FLOW ( + ID VARCHAR(50) PRIMARY KEY, + TENANT_ID INT, + FLOW_NAME VARCHAR(255), + TYPE VARCHAR(100), + IS_DEFAULT BOOLEAN DEFAULT FALSE, + UNIQUE (TENANT_ID, FLOW_NAME) +); + +CREATE TABLE IDN_FLOW_NODE ( + ID INT PRIMARY KEY AUTO_INCREMENT, + NODE_ID VARCHAR(50), + FLOW_ID VARCHAR(50), + NODE_TYPE VARCHAR(100), + IS_FIRST_NODE BOOLEAN DEFAULT FALSE, + UNIQUE (NODE_ID, FLOW_ID), + FOREIGN KEY (FLOW_ID) REFERENCES IDN_FLOW(ID) ON DELETE CASCADE +); + +CREATE TABLE IDN_FLOW_NODE_MAPPING ( + ID INT PRIMARY KEY AUTO_INCREMENT, + FLOW_NODE_ID INT, + NEXT_NODE_ID INT, + TRIGGERING_ELEMENT VARCHAR(100), + FOREIGN KEY (FLOW_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE, + FOREIGN KEY (NEXT_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE +); + +CREATE TABLE IDN_FLOW_PAGE ( + ID INT PRIMARY KEY AUTO_INCREMENT, + FLOW_ID VARCHAR(50), + FLOW_NODE_ID INT, + STEP_ID VARCHAR(50), + PAGE_CONTENT BLOB, + TYPE VARCHAR(100), + FOREIGN KEY (FLOW_ID) REFERENCES IDN_FLOW(ID) ON DELETE CASCADE, + FOREIGN KEY (FLOW_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE +); + +CREATE TABLE IDN_FLOW_PAGE_META ( + ID INT PRIMARY KEY AUTO_INCREMENT, + PAGE_ID INT, + COORDINATE_X DOUBLE, + COORDINATE_Y DOUBLE, + HEIGHT DOUBLE, + WIDTH DOUBLE, + FOREIGN KEY (PAGE_ID) REFERENCES IDN_FLOW_PAGE(ID) ON DELETE CASCADE +); + +CREATE TABLE IDN_FLOW_NODE_EXECUTOR ( + ID INT PRIMARY KEY AUTO_INCREMENT, + FLOW_NODE_ID INT, + EXECUTOR_NAME VARCHAR(255), + IDP_NAME INT DEFAULT NULL, + FOREIGN KEY (FLOW_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE +); + -- --------------------------- INDEX CREATION ----------------------------- -- IDN_OAUTH2_ACCESS_TOKEN -- CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED); 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 104ddfb036cd..4ea83f39ebd1 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 @@ -1581,6 +1581,68 @@ CREATE TABLE IF NOT EXISTS IDN_PUSH_DEVICE_STORE ( UNIQUE (USER_ID) ) DEFAULT CHARACTER SET latin1 ENGINE INNODB; +-- Create IDN_FLOW table +CREATE TABLE IDN_FLOW ( + ID VARCHAR(50) PRIMARY KEY, -- Unique identifier for the flow + TENANT_ID INT, -- References the tenant ID + FLOW_NAME VARCHAR(255), -- Human-readable name of the flow + TYPE VARCHAR(100), -- Type of flow (e.g., "user_registration", "approval_process") + IS_DEFAULT BOOLEAN DEFAULT FALSE, -- Indicates if this is the default flow + UNIQUE (TENANT_ID, FLOW_NAME) -- Ensures a unique flow name per tenant +); + +-- Create IDN_FLOW_NODE table with auto-incremental ID +CREATE TABLE IDN_FLOW_NODE ( + ID INT PRIMARY KEY AUTO_INCREMENT, + NODE_ID VARCHAR(50), + FLOW_ID VARCHAR(50), + NODE_TYPE VARCHAR(100), + IS_FIRST_NODE BOOLEAN DEFAULT FALSE, + UNIQUE (NODE_ID, FLOW_ID), + FOREIGN KEY (FLOW_ID) REFERENCES IDN_FLOW(ID) ON DELETE CASCADE -- Delete all nodes when a flow is deleted +); + +-- Create IDN_FLOW_NODE_MAPPING table (to store multiple next nodes per node) +CREATE TABLE IDN_FLOW_NODE_MAPPING ( + ID INT PRIMARY KEY AUTO_INCREMENT, -- Unique auto-increment ID + FLOW_NODE_ID INT, -- References the current node + NEXT_NODE_ID INT, -- References the next node + TRIGGERING_ELEMENT VARCHAR(100), -- Defines the UI/event element triggering the transition + FOREIGN KEY (FLOW_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE, -- Delete mappings when node is deleted + FOREIGN KEY (NEXT_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) +); + +CREATE TABLE IDN_FLOW_PAGE ( + ID INT PRIMARY KEY AUTO_INCREMENT, + FLOW_ID VARCHAR(50), -- References IDN_FLOW + FLOW_NODE_ID INT, -- References IDN_FLOW_NODE + STEP_ID VARCHAR(50), -- Stores step identifier for the page + PAGE_CONTENT BLOB, + TYPE VARCHAR(100), -- Page type (e.g., "FORM", "CONFIRMATION") + FOREIGN KEY (FLOW_ID) REFERENCES IDN_FLOW(ID) ON DELETE CASCADE, + FOREIGN KEY (FLOW_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE +); + +-- Create IDN_FLOW_PAGE_META table (New) +CREATE TABLE IDN_FLOW_PAGE_META ( + ID INT PRIMARY KEY AUTO_INCREMENT, + PAGE_ID INT, + COORDINATE_X DOUBLE, + COORDINATE_Y DOUBLE, + HEIGHT DOUBLE, + WIDTH DOUBLE, + FOREIGN KEY (PAGE_ID) REFERENCES IDN_FLOW_PAGE(ID) ON DELETE CASCADE -- Delete metadata when page is deleted +); + +-- Create IDN_FLOW_NODE_EXECUTOR table +CREATE TABLE IDN_FLOW_NODE_EXECUTOR ( + ID INT PRIMARY KEY AUTO_INCREMENT, + FLOW_NODE_ID INT, -- References IDN_FLOW_NODE + EXECUTOR_NAME VARCHAR(255), + IDP_NAME INT DEFAULT NULL, -- Default to NULL + FOREIGN KEY (FLOW_NODE_ID) REFERENCES IDN_FLOW_NODE(ID) ON DELETE CASCADE -- Delete executors when node is deleted +); + -- --------------------------- INDEX CREATION ----------------------------- -- IDN_OAUTH2_ACCESS_TOKEN -- CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED); diff --git a/features/registration-framework/org.wso2.carbon.identity.registration.framework.feature/pom.xml b/features/registration-framework/org.wso2.carbon.identity.registration.framework.feature/pom.xml new file mode 100644 index 000000000000..7bf56a8429f3 --- /dev/null +++ b/features/registration-framework/org.wso2.carbon.identity.registration.framework.feature/pom.xml @@ -0,0 +1,71 @@ + + + + + org.wso2.carbon.identity.framework + registration-framework-feature + 7.8.28-SNAPSHOT + ../pom.xml + + + 4.0.0 + org.wso2.carbon.identity.registration.framework.feature + pom + Registration Framework Feature + http://wso2.org + This feature contains the bundles required for registration framework aggregated functionality + + + + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.user.registration.mgt.server.feature + zip + + + + + + + org.wso2.maven + carbon-p2-plugin + ${carbon.p2.plugin.version} + + + 4-p2-feature-generation + package + + p2-feature-gen + + + org.wso2.carbon.identity.registration.framework + ../../etc/feature.properties + + org.wso2.carbon.identity.framework:org.wso2.carbon.identity.user.registration.mgt.server.feature + + + + + + + + + diff --git a/features/registration-framework/org.wso2.carbon.identity.user.registration.mgt.server.feature/pom.xml b/features/registration-framework/org.wso2.carbon.identity.user.registration.mgt.server.feature/pom.xml new file mode 100644 index 000000000000..5936ffda6809 --- /dev/null +++ b/features/registration-framework/org.wso2.carbon.identity.user.registration.mgt.server.feature/pom.xml @@ -0,0 +1,74 @@ + + + + + + org.wso2.carbon.identity.framework + registration-framework-feature + 7.8.28-SNAPSHOT + ../pom.xml + + + 4.0.0 + org.wso2.carbon.identity.user.registration.mgt.server.feature + pom + User Registration Management Feature + https://wso2.com + This feature contains the core bundles required for User Registration Management + + + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.user.registration.mgt + + + + + + + org.wso2.maven + carbon-p2-plugin + ${carbon.p2.plugin.version} + + + 4-p2-feature-generation + package + + p2-feature-gen + + + org.wso2.carbon.identity.user.registration.mgt.server + ../../etc/feature.properties + + + org.wso2.carbon.p2.category.type:server + + + + + org.wso2.carbon.identity.framework:org.wso2.carbon.identity.user.registration.mgt + + + + + + + + + diff --git a/features/registration-framework/pom.xml b/features/registration-framework/pom.xml new file mode 100644 index 000000000000..8e8f192f72ed --- /dev/null +++ b/features/registration-framework/pom.xml @@ -0,0 +1,39 @@ + + + + + + org.wso2.carbon.identity.framework + identity-framework + 7.8.28-SNAPSHOT + ../../pom.xml + + + 4.0.0 + registration-framework-feature + pom + WSO2 Carbon - Registration Framework Module + http://wso2.org + + + org.wso2.carbon.identity.user.registration.mgt.server.feature + org.wso2.carbon.identity.registration.framework.feature + + + diff --git a/pom.xml b/pom.xml index 48d7c4628b9b..3e7bc24bf9ef 100644 --- a/pom.xml +++ b/pom.xml @@ -77,6 +77,7 @@ components/certificate-mgt components/servlet-mgt components/system-config-mgt + components/registration-framework features/extension-mgt components/consent-server-configs-mgt features/security-mgt @@ -116,6 +117,7 @@ features/certificate-mgt features/servlet-mgt features/system-config-mgt + features/registration-framework @@ -755,6 +757,12 @@ org.wso2.carbon.identity.ai.service.mgt.server.feature ${project.version} + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.user.registration.mgt.server.feature + zip + ${project.version} + com.google.api-client google-api-client @@ -1777,6 +1785,11 @@ org.wso2.carbon.identity.ai.service.mgt ${project.version} + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.user.registration.mgt + ${project.version} + org.wso2.carbon.identity.framework org.wso2.carbon.identity.servlet.mgt