Skip to content

Commit

Permalink
Merge pull request #6324 from malithie/rules-management
Browse files Browse the repository at this point in the history
Support for rule evaluation in action execution.
  • Loading branch information
malithie authored Jan 23, 2025
2 parents f4b9113 + 4f5f60f commit f7bae7f
Show file tree
Hide file tree
Showing 7 changed files with 245 additions and 43 deletions.
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,14 @@ public void setActionManagementService(ActionManagementService actionManagementS

this.actionManagementService = actionManagementService;
}

public RuleEvaluationService getRuleEvaluationService() {

return ruleEvaluationService;
}

public void setRuleEvaluationService(RuleEvaluationService ruleEvaluationService) {

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

0 comments on commit f7bae7f

Please sign in to comment.