From 9725a5b0e52fb092b8c34da652dccd882e2888fd Mon Sep 17 00:00:00 2001 From: malithie Date: Mon, 22 Jul 2024 12:04:16 +0530 Subject: [PATCH 01/21] Add action execution component. --- .../pom.xml | 180 ++++++++++ .../ActionExecutionRequestBuilder.java | 37 ++ .../ActionExecutionRequestBuilderFactory.java | 52 +++ .../ActionExecutionResponseProcessor.java | 46 +++ ...tionExecutionResponseProcessorFactory.java | 53 +++ .../execution/ActionExecutorService.java | 36 ++ .../execution/ActionExecutorServiceImpl.java | 340 ++++++++++++++++++ .../exception/ActionExecutionException.java | 36 ++ .../exception/ActionInvocationException.java | 32 ++ .../ActionExecutionServiceComponent.java | 66 ++++ ...ActionExecutionServiceComponentHolder.java | 51 +++ .../model/ActionExecutionRequest.java | 117 ++++++ .../model/ActionExecutionResponse.java | 50 +++ .../model/ActionExecutionStatus.java | 58 +++ .../model/ActionInvocationErrorResponse.java | 83 +++++ .../model/ActionInvocationResponse.java | 121 +++++++ .../ActionInvocationSuccessResponse.java | 56 +++ .../action/execution/model/ActionType.java | 28 ++ .../execution/model/AllowedOperation.java | 55 +++ .../action/execution/model/Application.java | 45 +++ .../action/execution/model/Event.java | 59 +++ .../action/execution/model/Organization.java | 46 +++ .../execution/model/PerformableOperation.java | 64 ++++ .../action/execution/model/Request.java | 48 +++ .../action/execution/model/Tenant.java | 45 +++ .../identity/action/execution/model/User.java | 43 +++ .../action/execution/model/UserStore.java | 57 +++ .../action/execution/util/APIClient.java | 199 ++++++++++ .../action/execution/util/AuthMethods.java | 136 +++++++ .../execution/util/OperationComparator.java | 59 +++ .../action/execution/util/APIClientTest.java | 72 ++++ .../src/test/resources/testng.xml | 22 ++ components/action-mgt/pom.xml | 1 + 33 files changed, 2393 insertions(+) create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilder.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilderFactory.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessor.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessorFactory.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorService.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionException.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionInvocationException.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponentHolder.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionRequest.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionResponse.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionStatus.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationErrorResponse.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationSuccessResponse.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionType.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/AllowedOperation.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Application.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Event.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Organization.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/PerformableOperation.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Request.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Tenant.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/User.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/UserStore.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/AuthMethods.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/OperationComparator.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/java/org/wso2/carbon/identity/action/execution/util/APIClientTest.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/resources/testng.xml diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml b/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml new file mode 100644 index 000000000000..1075dbde2e0c --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml @@ -0,0 +1,180 @@ + + + + + + org.wso2.carbon.identity.framework + action-mgt + 7.3.42-SNAPSHOT + ../pom.xml + + + 4.0.0 + org.wso2.carbon.identity.action.execution + bundle + WSO2 Identity - Action Executor Component + Action execution backend component + http://wso2.org + + + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.core + + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.action.management + + + com.fasterxml.jackson.core + jackson-databind + + + + org.testng + testng + test + + + org.mockito + mockito-core + test + + + org.mockito + mockito-testng + test + + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + + ${project.artifactId} + + ${project.artifactId} + + org.wso2.carbon.identity.action.execution.internal, + + + !org.wso2.carbon.identity.action.execution.internal, + org.wso2.carbon.identity.action.execution.*; + version="${carbon.identity.package.export.version}" + + + org.wso2.carbon.identity.action.management; + version="${carbon.identity.package.import.version.range}", + org.apache.commons.lang; version="${commons-lang.wso2.osgi.version.range}", + org.apache.commons.logging; version="${import.package.version.commons.logging}", + org.apache.commons.collections; version="${commons-collections.wso2.osgi.version.range}", + org.osgi.framework; version="${osgi.framework.imp.pkg.version.range}", + org.osgi.service.component; version="${osgi.service.component.imp.pkg.version.range}", + org.wso2.carbon.utils; version="${carbon.kernel.package.import.version.range}", + com.fasterxml.jackson.databind.*; + version="${com.fasterxml.jackson.annotation.version.range}", + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven.surefire.plugin.version} + + + --add-opens java.xml/jdk.xml.internal=ALL-UNNAMED + --add-exports java.base/jdk.internal.loader=ALL-UNNAMED + + + src/test/resources/testng.xml + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + default-prepare-agent + + prepare-agent + + + + default-prepare-agent-integration + + prepare-agent-integration + + + + default-report + + report + + + + default-report-integration + + report-integration + + + + default-check + + check + + + + + BUNDLE + + + COMPLEXITY + COVEREDRATIO + 0.90 + + + + + + + + + + com.github.spotbugs + spotbugs-maven-plugin + + ../../../spotbugs-exclude.xml + Max + Low + true + + + + + diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilder.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilder.java new file mode 100644 index 000000000000..eead9b1cebd2 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilder.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution; + +import org.wso2.carbon.identity.action.execution.exception.ActionExecutionException; +import org.wso2.carbon.identity.action.execution.model.ActionExecutionRequest; +import org.wso2.carbon.identity.action.execution.model.ActionType; + +import java.util.Map; + +/** + * This interface defines the Action Execution Request Builder. + * Action Execution Request Builder is the component that is responsible for building the Action Execution Request + * based on the action type and the event context. + */ +public interface ActionExecutionRequestBuilder { + + ActionExecutionRequest buildActionInvocationRequest(ActionType actionType, Map eventContext) throws + ActionExecutionException; + +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilderFactory.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilderFactory.java new file mode 100644 index 000000000000..958f9429efee --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilderFactory.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution; + +import org.wso2.carbon.identity.action.execution.exception.ActionExecutionException; +import org.wso2.carbon.identity.action.execution.model.ActionExecutionRequest; +import org.wso2.carbon.identity.action.execution.model.ActionType; + +import java.util.HashMap; +import java.util.Map; + +/** + * This class defines the Action Execution Request Builder Factory. + * Action Execution Request Builder Factory is the component that is responsible for building + * {@link ActionExecutionRequest} + * based on the action type and the event context. + */ +public class ActionExecutionRequestBuilderFactory { + + private static final Map actionInvocationRequestBuilders = + new HashMap<>(); + + public static ActionExecutionRequest buildActionExecutionRequest(ActionType actionType, + Map eventContext) + throws ActionExecutionException { + + return actionInvocationRequestBuilders.get(actionType).buildActionInvocationRequest(actionType, eventContext); + } + + public static void registerActionExecutionRequestBuilder(ActionType actionType, + ActionExecutionRequestBuilder actionExecutionRequestBuilder) { + + actionInvocationRequestBuilders.put(actionType, actionExecutionRequestBuilder); + } + +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessor.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessor.java new file mode 100644 index 000000000000..666a4a578a4f --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessor.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution; + +import org.wso2.carbon.identity.action.execution.exception.ActionExecutionException; +import org.wso2.carbon.identity.action.execution.model.ActionInvocationErrorResponse; +import org.wso2.carbon.identity.action.execution.model.ActionInvocationSuccessResponse; +import org.wso2.carbon.identity.action.execution.model.ActionExecutionStatus; +import org.wso2.carbon.identity.action.execution.model.ActionType; +import org.wso2.carbon.identity.action.execution.model.Event; + +import java.util.Map; + +/** + * This interface defines the Action Execution Response Processor. + * Action Execution Response Processor is the component that is responsible for processing the response received + * from the action execution. + */ +public interface ActionExecutionResponseProcessor { + + ActionExecutionStatus processSuccessResponse(ActionType actionType, Map eventContext, + Event actionEvent, + ActionInvocationSuccessResponse successResponse) throws + ActionExecutionException; + + ActionExecutionStatus processErrorResponse(ActionType actionType, Map eventContext, + Event actionEvent, + ActionInvocationErrorResponse errorResponse) throws + ActionExecutionException; +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessorFactory.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessorFactory.java new file mode 100644 index 000000000000..6c37e13d1db0 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessorFactory.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution; + +import org.wso2.carbon.identity.action.execution.model.ActionType; + +import java.util.HashMap; +import java.util.Map; + +/** + * This class defines the Action Execution Response Processor Factory. + * Action Execution Response Processor Factory is the component that is responsible for providing the + * {@link ActionExecutionResponseProcessor} based on the action type. + */ +public class ActionExecutionResponseProcessorFactory { + + private static final Map actionInvocationResponseProcessors = + new HashMap<>(); + + public static ActionExecutionResponseProcessor getActionExecutionResponseProcessor(ActionType actionType) { + + switch (actionType) { + case PRE_ISSUE_ACCESS_TOKEN: + return actionInvocationResponseProcessors.get(ActionType.PRE_ISSUE_ACCESS_TOKEN); + default: + return null; + } + } + + public static void registerActionExecutionResponseProcessor(ActionType actionType, + ActionExecutionResponseProcessor + actionExecutionResponseProcessor) { + + actionInvocationResponseProcessors.put(actionType, actionExecutionResponseProcessor); + } + +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorService.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorService.java new file mode 100644 index 000000000000..2d4de228bd7f --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorService.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution; + +import org.wso2.carbon.identity.action.execution.exception.ActionExecutionException; +import org.wso2.carbon.identity.action.execution.model.ActionType; + +import java.util.Map; + +/** + * This interface defines the Action Executor Service. + * Action Executor Service is the component that is responsible for executing the action based on the action type + * and the event context. + */ +public interface ActionExecutorService { + + void execute(ActionType actionType, Map eventContext, String tenantDomain) throws + ActionExecutionException; + +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java new file mode 100644 index 000000000000..c98c4bd294ef --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.identity.action.execution.exception.ActionExecutionException; +import org.wso2.carbon.identity.action.execution.internal.ActionExecutionServiceComponentHolder; +import org.wso2.carbon.identity.action.execution.model.ActionExecutionRequest; +import org.wso2.carbon.identity.action.execution.model.ActionInvocationErrorResponse; +import org.wso2.carbon.identity.action.execution.model.ActionInvocationResponse; +import org.wso2.carbon.identity.action.execution.model.ActionInvocationSuccessResponse; +import org.wso2.carbon.identity.action.execution.model.ActionType; +import org.wso2.carbon.identity.action.execution.model.AllowedOperation; +import org.wso2.carbon.identity.action.execution.model.PerformableOperation; +import org.wso2.carbon.identity.action.execution.util.APIClient; +import org.wso2.carbon.identity.action.execution.util.AuthMethods; +import org.wso2.carbon.identity.action.execution.util.OperationComparator; +import org.wso2.carbon.identity.action.management.exception.ActionMgtException; +import org.wso2.carbon.identity.action.management.model.Action; +import org.wso2.carbon.identity.action.management.model.AuthProperty; +import org.wso2.carbon.identity.action.management.model.AuthType; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * ActionInvocationService. + */ + +public class ActionExecutorServiceImpl implements ActionExecutorService { + + private static final Log log = LogFactory.getLog(ActionExecutorServiceImpl.class); + + private static final ActionExecutorServiceImpl instance = new ActionExecutorServiceImpl(); + private final APIClient apiClient; + + private ActionExecutorServiceImpl() { + + apiClient = new APIClient(); + } + + public static ActionExecutorServiceImpl getInstance() { + + return instance; + } + + public void execute(ActionType actionType, Map eventContext, String tenantDomain) + throws ActionExecutionException { + + ActionExecutionRequest actionRequest = + ActionExecutionRequestBuilderFactory.buildActionExecutionRequest(actionType, eventContext); + + List actions; + try { + actions = ActionExecutionServiceComponentHolder.getInstance().getActionManagementService() + .getActionsByActionType(actionType.name(), tenantDomain); + } catch (ActionMgtException e) { + //todo: throw and block ? + log.warn("Skip executing actions for action type: " + actionType.name() + + ". Error occurred while retrieving actions."); + if (log.isDebugEnabled()) { + log.debug("Error: " + e.getMessage(), e); + } + + return; + } + + if (actions.isEmpty()) { + log.info("No actions found for action type: " + actionType.name()); + return; + } + + if (actions.size() > 1) { + // when multiple actions are supported for an action type the logic below needs to be improved such that, + // a successful processing from one action becomes the input to the successor. + throw new ActionExecutionException("Multiple actions found for action type: " + actionType.name() + + "Current implementation doesn't support for multiple actions for a single action type."); + } + + ActionExecutionResponseProcessor actionExecutionResponseProcessor = + ActionExecutionResponseProcessorFactory.getActionExecutionResponseProcessor(actionType); + if (actionExecutionResponseProcessor == null) { + throw new ActionExecutionException("No response processor found for action type: " + actionType); + } + + for (Action action : actions) { + executeAction(action, actionRequest, actionType, eventContext, actionExecutionResponseProcessor); + } + } + + private void executeAction(Action action, ActionExecutionRequest actionRequest, ActionType actionType, + Map eventContext, + ActionExecutionResponseProcessor actionExecutionResponseProcessor) + throws ActionExecutionException { + + String apiEndpoint = action.getEndpoint().getUri(); + AuthType endpointAuthentication = action.getEndpoint().getAuthentication(); + AuthMethods.AuthMethod authenticationMethod = null; + + try { + authenticationMethod = getAuthenticationMethod(action.getId(), endpointAuthentication); + String payload = serializeRequest(actionRequest); + + logActionRequest(apiEndpoint, actionType, action.getId(), authenticationMethod, payload); + + ActionInvocationResponse actionInvocationResponse = + apiClient.callAPI(apiEndpoint, authenticationMethod, payload); + processActionResponse(actionInvocationResponse, actionType, eventContext, actionRequest, action.getId(), + apiEndpoint, authenticationMethod, actionExecutionResponseProcessor + ); + + } catch (ActionMgtException | JsonProcessingException e) { + //todo: Add to diagnostics + log.warn("Skip executing action: " + action.getId() + " for action type: " + actionType + + ". Error occurred during action execution.", e); + } + } + + private void logActionRequest(String apiEndpoint, ActionType actionType, String actionId, + AuthMethods.AuthMethod authenticationMethod, String payload) { + + //todo: Add to diagnostics + if (log.isDebugEnabled()) { + log.debug(String.format( + "Calling API: %s for action type: %s action id: %s with authentication: %s payload: %s", + apiEndpoint, + actionType, + actionId, + Optional.ofNullable(authenticationMethod).map(AuthMethods.AuthMethod::getAuthType) + .orElse("NONE"), + payload)); + } + } + + private void processActionResponse(ActionInvocationResponse actionInvocationResponse, ActionType actionType, + Map eventContext, ActionExecutionRequest actionRequest, + String actionId, String apiEndpoint, + AuthMethods.AuthMethod authenticationMethod, + ActionExecutionResponseProcessor actionExecutionResponseProcessor) + throws ActionExecutionException { + + if (actionInvocationResponse.isSuccess()) { + processSuccessResponse((ActionInvocationSuccessResponse) actionInvocationResponse.getResponse(), actionType, + eventContext, actionRequest, actionId, apiEndpoint, authenticationMethod, + actionExecutionResponseProcessor); + } else if (actionInvocationResponse.isError() && actionInvocationResponse.getResponse() != null) { + processErrorResponse((ActionInvocationErrorResponse) actionInvocationResponse.getResponse(), actionType, + eventContext, actionRequest, actionId, apiEndpoint, authenticationMethod, + actionExecutionResponseProcessor); + } else { + logErrorResponse(actionInvocationResponse, actionType, actionId, apiEndpoint, authenticationMethod); + } + } + + private void processSuccessResponse(ActionInvocationSuccessResponse successResponse, ActionType actionType, + Map eventContext, ActionExecutionRequest actionRequest, + String actionId, String apiEndpoint, + AuthMethods.AuthMethod authenticationMethod, + ActionExecutionResponseProcessor actionExecutionResponseProcessor) + throws ActionExecutionException { + + if (log.isDebugEnabled()) { + // todo: add to diagnostic logs + logSuccessResponse(successResponse, actionType, actionId, apiEndpoint, authenticationMethod); + } + + List allowedPerformableOperations = + validatePerformableOperations(actionRequest, successResponse); + ActionInvocationSuccessResponse.Builder successResponseBuilder = + new ActionInvocationSuccessResponse.Builder().setOperations(allowedPerformableOperations); + actionExecutionResponseProcessor.processSuccessResponse(actionType, eventContext, actionRequest.getEvent(), + successResponseBuilder.build()); + } + + private void processErrorResponse(ActionInvocationErrorResponse errorResponse, ActionType actionType, + Map eventContext, ActionExecutionRequest actionRequest, + String actionId, String apiEndpoint, + AuthMethods.AuthMethod authenticationMethod, + ActionExecutionResponseProcessor actionExecutionResponseProcessor) + throws ActionExecutionException { + + if (log.isDebugEnabled()) { + // todo: add to diagnostic logs + logErrorResponse(errorResponse, actionType, actionId, apiEndpoint, authenticationMethod); + } + + actionExecutionResponseProcessor.processErrorResponse(actionType, eventContext, actionRequest.getEvent(), + errorResponse); + } + + private void logSuccessResponse(ActionInvocationSuccessResponse successResponse, ActionType actionType, + String actionId, String apiEndpoint, AuthMethods.AuthMethod authenticationMethod) { + + try { + String responseBody = serializeSuccessResponse(successResponse); + log.debug(String.format( + "Received success response from API: %s for action type: %s action id: %s with authentication: %s. Response: %s", + apiEndpoint, + actionType, + actionId, + Optional.ofNullable(authenticationMethod).map(AuthMethods.AuthMethod::getAuthType) + .orElse("NONE"), + responseBody)); + } catch (JsonProcessingException e) { + log.error("Error occurred while deserializing the success response for action: " + + actionId + " for action type: " + actionType, e); + } + } + + private void logErrorResponse(ActionInvocationErrorResponse errorResponse, ActionType actionType, + String actionId, String apiEndpoint, AuthMethods.AuthMethod authenticationMethod) { + + try { + String responseBody = serializeErrorResponse(errorResponse); + log.debug(String.format( + "Received error response from API: %s for action type: %s action id: %s with authentication: %s. Response: %s", + apiEndpoint, + actionType, + actionId, + Optional.ofNullable(authenticationMethod).map(AuthMethods.AuthMethod::getAuthType) + .orElse("NONE"), + responseBody)); + } catch (JsonProcessingException e) { + log.error("Error occurred while deserializing the error response for action: " + + actionId + " for action type: " + actionType, e); + } + } + + private void logErrorResponse(ActionInvocationResponse actionInvocationResponse, ActionType actionType, + String actionId, String apiEndpoint, AuthMethods.AuthMethod authenticationMethod) { + // todo: add to diagnostic logs + if (log.isDebugEnabled()) { + log.debug(String.format( + "Failed to call API: %s for action type: %s action id: %s with authentication: %s. Error: %s", + apiEndpoint, + actionType, + actionId, + Optional.ofNullable(authenticationMethod).map(AuthMethods.AuthMethod::getAuthType) + .orElse("NONE"), + actionInvocationResponse.getErrorLog() != null ? actionInvocationResponse.getErrorLog() : + "Unknown")); + } + } + + private String serializeRequest(ActionExecutionRequest request) throws JsonProcessingException { + + ObjectMapper requestObjectmapper = new ObjectMapper(); + requestObjectmapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + requestObjectmapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + return requestObjectmapper.writeValueAsString(request); + } + + private String serializeSuccessResponse(ActionInvocationSuccessResponse response) throws JsonProcessingException { + + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.writeValueAsString(response); + } + + private String serializeErrorResponse(ActionInvocationErrorResponse response) throws JsonProcessingException { + + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.writeValueAsString(response); + } + + private List validatePerformableOperations(ActionExecutionRequest request, + ActionInvocationSuccessResponse response) { + + List allowedOperations = request.getAllowedOperations(); + + List allowedPerformableOperations = response.getOperations().stream() + .filter(performableOperation -> allowedOperations.stream() + .anyMatch(allowedOperation -> OperationComparator.compare(allowedOperation, + performableOperation))) + .collect(Collectors.toList()); + + if (log.isDebugEnabled()) { + // todo: add to diagnostics + List allowedOps = new ArrayList<>(); + List notAllowedOps = new ArrayList<>(); + + response.getOperations().forEach(operation -> { + String operationDetails = "Operation: " + operation.getOp() + " with path: " + operation.getPath(); + if (allowedPerformableOperations.contains(operation)) { + allowedOps.add(operationDetails); + } else { + notAllowedOps.add(operationDetails); + } + }); + + String logMessage = "Allowed Operations: " + String.join(", ", allowedOps) + + ". Not Allowed Operations: " + String.join(", ", notAllowedOps); + log.debug(logMessage); + } + + return allowedPerformableOperations; + } + + private AuthMethods.AuthMethod getAuthenticationMethod(String actionId, AuthType authType) + throws ActionMgtException { + + List authProperties = authType.getPropertiesWithDecryptedValues(actionId); + + switch (authType.getType()) { + case BASIC: + return new AuthMethods.BasicAuth(authProperties); + case BEARER: + return new AuthMethods.BearerAuth(authProperties); + case API_KEY: + return new AuthMethods.APIKeyAuth(authProperties); + case NONE: + return null; + default: + throw new ActionMgtException("Unsupported authentication type: " + authType.getType()); + + } + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionException.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionException.java new file mode 100644 index 000000000000..9002c71a2573 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionException.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.exception; + +/** + * Exception class for Action Execution. + */ +public class ActionExecutionException extends Exception { + + public ActionExecutionException(String message) { + + super(message); + } + + public ActionExecutionException(String message, Throwable cause) { + + super(message, cause); + } + +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionInvocationException.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionInvocationException.java new file mode 100644 index 000000000000..8c112d740755 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionInvocationException.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.exception; + +public class ActionInvocationException extends Exception { + + public ActionInvocationException(String message) { + + super(message); + } + + public ActionInvocationException(String message, Throwable cause) { + + super(message, cause); + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java new file mode 100644 index 000000000000..69cec1821482 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.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.action.execution.ActionExecutorService; +import org.wso2.carbon.identity.action.execution.ActionExecutorServiceImpl; + +/** + * OSGI service component for the Action execution. + */ +@Component( + name = "action.execution.service.component", + immediate = true +) +public class ActionExecutionServiceComponent { + + private static final Log LOG = LogFactory.getLog(ActionExecutionServiceComponent.class); + + @Activate + protected void activate(ComponentContext context) { + + try { + BundleContext bundleCtx = context.getBundleContext(); + bundleCtx.registerService(ActionExecutorService.class, ActionExecutorServiceImpl.getInstance(), null); + LOG.debug("Action execution bundle is activated"); + } catch (Throwable e) { + LOG.error("Error while initializing Action execution service component.", e); + } + } + + @Deactivate + protected void deactivate(ComponentContext context) { + + try { + BundleContext bundleCtx = context.getBundleContext(); + bundleCtx.ungetService(bundleCtx.getServiceReference(ActionExecutorService.class)); + LOG.debug("Action execution bundle is deactivated"); + } catch (Throwable e) { + LOG.error("Error while deactivating Action execution service component.", e); + } + } + +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponentHolder.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponentHolder.java new file mode 100644 index 000000000000..5acef30d4b6e --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponentHolder.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.internal; + +import org.wso2.carbon.identity.action.management.ActionManagementService; + +/** + * This class holds references for dependent services required for Action Execution Service to function. + */ +public class ActionExecutionServiceComponentHolder { + + private static final ActionExecutionServiceComponentHolder instance = new ActionExecutionServiceComponentHolder(); + + private ActionManagementService actionManagementService; + + private ActionExecutionServiceComponentHolder() { + + } + + public static ActionExecutionServiceComponentHolder getInstance() { + + return instance; + } + + public ActionManagementService getActionManagementService() { + + return actionManagementService; + } + + public void setActionManagementService( + ActionManagementService actionManagementService) { + + this.actionManagementService = actionManagementService; + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionRequest.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionRequest.java new file mode 100644 index 000000000000..04959234c636 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionRequest.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +import org.slf4j.MDC; + +import java.util.List; +import java.util.Optional; + +/** + * This class models the Action Execution Request. + * Action Execution Request is the request object that is passed to the Action Executor Service to execute an action. + * It contains the {@link ActionType}, flow id, {@link Event} and a list of {@link AllowedOperation}. + */ +public class ActionExecutionRequest { + + private static final String CORRELATION_ID_MDC = "Correlation-ID"; + private final ActionType actionType; + private final String flowId; + private final Event event; + private final List allowedOperations; + + public ActionExecutionRequest(Builder builder) { + + this.actionType = builder.actionType; + this.flowId = builder.flowId; + this.event = builder.event; + this.allowedOperations = builder.allowedOperations; + } + + public ActionType getActionType() { + + return actionType; + } + + public String getFlowId() { + + return flowId; + } + + public String getRequestId() { + + return getCorrelationId(); + } + + public Event getEvent() { + + return event; + } + + public List getAllowedOperations() { + + return allowedOperations; + } + + private String getCorrelationId() { + + return Optional.ofNullable(MDC.get(CORRELATION_ID_MDC)).orElse(""); + } + + /** + * Builder for the {@link ActionExecutionRequest}. + */ + public static class Builder { + + private ActionType actionType; + private String flowId; + private Event event; + private List allowedOperations; + + public Builder actionType(ActionType actionType) { + + this.actionType = actionType; + return this; + } + + public Builder flowId(String flowId) { + + this.flowId = flowId; + return this; + } + + public Builder event(Event event) { + + this.event = event; + return this; + } + + public Builder allowedOperations(List allowedOperations) { + + this.allowedOperations = allowedOperations; + return this; + } + + public ActionExecutionRequest build() { + + return new ActionExecutionRequest(this); + } + } +} + diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionResponse.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionResponse.java new file mode 100644 index 000000000000..d638a9983b42 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionResponse.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +import java.util.List; + +/** + * Action Invocation Response. + */ +public class ActionExecutionResponse { + + private String actionStatus; + private List operations; + + public String getActionStatus() { + + return actionStatus; + } + + public void setActionStatus(String actionStatus) { + + this.actionStatus = actionStatus; + } + + public List getOperations() { + + return operations; + } + + public void setOperations(List operations) { + + this.operations = operations; + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionStatus.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionStatus.java new file mode 100644 index 000000000000..4021bce1cef4 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionStatus.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +import java.util.Map; + +/** + * This class models the Action Execution Status. + * Action Execution Status is the status object that is returned by the Action Executor Service after executing an + * action. It contains the status of the action execution and the response context. + */ +public class ActionExecutionStatus { + + private final Status status; + private final Map responseContext; + + public ActionExecutionStatus(Status status, Map responseContext) { + + this.status = status; + this.responseContext = responseContext; + } + + public Status getStatus() { + + return status; + } + + public Map getResponseContext() { + + return responseContext; + } + + /** + * This enum defines the Action Execution Status. + */ + public enum Status { + SUCCESS, + FAILURE, + INCOMPLETE, + ERROR + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationErrorResponse.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationErrorResponse.java new file mode 100644 index 000000000000..b4b5dbeedbfa --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationErrorResponse.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +@JsonDeserialize(builder = ActionInvocationErrorResponse.Builder.class) +public class ActionInvocationErrorResponse implements ActionInvocationResponse.APIResponse { + + private final String error; + private final String errorDescription; + private final String errorUri; + + private ActionInvocationErrorResponse(Builder builder) { + + this.error = builder.error; + this.errorDescription = builder.errorDescription; + this.errorUri = builder.errorUri; + } + + public String getError() { + + return error; + } + + public String getErrorDescription() { + + return errorDescription; + } + + public String getErrorUri() { + + return errorUri; + } + + public static class Builder { + + private String error; + private String errorDescription; + private String errorUri; + + public ActionInvocationErrorResponse.Builder setError(@JsonProperty("error") String error) { + + this.error = error; + return this; + } + + public ActionInvocationErrorResponse.Builder setErrorDescription( + @JsonProperty("errorDescription") String errorDescription) { + + this.errorDescription = errorDescription; + return this; + } + + public ActionInvocationErrorResponse.Builder setErrorUri(@JsonProperty("errorUri") String errorUri) { + + this.errorUri = errorUri; + return this; + } + + public ActionInvocationErrorResponse build() { + + return new ActionInvocationErrorResponse(this); + } + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java new file mode 100644 index 000000000000..8eacc38cc350 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +/** + * This class models the Action Execution Response. + * Action Execution Response is the response object that is returned by the Action Executor Service after executing an + * action. It contains the action status and the operations that needs to be performed. + */ +public class ActionInvocationResponse { + + private int httpStatusCode; + private String actionStatus; + private APIResponse response; + + private boolean retry; + + private String errorLog; + + private ActionInvocationResponse() { + // Private constructor to enforce the use of the Builder + } + + public APIResponse getResponse() { + + return response; + } + + public boolean isSuccess() { + + return "SUCCESS".equalsIgnoreCase(actionStatus); + } + + public boolean isError() { + + return "ERROR".equalsIgnoreCase(actionStatus); + } + + public boolean isRetry() { + + return retry; + } + + public String getErrorLog() { + + return errorLog; + } + + public interface APIResponse { + + } + + public static class Builder { + + private int httpStatusCode; + private String actionStatus; + private APIResponse response; + private boolean retry; + + private String errorLog; + + public Builder setHttpStatusCode(int httpStatusCode) { + + this.httpStatusCode = httpStatusCode; + return this; + } + + public Builder setResponse(APIResponse response) { + + if (response instanceof ActionInvocationSuccessResponse) { + this.actionStatus = "SUCCESS"; + } else if (response instanceof ActionInvocationErrorResponse) { + this.actionStatus = "ERROR"; + } + + this.response = response; + return this; + } + + public Builder setRetry(boolean retry) { + + this.retry = retry; + this.actionStatus = "ERROR"; + return this; + } + + public Builder setErrorLog(String errorLog) { + + this.errorLog = errorLog; + this.actionStatus = "ERROR"; + return this; + } + + public ActionInvocationResponse build() { + + ActionInvocationResponse response = new ActionInvocationResponse(); + response.httpStatusCode = this.httpStatusCode; + response.actionStatus = this.actionStatus; + response.response = this.response; + response.retry = this.retry; + response.errorLog = this.errorLog; + return response; + } + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationSuccessResponse.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationSuccessResponse.java new file mode 100644 index 000000000000..f2961a0294ed --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationSuccessResponse.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +import java.util.List; + +@JsonDeserialize(builder = ActionInvocationSuccessResponse.Builder.class) +public class ActionInvocationSuccessResponse implements ActionInvocationResponse.APIResponse { + + private final List operations; + + private ActionInvocationSuccessResponse(Builder builder) { + + this.operations = builder.operations; + } + + public List getOperations() { + + return operations; + } + + public static class Builder { + + private List operations; + + public Builder setOperations(@JsonProperty("operations") List operations) { + + this.operations = operations; + return this; + } + + public ActionInvocationSuccessResponse build() { + + return new ActionInvocationSuccessResponse(this); + } + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionType.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionType.java new file mode 100644 index 000000000000..293524740010 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionType.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +/** + * This class models the Action Type. + * Action Type is the type of the action that is executed by the Action Executor Service. + */ +public enum ActionType { + PRE_ISSUE_ACCESS_TOKEN, + POST_ISSUE_ACCESS_TOKEN, +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/AllowedOperation.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/AllowedOperation.java new file mode 100644 index 000000000000..b67c8c45f3e9 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/AllowedOperation.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +import java.util.List; + +/** + * This class models the Allowed Operation. + * Allowed Operation is the operation that is allowed to be performed during the execution of a particular action. + * The operation is defined by the operation type and the paths that the operation is allowed to be performed on. + * Allowed operations follow JSON Patch format (RFC 6902) semantics to define the operations that are allowed to be + * performed. + */ +public class AllowedOperation { + + String op; + + List paths; + + public String getOp() { + + return op; + } + + public void setOp(String op) { + + this.op = op; + } + + public List getPaths() { + + return paths; + } + + public void setPaths(List paths) { + + this.paths = paths; + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Application.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Application.java new file mode 100644 index 000000000000..d74aa1cfdbb8 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Application.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +/** + * This class models the Application. + * Application is the entity that represents the application that the action is triggered for. + */ +public class Application { + + String id; + String name; + + public Application(String id, String name) { + + this.id = id; + this.name = name; + } + + public String getId() { + + return id; + } + + public String getName() { + + return name; + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Event.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Event.java new file mode 100644 index 000000000000..cbb5a14ccbb5 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Event.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +/** + * This class models the Event. + * Event is the entity that represents the event that is sent to the Action over Action Execution Request. + * It contains the request, tenant, organization, user, and user store information. + * The abstraction allows to model events with additional context based on the action type. + */ +public abstract class Event { + + protected Request request; + protected Tenant tenant; + protected Organization organization; + protected User user; + protected UserStore userStore; + + public Tenant getTenant() { + + return tenant; + } + + public Organization getOrganization() { + + return organization; + } + + public Request getRequest() { + + return request; + } + + public User getUser() { + + return user; + } + + public UserStore getUserStore() { + + return userStore; + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Organization.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Organization.java new file mode 100644 index 000000000000..c6f642242104 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Organization.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +/** + * This class models the Organization. + * Organization is the entity that represents the organization of the user for whom the action is triggered for. + */ +public class Organization { + + private final String id; + + private final String name; + + public Organization(String id, String name) { + + this.id = id; + this.name = name; + } + + public String getId() { + + return id; + } + + public String getName() { + + return name; + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/PerformableOperation.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/PerformableOperation.java new file mode 100644 index 000000000000..f526744384d3 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/PerformableOperation.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +/** + * This class models the Performable Operation. + * Performable Operation is the operation that is requested to be performed in the system during the execution of an + * action. It contains the operation type, the path of the operation and the value associated with add or replace + * operations. + * Performable operations are expected to follow the JSON Patch format (RFC 6902), providing a standardized way to + * express the operations to be performed. + */ +public class PerformableOperation { + + private String op; + private String path; + private Object value; + + public String getOp() { + + return op; + } + + public void setOp(String op) { + + this.op = op; + } + + public String getPath() { + + return path; + } + + public void setPath(String path) { + + this.path = path; + } + + public Object getValue() { + + return value; + } + + public void setValue(Object value) { + + this.value = value; + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Request.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Request.java new file mode 100644 index 000000000000..653f9363a343 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Request.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * This class models the Request. + * Request is the entity that represents the request that is sent to Action over Action Execution Request. + * Request contains additional headers and additional parameters relevant to the trigger that are sent to the Action. + * The abstraction allows to model requests with additional context based on the action type. + */ +public abstract class Request { + + protected Map additionalHeaders = new HashMap<>(); + protected Map additionalParams = new HashMap<>(); + + // implement a method to get additional headers + public Map getAdditionalHeaders() { + + return additionalHeaders != null ? additionalHeaders : Collections.emptyMap(); + } + + // implement a method to get additional params + public Map getAdditionalParams() { + + return additionalParams != null ? additionalParams : Collections.emptyMap(); + } + +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Tenant.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Tenant.java new file mode 100644 index 000000000000..24c275c06f54 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Tenant.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +/** + * This class models the Tenant. + * Tenant is the entity that represents the tenant of the action trigger. + */ +public class Tenant { + + private final String id; + private final String name; + + public Tenant(String id, String name) { + + this.id = id; + this.name = name; + } + + public String getId() { + + return id; + } + + public String getName() { + + return name; + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/User.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/User.java new file mode 100644 index 000000000000..3944bea48522 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/User.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +/** + * This class models the User. + * User is the entity that represents the user for whom the action is triggered for. + */ +public class User { + + String id; + + public User(String id) { + + this.id = id; + } + + public String getId() { + + return id; + } + public void setId(String id) { + + this.id = id; + } + +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/UserStore.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/UserStore.java new file mode 100644 index 000000000000..e8773729b838 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/UserStore.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +/** + * This class models the UserStore. + * UserStore is the entity that represents the user store of the user for whom the action is triggered for. + * User store is present when the user's profile is managed within the system only. + */ +public class UserStore { + + String id; + String name; + + public UserStore(String name) { + + setName(name); + } + + public String getId() { + + return id; + } + + public String getName() { + + return name; + } + + public void setName(String name) { + + /* + * As of now user store id is generated by encoding the user store name. + */ + this.id = name != null ? Base64.getEncoder().encodeToString(name.getBytes(StandardCharsets.UTF_8)) : null; + this.name = name; + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java new file mode 100644 index 000000000000..9a3b11809b7e --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.wso2.carbon.identity.action.execution.exception.ActionInvocationException; +import org.wso2.carbon.identity.action.execution.model.ActionInvocationErrorResponse; +import org.wso2.carbon.identity.action.execution.model.ActionInvocationSuccessResponse; +import org.wso2.carbon.identity.action.execution.model.ActionInvocationResponse; + +import java.io.IOException; +import java.net.SocketTimeoutException; +import java.nio.charset.StandardCharsets; +import java.util.Optional; + +/** + * This class is responsible for invoking the HTTP endpoint. + */ +public class APIClient { + + private static final Log log = LogFactory.getLog(APIClient.class); + private final CloseableHttpClient httpClient; + + public APIClient() { + + // todo: read connection configurations related to the http client of actions from the server configuration. + // Initialize the http client. Set connection time out to 2s and read time out to 5s. + int readTimeout = 5000; + int connectionRequestTimeout = 2000; + int connectionTimeout = 2000; + + RequestConfig config = RequestConfig.custom() + .setConnectTimeout(connectionTimeout) + .setConnectionRequestTimeout(connectionRequestTimeout) + .setSocketTimeout(readTimeout) + .setRedirectsEnabled(false) + .setRelativeRedirectsAllowed(false) + .build(); + httpClient = HttpClientBuilder.create().setDefaultRequestConfig(config).build(); + } + + public ActionInvocationResponse callAPI(String url, AuthMethods.AuthMethod authMethod, + String payload) { + + HttpPost httpPost = new HttpPost(url); + setRequestEntity(httpPost, payload, authMethod); + + return executeRequest(httpPost).orElse(new ActionInvocationResponse.Builder() + .setErrorLog("Failed to execute the action request or maximum retry attempts reached.") + .build()); + } + + private void setRequestEntity(HttpPost httpPost, String jsonRequest, AuthMethods.AuthMethod authMethod) { + + StringEntity entity = new StringEntity(jsonRequest, StandardCharsets.UTF_8); + if (authMethod != null) { + authMethod.applyAuth(httpPost); + } + httpPost.setEntity(entity); + httpPost.setHeader("Accept", "application/json"); + httpPost.setHeader("Content-type", "application/json"); + } + + private Optional executeRequest(HttpPost request) { + + int attempts = 0; + int retryCount = 2; // todo: read from server configurations + + while (attempts < retryCount) { + try (CloseableHttpResponse response = httpClient.execute(request)) { + ActionInvocationResponse actionInvocationResponse = handleResponse(response); + if (!actionInvocationResponse.isError() || !actionInvocationResponse.isRetry()) { + return Optional.of(actionInvocationResponse); + } + //todo: add to diagnostic logs + log.warn("API: " + request.getURI() + " seems to be unavailable. Retrying the request. Attempt " + + (attempts + 1) + " of " + retryCount); + } catch (ConnectTimeoutException | SocketTimeoutException e) { + //todo: add to diagnostic logs + log.warn("Request for API: " + request.getURI() + " timed out. Retrying the request. Attempt " + + (attempts + 1) + " of " + retryCount); + } catch (Exception e) { + //todo: add to diagnostic logs + log.error("Request for API: " + request.getURI() + " failed due to an error.", e); + break; + } finally { + request.releaseConnection(); + } + attempts++; + } + + log.warn("Maximum retry attempts reached for API: " + request.getURI()); + return Optional.empty(); + } + + private ActionInvocationResponse handleResponse(HttpResponse response) { + + int statusCode = response.getStatusLine().getStatusCode(); + HttpEntity responseEntity = response.getEntity(); + + ActionInvocationResponse.Builder actionInvocationResponseBuilder = new ActionInvocationResponse.Builder(); + actionInvocationResponseBuilder.setHttpStatusCode(statusCode); + + try { + switch (statusCode) { + case HttpStatus.SC_OK: + ActionInvocationSuccessResponse successResponse = handleSuccessResponse(responseEntity); + actionInvocationResponseBuilder.setResponse(successResponse); + break; + case HttpStatus.SC_BAD_REQUEST: + case HttpStatus.SC_INTERNAL_SERVER_ERROR: + ActionInvocationErrorResponse errorResponse = handleErrorResponse(responseEntity); + actionInvocationResponseBuilder.setResponse(errorResponse); + break; + case HttpStatus.SC_UNAUTHORIZED: + break; + case HttpStatus.SC_BAD_GATEWAY: + case HttpStatus.SC_SERVICE_UNAVAILABLE: + case HttpStatus.SC_GATEWAY_TIMEOUT: + actionInvocationResponseBuilder.setRetry(true); + break; + default: + throw new ActionInvocationException("Unexpected response status code: " + statusCode); + } + } catch (ActionInvocationException e) { + // Set error in response to be logged at diagnostic logs for troubleshooting. + actionInvocationResponseBuilder.setErrorLog("Unexpected response. Error: " + e.getMessage()); + } + + return actionInvocationResponseBuilder.build(); + } + + private ActionInvocationSuccessResponse handleSuccessResponse(HttpEntity responseEntity) throws ActionInvocationException { + + if (!isAcceptablePayload(responseEntity)) { + throw new ActionInvocationException("The response content type is not application/json."); + } + return deserialize(responseEntity, ActionInvocationSuccessResponse.class); + } + + private ActionInvocationErrorResponse handleErrorResponse(HttpEntity responseEntity) throws ActionInvocationException { + + // If an error response is received, return the error response in order to communicate back to the client. + if (isAcceptablePayload(responseEntity)) { + return deserialize(responseEntity, ActionInvocationErrorResponse.class); + } + return null; + } + + private T deserialize(HttpEntity responseEntity, Class returnType) throws ActionInvocationException { + + if (!isAcceptablePayload(responseEntity)) { + throw new ActionInvocationException("The response content type is not application/json."); + } + + ObjectMapper objectMapper = new ObjectMapper(); + try { + String jsonResponse = EntityUtils.toString(responseEntity); + return objectMapper.readValue(jsonResponse, returnType); + } catch (IOException e) { + throw new ActionInvocationException("Error parsing the JSON response.", e); + } + } + + private boolean isAcceptablePayload(HttpEntity responseEntity) { + + return responseEntity != null && responseEntity.getContentType() != null && + responseEntity.getContentType().getValue().contains("application/json"); + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/AuthMethods.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/AuthMethods.java new file mode 100644 index 000000000000..c8652ed71085 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/AuthMethods.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.util; + +import org.apache.http.client.methods.HttpPost; +import org.wso2.carbon.identity.action.management.model.AuthProperty; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.List; + +public final class AuthMethods { + + private AuthMethods() { + + } + + public interface AuthMethod { + + void applyAuth(HttpPost httpPost); + + String getAuthType(); + } + + public static final class BearerAuth implements AuthMethod { + + private String token; + + public BearerAuth(List authPropertyList) { + + authPropertyList.stream() + .filter(authProperty -> "ACCESS_TOKEN".equals(authProperty.getName())) + .findFirst() + .ifPresent(authProperty -> this.token = authProperty.getValue()); + } + + @Override + public void applyAuth(HttpPost httpPost) { + + httpPost.setHeader("Authorization", "Bearer " + token); + } + + @Override + public String getAuthType() { + + return "BEARER"; + } + } + + public static final class BasicAuth implements AuthMethod { + + private String username; + private String password; + + public BasicAuth(List authPropertyList) { + + authPropertyList.forEach(authProperty -> { + switch (authProperty.getName()) { + case "USERNAME": + this.username = authProperty.getValue(); + break; + case "PASSWORD": + this.password = authProperty.getValue(); + break; + default: + break; + } + }); + } + + @Override + public void applyAuth(HttpPost httpPost) { + + String auth = username + ":" + password; + byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes(StandardCharsets.UTF_8)); + String authHeader = "Basic " + new String(encodedAuth, StandardCharsets.UTF_8); + httpPost.setHeader("Authorization", authHeader); + } + + @Override + public String getAuthType() { + + return "BASIC"; + } + } + + public static final class APIKeyAuth implements AuthMethod { + + private String apiHeader; + private String apiKey; + + public APIKeyAuth(List authPropertyList) { + + authPropertyList.forEach(authProperty -> { + switch (authProperty.getName()) { + case "HEADER": + this.apiHeader = authProperty.getValue(); + break; + case "VALUE": + this.apiKey = authProperty.getValue(); + break; + default: + break; + } + }); + } + + @Override + public void applyAuth(HttpPost httpPost) { + + httpPost.setHeader(apiHeader, apiKey); + } + + @Override + public String getAuthType() { + + return "API-KEY"; + } + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/OperationComparator.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/OperationComparator.java new file mode 100644 index 000000000000..ecb9cae0d4ff --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/OperationComparator.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.util; + +import org.wso2.carbon.identity.action.execution.model.AllowedOperation; +import org.wso2.carbon.identity.action.execution.model.PerformableOperation; + +/** + * This class compares an allowed operation against a performable operation to determine if the latter is permitted. + * The comparison between an {@link AllowedOperation} and a {@link PerformableOperation} ensures that only authorized + * modifications are made during the execution of an action. This class facilitates the validation of performable + * operations against a set of predefined allowed operations, based on the action type. + * + *

Key aspects of the comparison include:

+ *
    + *
  • Equality of operation types (e.g., "add", "remove", "replace") as defined in the JSON Patch + * specification (RFC 6902).
  • + *
  • Matching of operation paths, considering both exact matches and base path matches to allow + * for flexibility in specifying allowed operations.
  • + *
+ */ +public class OperationComparator { + + public static boolean compare(AllowedOperation allowedOp, PerformableOperation performableOp) { + + if (!allowedOp.getOp().equals(performableOp.getOp())) { + return false; + } + + String performableOperationBasePath = performableOp.getPath().contains("/") + ? performableOp.getPath().substring(0, performableOp.getPath().lastIndexOf('/') + 1) + : ""; + + for (String allowedPath : allowedOp.getPaths()) { + if (performableOp.getPath().equals(allowedPath) || + performableOperationBasePath.equals(allowedPath)) { + return true; + } + } + + return false; + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/java/org/wso2/carbon/identity/action/execution/util/APIClientTest.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/java/org/wso2/carbon/identity/action/execution/util/APIClientTest.java new file mode 100644 index 000000000000..52d7daa53a32 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/java/org/wso2/carbon/identity/action/execution/util/APIClientTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.util; + +import org.apache.http.HttpResponse; +import org.apache.http.ProtocolVersion; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.message.BasicStatusLine; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.wso2.carbon.identity.action.execution.model.ActionInvocationSuccessResponse; +import org.wso2.carbon.identity.action.execution.model.ActionInvocationResponse; + +import java.nio.charset.StandardCharsets; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class APIClientTest { + +// @Mock + /*private CloseableHttpClient mockHttpClient; + + private APIClient apiClient; + + @BeforeMethod + public void setUp() { + + MockitoAnnotations.initMocks(this); + apiClient = new APIClient(mockHttpClient); + } + + @Test + public void testCallAPISuccess() throws Exception { + + HttpResponse mockResponse = mock(HttpResponse.class); + when(mockResponse.getStatusLine()).thenReturn( + new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); + String json = "{\"message\":\"success\"}"; + when(mockResponse.getEntity()).thenReturn(new StringEntity(json, StandardCharsets.UTF_8)); + + when(mockHttpClient.execute(any(HttpPost.class))).thenReturn(mockResponse); + + ActionInvocationResponse response = apiClient.callAPI("http://example.com/api", null, "{}"); + + Assert.assertNotNull(response); +// Assert.assertTrue(response instanceof ActionInvocationSuccessResponse); +// Assert.assertEquals(((ActionInvocationSuccessResponse) response).getMessage(), "success"); + }*/ +} \ No newline at end of file diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/resources/testng.xml b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/resources/testng.xml new file mode 100644 index 000000000000..19097b4b2abd --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/resources/testng.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/components/action-mgt/pom.xml b/components/action-mgt/pom.xml index 05e8a8818e3d..3741bfc7e5f6 100644 --- a/components/action-mgt/pom.xml +++ b/components/action-mgt/pom.xml @@ -37,5 +37,6 @@ org.wso2.carbon.identity.action.management + org.wso2.carbon.identity.action.execution From acc12639a5c6d0d0362b5a325bc399eaa3a1b692 Mon Sep 17 00:00:00 2001 From: malithie Date: Mon, 22 Jul 2024 12:04:16 +0530 Subject: [PATCH 02/21] Add action execution component. --- .../pom.xml | 187 +++++++++ .../ActionExecutionRequestBuilder.java | 37 ++ .../ActionExecutionRequestBuilderFactory.java | 49 +++ .../ActionExecutionResponseProcessor.java | 46 +++ ...tionExecutionResponseProcessorFactory.java | 53 +++ .../execution/ActionExecutorService.java | 37 ++ .../execution/ActionExecutorServiceImpl.java | 373 ++++++++++++++++++ .../exception/ActionExecutionException.java | 36 ++ ...ctionExecutionRequestBuilderException.java | 33 ++ ...onExecutionResponseProcessorException.java | 33 ++ .../exception/ActionInvocationException.java | 32 ++ .../ActionExecutionServiceComponent.java | 66 ++++ ...ActionExecutionServiceComponentHolder.java | 51 +++ .../model/ActionExecutionRequest.java | 117 ++++++ .../model/ActionExecutionResponse.java | 50 +++ .../model/ActionExecutionStatus.java | 58 +++ .../model/ActionInvocationErrorResponse.java | 83 ++++ .../model/ActionInvocationResponse.java | 121 ++++++ .../ActionInvocationSuccessResponse.java | 56 +++ .../action/execution/model/ActionType.java | 28 ++ .../execution/model/AllowedOperation.java | 55 +++ .../action/execution/model/Application.java | 45 +++ .../action/execution/model/Event.java | 59 +++ .../action/execution/model/Organization.java | 46 +++ .../execution/model/PerformableOperation.java | 64 +++ .../action/execution/model/Request.java | 48 +++ .../action/execution/model/Tenant.java | 45 +++ .../identity/action/execution/model/User.java | 43 ++ .../action/execution/model/UserStore.java | 57 +++ .../action/execution/util/APIClient.java | 205 ++++++++++ .../action/execution/util/AuthMethods.java | 136 +++++++ .../execution/util/OperationComparator.java | 59 +++ .../action/execution/util/APIClientTest.java | 72 ++++ .../src/test/resources/testng.xml | 22 ++ components/action-mgt/pom.xml | 1 + 35 files changed, 2503 insertions(+) create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilder.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilderFactory.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessor.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessorFactory.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorService.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionException.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionRequestBuilderException.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionResponseProcessorException.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionInvocationException.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponentHolder.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionRequest.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionResponse.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionStatus.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationErrorResponse.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationSuccessResponse.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionType.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/AllowedOperation.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Application.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Event.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Organization.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/PerformableOperation.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Request.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Tenant.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/User.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/UserStore.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/AuthMethods.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/OperationComparator.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/java/org/wso2/carbon/identity/action/execution/util/APIClientTest.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/resources/testng.xml diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml b/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml new file mode 100644 index 000000000000..c27a67e85044 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml @@ -0,0 +1,187 @@ + + + + + + org.wso2.carbon.identity.framework + action-mgt + 7.3.42-SNAPSHOT + ../pom.xml + + + 4.0.0 + org.wso2.carbon.identity.action.execution + bundle + WSO2 Identity - Action Executor Component + Action execution backend component + http://wso2.org + + + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.core + + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.action.management + + + com.fasterxml.jackson.core + jackson-databind + + + + org.testng + testng + test + + + org.mockito + mockito-core + test + + + org.mockito + mockito-testng + test + + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + + ${project.artifactId} + + ${project.artifactId} + + org.wso2.carbon.identity.action.execution.internal, + + + !org.wso2.carbon.identity.action.execution.internal, + org.wso2.carbon.identity.action.execution.*; + version="${carbon.identity.package.export.version}" + + + org.wso2.carbon.identity.action.management.*; + version="${carbon.identity.package.import.version.range}", + org.apache.commons.lang; version="${commons-lang.wso2.osgi.version.range}", + org.apache.commons.logging; version="${import.package.version.commons.logging}", + org.apache.commons.collections; version="${commons-collections.wso2.osgi.version.range}", + org.apache.http; version="${httpcore.version.osgi.import.range}", + org.apache.http.client.config; version="${httpcore.version.osgi.import.range}", + org.apache.http.client.methods; version="${httpcore.version.osgi.import.range}", + org.apache.http.concurrent; version="${httpcore.version.osgi.import.range}", + org.apache.http.conn; version="${httpcore.version.osgi.import.range}", + org.apache.http.impl.client; version="${httpcomponents-httpclient.imp.pkg.version.range}", + org.apache.http.impl.conn; version="${httpcomponents-httpclient.imp.pkg.version.range}", + org.osgi.framework; version="${osgi.framework.imp.pkg.version.range}", + org.osgi.service.component; version="${osgi.service.component.imp.pkg.version.range}", + org.wso2.carbon.utils; version="${carbon.kernel.package.import.version.range}", + com.fasterxml.jackson.databind.*; + version="${com.fasterxml.jackson.annotation.version.range}", + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven.surefire.plugin.version} + + + --add-opens java.xml/jdk.xml.internal=ALL-UNNAMED + --add-exports java.base/jdk.internal.loader=ALL-UNNAMED + + + src/test/resources/testng.xml + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + default-prepare-agent + + prepare-agent + + + + default-prepare-agent-integration + + prepare-agent-integration + + + + default-report + + report + + + + default-report-integration + + report-integration + + + + default-check + + check + + + + + BUNDLE + + + COMPLEXITY + COVEREDRATIO + 0.90 + + + + + + + + + + com.github.spotbugs + spotbugs-maven-plugin + + ../../../spotbugs-exclude.xml + Max + Low + true + + + + + diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilder.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilder.java new file mode 100644 index 000000000000..5f5aacced9e5 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilder.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution; + +import org.wso2.carbon.identity.action.execution.exception.ActionExecutionRequestBuilderException; +import org.wso2.carbon.identity.action.execution.model.ActionExecutionRequest; +import org.wso2.carbon.identity.action.execution.model.ActionType; + +import java.util.Map; + +/** + * This interface defines the Action Execution Request Builder. + * Action Execution Request Builder is the component that is responsible for building the Action Execution Request + * based on the action type and the event context. + */ +public interface ActionExecutionRequestBuilder { + + ActionExecutionRequest buildActionExecutionRequest(ActionType actionType, Map eventContext) throws + ActionExecutionRequestBuilderException; + +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilderFactory.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilderFactory.java new file mode 100644 index 000000000000..f3e449216ed1 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilderFactory.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution; + +import org.wso2.carbon.identity.action.execution.model.ActionExecutionRequest; +import org.wso2.carbon.identity.action.execution.model.ActionType; + +import java.util.HashMap; +import java.util.Map; + +/** + * This class defines the Action Execution Request Builder Factory. + * Action Execution Request Builder Factory is the component that is responsible for building + * {@link ActionExecutionRequest} + * based on the action type and the event context. + */ +public class ActionExecutionRequestBuilderFactory { + + private static final Map actionInvocationRequestBuilders = + new HashMap<>(); + + public static ActionExecutionRequestBuilder getActionExecutionRequestBuilder(ActionType actionType) { + + return actionInvocationRequestBuilders.get(actionType); + } + + public static void registerActionExecutionRequestBuilder(ActionType actionType, + ActionExecutionRequestBuilder actionExecutionRequestBuilder) { + + actionInvocationRequestBuilders.put(actionType, actionExecutionRequestBuilder); + } + +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessor.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessor.java new file mode 100644 index 000000000000..2c2d695de117 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessor.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution; + +import org.wso2.carbon.identity.action.execution.exception.ActionExecutionResponseProcessorException; +import org.wso2.carbon.identity.action.execution.model.ActionExecutionStatus; +import org.wso2.carbon.identity.action.execution.model.ActionInvocationErrorResponse; +import org.wso2.carbon.identity.action.execution.model.ActionInvocationSuccessResponse; +import org.wso2.carbon.identity.action.execution.model.ActionType; +import org.wso2.carbon.identity.action.execution.model.Event; + +import java.util.Map; + +/** + * This interface defines the Action Execution Response Processor. + * Action Execution Response Processor is the component that is responsible for processing the response received + * from the action execution. + */ +public interface ActionExecutionResponseProcessor { + + ActionExecutionStatus processSuccessResponse(ActionType actionType, Map eventContext, + Event actionEvent, + ActionInvocationSuccessResponse successResponse) throws + ActionExecutionResponseProcessorException; + + ActionExecutionStatus processErrorResponse(ActionType actionType, Map eventContext, + Event actionEvent, + ActionInvocationErrorResponse errorResponse) throws + ActionExecutionResponseProcessorException; +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessorFactory.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessorFactory.java new file mode 100644 index 000000000000..6c37e13d1db0 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessorFactory.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution; + +import org.wso2.carbon.identity.action.execution.model.ActionType; + +import java.util.HashMap; +import java.util.Map; + +/** + * This class defines the Action Execution Response Processor Factory. + * Action Execution Response Processor Factory is the component that is responsible for providing the + * {@link ActionExecutionResponseProcessor} based on the action type. + */ +public class ActionExecutionResponseProcessorFactory { + + private static final Map actionInvocationResponseProcessors = + new HashMap<>(); + + public static ActionExecutionResponseProcessor getActionExecutionResponseProcessor(ActionType actionType) { + + switch (actionType) { + case PRE_ISSUE_ACCESS_TOKEN: + return actionInvocationResponseProcessors.get(ActionType.PRE_ISSUE_ACCESS_TOKEN); + default: + return null; + } + } + + public static void registerActionExecutionResponseProcessor(ActionType actionType, + ActionExecutionResponseProcessor + actionExecutionResponseProcessor) { + + actionInvocationResponseProcessors.put(actionType, actionExecutionResponseProcessor); + } + +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorService.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorService.java new file mode 100644 index 000000000000..98646f5c9a5d --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorService.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution; + +import org.wso2.carbon.identity.action.execution.exception.ActionExecutionException; +import org.wso2.carbon.identity.action.execution.model.ActionExecutionStatus; +import org.wso2.carbon.identity.action.execution.model.ActionType; + +import java.util.Map; + +/** + * This interface defines the Action Executor Service. + * Action Executor Service is the component that is responsible for executing the action based on the action type + * and the event context. + */ +public interface ActionExecutorService { + + ActionExecutionStatus execute(ActionType actionType, Map eventContext, String tenantDomain) throws + ActionExecutionException; + +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java new file mode 100644 index 000000000000..cc794fc1155e --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.identity.action.execution.exception.ActionExecutionException; +import org.wso2.carbon.identity.action.execution.exception.ActionExecutionRequestBuilderException; +import org.wso2.carbon.identity.action.execution.exception.ActionExecutionResponseProcessorException; +import org.wso2.carbon.identity.action.execution.internal.ActionExecutionServiceComponentHolder; +import org.wso2.carbon.identity.action.execution.model.ActionExecutionRequest; +import org.wso2.carbon.identity.action.execution.model.ActionExecutionStatus; +import org.wso2.carbon.identity.action.execution.model.ActionInvocationErrorResponse; +import org.wso2.carbon.identity.action.execution.model.ActionInvocationResponse; +import org.wso2.carbon.identity.action.execution.model.ActionInvocationSuccessResponse; +import org.wso2.carbon.identity.action.execution.model.ActionType; +import org.wso2.carbon.identity.action.execution.model.AllowedOperation; +import org.wso2.carbon.identity.action.execution.model.PerformableOperation; +import org.wso2.carbon.identity.action.execution.util.APIClient; +import org.wso2.carbon.identity.action.execution.util.AuthMethods; +import org.wso2.carbon.identity.action.execution.util.OperationComparator; +import org.wso2.carbon.identity.action.management.exception.ActionMgtException; +import org.wso2.carbon.identity.action.management.model.Action; +import org.wso2.carbon.identity.action.management.model.AuthProperty; +import org.wso2.carbon.identity.action.management.model.AuthType; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + +/** + * ActionInvocationService. + */ + +public class ActionExecutorServiceImpl implements ActionExecutorService { + + private static final Log log = LogFactory.getLog(ActionExecutorServiceImpl.class); + + private static final ActionExecutorServiceImpl instance = new ActionExecutorServiceImpl(); + private final APIClient apiClient; + + private ActionExecutorServiceImpl() { + + apiClient = new APIClient(); + } + + public static ActionExecutorServiceImpl getInstance() { + + return instance; + } + + public ActionExecutionStatus execute(ActionType actionType, Map eventContext, String tenantDomain) + throws ActionExecutionException { + + List actions; + try { + actions = ActionExecutionServiceComponentHolder.getInstance().getActionManagementService() + .getActionsByActionType(actionType.name(), tenantDomain); + } catch (ActionMgtException e) { + //todo: throw and block ? + log.error("Skip executing actions for action type: " + actionType.name() + + ". Error occurred while retrieving actions.", e); + return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); + } + + if (actions.isEmpty()) { + log.info("No actions found for action type: " + actionType.name()); + return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); + } + + if (actions.size() > 1) { + // when multiple actions are supported for an action type the logic below needs to be improved such that, + // a successful processing from one action becomes the input to the successor. + throw new ActionExecutionException("Multiple actions found for action type: " + actionType.name() + + "Current implementation doesn't support for multiple actions for a single action type."); + } + + ActionExecutionRequestBuilder actionExecutionRequestBuilder = + ActionExecutionRequestBuilderFactory.getActionExecutionRequestBuilder(actionType); + if (actionExecutionRequestBuilder == null) { + throw new ActionExecutionException("No request builder found for action type: " + actionType); + } + + ActionExecutionResponseProcessor actionExecutionResponseProcessor = + ActionExecutionResponseProcessorFactory.getActionExecutionResponseProcessor(actionType); + if (actionExecutionResponseProcessor == null) { + throw new ActionExecutionException("No response processor found for action type: " + actionType); + } + + ActionExecutionRequest actionRequest; + try { + actionRequest = actionExecutionRequestBuilder.buildActionExecutionRequest(actionType, eventContext); + } catch (ActionExecutionRequestBuilderException e) { + //todo: Add to diagnostics ? + log.error("Skip executing actions for action type: " + actionType.name() + + ". Error occurred while building the request payload.", e); + return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); + } + + CompletableFuture actionExecutor = CompletableFuture.supplyAsync( + () -> executeAction(actions.get(0), actionRequest, actionType, eventContext, + actionExecutionResponseProcessor)); + try { + return actionExecutor.get(); + } catch (InterruptedException | ExecutionException e) { + return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); + } + } + + private ActionExecutionStatus executeAction(Action action, ActionExecutionRequest actionRequest, + ActionType actionType, + Map eventContext, + ActionExecutionResponseProcessor actionExecutionResponseProcessor) { + + String apiEndpoint = action.getEndpoint().getUri(); + AuthType endpointAuthentication = action.getEndpoint().getAuthentication(); + AuthMethods.AuthMethod authenticationMethod; + + try { + authenticationMethod = getAuthenticationMethod(action.getId(), endpointAuthentication); + String payload = serializeRequest(actionRequest); + + logActionRequest(apiEndpoint, actionType, action.getId(), authenticationMethod, payload); + + ActionInvocationResponse actionInvocationResponse = + apiClient.callAPI(apiEndpoint, authenticationMethod, payload); + return processActionResponse(actionInvocationResponse, actionType, eventContext, actionRequest, + action.getId(), + apiEndpoint, authenticationMethod, actionExecutionResponseProcessor + ); + + } catch (ActionMgtException | JsonProcessingException | ActionExecutionResponseProcessorException e) { + //todo: Add to diagnostics + log.error("Skip executing action: " + action.getId() + " for action type: " + actionType + + ". Error occurred during action execution.", e); + } + + return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); + } + + private void logActionRequest(String apiEndpoint, ActionType actionType, String actionId, + AuthMethods.AuthMethod authenticationMethod, String payload) { + + //todo: Add to diagnostics + if (log.isDebugEnabled()) { + log.debug(String.format( + "Calling API: %s for action type: %s action id: %s with authentication: %s payload: %s", + apiEndpoint, + actionType, + actionId, + Optional.ofNullable(authenticationMethod).map(AuthMethods.AuthMethod::getAuthType) + .orElse("NONE"), + payload)); + } + } + + private ActionExecutionStatus processActionResponse(ActionInvocationResponse actionInvocationResponse, + ActionType actionType, + Map eventContext, + ActionExecutionRequest actionRequest, + String actionId, String apiEndpoint, + AuthMethods.AuthMethod authenticationMethod, + ActionExecutionResponseProcessor actionExecutionResponseProcessor) + throws ActionExecutionResponseProcessorException { + + if (actionInvocationResponse.isSuccess()) { + return processSuccessResponse((ActionInvocationSuccessResponse) actionInvocationResponse.getResponse(), + actionType, + eventContext, actionRequest, actionId, apiEndpoint, authenticationMethod, + actionExecutionResponseProcessor); + } else if (actionInvocationResponse.isError() && actionInvocationResponse.getResponse() != null) { + return processErrorResponse((ActionInvocationErrorResponse) actionInvocationResponse.getResponse(), + actionType, + eventContext, actionRequest, actionId, apiEndpoint, authenticationMethod, + actionExecutionResponseProcessor); + } else { + logErrorResponse(actionInvocationResponse, actionType, actionId, apiEndpoint, authenticationMethod); + } + + return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); + } + + private ActionExecutionStatus processSuccessResponse(ActionInvocationSuccessResponse successResponse, + ActionType actionType, + Map eventContext, + ActionExecutionRequest actionRequest, + String actionId, String apiEndpoint, + AuthMethods.AuthMethod authenticationMethod, + ActionExecutionResponseProcessor actionExecutionResponseProcessor) + throws ActionExecutionResponseProcessorException { + + if (log.isDebugEnabled()) { + // todo: add to diagnostic logs + logSuccessResponse(successResponse, actionType, actionId, apiEndpoint, authenticationMethod); + } + + List allowedPerformableOperations = + validatePerformableOperations(actionRequest, successResponse); + ActionInvocationSuccessResponse.Builder successResponseBuilder = + new ActionInvocationSuccessResponse.Builder().setOperations(allowedPerformableOperations); + return actionExecutionResponseProcessor.processSuccessResponse(actionType, eventContext, + actionRequest.getEvent(), + successResponseBuilder.build()); + } + + private ActionExecutionStatus processErrorResponse(ActionInvocationErrorResponse errorResponse, + ActionType actionType, + Map eventContext, + ActionExecutionRequest actionRequest, + String actionId, String apiEndpoint, + AuthMethods.AuthMethod authenticationMethod, + ActionExecutionResponseProcessor actionExecutionResponseProcessor) + throws ActionExecutionResponseProcessorException { + + if (log.isDebugEnabled()) { + // todo: add to diagnostic logs + logErrorResponse(errorResponse, actionType, actionId, apiEndpoint, authenticationMethod); + } + + return actionExecutionResponseProcessor.processErrorResponse(actionType, eventContext, actionRequest.getEvent(), + errorResponse); + } + + private void logSuccessResponse(ActionInvocationSuccessResponse successResponse, ActionType actionType, + String actionId, String apiEndpoint, AuthMethods.AuthMethod authenticationMethod) { + + try { + String responseBody = serializeSuccessResponse(successResponse); + log.debug(String.format( + "Received success response from API: %s for action type: %s action id: %s with authentication: %s. Response: %s", + apiEndpoint, + actionType, + actionId, + Optional.ofNullable(authenticationMethod).map(AuthMethods.AuthMethod::getAuthType) + .orElse("NONE"), + responseBody)); + } catch (JsonProcessingException e) { + log.error("Error occurred while deserializing the success response for action: " + + actionId + " for action type: " + actionType, e); + } + } + + private void logErrorResponse(ActionInvocationErrorResponse errorResponse, ActionType actionType, + String actionId, String apiEndpoint, AuthMethods.AuthMethod authenticationMethod) { + + try { + String responseBody = serializeErrorResponse(errorResponse); + log.debug(String.format( + "Received error response from API: %s for action type: %s action id: %s with authentication: %s. Response: %s", + apiEndpoint, + actionType, + actionId, + Optional.ofNullable(authenticationMethod).map(AuthMethods.AuthMethod::getAuthType) + .orElse("NONE"), + responseBody)); + } catch (JsonProcessingException e) { + log.error("Error occurred while deserializing the error response for action: " + + actionId + " for action type: " + actionType, e); + } + } + + private void logErrorResponse(ActionInvocationResponse actionInvocationResponse, ActionType actionType, + String actionId, String apiEndpoint, AuthMethods.AuthMethod authenticationMethod) { + // todo: add to diagnostic logs + if (log.isDebugEnabled()) { + log.debug(String.format( + "Failed to call API: %s for action type: %s action id: %s with authentication: %s. Error: %s", + apiEndpoint, + actionType, + actionId, + Optional.ofNullable(authenticationMethod).map(AuthMethods.AuthMethod::getAuthType) + .orElse("NONE"), + actionInvocationResponse.getErrorLog() != null ? actionInvocationResponse.getErrorLog() : + "Unknown")); + } + } + + private String serializeRequest(ActionExecutionRequest request) throws JsonProcessingException { + + ObjectMapper requestObjectmapper = new ObjectMapper(); + requestObjectmapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + requestObjectmapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + return requestObjectmapper.writeValueAsString(request); + } + + private String serializeSuccessResponse(ActionInvocationSuccessResponse response) throws JsonProcessingException { + + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.writeValueAsString(response); + } + + private String serializeErrorResponse(ActionInvocationErrorResponse response) throws JsonProcessingException { + + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.writeValueAsString(response); + } + + private List validatePerformableOperations(ActionExecutionRequest request, + ActionInvocationSuccessResponse response) { + + List allowedOperations = request.getAllowedOperations(); + + List allowedPerformableOperations = response.getOperations().stream() + .filter(performableOperation -> allowedOperations.stream() + .anyMatch(allowedOperation -> OperationComparator.compare(allowedOperation, + performableOperation))) + .collect(Collectors.toList()); + + if (log.isDebugEnabled()) { + // todo: add to diagnostics + List allowedOps = new ArrayList<>(); + List notAllowedOps = new ArrayList<>(); + + response.getOperations().forEach(operation -> { + String operationDetails = "Operation: " + operation.getOp() + " with path: " + operation.getPath(); + if (allowedPerformableOperations.contains(operation)) { + allowedOps.add(operationDetails); + } else { + notAllowedOps.add(operationDetails); + } + }); + + String logMessage = "Allowed Operations: " + String.join(", ", allowedOps) + + ". Not Allowed Operations: " + String.join(", ", notAllowedOps); + log.debug(logMessage); + } + + return allowedPerformableOperations; + } + + private AuthMethods.AuthMethod getAuthenticationMethod(String actionId, AuthType authType) + throws ActionMgtException { + + List authProperties = authType.getPropertiesWithDecryptedValues(actionId); + + switch (authType.getType()) { + case BASIC: + return new AuthMethods.BasicAuth(authProperties); + case BEARER: + return new AuthMethods.BearerAuth(authProperties); + case API_KEY: + return new AuthMethods.APIKeyAuth(authProperties); + case NONE: + return null; + default: + throw new ActionMgtException("Unsupported authentication type: " + authType.getType()); + + } + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionException.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionException.java new file mode 100644 index 000000000000..9002c71a2573 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionException.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.exception; + +/** + * Exception class for Action Execution. + */ +public class ActionExecutionException extends Exception { + + public ActionExecutionException(String message) { + + super(message); + } + + public ActionExecutionException(String message, Throwable cause) { + + super(message, cause); + } + +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionRequestBuilderException.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionRequestBuilderException.java new file mode 100644 index 000000000000..f396cba41889 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionRequestBuilderException.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.exception; + +public class ActionExecutionRequestBuilderException extends Exception { + + public ActionExecutionRequestBuilderException(String message) { + + super(message); + } + + public ActionExecutionRequestBuilderException(String message, Throwable cause) { + + super(message, cause); + } + +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionResponseProcessorException.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionResponseProcessorException.java new file mode 100644 index 000000000000..9d6f49c5e46b --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionResponseProcessorException.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.exception; + +public class ActionExecutionResponseProcessorException extends Exception { + + public ActionExecutionResponseProcessorException(String message) { + + super(message); + } + + public ActionExecutionResponseProcessorException(String message, Throwable cause) { + + super(message, cause); + } + +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionInvocationException.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionInvocationException.java new file mode 100644 index 000000000000..8c112d740755 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionInvocationException.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.exception; + +public class ActionInvocationException extends Exception { + + public ActionInvocationException(String message) { + + super(message); + } + + public ActionInvocationException(String message, Throwable cause) { + + super(message, cause); + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java new file mode 100644 index 000000000000..69cec1821482 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.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.action.execution.ActionExecutorService; +import org.wso2.carbon.identity.action.execution.ActionExecutorServiceImpl; + +/** + * OSGI service component for the Action execution. + */ +@Component( + name = "action.execution.service.component", + immediate = true +) +public class ActionExecutionServiceComponent { + + private static final Log LOG = LogFactory.getLog(ActionExecutionServiceComponent.class); + + @Activate + protected void activate(ComponentContext context) { + + try { + BundleContext bundleCtx = context.getBundleContext(); + bundleCtx.registerService(ActionExecutorService.class, ActionExecutorServiceImpl.getInstance(), null); + LOG.debug("Action execution bundle is activated"); + } catch (Throwable e) { + LOG.error("Error while initializing Action execution service component.", e); + } + } + + @Deactivate + protected void deactivate(ComponentContext context) { + + try { + BundleContext bundleCtx = context.getBundleContext(); + bundleCtx.ungetService(bundleCtx.getServiceReference(ActionExecutorService.class)); + LOG.debug("Action execution bundle is deactivated"); + } catch (Throwable e) { + LOG.error("Error while deactivating Action execution service component.", e); + } + } + +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponentHolder.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponentHolder.java new file mode 100644 index 000000000000..5acef30d4b6e --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponentHolder.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.internal; + +import org.wso2.carbon.identity.action.management.ActionManagementService; + +/** + * This class holds references for dependent services required for Action Execution Service to function. + */ +public class ActionExecutionServiceComponentHolder { + + private static final ActionExecutionServiceComponentHolder instance = new ActionExecutionServiceComponentHolder(); + + private ActionManagementService actionManagementService; + + private ActionExecutionServiceComponentHolder() { + + } + + public static ActionExecutionServiceComponentHolder getInstance() { + + return instance; + } + + public ActionManagementService getActionManagementService() { + + return actionManagementService; + } + + public void setActionManagementService( + ActionManagementService actionManagementService) { + + this.actionManagementService = actionManagementService; + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionRequest.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionRequest.java new file mode 100644 index 000000000000..04959234c636 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionRequest.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +import org.slf4j.MDC; + +import java.util.List; +import java.util.Optional; + +/** + * This class models the Action Execution Request. + * Action Execution Request is the request object that is passed to the Action Executor Service to execute an action. + * It contains the {@link ActionType}, flow id, {@link Event} and a list of {@link AllowedOperation}. + */ +public class ActionExecutionRequest { + + private static final String CORRELATION_ID_MDC = "Correlation-ID"; + private final ActionType actionType; + private final String flowId; + private final Event event; + private final List allowedOperations; + + public ActionExecutionRequest(Builder builder) { + + this.actionType = builder.actionType; + this.flowId = builder.flowId; + this.event = builder.event; + this.allowedOperations = builder.allowedOperations; + } + + public ActionType getActionType() { + + return actionType; + } + + public String getFlowId() { + + return flowId; + } + + public String getRequestId() { + + return getCorrelationId(); + } + + public Event getEvent() { + + return event; + } + + public List getAllowedOperations() { + + return allowedOperations; + } + + private String getCorrelationId() { + + return Optional.ofNullable(MDC.get(CORRELATION_ID_MDC)).orElse(""); + } + + /** + * Builder for the {@link ActionExecutionRequest}. + */ + public static class Builder { + + private ActionType actionType; + private String flowId; + private Event event; + private List allowedOperations; + + public Builder actionType(ActionType actionType) { + + this.actionType = actionType; + return this; + } + + public Builder flowId(String flowId) { + + this.flowId = flowId; + return this; + } + + public Builder event(Event event) { + + this.event = event; + return this; + } + + public Builder allowedOperations(List allowedOperations) { + + this.allowedOperations = allowedOperations; + return this; + } + + public ActionExecutionRequest build() { + + return new ActionExecutionRequest(this); + } + } +} + diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionResponse.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionResponse.java new file mode 100644 index 000000000000..d638a9983b42 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionResponse.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +import java.util.List; + +/** + * Action Invocation Response. + */ +public class ActionExecutionResponse { + + private String actionStatus; + private List operations; + + public String getActionStatus() { + + return actionStatus; + } + + public void setActionStatus(String actionStatus) { + + this.actionStatus = actionStatus; + } + + public List getOperations() { + + return operations; + } + + public void setOperations(List operations) { + + this.operations = operations; + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionStatus.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionStatus.java new file mode 100644 index 000000000000..4021bce1cef4 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionStatus.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +import java.util.Map; + +/** + * This class models the Action Execution Status. + * Action Execution Status is the status object that is returned by the Action Executor Service after executing an + * action. It contains the status of the action execution and the response context. + */ +public class ActionExecutionStatus { + + private final Status status; + private final Map responseContext; + + public ActionExecutionStatus(Status status, Map responseContext) { + + this.status = status; + this.responseContext = responseContext; + } + + public Status getStatus() { + + return status; + } + + public Map getResponseContext() { + + return responseContext; + } + + /** + * This enum defines the Action Execution Status. + */ + public enum Status { + SUCCESS, + FAILURE, + INCOMPLETE, + ERROR + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationErrorResponse.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationErrorResponse.java new file mode 100644 index 000000000000..b4b5dbeedbfa --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationErrorResponse.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +@JsonDeserialize(builder = ActionInvocationErrorResponse.Builder.class) +public class ActionInvocationErrorResponse implements ActionInvocationResponse.APIResponse { + + private final String error; + private final String errorDescription; + private final String errorUri; + + private ActionInvocationErrorResponse(Builder builder) { + + this.error = builder.error; + this.errorDescription = builder.errorDescription; + this.errorUri = builder.errorUri; + } + + public String getError() { + + return error; + } + + public String getErrorDescription() { + + return errorDescription; + } + + public String getErrorUri() { + + return errorUri; + } + + public static class Builder { + + private String error; + private String errorDescription; + private String errorUri; + + public ActionInvocationErrorResponse.Builder setError(@JsonProperty("error") String error) { + + this.error = error; + return this; + } + + public ActionInvocationErrorResponse.Builder setErrorDescription( + @JsonProperty("errorDescription") String errorDescription) { + + this.errorDescription = errorDescription; + return this; + } + + public ActionInvocationErrorResponse.Builder setErrorUri(@JsonProperty("errorUri") String errorUri) { + + this.errorUri = errorUri; + return this; + } + + public ActionInvocationErrorResponse build() { + + return new ActionInvocationErrorResponse(this); + } + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java new file mode 100644 index 000000000000..8eacc38cc350 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +/** + * This class models the Action Execution Response. + * Action Execution Response is the response object that is returned by the Action Executor Service after executing an + * action. It contains the action status and the operations that needs to be performed. + */ +public class ActionInvocationResponse { + + private int httpStatusCode; + private String actionStatus; + private APIResponse response; + + private boolean retry; + + private String errorLog; + + private ActionInvocationResponse() { + // Private constructor to enforce the use of the Builder + } + + public APIResponse getResponse() { + + return response; + } + + public boolean isSuccess() { + + return "SUCCESS".equalsIgnoreCase(actionStatus); + } + + public boolean isError() { + + return "ERROR".equalsIgnoreCase(actionStatus); + } + + public boolean isRetry() { + + return retry; + } + + public String getErrorLog() { + + return errorLog; + } + + public interface APIResponse { + + } + + public static class Builder { + + private int httpStatusCode; + private String actionStatus; + private APIResponse response; + private boolean retry; + + private String errorLog; + + public Builder setHttpStatusCode(int httpStatusCode) { + + this.httpStatusCode = httpStatusCode; + return this; + } + + public Builder setResponse(APIResponse response) { + + if (response instanceof ActionInvocationSuccessResponse) { + this.actionStatus = "SUCCESS"; + } else if (response instanceof ActionInvocationErrorResponse) { + this.actionStatus = "ERROR"; + } + + this.response = response; + return this; + } + + public Builder setRetry(boolean retry) { + + this.retry = retry; + this.actionStatus = "ERROR"; + return this; + } + + public Builder setErrorLog(String errorLog) { + + this.errorLog = errorLog; + this.actionStatus = "ERROR"; + return this; + } + + public ActionInvocationResponse build() { + + ActionInvocationResponse response = new ActionInvocationResponse(); + response.httpStatusCode = this.httpStatusCode; + response.actionStatus = this.actionStatus; + response.response = this.response; + response.retry = this.retry; + response.errorLog = this.errorLog; + return response; + } + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationSuccessResponse.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationSuccessResponse.java new file mode 100644 index 000000000000..f2961a0294ed --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationSuccessResponse.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +import java.util.List; + +@JsonDeserialize(builder = ActionInvocationSuccessResponse.Builder.class) +public class ActionInvocationSuccessResponse implements ActionInvocationResponse.APIResponse { + + private final List operations; + + private ActionInvocationSuccessResponse(Builder builder) { + + this.operations = builder.operations; + } + + public List getOperations() { + + return operations; + } + + public static class Builder { + + private List operations; + + public Builder setOperations(@JsonProperty("operations") List operations) { + + this.operations = operations; + return this; + } + + public ActionInvocationSuccessResponse build() { + + return new ActionInvocationSuccessResponse(this); + } + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionType.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionType.java new file mode 100644 index 000000000000..293524740010 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionType.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +/** + * This class models the Action Type. + * Action Type is the type of the action that is executed by the Action Executor Service. + */ +public enum ActionType { + PRE_ISSUE_ACCESS_TOKEN, + POST_ISSUE_ACCESS_TOKEN, +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/AllowedOperation.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/AllowedOperation.java new file mode 100644 index 000000000000..b67c8c45f3e9 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/AllowedOperation.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +import java.util.List; + +/** + * This class models the Allowed Operation. + * Allowed Operation is the operation that is allowed to be performed during the execution of a particular action. + * The operation is defined by the operation type and the paths that the operation is allowed to be performed on. + * Allowed operations follow JSON Patch format (RFC 6902) semantics to define the operations that are allowed to be + * performed. + */ +public class AllowedOperation { + + String op; + + List paths; + + public String getOp() { + + return op; + } + + public void setOp(String op) { + + this.op = op; + } + + public List getPaths() { + + return paths; + } + + public void setPaths(List paths) { + + this.paths = paths; + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Application.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Application.java new file mode 100644 index 000000000000..d74aa1cfdbb8 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Application.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +/** + * This class models the Application. + * Application is the entity that represents the application that the action is triggered for. + */ +public class Application { + + String id; + String name; + + public Application(String id, String name) { + + this.id = id; + this.name = name; + } + + public String getId() { + + return id; + } + + public String getName() { + + return name; + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Event.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Event.java new file mode 100644 index 000000000000..cbb5a14ccbb5 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Event.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +/** + * This class models the Event. + * Event is the entity that represents the event that is sent to the Action over Action Execution Request. + * It contains the request, tenant, organization, user, and user store information. + * The abstraction allows to model events with additional context based on the action type. + */ +public abstract class Event { + + protected Request request; + protected Tenant tenant; + protected Organization organization; + protected User user; + protected UserStore userStore; + + public Tenant getTenant() { + + return tenant; + } + + public Organization getOrganization() { + + return organization; + } + + public Request getRequest() { + + return request; + } + + public User getUser() { + + return user; + } + + public UserStore getUserStore() { + + return userStore; + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Organization.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Organization.java new file mode 100644 index 000000000000..c6f642242104 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Organization.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +/** + * This class models the Organization. + * Organization is the entity that represents the organization of the user for whom the action is triggered for. + */ +public class Organization { + + private final String id; + + private final String name; + + public Organization(String id, String name) { + + this.id = id; + this.name = name; + } + + public String getId() { + + return id; + } + + public String getName() { + + return name; + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/PerformableOperation.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/PerformableOperation.java new file mode 100644 index 000000000000..f526744384d3 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/PerformableOperation.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +/** + * This class models the Performable Operation. + * Performable Operation is the operation that is requested to be performed in the system during the execution of an + * action. It contains the operation type, the path of the operation and the value associated with add or replace + * operations. + * Performable operations are expected to follow the JSON Patch format (RFC 6902), providing a standardized way to + * express the operations to be performed. + */ +public class PerformableOperation { + + private String op; + private String path; + private Object value; + + public String getOp() { + + return op; + } + + public void setOp(String op) { + + this.op = op; + } + + public String getPath() { + + return path; + } + + public void setPath(String path) { + + this.path = path; + } + + public Object getValue() { + + return value; + } + + public void setValue(Object value) { + + this.value = value; + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Request.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Request.java new file mode 100644 index 000000000000..653f9363a343 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Request.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * This class models the Request. + * Request is the entity that represents the request that is sent to Action over Action Execution Request. + * Request contains additional headers and additional parameters relevant to the trigger that are sent to the Action. + * The abstraction allows to model requests with additional context based on the action type. + */ +public abstract class Request { + + protected Map additionalHeaders = new HashMap<>(); + protected Map additionalParams = new HashMap<>(); + + // implement a method to get additional headers + public Map getAdditionalHeaders() { + + return additionalHeaders != null ? additionalHeaders : Collections.emptyMap(); + } + + // implement a method to get additional params + public Map getAdditionalParams() { + + return additionalParams != null ? additionalParams : Collections.emptyMap(); + } + +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Tenant.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Tenant.java new file mode 100644 index 000000000000..24c275c06f54 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Tenant.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +/** + * This class models the Tenant. + * Tenant is the entity that represents the tenant of the action trigger. + */ +public class Tenant { + + private final String id; + private final String name; + + public Tenant(String id, String name) { + + this.id = id; + this.name = name; + } + + public String getId() { + + return id; + } + + public String getName() { + + return name; + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/User.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/User.java new file mode 100644 index 000000000000..3944bea48522 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/User.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +/** + * This class models the User. + * User is the entity that represents the user for whom the action is triggered for. + */ +public class User { + + String id; + + public User(String id) { + + this.id = id; + } + + public String getId() { + + return id; + } + public void setId(String id) { + + this.id = id; + } + +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/UserStore.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/UserStore.java new file mode 100644 index 000000000000..e8773729b838 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/UserStore.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +/** + * This class models the UserStore. + * UserStore is the entity that represents the user store of the user for whom the action is triggered for. + * User store is present when the user's profile is managed within the system only. + */ +public class UserStore { + + String id; + String name; + + public UserStore(String name) { + + setName(name); + } + + public String getId() { + + return id; + } + + public String getName() { + + return name; + } + + public void setName(String name) { + + /* + * As of now user store id is generated by encoding the user store name. + */ + this.id = name != null ? Base64.getEncoder().encodeToString(name.getBytes(StandardCharsets.UTF_8)) : null; + this.name = name; + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java new file mode 100644 index 000000000000..2784e385bc18 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.util.EntityUtils; +import org.wso2.carbon.identity.action.execution.exception.ActionInvocationException; +import org.wso2.carbon.identity.action.execution.model.ActionInvocationErrorResponse; +import org.wso2.carbon.identity.action.execution.model.ActionInvocationResponse; +import org.wso2.carbon.identity.action.execution.model.ActionInvocationSuccessResponse; + +import java.io.IOException; +import java.net.SocketTimeoutException; +import java.nio.charset.StandardCharsets; +import java.util.Optional; + +/** + * This class is responsible for invoking the HTTP endpoint. + */ +public class APIClient { + + private static final Log log = LogFactory.getLog(APIClient.class); + private final CloseableHttpClient httpClient; + + public APIClient() { + + // todo: read connection configurations related to the http client of actions from the server configuration. + // Initialize the http client. Set connection time out to 2s and read time out to 5s. + int readTimeout = 5000; + int connectionRequestTimeout = 2000; + int connectionTimeout = 2000; + + RequestConfig config = RequestConfig.custom() + .setConnectTimeout(connectionTimeout) + .setConnectionRequestTimeout(connectionRequestTimeout) + .setSocketTimeout(readTimeout) + .setRedirectsEnabled(false) + .setRelativeRedirectsAllowed(false) + .build(); + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(); + connectionManager.setMaxTotal(20); + httpClient = HttpClientBuilder.create().setDefaultRequestConfig(config).setConnectionManager(connectionManager) + .build(); + } + + public ActionInvocationResponse callAPI(String url, AuthMethods.AuthMethod authMethod, + String payload) { + + HttpPost httpPost = new HttpPost(url); + setRequestEntity(httpPost, payload, authMethod); + + return executeRequest(httpPost).orElse(new ActionInvocationResponse.Builder() + .setErrorLog("Failed to execute the action request or maximum retry attempts reached.") + .build()); + } + + private void setRequestEntity(HttpPost httpPost, String jsonRequest, AuthMethods.AuthMethod authMethod) { + + StringEntity entity = new StringEntity(jsonRequest, StandardCharsets.UTF_8); + if (authMethod != null) { + authMethod.applyAuth(httpPost); + } + httpPost.setEntity(entity); + httpPost.setHeader("Accept", "application/json"); + httpPost.setHeader("Content-type", "application/json"); + } + + private Optional executeRequest(HttpPost request) { + + int attempts = 0; + int retryCount = 2; // todo: read from server configurations + + while (attempts < retryCount) { + try (CloseableHttpResponse response = httpClient.execute(request)) { + ActionInvocationResponse actionInvocationResponse = handleResponse(response); + if (!actionInvocationResponse.isError() || !actionInvocationResponse.isRetry()) { + return Optional.of(actionInvocationResponse); + } + //todo: add to diagnostic logs + log.warn("API: " + request.getURI() + " seems to be unavailable. Retrying the request. Attempt " + + (attempts + 1) + " of " + retryCount); + } catch (ConnectTimeoutException | SocketTimeoutException e) { + //todo: add to diagnostic logs + log.warn("Request for API: " + request.getURI() + " timed out. Retrying the request. Attempt " + + (attempts + 1) + " of " + retryCount); + } catch (Exception e) { + //todo: add to diagnostic logs + log.error("Request for API: " + request.getURI() + " failed due to an error.", e); + break; + } finally { + request.releaseConnection(); + } + attempts++; + } + + log.warn("Maximum retry attempts reached for API: " + request.getURI()); + return Optional.empty(); + } + + private ActionInvocationResponse handleResponse(HttpResponse response) { + + int statusCode = response.getStatusLine().getStatusCode(); + HttpEntity responseEntity = response.getEntity(); + + ActionInvocationResponse.Builder actionInvocationResponseBuilder = new ActionInvocationResponse.Builder(); + actionInvocationResponseBuilder.setHttpStatusCode(statusCode); + + try { + switch (statusCode) { + case HttpStatus.SC_OK: + ActionInvocationSuccessResponse successResponse = handleSuccessResponse(responseEntity); + actionInvocationResponseBuilder.setResponse(successResponse); + break; + case HttpStatus.SC_BAD_REQUEST: + case HttpStatus.SC_INTERNAL_SERVER_ERROR: + ActionInvocationErrorResponse errorResponse = handleErrorResponse(responseEntity); + actionInvocationResponseBuilder.setResponse(errorResponse); + break; + case HttpStatus.SC_UNAUTHORIZED: + break; + case HttpStatus.SC_BAD_GATEWAY: + case HttpStatus.SC_SERVICE_UNAVAILABLE: + case HttpStatus.SC_GATEWAY_TIMEOUT: + actionInvocationResponseBuilder.setRetry(true); + break; + default: + throw new ActionInvocationException("Unexpected response status code: " + statusCode); + } + } catch (ActionInvocationException e) { + // Set error in response to be logged at diagnostic logs for troubleshooting. + actionInvocationResponseBuilder.setErrorLog("Unexpected response. Error: " + e.getMessage()); + } + + return actionInvocationResponseBuilder.build(); + } + + private ActionInvocationSuccessResponse handleSuccessResponse(HttpEntity responseEntity) + throws ActionInvocationException { + + if (!isAcceptablePayload(responseEntity)) { + throw new ActionInvocationException("The response content type is not application/json."); + } + return deserialize(responseEntity, ActionInvocationSuccessResponse.class); + } + + private ActionInvocationErrorResponse handleErrorResponse(HttpEntity responseEntity) + throws ActionInvocationException { + + // If an error response is received, return the error response in order to communicate back to the client. + if (isAcceptablePayload(responseEntity)) { + return deserialize(responseEntity, ActionInvocationErrorResponse.class); + } + return null; + } + + private T deserialize(HttpEntity responseEntity, Class returnType) throws ActionInvocationException { + + if (!isAcceptablePayload(responseEntity)) { + throw new ActionInvocationException("The response content type is not application/json."); + } + + ObjectMapper objectMapper = new ObjectMapper(); + try { + String jsonResponse = EntityUtils.toString(responseEntity); + return objectMapper.readValue(jsonResponse, returnType); + } catch (IOException e) { + throw new ActionInvocationException("Error parsing the JSON response.", e); + } + } + + private boolean isAcceptablePayload(HttpEntity responseEntity) { + + return responseEntity != null && responseEntity.getContentType() != null && + responseEntity.getContentType().getValue().contains("application/json"); + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/AuthMethods.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/AuthMethods.java new file mode 100644 index 000000000000..c8652ed71085 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/AuthMethods.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.util; + +import org.apache.http.client.methods.HttpPost; +import org.wso2.carbon.identity.action.management.model.AuthProperty; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.List; + +public final class AuthMethods { + + private AuthMethods() { + + } + + public interface AuthMethod { + + void applyAuth(HttpPost httpPost); + + String getAuthType(); + } + + public static final class BearerAuth implements AuthMethod { + + private String token; + + public BearerAuth(List authPropertyList) { + + authPropertyList.stream() + .filter(authProperty -> "ACCESS_TOKEN".equals(authProperty.getName())) + .findFirst() + .ifPresent(authProperty -> this.token = authProperty.getValue()); + } + + @Override + public void applyAuth(HttpPost httpPost) { + + httpPost.setHeader("Authorization", "Bearer " + token); + } + + @Override + public String getAuthType() { + + return "BEARER"; + } + } + + public static final class BasicAuth implements AuthMethod { + + private String username; + private String password; + + public BasicAuth(List authPropertyList) { + + authPropertyList.forEach(authProperty -> { + switch (authProperty.getName()) { + case "USERNAME": + this.username = authProperty.getValue(); + break; + case "PASSWORD": + this.password = authProperty.getValue(); + break; + default: + break; + } + }); + } + + @Override + public void applyAuth(HttpPost httpPost) { + + String auth = username + ":" + password; + byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes(StandardCharsets.UTF_8)); + String authHeader = "Basic " + new String(encodedAuth, StandardCharsets.UTF_8); + httpPost.setHeader("Authorization", authHeader); + } + + @Override + public String getAuthType() { + + return "BASIC"; + } + } + + public static final class APIKeyAuth implements AuthMethod { + + private String apiHeader; + private String apiKey; + + public APIKeyAuth(List authPropertyList) { + + authPropertyList.forEach(authProperty -> { + switch (authProperty.getName()) { + case "HEADER": + this.apiHeader = authProperty.getValue(); + break; + case "VALUE": + this.apiKey = authProperty.getValue(); + break; + default: + break; + } + }); + } + + @Override + public void applyAuth(HttpPost httpPost) { + + httpPost.setHeader(apiHeader, apiKey); + } + + @Override + public String getAuthType() { + + return "API-KEY"; + } + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/OperationComparator.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/OperationComparator.java new file mode 100644 index 000000000000..ecb9cae0d4ff --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/OperationComparator.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.util; + +import org.wso2.carbon.identity.action.execution.model.AllowedOperation; +import org.wso2.carbon.identity.action.execution.model.PerformableOperation; + +/** + * This class compares an allowed operation against a performable operation to determine if the latter is permitted. + * The comparison between an {@link AllowedOperation} and a {@link PerformableOperation} ensures that only authorized + * modifications are made during the execution of an action. This class facilitates the validation of performable + * operations against a set of predefined allowed operations, based on the action type. + * + *

Key aspects of the comparison include:

+ *
    + *
  • Equality of operation types (e.g., "add", "remove", "replace") as defined in the JSON Patch + * specification (RFC 6902).
  • + *
  • Matching of operation paths, considering both exact matches and base path matches to allow + * for flexibility in specifying allowed operations.
  • + *
+ */ +public class OperationComparator { + + public static boolean compare(AllowedOperation allowedOp, PerformableOperation performableOp) { + + if (!allowedOp.getOp().equals(performableOp.getOp())) { + return false; + } + + String performableOperationBasePath = performableOp.getPath().contains("/") + ? performableOp.getPath().substring(0, performableOp.getPath().lastIndexOf('/') + 1) + : ""; + + for (String allowedPath : allowedOp.getPaths()) { + if (performableOp.getPath().equals(allowedPath) || + performableOperationBasePath.equals(allowedPath)) { + return true; + } + } + + return false; + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/java/org/wso2/carbon/identity/action/execution/util/APIClientTest.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/java/org/wso2/carbon/identity/action/execution/util/APIClientTest.java new file mode 100644 index 000000000000..52d7daa53a32 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/java/org/wso2/carbon/identity/action/execution/util/APIClientTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.util; + +import org.apache.http.HttpResponse; +import org.apache.http.ProtocolVersion; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.message.BasicStatusLine; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.wso2.carbon.identity.action.execution.model.ActionInvocationSuccessResponse; +import org.wso2.carbon.identity.action.execution.model.ActionInvocationResponse; + +import java.nio.charset.StandardCharsets; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class APIClientTest { + +// @Mock + /*private CloseableHttpClient mockHttpClient; + + private APIClient apiClient; + + @BeforeMethod + public void setUp() { + + MockitoAnnotations.initMocks(this); + apiClient = new APIClient(mockHttpClient); + } + + @Test + public void testCallAPISuccess() throws Exception { + + HttpResponse mockResponse = mock(HttpResponse.class); + when(mockResponse.getStatusLine()).thenReturn( + new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); + String json = "{\"message\":\"success\"}"; + when(mockResponse.getEntity()).thenReturn(new StringEntity(json, StandardCharsets.UTF_8)); + + when(mockHttpClient.execute(any(HttpPost.class))).thenReturn(mockResponse); + + ActionInvocationResponse response = apiClient.callAPI("http://example.com/api", null, "{}"); + + Assert.assertNotNull(response); +// Assert.assertTrue(response instanceof ActionInvocationSuccessResponse); +// Assert.assertEquals(((ActionInvocationSuccessResponse) response).getMessage(), "success"); + }*/ +} \ No newline at end of file diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/resources/testng.xml b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/resources/testng.xml new file mode 100644 index 000000000000..19097b4b2abd --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/resources/testng.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/components/action-mgt/pom.xml b/components/action-mgt/pom.xml index 05e8a8818e3d..3741bfc7e5f6 100644 --- a/components/action-mgt/pom.xml +++ b/components/action-mgt/pom.xml @@ -37,5 +37,6 @@ org.wso2.carbon.identity.action.management + org.wso2.carbon.identity.action.execution From 51c26671699a156539804f0ffb4f5884eb3c70ed Mon Sep 17 00:00:00 2001 From: malithie Date: Mon, 22 Jul 2024 16:15:25 +0530 Subject: [PATCH 03/21] Add action execution component. --- .../action/execution/util/APIClientTest.java | 72 ------------------- 1 file changed, 72 deletions(-) delete mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/java/org/wso2/carbon/identity/action/execution/util/APIClientTest.java diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/java/org/wso2/carbon/identity/action/execution/util/APIClientTest.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/java/org/wso2/carbon/identity/action/execution/util/APIClientTest.java deleted file mode 100644 index 52d7daa53a32..000000000000 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/java/org/wso2/carbon/identity/action/execution/util/APIClientTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.wso2.carbon.identity.action.execution.util; - -import org.apache.http.HttpResponse; -import org.apache.http.ProtocolVersion; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.message.BasicStatusLine; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.testng.Assert; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; -import org.wso2.carbon.identity.action.execution.model.ActionInvocationSuccessResponse; -import org.wso2.carbon.identity.action.execution.model.ActionInvocationResponse; - -import java.nio.charset.StandardCharsets; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class APIClientTest { - -// @Mock - /*private CloseableHttpClient mockHttpClient; - - private APIClient apiClient; - - @BeforeMethod - public void setUp() { - - MockitoAnnotations.initMocks(this); - apiClient = new APIClient(mockHttpClient); - } - - @Test - public void testCallAPISuccess() throws Exception { - - HttpResponse mockResponse = mock(HttpResponse.class); - when(mockResponse.getStatusLine()).thenReturn( - new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); - String json = "{\"message\":\"success\"}"; - when(mockResponse.getEntity()).thenReturn(new StringEntity(json, StandardCharsets.UTF_8)); - - when(mockHttpClient.execute(any(HttpPost.class))).thenReturn(mockResponse); - - ActionInvocationResponse response = apiClient.callAPI("http://example.com/api", null, "{}"); - - Assert.assertNotNull(response); -// Assert.assertTrue(response instanceof ActionInvocationSuccessResponse); -// Assert.assertEquals(((ActionInvocationSuccessResponse) response).getMessage(), "success"); - }*/ -} \ No newline at end of file From 50e2895ad2ec45d96ce5048a0b6561aca7781a8d Mon Sep 17 00:00:00 2001 From: malithie Date: Mon, 22 Jul 2024 22:05:06 +0530 Subject: [PATCH 04/21] Fix checkstyle issues. --- .../ActionExecutionRequestBuilderFactory.java | 3 +- .../execution/ActionExecutorServiceImpl.java | 20 +++++--- .../exception/ActionExecutionException.java | 1 + ...ctionExecutionRequestBuilderException.java | 4 ++ ...onExecutionResponseProcessorException.java | 4 ++ .../exception/ActionInvocationException.java | 4 ++ .../model/ActionExecutionResponse.java | 50 ------------------- .../model/ActionInvocationErrorResponse.java | 7 +++ .../model/ActionInvocationResponse.java | 44 ++++++++-------- .../ActionInvocationSuccessResponse.java | 7 +++ .../action/execution/model/ActionType.java | 1 - .../action/execution/util/APIClient.java | 3 +- .../action/execution/util/AuthMethods.java | 16 ++++++ 13 files changed, 83 insertions(+), 81 deletions(-) delete mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionResponse.java diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilderFactory.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilderFactory.java index f3e449216ed1..1c8bad0aa97c 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilderFactory.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilderFactory.java @@ -41,7 +41,8 @@ public static ActionExecutionRequestBuilder getActionExecutionRequestBuilder(Act } public static void registerActionExecutionRequestBuilder(ActionType actionType, - ActionExecutionRequestBuilder actionExecutionRequestBuilder) { + ActionExecutionRequestBuilder + actionExecutionRequestBuilder) { actionInvocationRequestBuilders.put(actionType, actionExecutionRequestBuilder); } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java index cc794fc1155e..f1208c718aa4 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java @@ -52,9 +52,10 @@ import java.util.stream.Collectors; /** - * ActionInvocationService. + * This class is responsible for executing the action based on the action type and the event context. + * It is responsible for building the request payload, calling the API, processing the response and + * returning the status of the action execution. */ - public class ActionExecutorServiceImpl implements ActionExecutorService { private static final Log log = LogFactory.getLog(ActionExecutorServiceImpl.class); @@ -183,7 +184,8 @@ private ActionExecutionStatus processActionResponse(ActionInvocationResponse act ActionExecutionRequest actionRequest, String actionId, String apiEndpoint, AuthMethods.AuthMethod authenticationMethod, - ActionExecutionResponseProcessor actionExecutionResponseProcessor) + ActionExecutionResponseProcessor + actionExecutionResponseProcessor) throws ActionExecutionResponseProcessorException { if (actionInvocationResponse.isSuccess()) { @@ -209,7 +211,8 @@ private ActionExecutionStatus processSuccessResponse(ActionInvocationSuccessResp ActionExecutionRequest actionRequest, String actionId, String apiEndpoint, AuthMethods.AuthMethod authenticationMethod, - ActionExecutionResponseProcessor actionExecutionResponseProcessor) + ActionExecutionResponseProcessor + actionExecutionResponseProcessor) throws ActionExecutionResponseProcessorException { if (log.isDebugEnabled()) { @@ -232,7 +235,8 @@ private ActionExecutionStatus processErrorResponse(ActionInvocationErrorResponse ActionExecutionRequest actionRequest, String actionId, String apiEndpoint, AuthMethods.AuthMethod authenticationMethod, - ActionExecutionResponseProcessor actionExecutionResponseProcessor) + ActionExecutionResponseProcessor + actionExecutionResponseProcessor) throws ActionExecutionResponseProcessorException { if (log.isDebugEnabled()) { @@ -250,7 +254,8 @@ private void logSuccessResponse(ActionInvocationSuccessResponse successResponse, try { String responseBody = serializeSuccessResponse(successResponse); log.debug(String.format( - "Received success response from API: %s for action type: %s action id: %s with authentication: %s. Response: %s", + "Received success response from API: %s for action type: %s action id: %s with authentication: %s. " + + "Response: %s", apiEndpoint, actionType, actionId, @@ -269,7 +274,8 @@ private void logErrorResponse(ActionInvocationErrorResponse errorResponse, Actio try { String responseBody = serializeErrorResponse(errorResponse); log.debug(String.format( - "Received error response from API: %s for action type: %s action id: %s with authentication: %s. Response: %s", + "Received error response from API: %s for action type: %s action id: %s with authentication: %s. " + + "Response: %s", apiEndpoint, actionType, actionId, diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionException.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionException.java index 9002c71a2573..01830f768562 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionException.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionException.java @@ -20,6 +20,7 @@ /** * Exception class for Action Execution. + * This exception is thrown when there is an issue in executing the action. */ public class ActionExecutionException extends Exception { diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionRequestBuilderException.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionRequestBuilderException.java index f396cba41889..1e8635a3558f 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionRequestBuilderException.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionRequestBuilderException.java @@ -18,6 +18,10 @@ package org.wso2.carbon.identity.action.execution.exception; +/** + * Exception class for Action Execution Request Builder. + * This exception is thrown when there is an issue in building the Action Execution Request. + */ public class ActionExecutionRequestBuilderException extends Exception { public ActionExecutionRequestBuilderException(String message) { diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionResponseProcessorException.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionResponseProcessorException.java index 9d6f49c5e46b..57d6780b6495 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionResponseProcessorException.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionResponseProcessorException.java @@ -18,6 +18,10 @@ package org.wso2.carbon.identity.action.execution.exception; +/** + * Exception class for Action Execution Response Processor. + * This exception is thrown when there is an issue in processing the Action Execution Response. + */ public class ActionExecutionResponseProcessorException extends Exception { public ActionExecutionResponseProcessorException(String message) { diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionInvocationException.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionInvocationException.java index 8c112d740755..3a40a165e036 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionInvocationException.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionInvocationException.java @@ -18,6 +18,10 @@ package org.wso2.carbon.identity.action.execution.exception; +/** + * Exception class for Action Invocation. + * This exception is thrown when there is an issue in invoking API associated with the action. + */ public class ActionInvocationException extends Exception { public ActionInvocationException(String message) { diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionResponse.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionResponse.java deleted file mode 100644 index d638a9983b42..000000000000 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionResponse.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.wso2.carbon.identity.action.execution.model; - -import java.util.List; - -/** - * Action Invocation Response. - */ -public class ActionExecutionResponse { - - private String actionStatus; - private List operations; - - public String getActionStatus() { - - return actionStatus; - } - - public void setActionStatus(String actionStatus) { - - this.actionStatus = actionStatus; - } - - public List getOperations() { - - return operations; - } - - public void setOperations(List operations) { - - this.operations = operations; - } -} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationErrorResponse.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationErrorResponse.java index b4b5dbeedbfa..87ba7122ea74 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationErrorResponse.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationErrorResponse.java @@ -21,6 +21,9 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +/** + * This class is used to represent the error response of an action invocation. + */ @JsonDeserialize(builder = ActionInvocationErrorResponse.Builder.class) public class ActionInvocationErrorResponse implements ActionInvocationResponse.APIResponse { @@ -50,6 +53,9 @@ public String getErrorUri() { return errorUri; } + /** + * This class is used to build the {@link ActionInvocationErrorResponse}. + */ public static class Builder { private String error; @@ -63,6 +69,7 @@ public ActionInvocationErrorResponse.Builder setError(@JsonProperty("error") Str } public ActionInvocationErrorResponse.Builder setErrorDescription( + @JsonProperty("errorDescription") String errorDescription) { this.errorDescription = errorDescription; diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java index 8eacc38cc350..39649cea8f06 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java @@ -19,14 +19,12 @@ package org.wso2.carbon.identity.action.execution.model; /** - * This class models the Action Execution Response. - * Action Execution Response is the response object that is returned by the Action Executor Service after executing an - * action. It contains the action status and the operations that needs to be performed. + * This class is used to represent the response of an action invocation. + * The response can be either a success or an error. */ public class ActionInvocationResponse { - private int httpStatusCode; - private String actionStatus; + private Status actionStatus; private APIResponse response; private boolean retry; @@ -44,12 +42,12 @@ public APIResponse getResponse() { public boolean isSuccess() { - return "SUCCESS".equalsIgnoreCase(actionStatus); + return Status.SUCCESS.equals(actionStatus); } public boolean isError() { - return "ERROR".equalsIgnoreCase(actionStatus); + return Status.ERROR.equals(actionStatus); } public boolean isRetry() { @@ -62,31 +60,38 @@ public String getErrorLog() { return errorLog; } + /** + * Defines action invocation status. + */ + public enum Status { + SUCCESS, + ERROR + } + + /** + * This interface defines the response of the API call. + */ public interface APIResponse { } + /** + * This class is used to build the {@link ActionInvocationResponse}. + */ public static class Builder { - private int httpStatusCode; - private String actionStatus; + private Status actionStatus; private APIResponse response; private boolean retry; private String errorLog; - public Builder setHttpStatusCode(int httpStatusCode) { - - this.httpStatusCode = httpStatusCode; - return this; - } - public Builder setResponse(APIResponse response) { if (response instanceof ActionInvocationSuccessResponse) { - this.actionStatus = "SUCCESS"; + this.actionStatus = Status.SUCCESS; } else if (response instanceof ActionInvocationErrorResponse) { - this.actionStatus = "ERROR"; + this.actionStatus = Status.ERROR; } this.response = response; @@ -96,21 +101,20 @@ public Builder setResponse(APIResponse response) { public Builder setRetry(boolean retry) { this.retry = retry; - this.actionStatus = "ERROR"; + this.actionStatus = Status.ERROR; return this; } public Builder setErrorLog(String errorLog) { this.errorLog = errorLog; - this.actionStatus = "ERROR"; + this.actionStatus = Status.ERROR; return this; } public ActionInvocationResponse build() { ActionInvocationResponse response = new ActionInvocationResponse(); - response.httpStatusCode = this.httpStatusCode; response.actionStatus = this.actionStatus; response.response = this.response; response.retry = this.retry; diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationSuccessResponse.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationSuccessResponse.java index f2961a0294ed..a28a5aa8858a 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationSuccessResponse.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationSuccessResponse.java @@ -23,6 +23,10 @@ import java.util.List; +/** + * This class is used to represent the success response of an action invocation. + * This response will contain the list of operations that need to be performed. + */ @JsonDeserialize(builder = ActionInvocationSuccessResponse.Builder.class) public class ActionInvocationSuccessResponse implements ActionInvocationResponse.APIResponse { @@ -38,6 +42,9 @@ public List getOperations() { return operations; } + /** + * This class is used to build the {@link ActionInvocationSuccessResponse}. + */ public static class Builder { private List operations; diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionType.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionType.java index 293524740010..5b1c1df4e0b3 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionType.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionType.java @@ -24,5 +24,4 @@ */ public enum ActionType { PRE_ISSUE_ACCESS_TOKEN, - POST_ISSUE_ACCESS_TOKEN, } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java index 2784e385bc18..319440a788d1 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java @@ -44,7 +44,7 @@ import java.util.Optional; /** - * This class is responsible for invoking the HTTP endpoint. + * This class is responsible for making API calls to the external services. */ public class APIClient { @@ -132,7 +132,6 @@ private ActionInvocationResponse handleResponse(HttpResponse response) { HttpEntity responseEntity = response.getEntity(); ActionInvocationResponse.Builder actionInvocationResponseBuilder = new ActionInvocationResponse.Builder(); - actionInvocationResponseBuilder.setHttpStatusCode(statusCode); try { switch (statusCode) { diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/AuthMethods.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/AuthMethods.java index c8652ed71085..c6bb1ab33318 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/AuthMethods.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/AuthMethods.java @@ -25,12 +25,19 @@ import java.util.Base64; import java.util.List; +/** + * This class contains the authentication methods. + * The authentication methods are used to authenticate the HTTP requests. + */ public final class AuthMethods { private AuthMethods() { } + /** + * This interface defines how authentication methods are applied to http request. + */ public interface AuthMethod { void applyAuth(HttpPost httpPost); @@ -38,6 +45,9 @@ public interface AuthMethod { String getAuthType(); } + /** + * This class applies bearer authentication to the http request. + */ public static final class BearerAuth implements AuthMethod { private String token; @@ -63,6 +73,9 @@ public String getAuthType() { } } + /** + * This class applies basic authentication to the http request. + */ public static final class BasicAuth implements AuthMethod { private String username; @@ -100,6 +113,9 @@ public String getAuthType() { } } + /** + * This class applies API Key based authentication to the http request. + */ public static final class APIKeyAuth implements AuthMethod { private String apiHeader; From 6e68ec93f3cc22e68f0c84ab76e050479ab48a48 Mon Sep 17 00:00:00 2001 From: malithie Date: Tue, 23 Jul 2024 12:27:52 +0530 Subject: [PATCH 05/21] Implement request/response service registration. --- .../ActionExecutionRequestBuilder.java | 2 + .../ActionExecutionRequestBuilderFactory.java | 12 +++- .../ActionExecutionResponseProcessor.java | 2 + ...tionExecutionResponseProcessorFactory.java | 12 +++- .../execution/ActionExecutorServiceImpl.java | 3 +- .../ActionExecutionServiceComponent.java | 66 ++++++++++++++++++- 6 files changed, 89 insertions(+), 8 deletions(-) diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilder.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilder.java index 5f5aacced9e5..547326e416e3 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilder.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilder.java @@ -31,6 +31,8 @@ */ public interface ActionExecutionRequestBuilder { + ActionType getSupportedActionType(); + ActionExecutionRequest buildActionExecutionRequest(ActionType actionType, Map eventContext) throws ActionExecutionRequestBuilderException; diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilderFactory.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilderFactory.java index 1c8bad0aa97c..196362691724 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilderFactory.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilderFactory.java @@ -40,11 +40,17 @@ public static ActionExecutionRequestBuilder getActionExecutionRequestBuilder(Act return actionInvocationRequestBuilders.get(actionType); } - public static void registerActionExecutionRequestBuilder(ActionType actionType, - ActionExecutionRequestBuilder + public static void registerActionExecutionRequestBuilder(ActionExecutionRequestBuilder actionExecutionRequestBuilder) { - actionInvocationRequestBuilders.put(actionType, actionExecutionRequestBuilder); + actionInvocationRequestBuilders.put(actionExecutionRequestBuilder.getSupportedActionType(), + actionExecutionRequestBuilder); + } + + public static void unregisterActionExecutionRequestBuilder(ActionExecutionRequestBuilder + actionExecutionRequestBuilder) { + + actionInvocationRequestBuilders.remove(actionExecutionRequestBuilder.getSupportedActionType()); } } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessor.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessor.java index 2c2d695de117..2c03482a2f31 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessor.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessor.java @@ -34,6 +34,8 @@ */ public interface ActionExecutionResponseProcessor { + ActionType getSupportedActionType(); + ActionExecutionStatus processSuccessResponse(ActionType actionType, Map eventContext, Event actionEvent, ActionInvocationSuccessResponse successResponse) throws diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessorFactory.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessorFactory.java index 6c37e13d1db0..f0ca7e5039a8 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessorFactory.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessorFactory.java @@ -43,11 +43,17 @@ public static ActionExecutionResponseProcessor getActionExecutionResponseProcess } } - public static void registerActionExecutionResponseProcessor(ActionType actionType, - ActionExecutionResponseProcessor + public static void registerActionExecutionResponseProcessor(ActionExecutionResponseProcessor actionExecutionResponseProcessor) { - actionInvocationResponseProcessors.put(actionType, actionExecutionResponseProcessor); + actionInvocationResponseProcessors.put(actionExecutionResponseProcessor.getSupportedActionType(), + actionExecutionResponseProcessor); + } + + public static void unregisterActionExecutionResponseProcessor(ActionExecutionResponseProcessor + actionExecutionResponseProcessor) { + + actionInvocationResponseProcessors.remove(actionExecutionResponseProcessor.getSupportedActionType()); } } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java index f1208c718aa4..5f4396a8508e 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java @@ -125,7 +125,8 @@ public ActionExecutionStatus execute(ActionType actionType, Map () -> executeAction(actions.get(0), actionRequest, actionType, eventContext, actionExecutionResponseProcessor)); try { - return actionExecutor.get(); + return Optional.ofNullable(actionExecutor.get()).isPresent() ? actionExecutor.get() : + new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); } catch (InterruptedException | ExecutionException e) { return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java index 69cec1821482..a88a9d3965bf 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java @@ -25,6 +25,13 @@ import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.wso2.carbon.identity.action.execution.ActionExecutionRequestBuilder; +import org.wso2.carbon.identity.action.execution.ActionExecutionRequestBuilderFactory; +import org.wso2.carbon.identity.action.execution.ActionExecutionResponseProcessor; +import org.wso2.carbon.identity.action.execution.ActionExecutionResponseProcessorFactory; import org.wso2.carbon.identity.action.execution.ActionExecutorService; import org.wso2.carbon.identity.action.execution.ActionExecutorServiceImpl; @@ -44,7 +51,8 @@ protected void activate(ComponentContext context) { try { BundleContext bundleCtx = context.getBundleContext(); - bundleCtx.registerService(ActionExecutorService.class, ActionExecutorServiceImpl.getInstance(), null); + bundleCtx.registerService(ActionExecutorService.class.getName(), ActionExecutorServiceImpl.getInstance(), + null); LOG.debug("Action execution bundle is activated"); } catch (Throwable e) { LOG.error("Error while initializing Action execution service component.", e); @@ -63,4 +71,60 @@ protected void deactivate(ComponentContext context) { } } + @Reference( + name = "action.execution.request.builder", + service = ActionExecutionRequestBuilder.class, + cardinality = ReferenceCardinality.MULTIPLE, + policy = ReferencePolicy.DYNAMIC, + unbind = "unsetActionExecutionRequestBuilder" + ) + protected void setActionExecutionRequestBuilder(ActionExecutionRequestBuilder actionExecutionRequestBuilder) { + + if (LOG.isDebugEnabled()) { + LOG.debug( + "Registering ActionExecutionRequestBuilder: " + + actionExecutionRequestBuilder.getClass().getName() + + " in the ActionExecutionServiceComponent."); + } + ActionExecutionRequestBuilderFactory.registerActionExecutionRequestBuilder(actionExecutionRequestBuilder); + } + + protected void unsetActionExecutionRequestBuilder(ActionExecutionRequestBuilder actionExecutionRequestBuilder) { + + if (LOG.isDebugEnabled()) { + LOG.debug("Unregistering ActionExecutionRequestBuilder: " + + actionExecutionRequestBuilder.getClass().getName() + " in the ActionExecutionServiceComponent."); + } + ActionExecutionRequestBuilderFactory.unregisterActionExecutionRequestBuilder(actionExecutionRequestBuilder); + } + + @Reference( + name = "action.execution.response.processor", + service = ActionExecutionResponseProcessor.class, + cardinality = ReferenceCardinality.MULTIPLE, + policy = ReferencePolicy.DYNAMIC, + unbind = "unsetActionExecutionResponseProcessor" + ) + protected void setActionExecutionResponseProcessor( + ActionExecutionResponseProcessor actionExecutionResponseProcessor) { + + if (LOG.isDebugEnabled()) { + LOG.debug( + "Registering ActionExecutionResponseProcessor: " + + actionExecutionResponseProcessor.getClass().getName() + + " in the ActionExecutionServiceComponent."); + } + ActionExecutionResponseProcessorFactory.registerActionExecutionResponseProcessor( + actionExecutionResponseProcessor); + } + + protected void unsetActionExecutionResponseProcessor(ActionExecutionResponseProcessor actionExecutionResponseProcessor) { + + if (LOG.isDebugEnabled()) { + LOG.debug("Unregistering ActionExecutionResponseProcessor: " + + actionExecutionResponseProcessor.getClass().getName() + " in the ActionExecutionServiceComponent."); + } + ActionExecutionResponseProcessorFactory.unregisterActionExecutionResponseProcessor(actionExecutionResponseProcessor); + } + } From f7621c0a9d73862765544454031e889dee177a87 Mon Sep 17 00:00:00 2001 From: malithie Date: Wed, 24 Jul 2024 09:18:43 +0530 Subject: [PATCH 06/21] Fix request response parsing. --- .../pom.xml | 7 ++- .../execution/ActionExecutorServiceImpl.java | 34 ++++++----- .../ActionExecutionServiceComponent.java | 35 ++++++++++- .../model/ActionExecutionResponse.java | 50 ---------------- .../model/ActionInvocationErrorResponse.java | 38 +++++++----- .../model/ActionInvocationResponse.java | 11 ++-- .../ActionInvocationSuccessResponse.java | 25 +++++++- .../execution/model/AllowedOperation.java | 6 +- .../action/execution/model/Operation.java | 58 +++++++++++++++++++ .../execution/model/PerformableOperation.java | 6 +- .../action/execution/util/APIClient.java | 3 - 11 files changed, 169 insertions(+), 104 deletions(-) delete mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionResponse.java create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Operation.java diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml b/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml index c27a67e85044..f39d10fa9cd1 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml @@ -96,13 +96,16 @@ org.apache.http.client.methods; version="${httpcore.version.osgi.import.range}", org.apache.http.concurrent; version="${httpcore.version.osgi.import.range}", org.apache.http.conn; version="${httpcore.version.osgi.import.range}", + org.apache.http.entity; version="${httpcore.version.osgi.import.range}", + org.apache.http.util; version="${httpcore.version.osgi.import.range}", org.apache.http.impl.client; version="${httpcomponents-httpclient.imp.pkg.version.range}", org.apache.http.impl.conn; version="${httpcomponents-httpclient.imp.pkg.version.range}", org.osgi.framework; version="${osgi.framework.imp.pkg.version.range}", org.osgi.service.component; version="${osgi.service.component.imp.pkg.version.range}", org.wso2.carbon.utils; version="${carbon.kernel.package.import.version.range}", - com.fasterxml.jackson.databind.*; - version="${com.fasterxml.jackson.annotation.version.range}", + com.fasterxml.jackson.core.*; version="${com.fasterxml.jackson.annotation.version.range}", + com.fasterxml.jackson.databind.*; version="${com.fasterxml.jackson.annotation.version.range}", + com.fasterxml.jackson.annotation.*; version="${com.fasterxml.jackson.annotation.version.range}", diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java index 5f4396a8508e..ec4adcb03f54 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java @@ -79,7 +79,7 @@ public ActionExecutionStatus execute(ActionType actionType, Map List actions; try { actions = ActionExecutionServiceComponentHolder.getInstance().getActionManagementService() - .getActionsByActionType(actionType.name(), tenantDomain); + .getActionsByActionType(Action.ActionTypes.valueOf(actionType.name()).getPathParam(), tenantDomain); } catch (ActionMgtException e) { //todo: throw and block ? log.error("Skip executing actions for action type: " + actionType.name() + @@ -121,15 +121,8 @@ public ActionExecutionStatus execute(ActionType actionType, Map return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); } - CompletableFuture actionExecutor = CompletableFuture.supplyAsync( - () -> executeAction(actions.get(0), actionRequest, actionType, eventContext, - actionExecutionResponseProcessor)); - try { - return Optional.ofNullable(actionExecutor.get()).isPresent() ? actionExecutor.get() : - new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); - } catch (InterruptedException | ExecutionException e) { - return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); - } + return executeAction(actions.get(0), actionRequest, actionType, eventContext, + actionExecutionResponseProcessor); } private ActionExecutionStatus executeAction(Action action, ActionExecutionRequest actionRequest, @@ -147,13 +140,22 @@ private ActionExecutionStatus executeAction(Action action, ActionExecutionReques logActionRequest(apiEndpoint, actionType, action.getId(), authenticationMethod, payload); - ActionInvocationResponse actionInvocationResponse = - apiClient.callAPI(apiEndpoint, authenticationMethod, payload); + CompletableFuture actionExecutor = + CompletableFuture.supplyAsync(() -> apiClient.callAPI(apiEndpoint, authenticationMethod, payload)); + + ActionInvocationResponse actionInvocationResponse; + try { + actionInvocationResponse = actionExecutor.get(); + } catch (InterruptedException | ExecutionException e) { + //todo: Add to diagnostics + log.error("Skip executing action: " + action.getId() + " for action type: " + actionType + + ". Error occurred during action execution.", e); + return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); + } + return processActionResponse(actionInvocationResponse, actionType, eventContext, actionRequest, action.getId(), - apiEndpoint, authenticationMethod, actionExecutionResponseProcessor - ); - + apiEndpoint, authenticationMethod, actionExecutionResponseProcessor); } catch (ActionMgtException | JsonProcessingException | ActionExecutionResponseProcessorException e) { //todo: Add to diagnostics log.error("Skip executing action: " + action.getId() + " for action type: " + actionType + @@ -224,7 +226,7 @@ private ActionExecutionStatus processSuccessResponse(ActionInvocationSuccessResp List allowedPerformableOperations = validatePerformableOperations(actionRequest, successResponse); ActionInvocationSuccessResponse.Builder successResponseBuilder = - new ActionInvocationSuccessResponse.Builder().setOperations(allowedPerformableOperations); + new ActionInvocationSuccessResponse.Builder().operations(allowedPerformableOperations); return actionExecutionResponseProcessor.processSuccessResponse(actionType, eventContext, actionRequest.getEvent(), successResponseBuilder.build()); diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java index a88a9d3965bf..f52e50ce9e28 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java @@ -34,6 +34,7 @@ import org.wso2.carbon.identity.action.execution.ActionExecutionResponseProcessorFactory; import org.wso2.carbon.identity.action.execution.ActionExecutorService; import org.wso2.carbon.identity.action.execution.ActionExecutorServiceImpl; +import org.wso2.carbon.identity.action.management.ActionManagementService; /** * OSGI service component for the Action execution. @@ -71,6 +72,33 @@ protected void deactivate(ComponentContext context) { } } + @Reference( + name = "action.management.service", + service = ActionManagementService.class, + cardinality = ReferenceCardinality.MANDATORY, + policy = ReferencePolicy.DYNAMIC, + unbind = "unsetActionManagementService" + ) + protected void setActionManagementService(ActionManagementService actionManagementService) { + + if (LOG.isDebugEnabled()) { + LOG.debug("Registering a reference for ActionManagementService in the ActionExecutionServiceComponent."); + } + ActionExecutionServiceComponentHolder.getInstance().setActionManagementService(actionManagementService); + } + + protected void unsetActionManagementService(ActionManagementService actionManagementService) { + + if (LOG.isDebugEnabled()) { + LOG.debug( + "Unregistering the reference for ActionManagementService in the ActionExecutionServiceComponent."); + } + if (ActionExecutionServiceComponentHolder.getInstance().getActionManagementService() + .equals(actionManagementService)) { + ActionExecutionServiceComponentHolder.getInstance().setActionManagementService(null); + } + } + @Reference( name = "action.execution.request.builder", service = ActionExecutionRequestBuilder.class, @@ -118,13 +146,14 @@ protected void setActionExecutionResponseProcessor( actionExecutionResponseProcessor); } - protected void unsetActionExecutionResponseProcessor(ActionExecutionResponseProcessor actionExecutionResponseProcessor) { + protected void unsetActionExecutionResponseProcessor( + ActionExecutionResponseProcessor actionExecutionResponseProcessor) { if (LOG.isDebugEnabled()) { LOG.debug("Unregistering ActionExecutionResponseProcessor: " + actionExecutionResponseProcessor.getClass().getName() + " in the ActionExecutionServiceComponent."); } - ActionExecutionResponseProcessorFactory.unregisterActionExecutionResponseProcessor(actionExecutionResponseProcessor); + ActionExecutionResponseProcessorFactory.unregisterActionExecutionResponseProcessor( + actionExecutionResponseProcessor); } - } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionResponse.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionResponse.java deleted file mode 100644 index d638a9983b42..000000000000 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionResponse.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.wso2.carbon.identity.action.execution.model; - -import java.util.List; - -/** - * Action Invocation Response. - */ -public class ActionExecutionResponse { - - private String actionStatus; - private List operations; - - public String getActionStatus() { - - return actionStatus; - } - - public void setActionStatus(String actionStatus) { - - this.actionStatus = actionStatus; - } - - public List getOperations() { - - return operations; - } - - public void setOperations(List operations) { - - this.operations = operations; - } -} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationErrorResponse.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationErrorResponse.java index 87ba7122ea74..c2cb471e0b0d 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationErrorResponse.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationErrorResponse.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; /** * This class is used to represent the error response of an action invocation. @@ -27,15 +28,20 @@ @JsonDeserialize(builder = ActionInvocationErrorResponse.Builder.class) public class ActionInvocationErrorResponse implements ActionInvocationResponse.APIResponse { + private final ActionInvocationResponse.Status actionStatus; private final String error; private final String errorDescription; - private final String errorUri; private ActionInvocationErrorResponse(Builder builder) { + this.actionStatus = builder.actionStatus; this.error = builder.error; this.errorDescription = builder.errorDescription; - this.errorUri = builder.errorUri; + } + + public ActionInvocationResponse.Status getActionStatus() { + + return actionStatus; } public String getError() { @@ -48,37 +54,37 @@ public String getErrorDescription() { return errorDescription; } - public String getErrorUri() { - - return errorUri; - } - /** * This class is used to build the {@link ActionInvocationErrorResponse}. */ + @JsonPOJOBuilder(withPrefix = "") public static class Builder { + private ActionInvocationResponse.Status actionStatus; private String error; private String errorDescription; - private String errorUri; - public ActionInvocationErrorResponse.Builder setError(@JsonProperty("error") String error) { + @JsonProperty("actionStatus") + public Builder actionStatus(ActionInvocationResponse.Status actionStatus) { - this.error = error; + if (!ActionInvocationResponse.Status.ERROR.equals(actionStatus)) { + throw new IllegalArgumentException("actionStatus must be ERROR"); + } + this.actionStatus = actionStatus; return this; } - public ActionInvocationErrorResponse.Builder setErrorDescription( - - @JsonProperty("errorDescription") String errorDescription) { + @JsonProperty("error") + public Builder error(String error) { - this.errorDescription = errorDescription; + this.error = error; return this; } - public ActionInvocationErrorResponse.Builder setErrorUri(@JsonProperty("errorUri") String errorUri) { + @JsonProperty("errorDescription") + public Builder errorDescription(String errorDescription) { - this.errorUri = errorUri; + this.errorDescription = errorDescription; return this; } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java index 39649cea8f06..fcd2e6d24803 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java @@ -32,7 +32,7 @@ public class ActionInvocationResponse { private String errorLog; private ActionInvocationResponse() { - // Private constructor to enforce the use of the Builder + } public APIResponse getResponse() { @@ -73,6 +73,8 @@ public enum Status { */ public interface APIResponse { + Status getActionStatus(); + } /** @@ -88,12 +90,7 @@ public static class Builder { public Builder setResponse(APIResponse response) { - if (response instanceof ActionInvocationSuccessResponse) { - this.actionStatus = Status.SUCCESS; - } else if (response instanceof ActionInvocationErrorResponse) { - this.actionStatus = Status.ERROR; - } - + this.actionStatus = response.getActionStatus(); this.response = response; return this; } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationSuccessResponse.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationSuccessResponse.java index a28a5aa8858a..95e5df391a12 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationSuccessResponse.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationSuccessResponse.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; import java.util.List; @@ -30,13 +31,22 @@ @JsonDeserialize(builder = ActionInvocationSuccessResponse.Builder.class) public class ActionInvocationSuccessResponse implements ActionInvocationResponse.APIResponse { + private final ActionInvocationResponse.Status actionStatus; + private final List operations; private ActionInvocationSuccessResponse(Builder builder) { + this.actionStatus = builder.actionStatus; this.operations = builder.operations; } + @Override + public ActionInvocationResponse.Status getActionStatus() { + + return actionStatus; + } + public List getOperations() { return operations; @@ -45,11 +55,24 @@ public List getOperations() { /** * This class is used to build the {@link ActionInvocationSuccessResponse}. */ + @JsonPOJOBuilder(withPrefix = "") public static class Builder { + private ActionInvocationResponse.Status actionStatus; private List operations; - public Builder setOperations(@JsonProperty("operations") List operations) { + @JsonProperty("actionStatus") + public Builder actionStatus(String actionStatus) { + + if (!ActionInvocationResponse.Status.SUCCESS.name().equals(actionStatus)) { + throw new IllegalArgumentException("actionStatus must be SUCCESS"); + } + this.actionStatus = ActionInvocationResponse.Status.SUCCESS; + return this; + } + + @JsonProperty("operations") + public Builder operations(@JsonProperty("operations") List operations) { this.operations = operations; return this; diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/AllowedOperation.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/AllowedOperation.java index b67c8c45f3e9..fb358265eff5 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/AllowedOperation.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/AllowedOperation.java @@ -29,16 +29,16 @@ */ public class AllowedOperation { - String op; + Operation op; List paths; - public String getOp() { + public Operation getOp() { return op; } - public void setOp(String op) { + public void setOp(Operation op) { this.op = op; } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Operation.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Operation.java new file mode 100644 index 000000000000..65414f717062 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Operation.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * This class models the Operation types allowed. + * Operation is defined by the operation type and the paths that the operation is performed on. + * Operations follow JSON Patch format (RFC 6902) semantics to define the operations that are performed. + */ +public enum Operation { + ADD("add"), + REMOVE("remove"), + REPLACE("replace"); + + private final String value; + + Operation(String value) { + + this.value = value; + } + + @JsonValue + public String getValue() { + + return value; + } + + @JsonCreator + public static Operation forValue(String value) { + + for (Operation op : Operation.values()) { + if (op.getValue().equals(value)) { + return op; + } + } + throw new IllegalArgumentException("Invalid operation value: " + value); + } +} + diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/PerformableOperation.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/PerformableOperation.java index f526744384d3..d07765ccc0b3 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/PerformableOperation.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/PerformableOperation.java @@ -28,16 +28,16 @@ */ public class PerformableOperation { - private String op; + private Operation op; private String path; private Object value; - public String getOp() { + public Operation getOp() { return op; } - public void setOp(String op) { + public void setOp(Operation op) { this.op = op; } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java index 319440a788d1..f29a01adb4e5 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java @@ -165,9 +165,6 @@ private ActionInvocationResponse handleResponse(HttpResponse response) { private ActionInvocationSuccessResponse handleSuccessResponse(HttpEntity responseEntity) throws ActionInvocationException { - if (!isAcceptablePayload(responseEntity)) { - throw new ActionInvocationException("The response content type is not application/json."); - } return deserialize(responseEntity, ActionInvocationSuccessResponse.class); } From 98c52a3fab4a1d430b0b175d5e7100718df087cf Mon Sep 17 00:00:00 2001 From: malithie Date: Wed, 24 Jul 2024 11:28:57 +0530 Subject: [PATCH 07/21] Improve code readability. --- .../execution/ActionExecutorServiceImpl.java | 111 ++++++++++-------- .../ActionExecutionRuntimeException.java | 39 ++++++ .../action/execution/util/APIClient.java | 10 +- 3 files changed, 108 insertions(+), 52 deletions(-) create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionRuntimeException.java diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java index ec4adcb03f54..bc2ae77e9b53 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java @@ -26,6 +26,7 @@ import org.wso2.carbon.identity.action.execution.exception.ActionExecutionException; import org.wso2.carbon.identity.action.execution.exception.ActionExecutionRequestBuilderException; import org.wso2.carbon.identity.action.execution.exception.ActionExecutionResponseProcessorException; +import org.wso2.carbon.identity.action.execution.exception.ActionExecutionRuntimeException; import org.wso2.carbon.identity.action.execution.internal.ActionExecutionServiceComponentHolder; import org.wso2.carbon.identity.action.execution.model.ActionExecutionRequest; import org.wso2.carbon.identity.action.execution.model.ActionExecutionStatus; @@ -76,59 +77,76 @@ public static ActionExecutorServiceImpl getInstance() { public ActionExecutionStatus execute(ActionType actionType, Map eventContext, String tenantDomain) throws ActionExecutionException { - List actions; try { - actions = ActionExecutionServiceComponentHolder.getInstance().getActionManagementService() + List actions = getActionsByActionType(actionType, tenantDomain); + validateActions(actions, actionType); + ActionExecutionRequest actionRequest = buildActionExecutionRequest(actionType, eventContext); + ActionExecutionResponseProcessor actionExecutionResponseProcessor = getResponseProcessor(actionType); + return executeAction(actions.get(0), actionRequest, actionType, eventContext, + actionExecutionResponseProcessor); + } catch (ActionExecutionRuntimeException e) { + // todo: add to diagnostics + log.error("Skip executing actions for action type: " + actionType.name() + ". Error: " + e.getMessage(), e); + return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); + + } + } + + private List getActionsByActionType(ActionType actionType, String tenantDomain) throws + ActionExecutionRuntimeException { + + try { + return ActionExecutionServiceComponentHolder.getInstance().getActionManagementService() .getActionsByActionType(Action.ActionTypes.valueOf(actionType.name()).getPathParam(), tenantDomain); } catch (ActionMgtException e) { - //todo: throw and block ? - log.error("Skip executing actions for action type: " + actionType.name() + - ". Error occurred while retrieving actions.", e); - return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); + throw new ActionExecutionRuntimeException("Error occurred while retrieving actions.", e); } + } + + private void validateActions(List actions, ActionType actionType) throws ActionExecutionException { if (actions.isEmpty()) { - log.info("No actions found for action type: " + actionType.name()); - return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); + throw new ActionExecutionRuntimeException("No actions found for action type: " + actionType); } - if (actions.size() > 1) { // when multiple actions are supported for an action type the logic below needs to be improved such that, // a successful processing from one action becomes the input to the successor. throw new ActionExecutionException("Multiple actions found for action type: " + actionType.name() + - "Current implementation doesn't support for multiple actions for a single action type."); + ". Current implementation doesn't support multiple actions for a single action type."); } + } - ActionExecutionRequestBuilder actionExecutionRequestBuilder = + private ActionExecutionRequest buildActionExecutionRequest(ActionType actionType, Map eventContext) + throws ActionExecutionException { + + ActionExecutionRequestBuilder requestBuilder = ActionExecutionRequestBuilderFactory.getActionExecutionRequestBuilder(actionType); - if (actionExecutionRequestBuilder == null) { + if (requestBuilder == null) { throw new ActionExecutionException("No request builder found for action type: " + actionType); } - - ActionExecutionResponseProcessor actionExecutionResponseProcessor = - ActionExecutionResponseProcessorFactory.getActionExecutionResponseProcessor(actionType); - if (actionExecutionResponseProcessor == null) { - throw new ActionExecutionException("No response processor found for action type: " + actionType); - } - - ActionExecutionRequest actionRequest; try { - actionRequest = actionExecutionRequestBuilder.buildActionExecutionRequest(actionType, eventContext); + return requestBuilder.buildActionExecutionRequest(actionType, eventContext); } catch (ActionExecutionRequestBuilderException e) { - //todo: Add to diagnostics ? - log.error("Skip executing actions for action type: " + actionType.name() + - ". Error occurred while building the request payload.", e); - return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); + throw new ActionExecutionRuntimeException("Error occurred while building the request payload.", e); } + } - return executeAction(actions.get(0), actionRequest, actionType, eventContext, - actionExecutionResponseProcessor); + private ActionExecutionResponseProcessor getResponseProcessor(ActionType actionType) + throws ActionExecutionException { + + ActionExecutionResponseProcessor responseProcessor = + ActionExecutionResponseProcessorFactory.getActionExecutionResponseProcessor(actionType); + if (responseProcessor == null) { + throw new ActionExecutionException("No response processor found for action type: " + actionType); + } + return responseProcessor; } private ActionExecutionStatus executeAction(Action action, ActionExecutionRequest actionRequest, ActionType actionType, Map eventContext, - ActionExecutionResponseProcessor actionExecutionResponseProcessor) { + ActionExecutionResponseProcessor actionExecutionResponseProcessor) + throws ActionExecutionRuntimeException { String apiEndpoint = action.getEndpoint().getUri(); AuthType endpointAuthentication = action.getEndpoint().getAuthentication(); @@ -140,29 +158,28 @@ private ActionExecutionStatus executeAction(Action action, ActionExecutionReques logActionRequest(apiEndpoint, actionType, action.getId(), authenticationMethod, payload); - CompletableFuture actionExecutor = - CompletableFuture.supplyAsync(() -> apiClient.callAPI(apiEndpoint, authenticationMethod, payload)); - - ActionInvocationResponse actionInvocationResponse; - try { - actionInvocationResponse = actionExecutor.get(); - } catch (InterruptedException | ExecutionException e) { - //todo: Add to diagnostics - log.error("Skip executing action: " + action.getId() + " for action type: " + actionType + - ". Error occurred during action execution.", e); - return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); - } - + ActionInvocationResponse actionInvocationResponse = + executeActionAsynchronously(action, authenticationMethod, payload); return processActionResponse(actionInvocationResponse, actionType, eventContext, actionRequest, - action.getId(), - apiEndpoint, authenticationMethod, actionExecutionResponseProcessor); + action.getId(), apiEndpoint, authenticationMethod, actionExecutionResponseProcessor); } catch (ActionMgtException | JsonProcessingException | ActionExecutionResponseProcessorException e) { - //todo: Add to diagnostics - log.error("Skip executing action: " + action.getId() + " for action type: " + actionType + - ". Error occurred during action execution.", e); + throw new ActionExecutionRuntimeException("Error occurred while executing action: " + action.getId(), e); } + } - return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); + private ActionInvocationResponse executeActionAsynchronously(Action action, + AuthMethods.AuthMethod authenticationMethod, + String payload) { + + String apiEndpoint = action.getEndpoint().getUri(); + CompletableFuture actionExecutor = CompletableFuture.supplyAsync( + () -> apiClient.callAPI(apiEndpoint, authenticationMethod, payload)); + try { + return actionExecutor.get(); + } catch (InterruptedException | ExecutionException e) { + throw new ActionExecutionRuntimeException("Error occurred while executing action: " + action.getId(), + e); + } } private void logActionRequest(String apiEndpoint, ActionType actionType, String actionId, diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionRuntimeException.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionRuntimeException.java new file mode 100644 index 000000000000..a4142c9e20a7 --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/exception/ActionExecutionRuntimeException.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.execution.exception; + +/** + * Runtime Exception class for Action Execution. + * This exception is thrown when there is an issue in executing the action, + * that doesn't need to handled by the consuming party of the ActionExecutionService, + * yet be logged for troubleshooting requirements. + */ +public class ActionExecutionRuntimeException extends RuntimeException { + + public ActionExecutionRuntimeException(String message) { + + super(message); + } + + public ActionExecutionRuntimeException(String message, Throwable cause) { + + super(message, cause); + } + +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java index f29a01adb4e5..fdac97c406af 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java @@ -48,7 +48,7 @@ */ public class APIClient { - private static final Log log = LogFactory.getLog(APIClient.class); + private static final Log LOG = LogFactory.getLog(APIClient.class); private final CloseableHttpClient httpClient; public APIClient() { @@ -106,15 +106,15 @@ private Optional executeRequest(HttpPost request) { return Optional.of(actionInvocationResponse); } //todo: add to diagnostic logs - log.warn("API: " + request.getURI() + " seems to be unavailable. Retrying the request. Attempt " + + LOG.warn("API: " + request.getURI() + " seems to be unavailable. Retrying the request. Attempt " + (attempts + 1) + " of " + retryCount); } catch (ConnectTimeoutException | SocketTimeoutException e) { //todo: add to diagnostic logs - log.warn("Request for API: " + request.getURI() + " timed out. Retrying the request. Attempt " + + LOG.warn("Request for API: " + request.getURI() + " timed out. Retrying the request. Attempt " + (attempts + 1) + " of " + retryCount); } catch (Exception e) { //todo: add to diagnostic logs - log.error("Request for API: " + request.getURI() + " failed due to an error.", e); + LOG.error("Request for API: " + request.getURI() + " failed due to an error.", e); break; } finally { request.releaseConnection(); @@ -122,7 +122,7 @@ private Optional executeRequest(HttpPost request) { attempts++; } - log.warn("Maximum retry attempts reached for API: " + request.getURI()); + LOG.warn("Maximum retry attempts reached for API: " + request.getURI()); return Optional.empty(); } From 20fead1dac407ec02ea0306ffe28c9a1821b1d50 Mon Sep 17 00:00:00 2001 From: malithie Date: Wed, 24 Jul 2024 11:33:06 +0530 Subject: [PATCH 08/21] Changed variable all caps when static final. --- .../execution/ActionExecutorServiceImpl.java | 32 +++++++++---------- ...ActionExecutionServiceComponentHolder.java | 4 +-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java index bc2ae77e9b53..62e760fa6ece 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java @@ -59,9 +59,9 @@ */ public class ActionExecutorServiceImpl implements ActionExecutorService { - private static final Log log = LogFactory.getLog(ActionExecutorServiceImpl.class); + private static final Log LOG = LogFactory.getLog(ActionExecutorServiceImpl.class); - private static final ActionExecutorServiceImpl instance = new ActionExecutorServiceImpl(); + private static final ActionExecutorServiceImpl INSTANCE = new ActionExecutorServiceImpl(); private final APIClient apiClient; private ActionExecutorServiceImpl() { @@ -71,7 +71,7 @@ private ActionExecutorServiceImpl() { public static ActionExecutorServiceImpl getInstance() { - return instance; + return INSTANCE; } public ActionExecutionStatus execute(ActionType actionType, Map eventContext, String tenantDomain) @@ -86,7 +86,7 @@ public ActionExecutionStatus execute(ActionType actionType, Map actionExecutionResponseProcessor); } catch (ActionExecutionRuntimeException e) { // todo: add to diagnostics - log.error("Skip executing actions for action type: " + actionType.name() + ". Error: " + e.getMessage(), e); + LOG.error("Skip executing actions for action type: " + actionType.name() + ". Error: " + e.getMessage(), e); return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); } @@ -186,8 +186,8 @@ private void logActionRequest(String apiEndpoint, ActionType actionType, String AuthMethods.AuthMethod authenticationMethod, String payload) { //todo: Add to diagnostics - if (log.isDebugEnabled()) { - log.debug(String.format( + if (LOG.isDebugEnabled()) { + LOG.debug(String.format( "Calling API: %s for action type: %s action id: %s with authentication: %s payload: %s", apiEndpoint, actionType, @@ -235,7 +235,7 @@ private ActionExecutionStatus processSuccessResponse(ActionInvocationSuccessResp actionExecutionResponseProcessor) throws ActionExecutionResponseProcessorException { - if (log.isDebugEnabled()) { + if (LOG.isDebugEnabled()) { // todo: add to diagnostic logs logSuccessResponse(successResponse, actionType, actionId, apiEndpoint, authenticationMethod); } @@ -259,7 +259,7 @@ private ActionExecutionStatus processErrorResponse(ActionInvocationErrorResponse actionExecutionResponseProcessor) throws ActionExecutionResponseProcessorException { - if (log.isDebugEnabled()) { + if (LOG.isDebugEnabled()) { // todo: add to diagnostic logs logErrorResponse(errorResponse, actionType, actionId, apiEndpoint, authenticationMethod); } @@ -273,7 +273,7 @@ private void logSuccessResponse(ActionInvocationSuccessResponse successResponse, try { String responseBody = serializeSuccessResponse(successResponse); - log.debug(String.format( + LOG.debug(String.format( "Received success response from API: %s for action type: %s action id: %s with authentication: %s. " + "Response: %s", apiEndpoint, @@ -283,7 +283,7 @@ private void logSuccessResponse(ActionInvocationSuccessResponse successResponse, .orElse("NONE"), responseBody)); } catch (JsonProcessingException e) { - log.error("Error occurred while deserializing the success response for action: " + + LOG.error("Error occurred while deserializing the success response for action: " + actionId + " for action type: " + actionType, e); } } @@ -293,7 +293,7 @@ private void logErrorResponse(ActionInvocationErrorResponse errorResponse, Actio try { String responseBody = serializeErrorResponse(errorResponse); - log.debug(String.format( + LOG.debug(String.format( "Received error response from API: %s for action type: %s action id: %s with authentication: %s. " + "Response: %s", apiEndpoint, @@ -303,7 +303,7 @@ private void logErrorResponse(ActionInvocationErrorResponse errorResponse, Actio .orElse("NONE"), responseBody)); } catch (JsonProcessingException e) { - log.error("Error occurred while deserializing the error response for action: " + + LOG.error("Error occurred while deserializing the error response for action: " + actionId + " for action type: " + actionType, e); } } @@ -311,8 +311,8 @@ private void logErrorResponse(ActionInvocationErrorResponse errorResponse, Actio private void logErrorResponse(ActionInvocationResponse actionInvocationResponse, ActionType actionType, String actionId, String apiEndpoint, AuthMethods.AuthMethod authenticationMethod) { // todo: add to diagnostic logs - if (log.isDebugEnabled()) { - log.debug(String.format( + if (LOG.isDebugEnabled()) { + LOG.debug(String.format( "Failed to call API: %s for action type: %s action id: %s with authentication: %s. Error: %s", apiEndpoint, actionType, @@ -355,7 +355,7 @@ private List validatePerformableOperations(ActionExecution performableOperation))) .collect(Collectors.toList()); - if (log.isDebugEnabled()) { + if (LOG.isDebugEnabled()) { // todo: add to diagnostics List allowedOps = new ArrayList<>(); List notAllowedOps = new ArrayList<>(); @@ -371,7 +371,7 @@ private List validatePerformableOperations(ActionExecution String logMessage = "Allowed Operations: " + String.join(", ", allowedOps) + ". Not Allowed Operations: " + String.join(", ", notAllowedOps); - log.debug(logMessage); + LOG.debug(logMessage); } return allowedPerformableOperations; diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponentHolder.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponentHolder.java index 5acef30d4b6e..c280d16e495d 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponentHolder.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponentHolder.java @@ -25,7 +25,7 @@ */ public class ActionExecutionServiceComponentHolder { - private static final ActionExecutionServiceComponentHolder instance = new ActionExecutionServiceComponentHolder(); + private static final ActionExecutionServiceComponentHolder INSTANCE = new ActionExecutionServiceComponentHolder(); private ActionManagementService actionManagementService; @@ -35,7 +35,7 @@ private ActionExecutionServiceComponentHolder() { public static ActionExecutionServiceComponentHolder getInstance() { - return instance; + return INSTANCE; } public ActionManagementService getActionManagementService() { From 0bd1c7eab1c20b1951a7bdf070883a4fd92ea2b8 Mon Sep 17 00:00:00 2001 From: malithie Date: Wed, 24 Jul 2024 12:15:42 +0530 Subject: [PATCH 09/21] Remove incomplete tests. --- .../action/execution/util/APIClientTest.java | 72 ------------------- 1 file changed, 72 deletions(-) delete mode 100644 components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/java/org/wso2/carbon/identity/action/execution/util/APIClientTest.java diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/java/org/wso2/carbon/identity/action/execution/util/APIClientTest.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/java/org/wso2/carbon/identity/action/execution/util/APIClientTest.java deleted file mode 100644 index 52d7daa53a32..000000000000 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/test/java/org/wso2/carbon/identity/action/execution/util/APIClientTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.wso2.carbon.identity.action.execution.util; - -import org.apache.http.HttpResponse; -import org.apache.http.ProtocolVersion; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.message.BasicStatusLine; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.testng.Assert; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; -import org.wso2.carbon.identity.action.execution.model.ActionInvocationSuccessResponse; -import org.wso2.carbon.identity.action.execution.model.ActionInvocationResponse; - -import java.nio.charset.StandardCharsets; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class APIClientTest { - -// @Mock - /*private CloseableHttpClient mockHttpClient; - - private APIClient apiClient; - - @BeforeMethod - public void setUp() { - - MockitoAnnotations.initMocks(this); - apiClient = new APIClient(mockHttpClient); - } - - @Test - public void testCallAPISuccess() throws Exception { - - HttpResponse mockResponse = mock(HttpResponse.class); - when(mockResponse.getStatusLine()).thenReturn( - new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); - String json = "{\"message\":\"success\"}"; - when(mockResponse.getEntity()).thenReturn(new StringEntity(json, StandardCharsets.UTF_8)); - - when(mockHttpClient.execute(any(HttpPost.class))).thenReturn(mockResponse); - - ActionInvocationResponse response = apiClient.callAPI("http://example.com/api", null, "{}"); - - Assert.assertNotNull(response); -// Assert.assertTrue(response instanceof ActionInvocationSuccessResponse); -// Assert.assertEquals(((ActionInvocationSuccessResponse) response).getMessage(), "success"); - }*/ -} \ No newline at end of file From b8c8c3da62a26f7051f1c080b451ad712b16bb4d Mon Sep 17 00:00:00 2001 From: malithie Date: Wed, 24 Jul 2024 19:22:04 +0530 Subject: [PATCH 10/21] Update action.mgt dependency version. --- .../org.wso2.carbon.identity.action.execution/pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml b/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml index f39d10fa9cd1..633de3718430 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml @@ -41,6 +41,8 @@ org.wso2.carbon.identity.framework org.wso2.carbon.identity.action.management + + 7.3.47 com.fasterxml.jackson.core From 22aed58101e07cf32f0afaf47ff475428b19d4e4 Mon Sep 17 00:00:00 2001 From: malithie Date: Wed, 24 Jul 2024 19:40:21 +0530 Subject: [PATCH 11/21] Merge master branch and update version. --- .../org.wso2.carbon.identity.action.execution/pom.xml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml b/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml index 633de3718430..ab707e1a3a8c 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.identity.framework action-mgt - 7.3.42-SNAPSHOT + 7.3.48-SNAPSHOT ../pom.xml @@ -41,8 +41,6 @@ org.wso2.carbon.identity.framework org.wso2.carbon.identity.action.management - - 7.3.47 com.fasterxml.jackson.core From 16b65a45a1203e3e211124cd04aa6d51d35b0cae Mon Sep 17 00:00:00 2001 From: malithie Date: Thu, 25 Jul 2024 11:32:00 +0530 Subject: [PATCH 12/21] Add isActive method. --- .../wso2/carbon/identity/action/management/model/Action.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/model/Action.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/model/Action.java index 2ac5a22f401b..0c3f4e7c0a53 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/model/Action.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/model/Action.java @@ -175,6 +175,11 @@ public void setEndpoint(EndpointConfig endpointConfig) { this.endpointConfig = endpointConfig; } + public boolean isActive() { + + return Status.ACTIVE.equals(status); + } + /** * ActionResponseBuilder. */ From 35ba937e1f178c3effd51c30afb6f351bd0f2678 Mon Sep 17 00:00:00 2001 From: malithie Date: Thu, 25 Jul 2024 11:37:10 +0530 Subject: [PATCH 13/21] Revert "Add isActive method." This reverts commit 16b65a45a1203e3e211124cd04aa6d51d35b0cae. --- .../wso2/carbon/identity/action/management/model/Action.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/model/Action.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/model/Action.java index 0c3f4e7c0a53..2ac5a22f401b 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/model/Action.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/model/Action.java @@ -175,11 +175,6 @@ public void setEndpoint(EndpointConfig endpointConfig) { this.endpointConfig = endpointConfig; } - public boolean isActive() { - - return Status.ACTIVE.equals(status); - } - /** * ActionResponseBuilder. */ From 64040fff615c16f0bad8c08be79e3f8d3329a82d Mon Sep 17 00:00:00 2001 From: malithie Date: Thu, 25 Jul 2024 11:43:30 +0530 Subject: [PATCH 14/21] Check for active actions before invoking. --- .../action/execution/ActionExecutorServiceImpl.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java index 62e760fa6ece..e6f908b78d57 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.identity.action.execution.exception.ActionExecutionException; @@ -82,8 +83,13 @@ public ActionExecutionStatus execute(ActionType actionType, Map validateActions(actions, actionType); ActionExecutionRequest actionRequest = buildActionExecutionRequest(actionType, eventContext); ActionExecutionResponseProcessor actionExecutionResponseProcessor = getResponseProcessor(actionType); - return executeAction(actions.get(0), actionRequest, actionType, eventContext, - actionExecutionResponseProcessor); + + Action action = actions.get(0); // As of now only one action is allowed. + return Optional.ofNullable(action) + .filter(activeAction -> activeAction.getStatus() == Action.Status.ACTIVE) + .map(activeAction -> executeAction(activeAction, actionRequest, actionType, eventContext, + actionExecutionResponseProcessor)) + .orElse(new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext)); } catch (ActionExecutionRuntimeException e) { // todo: add to diagnostics LOG.error("Skip executing actions for action type: " + actionType.name() + ". Error: " + e.getMessage(), e); @@ -105,7 +111,7 @@ private List getActionsByActionType(ActionType actionType, String tenant private void validateActions(List actions, ActionType actionType) throws ActionExecutionException { - if (actions.isEmpty()) { + if (CollectionUtils.isEmpty(actions)) { throw new ActionExecutionRuntimeException("No actions found for action type: " + actionType); } if (actions.size() > 1) { From 009a79a379443f413dee2a24542d4da52fa6ea08 Mon Sep 17 00:00:00 2001 From: malithie Date: Fri, 26 Jul 2024 18:35:03 +0530 Subject: [PATCH 15/21] Address review comments. --- .../execution/ActionExecutorServiceImpl.java | 125 ++++++++---------- .../ActionExecutionServiceComponent.java | 10 +- ...ActionExecutionServiceComponentHolder.java | 3 +- .../model/ActionInvocationResponse.java | 6 +- .../execution/model/AllowedOperation.java | 4 +- .../action/execution/model/Application.java | 4 +- .../action/execution/model/Request.java | 3 - .../identity/action/execution/model/User.java | 1 - .../action/execution/model/UserStore.java | 4 +- .../action/execution/util/APIClient.java | 10 +- 10 files changed, 73 insertions(+), 97 deletions(-) diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java index e6f908b78d57..67b4b9f0938a 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java @@ -87,7 +87,7 @@ public ActionExecutionStatus execute(ActionType actionType, Map Action action = actions.get(0); // As of now only one action is allowed. return Optional.ofNullable(action) .filter(activeAction -> activeAction.getStatus() == Action.Status.ACTIVE) - .map(activeAction -> executeAction(activeAction, actionRequest, actionType, eventContext, + .map(activeAction -> executeAction(actionType, activeAction, actionRequest, eventContext, actionExecutionResponseProcessor)) .orElse(new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext)); } catch (ActionExecutionRuntimeException e) { @@ -148,8 +148,8 @@ private ActionExecutionResponseProcessor getResponseProcessor(ActionType actionT return responseProcessor; } - private ActionExecutionStatus executeAction(Action action, ActionExecutionRequest actionRequest, - ActionType actionType, + private ActionExecutionStatus executeAction(ActionType actionType, Action action, + ActionExecutionRequest actionRequest, Map eventContext, ActionExecutionResponseProcessor actionExecutionResponseProcessor) throws ActionExecutionRuntimeException { @@ -162,12 +162,13 @@ private ActionExecutionStatus executeAction(Action action, ActionExecutionReques authenticationMethod = getAuthenticationMethod(action.getId(), endpointAuthentication); String payload = serializeRequest(actionRequest); - logActionRequest(apiEndpoint, actionType, action.getId(), authenticationMethod, payload); + logActionRequest(apiEndpoint, action.getType().getActionType(), action.getId(), authenticationMethod, + payload); ActionInvocationResponse actionInvocationResponse = executeActionAsynchronously(action, authenticationMethod, payload); - return processActionResponse(actionInvocationResponse, actionType, eventContext, actionRequest, - action.getId(), apiEndpoint, authenticationMethod, actionExecutionResponseProcessor); + return processActionResponse(actionType, action, actionInvocationResponse, eventContext, actionRequest, + actionExecutionResponseProcessor); } catch (ActionMgtException | JsonProcessingException | ActionExecutionResponseProcessorException e) { throw new ActionExecutionRuntimeException("Error occurred while executing action: " + action.getId(), e); } @@ -188,7 +189,7 @@ private ActionInvocationResponse executeActionAsynchronously(Action action, } } - private void logActionRequest(String apiEndpoint, ActionType actionType, String actionId, + private void logActionRequest(String apiEndpoint, String actionType, String actionId, AuthMethods.AuthMethod authenticationMethod, String payload) { //todo: Add to diagnostics @@ -204,46 +205,40 @@ private void logActionRequest(String apiEndpoint, ActionType actionType, String } } - private ActionExecutionStatus processActionResponse(ActionInvocationResponse actionInvocationResponse, - ActionType actionType, + private ActionExecutionStatus processActionResponse(ActionType actionType, Action action, + ActionInvocationResponse actionInvocationResponse, Map eventContext, ActionExecutionRequest actionRequest, - String actionId, String apiEndpoint, - AuthMethods.AuthMethod authenticationMethod, ActionExecutionResponseProcessor actionExecutionResponseProcessor) throws ActionExecutionResponseProcessorException { if (actionInvocationResponse.isSuccess()) { - return processSuccessResponse((ActionInvocationSuccessResponse) actionInvocationResponse.getResponse(), - actionType, - eventContext, actionRequest, actionId, apiEndpoint, authenticationMethod, - actionExecutionResponseProcessor); + return processSuccessResponse(actionType, action, + (ActionInvocationSuccessResponse) actionInvocationResponse.getResponse(), + eventContext, actionRequest, actionExecutionResponseProcessor); } else if (actionInvocationResponse.isError() && actionInvocationResponse.getResponse() != null) { - return processErrorResponse((ActionInvocationErrorResponse) actionInvocationResponse.getResponse(), - actionType, - eventContext, actionRequest, actionId, apiEndpoint, authenticationMethod, - actionExecutionResponseProcessor); + return processErrorResponse(actionType, action, + (ActionInvocationErrorResponse) actionInvocationResponse.getResponse(), + eventContext, actionRequest, actionExecutionResponseProcessor); } else { - logErrorResponse(actionInvocationResponse, actionType, actionId, apiEndpoint, authenticationMethod); + logErrorResponse(action, actionInvocationResponse); } return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); } - private ActionExecutionStatus processSuccessResponse(ActionInvocationSuccessResponse successResponse, - ActionType actionType, + private ActionExecutionStatus processSuccessResponse(ActionType actionType, Action action, + ActionInvocationSuccessResponse successResponse, Map eventContext, ActionExecutionRequest actionRequest, - String actionId, String apiEndpoint, - AuthMethods.AuthMethod authenticationMethod, ActionExecutionResponseProcessor actionExecutionResponseProcessor) throws ActionExecutionResponseProcessorException { if (LOG.isDebugEnabled()) { // todo: add to diagnostic logs - logSuccessResponse(successResponse, actionType, actionId, apiEndpoint, authenticationMethod); + logSuccessResponse(action, successResponse); } List allowedPerformableOperations = @@ -251,80 +246,70 @@ private ActionExecutionStatus processSuccessResponse(ActionInvocationSuccessResp ActionInvocationSuccessResponse.Builder successResponseBuilder = new ActionInvocationSuccessResponse.Builder().operations(allowedPerformableOperations); return actionExecutionResponseProcessor.processSuccessResponse(actionType, eventContext, - actionRequest.getEvent(), - successResponseBuilder.build()); + actionRequest.getEvent(), successResponseBuilder.build()); } - private ActionExecutionStatus processErrorResponse(ActionInvocationErrorResponse errorResponse, - ActionType actionType, + private ActionExecutionStatus processErrorResponse(ActionType actionType, Action action, + ActionInvocationErrorResponse errorResponse, Map eventContext, ActionExecutionRequest actionRequest, - String actionId, String apiEndpoint, - AuthMethods.AuthMethod authenticationMethod, ActionExecutionResponseProcessor actionExecutionResponseProcessor) throws ActionExecutionResponseProcessorException { - if (LOG.isDebugEnabled()) { - // todo: add to diagnostic logs - logErrorResponse(errorResponse, actionType, actionId, apiEndpoint, authenticationMethod); - } - + logErrorResponse(action, errorResponse); return actionExecutionResponseProcessor.processErrorResponse(actionType, eventContext, actionRequest.getEvent(), errorResponse); } - private void logSuccessResponse(ActionInvocationSuccessResponse successResponse, ActionType actionType, - String actionId, String apiEndpoint, AuthMethods.AuthMethod authenticationMethod) { + private void logSuccessResponse(Action action, ActionInvocationSuccessResponse successResponse) { try { String responseBody = serializeSuccessResponse(successResponse); LOG.debug(String.format( "Received success response from API: %s for action type: %s action id: %s with authentication: %s. " + "Response: %s", - apiEndpoint, - actionType, - actionId, - Optional.ofNullable(authenticationMethod).map(AuthMethods.AuthMethod::getAuthType) - .orElse("NONE"), + action.getEndpoint().getUri(), + action.getType().getActionType(), + action.getId(), + action.getEndpoint().getAuthentication().getType(), responseBody)); } catch (JsonProcessingException e) { LOG.error("Error occurred while deserializing the success response for action: " + - actionId + " for action type: " + actionType, e); + action.getId() + " for action type: " + action.getType().getActionType(), e); } } - private void logErrorResponse(ActionInvocationErrorResponse errorResponse, ActionType actionType, - String actionId, String apiEndpoint, AuthMethods.AuthMethod authenticationMethod) { + private void logErrorResponse(Action action, ActionInvocationErrorResponse errorResponse) { - try { - String responseBody = serializeErrorResponse(errorResponse); - LOG.debug(String.format( - "Received error response from API: %s for action type: %s action id: %s with authentication: %s. " + - "Response: %s", - apiEndpoint, - actionType, - actionId, - Optional.ofNullable(authenticationMethod).map(AuthMethods.AuthMethod::getAuthType) - .orElse("NONE"), - responseBody)); - } catch (JsonProcessingException e) { - LOG.error("Error occurred while deserializing the error response for action: " + - actionId + " for action type: " + actionType, e); + if (LOG.isDebugEnabled()) { + // todo: add to diagnostic logs + try { + String responseBody = serializeErrorResponse(errorResponse); + LOG.debug(String.format( + "Received error response from API: %s for action type: %s action id: %s with " + + "authentication: %s. Response: %s", + action.getEndpoint().getUri(), + action.getType().getActionType(), + action.getId(), + action.getEndpoint().getAuthentication().getType(), + responseBody)); + } catch (JsonProcessingException e) { + LOG.debug("Error occurred while deserializing the error response for action: " + + action.getId() + " for action type: " + action.getType().getActionType(), e); + } } } - private void logErrorResponse(ActionInvocationResponse actionInvocationResponse, ActionType actionType, - String actionId, String apiEndpoint, AuthMethods.AuthMethod authenticationMethod) { + private void logErrorResponse(Action action, ActionInvocationResponse actionInvocationResponse) { // todo: add to diagnostic logs if (LOG.isDebugEnabled()) { LOG.debug(String.format( "Failed to call API: %s for action type: %s action id: %s with authentication: %s. Error: %s", - apiEndpoint, - actionType, - actionId, - Optional.ofNullable(authenticationMethod).map(AuthMethods.AuthMethod::getAuthType) - .orElse("NONE"), + action.getEndpoint().getUri(), + action.getType().getActionType(), + action.getId(), + action.getEndpoint().getAuthentication(), actionInvocationResponse.getErrorLog() != null ? actionInvocationResponse.getErrorLog() : "Unknown")); } @@ -374,10 +359,8 @@ private List validatePerformableOperations(ActionExecution notAllowedOps.add(operationDetails); } }); - - String logMessage = "Allowed Operations: " + String.join(", ", allowedOps) + - ". Not Allowed Operations: " + String.join(", ", notAllowedOps); - LOG.debug(logMessage); + LOG.debug("Allowed Operations: " + String.join(", ", allowedOps) + + ". Not Allowed Operations: " + String.join(", ", notAllowedOps)); } return allowedPerformableOperations; diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java index f52e50ce9e28..3615860846b1 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java @@ -54,7 +54,7 @@ protected void activate(ComponentContext context) { BundleContext bundleCtx = context.getBundleContext(); bundleCtx.registerService(ActionExecutorService.class.getName(), ActionExecutorServiceImpl.getInstance(), null); - LOG.debug("Action execution bundle is activated"); + LOG.debug("Action execution bundle is activated."); } catch (Throwable e) { LOG.error("Error while initializing Action execution service component.", e); } @@ -66,7 +66,7 @@ protected void deactivate(ComponentContext context) { try { BundleContext bundleCtx = context.getBundleContext(); bundleCtx.ungetService(bundleCtx.getServiceReference(ActionExecutorService.class)); - LOG.debug("Action execution bundle is deactivated"); + LOG.debug("Action execution bundle is deactivated."); } catch (Throwable e) { LOG.error("Error while deactivating Action execution service component.", e); } @@ -109,8 +109,7 @@ protected void unsetActionManagementService(ActionManagementService actionManage protected void setActionExecutionRequestBuilder(ActionExecutionRequestBuilder actionExecutionRequestBuilder) { if (LOG.isDebugEnabled()) { - LOG.debug( - "Registering ActionExecutionRequestBuilder: " + + LOG.debug("Registering ActionExecutionRequestBuilder: " + actionExecutionRequestBuilder.getClass().getName() + " in the ActionExecutionServiceComponent."); } @@ -137,8 +136,7 @@ protected void setActionExecutionResponseProcessor( ActionExecutionResponseProcessor actionExecutionResponseProcessor) { if (LOG.isDebugEnabled()) { - LOG.debug( - "Registering ActionExecutionResponseProcessor: " + + LOG.debug("Registering ActionExecutionResponseProcessor: " + actionExecutionResponseProcessor.getClass().getName() + " in the ActionExecutionServiceComponent."); } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponentHolder.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponentHolder.java index c280d16e495d..83be753ef413 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponentHolder.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponentHolder.java @@ -43,8 +43,7 @@ public ActionManagementService getActionManagementService() { return actionManagementService; } - public void setActionManagementService( - ActionManagementService actionManagementService) { + public void setActionManagementService(ActionManagementService actionManagementService) { this.actionManagementService = actionManagementService; } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java index fcd2e6d24803..03cc7dab3663 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java @@ -88,21 +88,21 @@ public static class Builder { private String errorLog; - public Builder setResponse(APIResponse response) { + public Builder response(APIResponse response) { this.actionStatus = response.getActionStatus(); this.response = response; return this; } - public Builder setRetry(boolean retry) { + public Builder retry(boolean retry) { this.retry = retry; this.actionStatus = Status.ERROR; return this; } - public Builder setErrorLog(String errorLog) { + public Builder errorLog(String errorLog) { this.errorLog = errorLog; this.actionStatus = Status.ERROR; diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/AllowedOperation.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/AllowedOperation.java index fb358265eff5..eaa412242d18 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/AllowedOperation.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/AllowedOperation.java @@ -29,9 +29,9 @@ */ public class AllowedOperation { - Operation op; + private Operation op; - List paths; + private List paths; public Operation getOp() { diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Application.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Application.java index d74aa1cfdbb8..9019e08b524c 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Application.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Application.java @@ -24,8 +24,8 @@ */ public class Application { - String id; - String name; + private final String id; + private final String name; public Application(String id, String name) { diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Request.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Request.java index 653f9363a343..8998afaf417d 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Request.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Request.java @@ -33,16 +33,13 @@ public abstract class Request { protected Map additionalHeaders = new HashMap<>(); protected Map additionalParams = new HashMap<>(); - // implement a method to get additional headers public Map getAdditionalHeaders() { return additionalHeaders != null ? additionalHeaders : Collections.emptyMap(); } - // implement a method to get additional params public Map getAdditionalParams() { return additionalParams != null ? additionalParams : Collections.emptyMap(); } - } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/User.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/User.java index 3944bea48522..c9f63919d2a8 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/User.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/User.java @@ -39,5 +39,4 @@ public void setId(String id) { this.id = id; } - } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/UserStore.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/UserStore.java index e8773729b838..0753c072d35f 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/UserStore.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/UserStore.java @@ -28,8 +28,8 @@ */ public class UserStore { - String id; - String name; + private String id; + private String name; public UserStore(String name) { diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java index fdac97c406af..f5fa137795bb 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java @@ -79,7 +79,7 @@ public ActionInvocationResponse callAPI(String url, AuthMethods.AuthMethod authM setRequestEntity(httpPost, payload, authMethod); return executeRequest(httpPost).orElse(new ActionInvocationResponse.Builder() - .setErrorLog("Failed to execute the action request or maximum retry attempts reached.") + .errorLog("Failed to execute the action request or maximum retry attempts reached.") .build()); } @@ -137,26 +137,26 @@ private ActionInvocationResponse handleResponse(HttpResponse response) { switch (statusCode) { case HttpStatus.SC_OK: ActionInvocationSuccessResponse successResponse = handleSuccessResponse(responseEntity); - actionInvocationResponseBuilder.setResponse(successResponse); + actionInvocationResponseBuilder.response(successResponse); break; case HttpStatus.SC_BAD_REQUEST: case HttpStatus.SC_INTERNAL_SERVER_ERROR: ActionInvocationErrorResponse errorResponse = handleErrorResponse(responseEntity); - actionInvocationResponseBuilder.setResponse(errorResponse); + actionInvocationResponseBuilder.response(errorResponse); break; case HttpStatus.SC_UNAUTHORIZED: break; case HttpStatus.SC_BAD_GATEWAY: case HttpStatus.SC_SERVICE_UNAVAILABLE: case HttpStatus.SC_GATEWAY_TIMEOUT: - actionInvocationResponseBuilder.setRetry(true); + actionInvocationResponseBuilder.retry(true); break; default: throw new ActionInvocationException("Unexpected response status code: " + statusCode); } } catch (ActionInvocationException e) { // Set error in response to be logged at diagnostic logs for troubleshooting. - actionInvocationResponseBuilder.setErrorLog("Unexpected response. Error: " + e.getMessage()); + actionInvocationResponseBuilder.errorLog("Unexpected response. Error: " + e.getMessage()); } return actionInvocationResponseBuilder.build(); From bd9b4c28fc4a667bbba1b49e480a47b111218323 Mon Sep 17 00:00:00 2001 From: malithie Date: Fri, 26 Jul 2024 18:35:03 +0530 Subject: [PATCH 16/21] Address review comments. --- .../execution/ActionExecutorServiceImpl.java | 134 ++++++++---------- .../ActionExecutionServiceComponent.java | 10 +- ...ActionExecutionServiceComponentHolder.java | 3 +- .../model/ActionInvocationResponse.java | 6 +- .../execution/model/AllowedOperation.java | 4 +- .../action/execution/model/Application.java | 4 +- .../action/execution/model/Request.java | 3 - .../identity/action/execution/model/User.java | 1 - .../action/execution/model/UserStore.java | 4 +- .../action/execution/util/APIClient.java | 10 +- 10 files changed, 76 insertions(+), 103 deletions(-) diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java index e6f908b78d57..39de8b1111bf 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java @@ -87,7 +87,7 @@ public ActionExecutionStatus execute(ActionType actionType, Map Action action = actions.get(0); // As of now only one action is allowed. return Optional.ofNullable(action) .filter(activeAction -> activeAction.getStatus() == Action.Status.ACTIVE) - .map(activeAction -> executeAction(activeAction, actionRequest, actionType, eventContext, + .map(activeAction -> executeAction(actionType, activeAction, actionRequest, eventContext, actionExecutionResponseProcessor)) .orElse(new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext)); } catch (ActionExecutionRuntimeException e) { @@ -148,8 +148,8 @@ private ActionExecutionResponseProcessor getResponseProcessor(ActionType actionT return responseProcessor; } - private ActionExecutionStatus executeAction(Action action, ActionExecutionRequest actionRequest, - ActionType actionType, + private ActionExecutionStatus executeAction(ActionType actionType, Action action, + ActionExecutionRequest actionRequest, Map eventContext, ActionExecutionResponseProcessor actionExecutionResponseProcessor) throws ActionExecutionRuntimeException { @@ -162,12 +162,12 @@ private ActionExecutionStatus executeAction(Action action, ActionExecutionReques authenticationMethod = getAuthenticationMethod(action.getId(), endpointAuthentication); String payload = serializeRequest(actionRequest); - logActionRequest(apiEndpoint, actionType, action.getId(), authenticationMethod, payload); + logActionRequest(action, payload); ActionInvocationResponse actionInvocationResponse = executeActionAsynchronously(action, authenticationMethod, payload); - return processActionResponse(actionInvocationResponse, actionType, eventContext, actionRequest, - action.getId(), apiEndpoint, authenticationMethod, actionExecutionResponseProcessor); + return processActionResponse(actionType, action, actionInvocationResponse, eventContext, actionRequest, + actionExecutionResponseProcessor); } catch (ActionMgtException | JsonProcessingException | ActionExecutionResponseProcessorException e) { throw new ActionExecutionRuntimeException("Error occurred while executing action: " + action.getId(), e); } @@ -188,62 +188,54 @@ private ActionInvocationResponse executeActionAsynchronously(Action action, } } - private void logActionRequest(String apiEndpoint, ActionType actionType, String actionId, - AuthMethods.AuthMethod authenticationMethod, String payload) { + private void logActionRequest(Action action, String payload) { //todo: Add to diagnostics if (LOG.isDebugEnabled()) { LOG.debug(String.format( "Calling API: %s for action type: %s action id: %s with authentication: %s payload: %s", - apiEndpoint, - actionType, - actionId, - Optional.ofNullable(authenticationMethod).map(AuthMethods.AuthMethod::getAuthType) - .orElse("NONE"), + action.getEndpoint().getUri(), + action.getType().getActionType(), + action.getId(), + action.getEndpoint().getAuthentication(), payload)); } } - private ActionExecutionStatus processActionResponse(ActionInvocationResponse actionInvocationResponse, - ActionType actionType, + private ActionExecutionStatus processActionResponse(ActionType actionType, Action action, + ActionInvocationResponse actionInvocationResponse, Map eventContext, ActionExecutionRequest actionRequest, - String actionId, String apiEndpoint, - AuthMethods.AuthMethod authenticationMethod, ActionExecutionResponseProcessor actionExecutionResponseProcessor) throws ActionExecutionResponseProcessorException { if (actionInvocationResponse.isSuccess()) { - return processSuccessResponse((ActionInvocationSuccessResponse) actionInvocationResponse.getResponse(), - actionType, - eventContext, actionRequest, actionId, apiEndpoint, authenticationMethod, - actionExecutionResponseProcessor); + return processSuccessResponse(actionType, action, + (ActionInvocationSuccessResponse) actionInvocationResponse.getResponse(), + eventContext, actionRequest, actionExecutionResponseProcessor); } else if (actionInvocationResponse.isError() && actionInvocationResponse.getResponse() != null) { - return processErrorResponse((ActionInvocationErrorResponse) actionInvocationResponse.getResponse(), - actionType, - eventContext, actionRequest, actionId, apiEndpoint, authenticationMethod, - actionExecutionResponseProcessor); + return processErrorResponse(actionType, action, + (ActionInvocationErrorResponse) actionInvocationResponse.getResponse(), + eventContext, actionRequest, actionExecutionResponseProcessor); } else { - logErrorResponse(actionInvocationResponse, actionType, actionId, apiEndpoint, authenticationMethod); + logErrorResponse(action, actionInvocationResponse); } return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); } - private ActionExecutionStatus processSuccessResponse(ActionInvocationSuccessResponse successResponse, - ActionType actionType, + private ActionExecutionStatus processSuccessResponse(ActionType actionType, Action action, + ActionInvocationSuccessResponse successResponse, Map eventContext, ActionExecutionRequest actionRequest, - String actionId, String apiEndpoint, - AuthMethods.AuthMethod authenticationMethod, ActionExecutionResponseProcessor actionExecutionResponseProcessor) throws ActionExecutionResponseProcessorException { if (LOG.isDebugEnabled()) { // todo: add to diagnostic logs - logSuccessResponse(successResponse, actionType, actionId, apiEndpoint, authenticationMethod); + logSuccessResponse(action, successResponse); } List allowedPerformableOperations = @@ -251,80 +243,70 @@ private ActionExecutionStatus processSuccessResponse(ActionInvocationSuccessResp ActionInvocationSuccessResponse.Builder successResponseBuilder = new ActionInvocationSuccessResponse.Builder().operations(allowedPerformableOperations); return actionExecutionResponseProcessor.processSuccessResponse(actionType, eventContext, - actionRequest.getEvent(), - successResponseBuilder.build()); + actionRequest.getEvent(), successResponseBuilder.build()); } - private ActionExecutionStatus processErrorResponse(ActionInvocationErrorResponse errorResponse, - ActionType actionType, + private ActionExecutionStatus processErrorResponse(ActionType actionType, Action action, + ActionInvocationErrorResponse errorResponse, Map eventContext, ActionExecutionRequest actionRequest, - String actionId, String apiEndpoint, - AuthMethods.AuthMethod authenticationMethod, ActionExecutionResponseProcessor actionExecutionResponseProcessor) throws ActionExecutionResponseProcessorException { - if (LOG.isDebugEnabled()) { - // todo: add to diagnostic logs - logErrorResponse(errorResponse, actionType, actionId, apiEndpoint, authenticationMethod); - } - + logErrorResponse(action, errorResponse); return actionExecutionResponseProcessor.processErrorResponse(actionType, eventContext, actionRequest.getEvent(), errorResponse); } - private void logSuccessResponse(ActionInvocationSuccessResponse successResponse, ActionType actionType, - String actionId, String apiEndpoint, AuthMethods.AuthMethod authenticationMethod) { + private void logSuccessResponse(Action action, ActionInvocationSuccessResponse successResponse) { try { String responseBody = serializeSuccessResponse(successResponse); LOG.debug(String.format( "Received success response from API: %s for action type: %s action id: %s with authentication: %s. " + "Response: %s", - apiEndpoint, - actionType, - actionId, - Optional.ofNullable(authenticationMethod).map(AuthMethods.AuthMethod::getAuthType) - .orElse("NONE"), + action.getEndpoint().getUri(), + action.getType().getActionType(), + action.getId(), + action.getEndpoint().getAuthentication().getType(), responseBody)); } catch (JsonProcessingException e) { LOG.error("Error occurred while deserializing the success response for action: " + - actionId + " for action type: " + actionType, e); + action.getId() + " for action type: " + action.getType().getActionType(), e); } } - private void logErrorResponse(ActionInvocationErrorResponse errorResponse, ActionType actionType, - String actionId, String apiEndpoint, AuthMethods.AuthMethod authenticationMethod) { + private void logErrorResponse(Action action, ActionInvocationErrorResponse errorResponse) { - try { - String responseBody = serializeErrorResponse(errorResponse); - LOG.debug(String.format( - "Received error response from API: %s for action type: %s action id: %s with authentication: %s. " + - "Response: %s", - apiEndpoint, - actionType, - actionId, - Optional.ofNullable(authenticationMethod).map(AuthMethods.AuthMethod::getAuthType) - .orElse("NONE"), - responseBody)); - } catch (JsonProcessingException e) { - LOG.error("Error occurred while deserializing the error response for action: " + - actionId + " for action type: " + actionType, e); + if (LOG.isDebugEnabled()) { + // todo: add to diagnostic logs + try { + String responseBody = serializeErrorResponse(errorResponse); + LOG.debug(String.format( + "Received error response from API: %s for action type: %s action id: %s with " + + "authentication: %s. Response: %s", + action.getEndpoint().getUri(), + action.getType().getActionType(), + action.getId(), + action.getEndpoint().getAuthentication().getType(), + responseBody)); + } catch (JsonProcessingException e) { + LOG.debug("Error occurred while deserializing the error response for action: " + + action.getId() + " for action type: " + action.getType().getActionType(), e); + } } } - private void logErrorResponse(ActionInvocationResponse actionInvocationResponse, ActionType actionType, - String actionId, String apiEndpoint, AuthMethods.AuthMethod authenticationMethod) { + private void logErrorResponse(Action action, ActionInvocationResponse actionInvocationResponse) { // todo: add to diagnostic logs if (LOG.isDebugEnabled()) { LOG.debug(String.format( "Failed to call API: %s for action type: %s action id: %s with authentication: %s. Error: %s", - apiEndpoint, - actionType, - actionId, - Optional.ofNullable(authenticationMethod).map(AuthMethods.AuthMethod::getAuthType) - .orElse("NONE"), + action.getEndpoint().getUri(), + action.getType().getActionType(), + action.getId(), + action.getEndpoint().getAuthentication(), actionInvocationResponse.getErrorLog() != null ? actionInvocationResponse.getErrorLog() : "Unknown")); } @@ -374,10 +356,8 @@ private List validatePerformableOperations(ActionExecution notAllowedOps.add(operationDetails); } }); - - String logMessage = "Allowed Operations: " + String.join(", ", allowedOps) + - ". Not Allowed Operations: " + String.join(", ", notAllowedOps); - LOG.debug(logMessage); + LOG.debug("Allowed Operations: " + String.join(", ", allowedOps) + + ". Not Allowed Operations: " + String.join(", ", notAllowedOps)); } return allowedPerformableOperations; diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java index f52e50ce9e28..3615860846b1 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponent.java @@ -54,7 +54,7 @@ protected void activate(ComponentContext context) { BundleContext bundleCtx = context.getBundleContext(); bundleCtx.registerService(ActionExecutorService.class.getName(), ActionExecutorServiceImpl.getInstance(), null); - LOG.debug("Action execution bundle is activated"); + LOG.debug("Action execution bundle is activated."); } catch (Throwable e) { LOG.error("Error while initializing Action execution service component.", e); } @@ -66,7 +66,7 @@ protected void deactivate(ComponentContext context) { try { BundleContext bundleCtx = context.getBundleContext(); bundleCtx.ungetService(bundleCtx.getServiceReference(ActionExecutorService.class)); - LOG.debug("Action execution bundle is deactivated"); + LOG.debug("Action execution bundle is deactivated."); } catch (Throwable e) { LOG.error("Error while deactivating Action execution service component.", e); } @@ -109,8 +109,7 @@ protected void unsetActionManagementService(ActionManagementService actionManage protected void setActionExecutionRequestBuilder(ActionExecutionRequestBuilder actionExecutionRequestBuilder) { if (LOG.isDebugEnabled()) { - LOG.debug( - "Registering ActionExecutionRequestBuilder: " + + LOG.debug("Registering ActionExecutionRequestBuilder: " + actionExecutionRequestBuilder.getClass().getName() + " in the ActionExecutionServiceComponent."); } @@ -137,8 +136,7 @@ protected void setActionExecutionResponseProcessor( ActionExecutionResponseProcessor actionExecutionResponseProcessor) { if (LOG.isDebugEnabled()) { - LOG.debug( - "Registering ActionExecutionResponseProcessor: " + + LOG.debug("Registering ActionExecutionResponseProcessor: " + actionExecutionResponseProcessor.getClass().getName() + " in the ActionExecutionServiceComponent."); } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponentHolder.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponentHolder.java index c280d16e495d..83be753ef413 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponentHolder.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/ActionExecutionServiceComponentHolder.java @@ -43,8 +43,7 @@ public ActionManagementService getActionManagementService() { return actionManagementService; } - public void setActionManagementService( - ActionManagementService actionManagementService) { + public void setActionManagementService(ActionManagementService actionManagementService) { this.actionManagementService = actionManagementService; } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java index fcd2e6d24803..03cc7dab3663 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionInvocationResponse.java @@ -88,21 +88,21 @@ public static class Builder { private String errorLog; - public Builder setResponse(APIResponse response) { + public Builder response(APIResponse response) { this.actionStatus = response.getActionStatus(); this.response = response; return this; } - public Builder setRetry(boolean retry) { + public Builder retry(boolean retry) { this.retry = retry; this.actionStatus = Status.ERROR; return this; } - public Builder setErrorLog(String errorLog) { + public Builder errorLog(String errorLog) { this.errorLog = errorLog; this.actionStatus = Status.ERROR; diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/AllowedOperation.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/AllowedOperation.java index fb358265eff5..eaa412242d18 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/AllowedOperation.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/AllowedOperation.java @@ -29,9 +29,9 @@ */ public class AllowedOperation { - Operation op; + private Operation op; - List paths; + private List paths; public Operation getOp() { diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Application.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Application.java index d74aa1cfdbb8..9019e08b524c 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Application.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Application.java @@ -24,8 +24,8 @@ */ public class Application { - String id; - String name; + private final String id; + private final String name; public Application(String id, String name) { diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Request.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Request.java index 653f9363a343..8998afaf417d 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Request.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/Request.java @@ -33,16 +33,13 @@ public abstract class Request { protected Map additionalHeaders = new HashMap<>(); protected Map additionalParams = new HashMap<>(); - // implement a method to get additional headers public Map getAdditionalHeaders() { return additionalHeaders != null ? additionalHeaders : Collections.emptyMap(); } - // implement a method to get additional params public Map getAdditionalParams() { return additionalParams != null ? additionalParams : Collections.emptyMap(); } - } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/User.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/User.java index 3944bea48522..c9f63919d2a8 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/User.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/User.java @@ -39,5 +39,4 @@ public void setId(String id) { this.id = id; } - } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/UserStore.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/UserStore.java index e8773729b838..0753c072d35f 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/UserStore.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/UserStore.java @@ -28,8 +28,8 @@ */ public class UserStore { - String id; - String name; + private String id; + private String name; public UserStore(String name) { diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java index fdac97c406af..f5fa137795bb 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/util/APIClient.java @@ -79,7 +79,7 @@ public ActionInvocationResponse callAPI(String url, AuthMethods.AuthMethod authM setRequestEntity(httpPost, payload, authMethod); return executeRequest(httpPost).orElse(new ActionInvocationResponse.Builder() - .setErrorLog("Failed to execute the action request or maximum retry attempts reached.") + .errorLog("Failed to execute the action request or maximum retry attempts reached.") .build()); } @@ -137,26 +137,26 @@ private ActionInvocationResponse handleResponse(HttpResponse response) { switch (statusCode) { case HttpStatus.SC_OK: ActionInvocationSuccessResponse successResponse = handleSuccessResponse(responseEntity); - actionInvocationResponseBuilder.setResponse(successResponse); + actionInvocationResponseBuilder.response(successResponse); break; case HttpStatus.SC_BAD_REQUEST: case HttpStatus.SC_INTERNAL_SERVER_ERROR: ActionInvocationErrorResponse errorResponse = handleErrorResponse(responseEntity); - actionInvocationResponseBuilder.setResponse(errorResponse); + actionInvocationResponseBuilder.response(errorResponse); break; case HttpStatus.SC_UNAUTHORIZED: break; case HttpStatus.SC_BAD_GATEWAY: case HttpStatus.SC_SERVICE_UNAVAILABLE: case HttpStatus.SC_GATEWAY_TIMEOUT: - actionInvocationResponseBuilder.setRetry(true); + actionInvocationResponseBuilder.retry(true); break; default: throw new ActionInvocationException("Unexpected response status code: " + statusCode); } } catch (ActionInvocationException e) { // Set error in response to be logged at diagnostic logs for troubleshooting. - actionInvocationResponseBuilder.setErrorLog("Unexpected response. Error: " + e.getMessage()); + actionInvocationResponseBuilder.errorLog("Unexpected response. Error: " + e.getMessage()); } return actionInvocationResponseBuilder.build(); From d69fa2cd50f234192bb3b353dfd73d209e5590d9 Mon Sep 17 00:00:00 2001 From: malithie Date: Fri, 26 Jul 2024 19:05:41 +0530 Subject: [PATCH 17/21] Address review comments. --- .../action/execution/ActionExecutionRequestBuilder.java | 2 +- .../action/execution/ActionExecutionResponseProcessor.java | 4 ++-- .../action/execution/ActionExecutorServiceImpl.java | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilder.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilder.java index 547326e416e3..021b9d5372bc 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilder.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionRequestBuilder.java @@ -33,7 +33,7 @@ public interface ActionExecutionRequestBuilder { ActionType getSupportedActionType(); - ActionExecutionRequest buildActionExecutionRequest(ActionType actionType, Map eventContext) throws + ActionExecutionRequest buildActionExecutionRequest(Map eventContext) throws ActionExecutionRequestBuilderException; } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessor.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessor.java index 2c03482a2f31..1b3feb4eb7f2 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessor.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutionResponseProcessor.java @@ -36,12 +36,12 @@ public interface ActionExecutionResponseProcessor { ActionType getSupportedActionType(); - ActionExecutionStatus processSuccessResponse(ActionType actionType, Map eventContext, + ActionExecutionStatus processSuccessResponse(Map eventContext, Event actionEvent, ActionInvocationSuccessResponse successResponse) throws ActionExecutionResponseProcessorException; - ActionExecutionStatus processErrorResponse(ActionType actionType, Map eventContext, + ActionExecutionStatus processErrorResponse(Map eventContext, Event actionEvent, ActionInvocationErrorResponse errorResponse) throws ActionExecutionResponseProcessorException; diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java index d4566e0bfbfd..e43b449a215a 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java @@ -131,7 +131,7 @@ private ActionExecutionRequest buildActionExecutionRequest(ActionType actionType throw new ActionExecutionException("No request builder found for action type: " + actionType); } try { - return requestBuilder.buildActionExecutionRequest(actionType, eventContext); + return requestBuilder.buildActionExecutionRequest(eventContext); } catch (ActionExecutionRequestBuilderException e) { throw new ActionExecutionRuntimeException("Error occurred while building the request payload.", e); } @@ -241,7 +241,7 @@ private ActionExecutionStatus processSuccessResponse(ActionType actionType, Acti validatePerformableOperations(actionRequest, successResponse); ActionInvocationSuccessResponse.Builder successResponseBuilder = new ActionInvocationSuccessResponse.Builder().operations(allowedPerformableOperations); - return actionExecutionResponseProcessor.processSuccessResponse(actionType, eventContext, + return actionExecutionResponseProcessor.processSuccessResponse(eventContext, actionRequest.getEvent(), successResponseBuilder.build()); } @@ -254,7 +254,7 @@ private ActionExecutionStatus processErrorResponse(ActionType actionType, Action throws ActionExecutionResponseProcessorException { logErrorResponse(action, errorResponse); - return actionExecutionResponseProcessor.processErrorResponse(actionType, eventContext, actionRequest.getEvent(), + return actionExecutionResponseProcessor.processErrorResponse(eventContext, actionRequest.getEvent(), errorResponse); } From 20c14a87dec18151a580afea0e1573a10ac143ef Mon Sep 17 00:00:00 2001 From: malithie Date: Sat, 27 Jul 2024 09:07:03 +0530 Subject: [PATCH 18/21] Address review comments. --- .../execution/ActionExecutorServiceImpl.java | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java index e43b449a215a..6db6bf866ca6 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java @@ -87,7 +87,7 @@ public ActionExecutionStatus execute(ActionType actionType, Map Action action = actions.get(0); // As of now only one action is allowed. return Optional.ofNullable(action) .filter(activeAction -> activeAction.getStatus() == Action.Status.ACTIVE) - .map(activeAction -> executeAction(actionType, activeAction, actionRequest, eventContext, + .map(activeAction -> executeAction(activeAction, actionRequest, eventContext, actionExecutionResponseProcessor)) .orElse(new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext)); } catch (ActionExecutionRuntimeException e) { @@ -112,8 +112,12 @@ private List getActionsByActionType(ActionType actionType, String tenant private void validateActions(List actions, ActionType actionType) throws ActionExecutionException { if (CollectionUtils.isEmpty(actions)) { - throw new ActionExecutionRuntimeException("No actions found for action type: " + actionType); + if (LOG.isDebugEnabled()) { + LOG.debug("No actions found for action type: " + actionType); + } + return; } + if (actions.size() > 1) { // when multiple actions are supported for an action type the logic below needs to be improved such that, // a successful processing from one action becomes the input to the successor. @@ -148,7 +152,7 @@ private ActionExecutionResponseProcessor getResponseProcessor(ActionType actionT return responseProcessor; } - private ActionExecutionStatus executeAction(ActionType actionType, Action action, + private ActionExecutionStatus executeAction(Action action, ActionExecutionRequest actionRequest, Map eventContext, ActionExecutionResponseProcessor actionExecutionResponseProcessor) @@ -165,7 +169,7 @@ private ActionExecutionStatus executeAction(ActionType actionType, Action action ActionInvocationResponse actionInvocationResponse = executeActionAsynchronously(action, authenticationMethod, payload); - return processActionResponse(actionType, action, actionInvocationResponse, eventContext, actionRequest, + return processActionResponse(action, actionInvocationResponse, eventContext, actionRequest, actionExecutionResponseProcessor); } catch (ActionMgtException | JsonProcessingException | ActionExecutionResponseProcessorException e) { throw new ActionExecutionRuntimeException("Error occurred while executing action: " + action.getId(), e); @@ -201,7 +205,7 @@ private void logActionRequest(Action action, String payload) { } } - private ActionExecutionStatus processActionResponse(ActionType actionType, Action action, + private ActionExecutionStatus processActionResponse(Action action, ActionInvocationResponse actionInvocationResponse, Map eventContext, ActionExecutionRequest actionRequest, @@ -210,12 +214,11 @@ private ActionExecutionStatus processActionResponse(ActionType actionType, Actio throws ActionExecutionResponseProcessorException { if (actionInvocationResponse.isSuccess()) { - return processSuccessResponse(actionType, action, + return processSuccessResponse(action, (ActionInvocationSuccessResponse) actionInvocationResponse.getResponse(), eventContext, actionRequest, actionExecutionResponseProcessor); } else if (actionInvocationResponse.isError() && actionInvocationResponse.getResponse() != null) { - return processErrorResponse(actionType, action, - (ActionInvocationErrorResponse) actionInvocationResponse.getResponse(), + return processErrorResponse(action, (ActionInvocationErrorResponse) actionInvocationResponse.getResponse(), eventContext, actionRequest, actionExecutionResponseProcessor); } else { logErrorResponse(action, actionInvocationResponse); @@ -224,7 +227,7 @@ private ActionExecutionStatus processActionResponse(ActionType actionType, Actio return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); } - private ActionExecutionStatus processSuccessResponse(ActionType actionType, Action action, + private ActionExecutionStatus processSuccessResponse(Action action, ActionInvocationSuccessResponse successResponse, Map eventContext, ActionExecutionRequest actionRequest, @@ -245,7 +248,7 @@ private ActionExecutionStatus processSuccessResponse(ActionType actionType, Acti actionRequest.getEvent(), successResponseBuilder.build()); } - private ActionExecutionStatus processErrorResponse(ActionType actionType, Action action, + private ActionExecutionStatus processErrorResponse(Action action, ActionInvocationErrorResponse errorResponse, Map eventContext, ActionExecutionRequest actionRequest, From 4f224ec1f97bd087f2cf2731ec40c5bd720d3c13 Mon Sep 17 00:00:00 2001 From: malithie Date: Mon, 29 Jul 2024 08:27:20 +0530 Subject: [PATCH 19/21] Changed FAILURE to FAILED. --- .../action/execution/ActionExecutorServiceImpl.java | 6 +++--- .../action/execution/model/ActionExecutionStatus.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java index 6db6bf866ca6..fa20f3c04bc0 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/ActionExecutorServiceImpl.java @@ -89,11 +89,11 @@ public ActionExecutionStatus execute(ActionType actionType, Map .filter(activeAction -> activeAction.getStatus() == Action.Status.ACTIVE) .map(activeAction -> executeAction(activeAction, actionRequest, eventContext, actionExecutionResponseProcessor)) - .orElse(new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext)); + .orElse(new ActionExecutionStatus(ActionExecutionStatus.Status.FAILED, eventContext)); } catch (ActionExecutionRuntimeException e) { // todo: add to diagnostics LOG.error("Skip executing actions for action type: " + actionType.name() + ". Error: " + e.getMessage(), e); - return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); + return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILED, eventContext); } } @@ -224,7 +224,7 @@ private ActionExecutionStatus processActionResponse(Action action, logErrorResponse(action, actionInvocationResponse); } - return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILURE, eventContext); + return new ActionExecutionStatus(ActionExecutionStatus.Status.FAILED, eventContext); } private ActionExecutionStatus processSuccessResponse(Action action, diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionStatus.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionStatus.java index 4021bce1cef4..c659dfa66dc9 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionStatus.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/ActionExecutionStatus.java @@ -51,7 +51,7 @@ public Map getResponseContext() { */ public enum Status { SUCCESS, - FAILURE, + FAILED, INCOMPLETE, ERROR } From a553164db1eaca85f4f5f264624c0901ec608e1a Mon Sep 17 00:00:00 2001 From: malithie Date: Mon, 29 Jul 2024 10:42:28 +0530 Subject: [PATCH 20/21] Updated user model. --- .../org/wso2/carbon/identity/action/execution/model/User.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/User.java b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/User.java index c9f63919d2a8..4a168c43973d 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/User.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/model/User.java @@ -24,7 +24,7 @@ */ public class User { - String id; + private String id; public User(String id) { From 2c162ccb6ad99f02667e2d8a6685af5235af5096 Mon Sep 17 00:00:00 2001 From: malithie Date: Mon, 29 Jul 2024 12:25:12 +0530 Subject: [PATCH 21/21] Update version. --- .../org.wso2.carbon.identity.action.execution/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml b/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml index ab707e1a3a8c..06293b8c868a 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml +++ b/components/action-mgt/org.wso2.carbon.identity.action.execution/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.identity.framework action-mgt - 7.3.48-SNAPSHOT + 7.3.50-SNAPSHOT ../pom.xml