Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Support for rule evaluation in action execution. #6324

Merged
merged 4 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.identity.action.management</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.identity.rule.evaluation</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.identity.central.log.mgt</artifactId>
Expand Down Expand Up @@ -94,6 +98,8 @@
<Import-Package>
org.wso2.carbon.identity.action.management.*;
version="${carbon.identity.package.import.version.range}",
org.wso2.carbon.identity.rule.evaluation.*;
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}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ private ActionExecutionLogConstants() {}
public static class ActionIDs {

public static final String EXECUTE_ACTION = "execute-action";
public static final String EVALUATE_RULE = "evaluate-rule";
public static final String PROCESS_ACTION_REQUEST = "process-action-request";
public static final String SEND_ACTION_REQUEST = "send-action-request";
public static final String RECEIVE_ACTION_RESPONSE = "receive-action-response";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@
import org.wso2.carbon.identity.action.management.model.Authentication;
import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils;
import org.wso2.carbon.identity.core.ThreadLocalAwareExecutors;
import org.wso2.carbon.identity.rule.evaluation.exception.RuleEvaluationException;
import org.wso2.carbon.identity.rule.evaluation.model.FlowContext;
import org.wso2.carbon.identity.rule.evaluation.model.FlowType;
import org.wso2.carbon.identity.rule.evaluation.model.RuleEvaluationResult;

import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -118,7 +122,7 @@ public ActionExecutionStatus<?> execute(ActionType actionType, Map<String, Objec
validateActions(actions, actionType);
// As of now only one action is allowed.
Action action = actions.get(0);
return execute(action, eventContext);
return execute(action, eventContext, tenantDomain);
} catch (ActionExecutionRuntimeException e) {
LOG.debug("Skip executing actions for action type: " + actionType.name(), e);
// Skip executing actions when no action available is considered as action execution being successful.
Expand All @@ -129,10 +133,10 @@ public ActionExecutionStatus<?> execute(ActionType actionType, Map<String, Objec
/**
* Resolve the action from given action id and execute it.
*
* @param actionType Action Type.
* @param actionId The action Id of the action that need to be executed.
* @param eventContext The event context of the corresponding flow.
* @param tenantDomain Tenant domain.
* @param actionType Action Type.
* @param actionId The action id of the action that need to be executed.
* @param eventContext The event context of the corresponding flow.
* @param tenantDomain Tenant domain.
* @return Action execution status.
*/
@Override
Expand All @@ -146,28 +150,36 @@ public ActionExecutionStatus<?> execute(ActionType actionType, String actionId,

Action action = getActionByActionId(actionType, actionId, tenantDomain);
try {
return execute(action, eventContext);
return execute(action, eventContext, tenantDomain);
} catch (ActionExecutionRuntimeException e) {
LOG.debug("Skip executing action for action type: " + actionType.name(), e);
// Skip executing actions when no action available is considered as action execution being successful.
return new SuccessStatus.Builder().setResponseContext(eventContext).build();
}
}

private ActionExecutionStatus<?> execute(Action action, Map<String, Object> eventContext)
private ActionExecutionStatus<?> execute(Action action, Map<String, Object> eventContext, String tenantDomain)
throws ActionExecutionException {

if (action.getStatus() != Action.Status.ACTIVE) {
// If no active actions are detected, it is regarded as the action execution being successful.
return new SuccessStatus.Builder().setResponseContext(eventContext).build();
}

DIAGNOSTIC_LOGGER.logActionInitiation(action);

if (!evaluateActionRule(action, eventContext, tenantDomain)) {
// If the action rule is not satisfied, it is regarded as the action execution being successful.
return new SuccessStatus.Builder().setResponseContext(eventContext).build();
}

DIAGNOSTIC_LOGGER.logActionExecution(action);

ActionType actionType = ActionType.valueOf(action.getType().getActionType());
ActionExecutionRequest actionRequest = buildActionExecutionRequest(actionType, eventContext);
ActionExecutionResponseProcessor actionExecutionResponseProcessor = getResponseProcessor(actionType);

if (action.getStatus() == Action.Status.ACTIVE) {
DIAGNOSTIC_LOGGER.logActionInitiation(action);
return executeAction(action, actionRequest, eventContext, actionExecutionResponseProcessor);
} else {
// If no active actions are detected, it is regarded as the action execution being successful.
return new SuccessStatus.Builder().setResponseContext(eventContext).build();
}
return executeAction(action, actionRequest, eventContext, actionExecutionResponseProcessor);
}

private Action getActionByActionId(ActionType actionType, String actionId, String tenantDomain)
Expand Down Expand Up @@ -265,6 +277,30 @@ private ActionExecutionStatus<?> executeAction(Action action,
}
}

private boolean evaluateActionRule(Action action, Map<String, Object> eventContext, String tenantDomain)
throws ActionExecutionException {

if (action.getActionRule() == null || action.getActionRule().getId() == null) {
logNoRuleConfiguredForAction(action);
return true; // If no action rule available, consider the rule as satisfied and execute action.
}

try {
RuleEvaluationResult ruleEvaluationResult =
ActionExecutionServiceComponentHolder.getInstance().getRuleEvaluationService()
.evaluate(action.getActionRule().getId(),
new FlowContext(FlowType.valueOf(action.getType().getActionType()),
eventContext), tenantDomain);

logActionRuleEvaluation(action, ruleEvaluationResult);

return ruleEvaluationResult.isRuleSatisfied();
} catch (RuleEvaluationException e) {
throw new ActionExecutionException(
"Error occurred while evaluating the rule for action: " + action.getId(), e);
}
}

private ActionInvocationResponse executeActionAsynchronously(Action action,
AuthMethods.AuthMethod authenticationMethod,
String payload) throws ActionExecutionException {
Expand Down Expand Up @@ -387,6 +423,19 @@ private ActionExecutionStatus<Failure> processFailureResponse(Action action,
failureResponse);
}

private void logActionRuleEvaluation(Action action, RuleEvaluationResult ruleEvaluationResult) {

DIAGNOSTIC_LOGGER.logActionRuleEvaluation(action, ruleEvaluationResult.isRuleSatisfied());
LOG.debug("Rule of action: " + action.getId() + " evaluated to: " +
ruleEvaluationResult.isRuleSatisfied());
}

private void logNoRuleConfiguredForAction(Action action) {

DIAGNOSTIC_LOGGER.logNoRuleConfiguredForAction(action);
LOG.debug("No rule configured for action " + action.getId() + ". Proceed executing the action.");
}

private void logSuccessResponse(Action action, ActionInvocationSuccessResponse successResponse) {

DIAGNOSTIC_LOGGER.logSuccessResponse(action);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.wso2.carbon.identity.action.execution.impl.ActionExecutionResponseProcessorFactory;
import org.wso2.carbon.identity.action.execution.impl.ActionExecutorServiceImpl;
import org.wso2.carbon.identity.action.management.service.ActionManagementService;
import org.wso2.carbon.identity.rule.evaluation.service.RuleEvaluationService;

/**
* OSGI service component for the Action execution.
Expand Down Expand Up @@ -81,18 +82,14 @@ protected void deactivate(ComponentContext context) {
)
protected void setActionManagementService(ActionManagementService actionManagementService) {

if (LOG.isDebugEnabled()) {
LOG.debug("Registering a reference for ActionManagementService in the ActionExecutionServiceComponent.");
}
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.");
}
LOG.debug(
"Unregistering the reference for ActionManagementService in the ActionExecutionServiceComponent.");
if (ActionExecutionServiceComponentHolder.getInstance().getActionManagementService()
.equals(actionManagementService)) {
ActionExecutionServiceComponentHolder.getInstance().setActionManagementService(null);
Expand All @@ -108,20 +105,16 @@ protected void unsetActionManagementService(ActionManagementService actionManage
)
protected void setActionExecutionRequestBuilder(ActionExecutionRequestBuilder actionExecutionRequestBuilder) {

if (LOG.isDebugEnabled()) {
LOG.debug("Registering ActionExecutionRequestBuilder: " +
actionExecutionRequestBuilder.getClass().getName() +
" in the ActionExecutionServiceComponent.");
}
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.");
}
LOG.debug("Unregistering ActionExecutionRequestBuilder: " +
actionExecutionRequestBuilder.getClass().getName() + " in the ActionExecutionServiceComponent.");
ActionExecutionRequestBuilderFactory.unregisterActionExecutionRequestBuilder(actionExecutionRequestBuilder);
}

Expand All @@ -135,23 +128,38 @@ protected void unsetActionExecutionRequestBuilder(ActionExecutionRequestBuilder
protected void setActionExecutionResponseProcessor(
ActionExecutionResponseProcessor actionExecutionResponseProcessor) {

if (LOG.isDebugEnabled()) {
LOG.debug("Registering ActionExecutionResponseProcessor: " +
actionExecutionResponseProcessor.getClass().getName() +
" in the ActionExecutionServiceComponent.");
}
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.");
}
LOG.debug("Unregistering ActionExecutionResponseProcessor: " +
actionExecutionResponseProcessor.getClass().getName() + " in the ActionExecutionServiceComponent.");
ActionExecutionResponseProcessorFactory.unregisterActionExecutionResponseProcessor(
actionExecutionResponseProcessor);
}

@Reference(
name = "rule.evaluation.service.component",
service = RuleEvaluationService.class,
cardinality = ReferenceCardinality.MANDATORY,
policy = ReferencePolicy.DYNAMIC,
unbind = "unsetRuleEvaluationService"
)
protected void setRuleEvaluationService(RuleEvaluationService ruleEvaluationService) {

LOG.debug("Registering a reference for RuleEvaluationService in the ActionExecutionServiceComponent.");
ActionExecutionServiceComponentHolder.getInstance().setRuleEvaluationService(ruleEvaluationService);
}

protected void unsetRuleEvaluationService(RuleEvaluationService ruleEvaluationService) {

LOG.debug("Unregistering reference for RuleEvaluationService in the ActionExecutionServiceComponent.");
ActionExecutionServiceComponentHolder.getInstance().setRuleEvaluationService(ruleEvaluationService);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package org.wso2.carbon.identity.action.execution.internal;

import org.wso2.carbon.identity.action.management.service.ActionManagementService;
import org.wso2.carbon.identity.rule.evaluation.service.RuleEvaluationService;

/**
* This class holds references for dependent services required for Action Execution Service to function.
Expand All @@ -28,6 +29,7 @@ public class ActionExecutionServiceComponentHolder {
private static final ActionExecutionServiceComponentHolder INSTANCE = new ActionExecutionServiceComponentHolder();

private ActionManagementService actionManagementService;
private RuleEvaluationService ruleEvaluationService;

private ActionExecutionServiceComponentHolder() {

Expand All @@ -47,4 +49,15 @@ public void setActionManagementService(ActionManagementService actionManagementS

this.actionManagementService = actionManagementService;
}

public RuleEvaluationService getRuleEvaluationService() {

return ruleEvaluationService;
}

public void setRuleEvaluationService(
RuleEvaluationService ruleEvaluationService) {
malithie marked this conversation as resolved.
Show resolved Hide resolved

this.ruleEvaluationService = ruleEvaluationService;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,46 @@ public void logActionInitiation(Action action) {
DiagnosticLog.ResultStatus.SUCCESS));
}

public void logActionExecution(Action action) {

if (!LoggerUtils.isDiagnosticLogsEnabled()) {
return;
}

triggerLogEvent(
initializeDiagnosticLogBuilder(
ActionExecutionLogConstants.ActionIDs.EXECUTE_ACTION,
action.getType().getDisplayName() + " action execution started.",
DiagnosticLog.ResultStatus.SUCCESS));
}

public void logActionRuleEvaluation(Action action, boolean ruleEvaluationResult) {

if (!LoggerUtils.isDiagnosticLogsEnabled()) {
return;
}

triggerLogEvent(
initializeDiagnosticLogBuilder(
ActionExecutionLogConstants.ActionIDs.EVALUATE_RULE,
"Rule of " + action.getType().getDisplayName() + " action evaluated to " +
ruleEvaluationResult, DiagnosticLog.ResultStatus.SUCCESS));
}

public void logNoRuleConfiguredForAction(Action action) {

if (!LoggerUtils.isDiagnosticLogsEnabled()) {
return;
}

triggerLogEvent(
initializeDiagnosticLogBuilder(
ActionExecutionLogConstants.ActionIDs.EVALUATE_RULE,
"No rule configured for action " + action.getType().getDisplayName() +
". Proceed executing the action."
, DiagnosticLog.ResultStatus.SUCCESS));
}

public void logActionRequest(Action action) {

if (!LoggerUtils.isDiagnosticLogsEnabled()) {
Expand Down
Loading
Loading