From f956e8718518f18749f8bd93a7bd84ba6bac5791 Mon Sep 17 00:00:00 2001 From: malithie Date: Tue, 7 Jan 2025 14:56:58 +0530 Subject: [PATCH 1/5] Support managing rules for actions. --- .../pom.xml | 34 ++++-- .../constant/ActionMgtConstants.java | 1 + .../dao/impl/ActionManagementDAOFacade.java | 99 +++++++++++++-- .../dao/impl/ActionManagementDAOImpl.java | 99 ++++++++++++++- .../internal/ActionMgtServiceComponent.java | 20 ++++ .../ActionMgtServiceComponentHolder.java | 23 ++++ .../action/management/model/Action.java | 24 +++- .../action/management/model/ActionDTO.java | 10 ++ .../action/management/model/ActionRule.java | 113 ++++++++++++++++++ .../impl/ActionManagementServiceImpl.java | 1 + .../management/util/ActionDTOBuilder.java | 16 +++ 11 files changed, 417 insertions(+), 23 deletions(-) create mode 100644 components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/model/ActionRule.java diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/pom.xml b/components/action-mgt/org.wso2.carbon.identity.action.management/pom.xml index a3d97af6f44e..8e954462c272 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/pom.xml +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/pom.xml @@ -45,6 +45,10 @@ org.wso2.carbon.identity.framework org.wso2.carbon.identity.certificate.management + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.rule.management + org.json.wso2 json @@ -100,7 +104,8 @@ org.wso2.carbon.identity.action.management.constant.error, org.wso2.carbon.identity.action.management.exception, org.wso2.carbon.identity.action.management.model, - org.wso2.carbon.identity.action.management.service; version="${carbon.identity.package.export.version}" + org.wso2.carbon.identity.action.management.service; + version="${carbon.identity.package.export.version}" org.apache.commons.lang; version="${commons-lang.wso2.osgi.version.range}", @@ -109,16 +114,27 @@ org.osgi.framework; version="${osgi.framework.imp.pkg.version.range}", org.osgi.service.component; version="${osgi.service.component.imp.pkg.version.range}", org.json.*; version="${json.wso2.version.range}", - org.wso2.carbon.database.utils.jdbc; version="${org.wso2.carbon.database.utils.version.range}", - org.wso2.carbon.database.utils.jdbc.exceptions; version="${org.wso2.carbon.database.utils.version.range}", - org.wso2.carbon.identity.core.cache; version="${carbon.identity.package.import.version.range}", - org.wso2.carbon.identity.core.util; version="${carbon.identity.package.import.version.range}", - org.wso2.carbon.identity.secret.mgt.core; version="${carbon.identity.package.import.version.range}", - org.wso2.carbon.identity.secret.mgt.core.exception; version="${carbon.identity.package.import.version.range}", - org.wso2.carbon.identity.secret.mgt.core.model; version="${carbon.identity.package.import.version.range}", + org.wso2.carbon.database.utils.jdbc; + version="${org.wso2.carbon.database.utils.version.range}", + org.wso2.carbon.database.utils.jdbc.exceptions; + version="${org.wso2.carbon.database.utils.version.range}", + org.wso2.carbon.identity.core.cache; + version="${carbon.identity.package.import.version.range}", + org.wso2.carbon.identity.core.util; + version="${carbon.identity.package.import.version.range}", + org.wso2.carbon.identity.secret.mgt.core; + version="${carbon.identity.package.import.version.range}", + org.wso2.carbon.identity.secret.mgt.core.exception; + version="${carbon.identity.package.import.version.range}", + org.wso2.carbon.identity.secret.mgt.core.model; + version="${carbon.identity.package.import.version.range}", org.wso2.carbon.utils; version="${carbon.kernel.package.import.version.range}", org.wso2.carbon.identity.event.*; version="${carbon.identity.package.import.version.range}", - org.wso2.carbon.identity.central.log.mgt.utils; version="${carbon.identity.package.import.version.range}", + org.wso2.carbon.identity.rule.management.exception; version="${carbon.identity.package.import.version.range}", + org.wso2.carbon.identity.rule.management.model; version="${carbon.identity.package.import.version.range}", + org.wso2.carbon.identity.rule.management.service; version="${carbon.identity.package.import.version.range}", + org.wso2.carbon.identity.central.log.mgt.utils; + version="${carbon.identity.package.import.version.range}", org.wso2.carbon; version="${carbon.kernel.package.import.version.range}", org.wso2.carbon.context; version="${carbon.kernel.package.import.version.range}", org.wso2.carbon.utils.*; version="${carbon.kernel.package.import.version.range}", diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/constant/ActionMgtConstants.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/constant/ActionMgtConstants.java index 9da7b6b3f5c3..4b1e3f6b66a7 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/constant/ActionMgtConstants.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/constant/ActionMgtConstants.java @@ -25,6 +25,7 @@ public class ActionMgtConstants { public static final String URI_PROPERTY = "uri"; public static final String AUTHN_TYPE_PROPERTY = "authnType"; + public static final String RULE_PROPERTY = "rule"; public static final String IDN_SECRET_TYPE_ACTION_SECRETS = "ACTION_API_ENDPOINT_AUTH_SECRETS"; public static final String ACTION_NAME_FIELD = "Action name"; diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/dao/impl/ActionManagementDAOFacade.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/dao/impl/ActionManagementDAOFacade.java index 76ca02ba9aea..2ea30739d6dd 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/dao/impl/ActionManagementDAOFacade.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/dao/impl/ActionManagementDAOFacade.java @@ -29,8 +29,10 @@ import org.wso2.carbon.identity.action.management.exception.ActionMgtClientException; import org.wso2.carbon.identity.action.management.exception.ActionMgtException; import org.wso2.carbon.identity.action.management.exception.ActionMgtServerException; +import org.wso2.carbon.identity.action.management.internal.ActionMgtServiceComponentHolder; import org.wso2.carbon.identity.action.management.model.Action; import org.wso2.carbon.identity.action.management.model.ActionDTO; +import org.wso2.carbon.identity.action.management.model.ActionRule; import org.wso2.carbon.identity.action.management.model.AuthProperty; import org.wso2.carbon.identity.action.management.model.Authentication; import org.wso2.carbon.identity.action.management.model.EndpointConfig; @@ -40,6 +42,7 @@ import org.wso2.carbon.identity.action.management.util.ActionSecretProcessor; import org.wso2.carbon.identity.core.util.IdentityDatabaseUtil; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; +import org.wso2.carbon.identity.rule.management.exception.RuleManagementException; import org.wso2.carbon.identity.secret.mgt.core.exception.SecretManagementException; import java.util.List; @@ -72,10 +75,11 @@ public void addAction(ActionDTO actionDTO, Integer tenantId) throws ActionMgtExc ActionDTOBuilder actionDTOBuilder = new ActionDTOBuilder(actionDTO); // Encrypt authentication secrets encryptAddingAuthSecrets(actionDTOBuilder); + // Add action rule + addActionRule(actionDTOBuilder, IdentityTenantUtil.getTenantDomain(tenantId)); // Resolve action properties ActionDTO resolvedActionDTO = getResolvedActionDTOForAddOperation(actionDTOBuilder.build(), tenantId); - actionManagementDAO.addAction(resolvedActionDTO, tenantId); return null; }); @@ -94,7 +98,6 @@ public List getActionsByActionType(String actionType, Integer tenantI try { List actionDTOS = actionManagementDAO.getActionsByActionType(actionType, tenantId); - return getResolvedActionDTOsForGetOperation(actionType, actionDTOS, tenantId); } catch (ActionMgtException | ActionDTOModelResolverException e) { throw ActionManagementExceptionHandler.handleServerException( @@ -106,15 +109,23 @@ public List getActionsByActionType(String actionType, Integer tenantI public ActionDTO getActionByActionId(String actionType, String actionId, Integer tenantId) throws ActionMgtException { + NamedJdbcTemplate jdbcTemplate = new NamedJdbcTemplate(IdentityDatabaseUtil.getDataSource()); try { - ActionDTO actionDTO = actionManagementDAO.getActionByActionId(actionType, actionId, tenantId); - if (actionDTO == null) { - return null; - } + return jdbcTemplate.withTransaction(template -> { + ActionDTO actionDTO = actionManagementDAO.getActionByActionId(actionType, actionId, tenantId); + if (actionDTO == null) { + return null; + } - // Populate action properties - return getResolvedActionDTOForGetOperation(actionDTO, tenantId); - } catch (ActionMgtException | ActionDTOModelResolverException e) { + ActionDTOBuilder actionDTOBuilder = new ActionDTOBuilder(actionDTO); + // Load action rule + loadActionRule(actionDTOBuilder, IdentityTenantUtil.getTenantDomain(tenantId)); + // Populate action properties + return getResolvedActionDTOForGetOperation(actionDTOBuilder.build(), tenantId); + }); + } catch (TransactionException e) { + // Since exceptions thrown are wrapped with TransactionException, extracting the actual cause. + handleActionPropertyResolverClientException(e.getCause()); throw ActionManagementExceptionHandler.handleServerException( ErrorMessage.ERROR_WHILE_RETRIEVING_ACTION_BY_ID, e); } @@ -130,6 +141,9 @@ public void updateAction(ActionDTO updatingActionDTO, ActionDTO existingActionDT ActionDTOBuilder updatingActionDTOBuilder = new ActionDTOBuilder(updatingActionDTO); // Encrypt authentication secrets encryptUpdatingAuthSecrets(updatingActionDTOBuilder, existingActionDTO); + // Update action rule + updateActionRule(updatingActionDTOBuilder, existingActionDTO, + IdentityTenantUtil.getTenantDomain(tenantId)); // Resolve action properties ActionDTO resolvedUpdatingActionDTO = getResolvedActionDTOForUpdateOperation(updatingActionDTOBuilder.build(), existingActionDTO, @@ -156,9 +170,8 @@ public void deleteAction(ActionDTO deletingActionDTO, Integer tenantId) throws A try { jdbcTemplate.withTransaction(template -> { actionManagementDAO.deleteAction(deletingActionDTO, tenantId); - // Encrypt authentication secrets deleteAuthenticationSecrets(deletingActionDTO); - // Resolve action properties + deleteActionRule(deletingActionDTO, IdentityTenantUtil.getTenantDomain(tenantId)); deleteProperties(deletingActionDTO, tenantId); return null; @@ -294,6 +307,70 @@ private void addEncryptedAuthSecretsToBuilder(ActionDTOBuilder actionDTOBuilder, .build()); } + private void addActionRule(ActionDTOBuilder actionDTOBuilder, String tenantDomain) throws ActionMgtException { + + if (actionDTOBuilder.getActionRule() != null) { + try { + ActionMgtServiceComponentHolder.getInstance() + .getRuleManagementService() + .addRule(actionDTOBuilder.getActionRule().getRule(), tenantDomain); + } catch (RuleManagementException e) { + throw new ActionMgtServerException("Error while adding the Rule associated with the Action.", e); + } + } + } + + private void loadActionRule(ActionDTOBuilder actionDTOBuilder, String tenantDomain) + throws ActionMgtServerException { + + if (actionDTOBuilder.getActionRule() != null) { + try { + ActionRule actionRule = ActionRule.create(ActionMgtServiceComponentHolder.getInstance() + .getRuleManagementService() + .getRuleByRuleId(actionDTOBuilder.getActionRule().getId(), tenantDomain)); + actionDTOBuilder.rule(actionRule); + } catch (RuleManagementException e) { + throw new ActionMgtServerException("Error while retrieving the Rule associated with the Action.", e); + } + } + } + + private void updateActionRule(ActionDTOBuilder updatingActionDTOBuilder, ActionDTO existingActionDTO, + String tenantDomain) + throws ActionMgtException { + + if (existingActionDTO.getActionRule() == null && updatingActionDTOBuilder.getActionRule() != null) { + // This means a new action rule is added when updating the action. + addActionRule(updatingActionDTOBuilder, tenantDomain); + } else if (existingActionDTO.getActionRule() != null && updatingActionDTOBuilder.getActionRule() == null) { + // This means the existing action rule is removed when updating the action. + deleteActionRule(existingActionDTO, tenantDomain); + } else if (existingActionDTO.getActionRule() != null && updatingActionDTOBuilder.getActionRule() != + null) { // This means the existing action rule is updated when updating the action. + try { + updatingActionDTOBuilder.getActionRule().getRule().setId(existingActionDTO.getActionRule().getId()); + ActionMgtServiceComponentHolder.getInstance() + .getRuleManagementService() + .updateRule(updatingActionDTOBuilder.getActionRule().getRule(), tenantDomain); + } catch (RuleManagementException e) { + throw new ActionMgtServerException("Error while updating the Rule associated with the Action.", e); + } + } + } + + private void deleteActionRule(ActionDTO actionDTO, String tenantDomain) throws ActionMgtServerException { + + if (actionDTO.getActionRule() != null) { + try { + ActionMgtServiceComponentHolder.getInstance() + .getRuleManagementService() + .deleteRule(actionDTO.getActionRule().getId(), tenantDomain); + } catch (RuleManagementException e) { + throw new ActionMgtServerException("Error while deleting the Rule associated with the Action.", e); + } + } + } + /** * Get the ActionDTO with resolved adding properties that needs to be added in the Action Management Service. * diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/dao/impl/ActionManagementDAOImpl.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/dao/impl/ActionManagementDAOImpl.java index 59850e7efc8d..8026085a1e9b 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/dao/impl/ActionManagementDAOImpl.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/dao/impl/ActionManagementDAOImpl.java @@ -21,17 +21,20 @@ import org.wso2.carbon.database.utils.jdbc.NamedJdbcTemplate; import org.wso2.carbon.database.utils.jdbc.NamedPreparedStatement; import org.wso2.carbon.database.utils.jdbc.exceptions.TransactionException; +import org.wso2.carbon.identity.action.management.constant.ActionMgtConstants; import org.wso2.carbon.identity.action.management.constant.ActionMgtSQLConstants; import org.wso2.carbon.identity.action.management.dao.ActionManagementDAO; import org.wso2.carbon.identity.action.management.exception.ActionMgtException; import org.wso2.carbon.identity.action.management.exception.ActionMgtServerException; import org.wso2.carbon.identity.action.management.model.Action; import org.wso2.carbon.identity.action.management.model.ActionDTO; +import org.wso2.carbon.identity.action.management.model.ActionRule; import org.wso2.carbon.identity.action.management.model.AuthProperty; import org.wso2.carbon.identity.action.management.model.Authentication; import org.wso2.carbon.identity.action.management.model.EndpointConfig; import org.wso2.carbon.identity.action.management.util.ActionDTOBuilder; import org.wso2.carbon.identity.core.util.IdentityDatabaseUtil; +import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import java.sql.Connection; import java.sql.ResultSet; @@ -58,6 +61,8 @@ public void addAction(ActionDTO actionDTO, Integer tenantId) throws ActionMgtExc addBasicInfo(actionDTO, tenantId); // Add action endpoint. addEndpoint(actionDTO, tenantId); + // Add action rule reference. + addRuleReference(actionDTO, tenantId); // Add action properties. addProperties(actionDTO, tenantId); } @@ -76,6 +81,7 @@ public List getActionsByActionType(String actionType, Integer tenantI try (ResultSet rs = statement.executeQuery()) { while (rs.next()) { String actionId = rs.getString(ActionMgtSQLConstants.Column.ACTION_UUID); + Map properties = getActionPropertiesFromDB(actionId, tenantId); ActionDTO actionDTO = new ActionDTOBuilder() .id(actionId) .type(org.wso2.carbon.identity.action.management.model.Action.ActionTypes.valueOf( @@ -84,7 +90,10 @@ public List getActionsByActionType(String actionType, Integer tenantI .description(rs.getString(ActionMgtSQLConstants.Column.ACTION_DESCRIPTION)) .status(org.wso2.carbon.identity.action.management.model.Action.Status.valueOf( rs.getString(ActionMgtSQLConstants.Column.ACTION_STATUS))) - .setEndpointAndProperties(getActionPropertiesFromDB(actionId, tenantId)) + .endpoint(populateEndpoint(properties)) + .rule(populateRule(properties, tenantId)) + .properties(properties.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))) .build(); actionDTOS.add(actionDTO); @@ -106,8 +115,12 @@ public ActionDTO getActionByActionId(String actionType, String actionId, Integer if (actionBuilder == null) { return null; } - actionBuilder.setEndpointAndProperties(getActionPropertiesFromDB(actionId, tenantId)); + Map actionProperties = getActionPropertiesFromDB(actionId, tenantId); + actionBuilder.endpoint(populateEndpoint(actionProperties)); + actionBuilder.rule(populateRule(actionProperties, tenantId)); + actionBuilder.properties(actionProperties.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); return actionBuilder.build(); } @@ -119,6 +132,8 @@ public void updateAction(ActionDTO updatingActionDTO, ActionDTO existingActionDT updateBasicInfo(updatingActionDTO, existingActionDTO, tenantId); // Update Action Endpoint. updateEndpoint(updatingActionDTO, existingActionDTO, tenantId); + // Update Rule Reference. + updateRuleReference(updatingActionDTO, existingActionDTO, tenantId); // Update Action Properties. updateProperties(updatingActionDTO, existingActionDTO, tenantId); } @@ -329,6 +344,39 @@ private void updateEndpoint(ActionDTO updatingActionDTO, ActionDTO existingActio } } + private EndpointConfig populateEndpoint(Map propertiesFromDB) throws ActionMgtException { + + Authentication authentication; + Authentication.Type authnType = + Authentication.Type.valueOf(propertiesFromDB.remove(ActionMgtConstants.AUTHN_TYPE_PROPERTY)); + switch (authnType) { + case BASIC: + authentication = new Authentication.BasicAuthBuilder( + propertiesFromDB.remove(Authentication.Property.USERNAME.getName()), + propertiesFromDB.remove(Authentication.Property.PASSWORD.getName())).build(); + break; + case BEARER: + authentication = new Authentication.BearerAuthBuilder( + propertiesFromDB.remove(Authentication.Property.ACCESS_TOKEN.getName())).build(); + break; + case API_KEY: + authentication = new Authentication.APIKeyAuthBuilder( + propertiesFromDB.remove(Authentication.Property.HEADER.getName()), + propertiesFromDB.remove(Authentication.Property.VALUE.getName())).build(); + break; + case NONE: + authentication = new Authentication.NoneAuthBuilder().build(); + break; + default: + throw new ActionMgtServerException("Authentication type is not defined for the Action Endpoint."); + } + + return new EndpointConfig.EndpointConfigBuilder() + .uri(propertiesFromDB.remove(ActionMgtConstants.URI_PROPERTY)) + .authentication(authentication) + .build(); + } + /** * Update Action Endpoint Authentication. * @@ -415,6 +463,53 @@ private void updateAuthentication(String actionId, Authentication updatingAuthen updateActionPropertiesInDB(actionId, nonSecretAuthenticationProperties, tenantId); } + private void addRuleReference(ActionDTO actionDTO, Integer tenantId) throws ActionMgtServerException { + + if (actionDTO.getActionRule() == null) { + return; + } + + Map propertiesMap = + Collections.singletonMap(ActionMgtConstants.RULE_PROPERTY, actionDTO.getActionRule().getId()); + try { + addActionPropertiesToDB(actionDTO.getId(), propertiesMap, tenantId); + } catch (TransactionException e) { + throw new ActionMgtServerException("Error while adding the reference for the Rule in Action.", e); + } + } + + private void updateRuleReference(ActionDTO updatingActionDTO, ActionDTO existingActionDTO, Integer tenantId) + throws ActionMgtServerException { + + if (existingActionDTO.getActionRule() == null && updatingActionDTO.getActionRule() != null) { + // This means a new action rule is added when updating the action. Add the rule reference. + addRuleReference(updatingActionDTO, tenantId); + } else if (existingActionDTO.getActionRule() != null && updatingActionDTO.getActionRule() == null) { + // This means the existing action rule is removed when updating the action. Remove the rule reference. + deleteRuleReference(updatingActionDTO, tenantId); + } + } + + private void deleteRuleReference(ActionDTO actionDTO, Integer tenantId) throws ActionMgtServerException { + + try { + deleteActionPropertiesInDB(actionDTO.getId(), + Collections.singletonList(ActionMgtConstants.RULE_PROPERTY), tenantId); + } catch (TransactionException e) { + throw new ActionMgtServerException("Error while removing the reference for the Rule in Action.", e); + } + } + + private ActionRule populateRule(Map propertiesFromDB, Integer tenantId) { + + if (!propertiesFromDB.containsKey(ActionMgtConstants.RULE_PROPERTY)) { + return null; + } + + return ActionRule.create(propertiesFromDB.remove(ActionMgtConstants.RULE_PROPERTY), + IdentityTenantUtil.getTenantDomain(tenantId)); + } + /** * Add Action properties. * diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/internal/ActionMgtServiceComponent.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/internal/ActionMgtServiceComponent.java index 8da0688e5a86..9493135a1413 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/internal/ActionMgtServiceComponent.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/internal/ActionMgtServiceComponent.java @@ -34,6 +34,7 @@ import org.wso2.carbon.identity.action.management.service.ActionManagementService; import org.wso2.carbon.identity.action.management.service.impl.ActionConverterFactory; import org.wso2.carbon.identity.action.management.service.impl.CacheBackedActionManagementService; +import org.wso2.carbon.identity.rule.management.service.RuleManagementService; import org.wso2.carbon.identity.secret.mgt.core.SecretManager; import org.wso2.carbon.identity.secret.mgt.core.SecretResolveManager; @@ -159,4 +160,23 @@ private void unsetSecretResolveManager(SecretResolveManager secretResolveManager ActionMgtServiceComponentHolder.getInstance().setSecretResolveManager(null); LOG.debug("SecretResolveManager unset in ActionMgtServiceComponentHolder bundle."); } + + @Reference( + name = "org.wso2.carbon.identity.rule.management.service.RuleManagementService", + service = RuleManagementService.class, + cardinality = ReferenceCardinality.MANDATORY, + policy = ReferencePolicy.DYNAMIC, + unbind = "unsetRuleManagementService" + ) + private void setRuleManagementService(RuleManagementService ruleManagementService) { + + ActionMgtServiceComponentHolder.getInstance().setRuleManagementService(ruleManagementService); + LOG.debug("RuleManagementService set in ActionMgtServiceComponentHolder bundle."); + } + + private void unsetRuleManagementService(RuleManagementService ruleManagementService) { + + ActionMgtServiceComponentHolder.getInstance().setRuleManagementService(null); + LOG.debug("RuleManagementService unset in ActionMgtServiceComponentHolder bundle."); + } } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/internal/ActionMgtServiceComponentHolder.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/internal/ActionMgtServiceComponentHolder.java index 5866841fdbaa..a5d73c12e881 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/internal/ActionMgtServiceComponentHolder.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/internal/ActionMgtServiceComponentHolder.java @@ -18,6 +18,7 @@ package org.wso2.carbon.identity.action.management.internal; +import org.wso2.carbon.identity.rule.management.service.RuleManagementService; import org.wso2.carbon.identity.secret.mgt.core.SecretManager; import org.wso2.carbon.identity.secret.mgt.core.SecretResolveManager; @@ -28,6 +29,7 @@ public class ActionMgtServiceComponentHolder { private SecretManager secretManager; private SecretResolveManager secretResolveManager; + private RuleManagementService ruleManagementService; public static final ActionMgtServiceComponentHolder INSTANCE = new ActionMgtServiceComponentHolder(); @@ -84,4 +86,25 @@ public void setSecretResolveManager(SecretResolveManager secretResolveManager) { this.secretResolveManager = secretResolveManager; } + + /** + * Get the RuleManagementService. + * + * @return RuleManagementService instance. + */ + public RuleManagementService getRuleManagementService() { + + return ruleManagementService; + } + + /** + * Set the RuleManagementService. + * + * @param ruleManagementService RuleManagementService instance. + */ + public void setRuleManagementService( + RuleManagementService ruleManagementService) { + + this.ruleManagementService = ruleManagementService; + } } 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 dc1f3030bf47..e4bcd84b7b2d 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 @@ -23,7 +23,7 @@ /** * Action. */ -public class Action { +public class Action { /** * Action Type. @@ -134,6 +134,7 @@ public enum Status { private String description; private Status status; private EndpointConfig endpointConfig; + private ActionRule rule; public Action(ActionResponseBuilder actionResponseBuilder) { @@ -143,6 +144,7 @@ public Action(ActionResponseBuilder actionResponseBuilder) { this.description = actionResponseBuilder.description; this.status = actionResponseBuilder.status; this.endpointConfig = actionResponseBuilder.endpointConfig; + this.rule = actionResponseBuilder.rule; } public Action(ActionRequestBuilder actionRequestBuilder) { @@ -150,6 +152,7 @@ public Action(ActionRequestBuilder actionRequestBuilder) { this.name = actionRequestBuilder.name; this.description = actionRequestBuilder.description; this.endpointConfig = actionRequestBuilder.endpointConfig; + this.rule = actionRequestBuilder.rule; } public String getId() { @@ -182,6 +185,11 @@ public EndpointConfig getEndpoint() { return endpointConfig; } + public ActionRule getActionRule() { + + return rule; + } + /** * ActionResponseBuilder. */ @@ -193,6 +201,7 @@ public static class ActionResponseBuilder { private String description; private Status status; private EndpointConfig endpointConfig; + private ActionRule rule; public ActionResponseBuilder id(String id) { @@ -230,6 +239,12 @@ public ActionResponseBuilder endpoint(EndpointConfig endpointConfig) { return this; } + public ActionResponseBuilder rule(ActionRule rule) { + + this.rule = rule; + return this; + } + public Action build() { return new Action(this); @@ -244,6 +259,7 @@ public static class ActionRequestBuilder { private String name; private String description; private EndpointConfig endpointConfig; + private ActionRule rule; public ActionRequestBuilder name(String name) { @@ -263,6 +279,12 @@ public ActionRequestBuilder endpoint(EndpointConfig endpointConfig) { return this; } + public ActionRequestBuilder rule(ActionRule rule) { + + this.rule = rule; + return this; + } + public Action build() { return new Action(this); diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/model/ActionDTO.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/model/ActionDTO.java index 3351ee606f1b..4d133cc6c33a 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/model/ActionDTO.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/model/ActionDTO.java @@ -31,6 +31,7 @@ public class ActionDTO { private final String description; private final Action.Status status; private final EndpointConfig endpoint; + private final ActionRule rule; private final Map properties; public ActionDTO(Builder builder) { @@ -41,6 +42,7 @@ public ActionDTO(Builder builder) { this.description = builder.description; this.status = builder.status; this.endpoint = builder.endpoint; + this.rule = builder.rule; this.properties = builder.properties; } @@ -74,6 +76,11 @@ public EndpointConfig getEndpoint() { return endpoint; } + public ActionRule getActionRule() { + + return rule; + } + public Map getProperties() { return properties; @@ -99,6 +106,7 @@ public static class Builder { private final String description; private final Action.Status status; private final EndpointConfig endpoint; + private final ActionRule rule; private Map properties; public Builder(ActionDTO actionDTO) { @@ -109,6 +117,7 @@ public Builder(ActionDTO actionDTO) { this.description = actionDTO.getDescription(); this.status = actionDTO.getStatus(); this.endpoint = actionDTO.getEndpoint(); + this.rule = actionDTO.getActionRule(); this.properties = actionDTO.getProperties(); } @@ -120,6 +129,7 @@ public Builder(Action action) { this.description = action.getDescription(); this.status = action.getStatus(); this.endpoint = action.getEndpoint(); + this.rule = action.getActionRule(); } public Builder properties(Map properties) { diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/model/ActionRule.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/model/ActionRule.java new file mode 100644 index 000000000000..e84902cc178e --- /dev/null +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/model/ActionRule.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.action.management.model; + +import org.apache.commons.lang.StringUtils; +import org.wso2.carbon.identity.action.management.exception.ActionMgtException; +import org.wso2.carbon.identity.action.management.exception.ActionMgtServerException; +import org.wso2.carbon.identity.action.management.internal.ActionMgtServiceComponentHolder; +import org.wso2.carbon.identity.rule.management.exception.RuleManagementException; +import org.wso2.carbon.identity.rule.management.model.Rule; + +/** + * Represents an Action Rule. + * This class wraps the Rule object and provides a way to lazily load the Rule from the Rule Management Service. + */ +public class ActionRule { + + private String id; + private String tenantDomain; + private Rule rule; + + private ActionRule(String id, String tenantDomain) { + + this.id = id; + this.tenantDomain = tenantDomain; + } + + private ActionRule(Rule rule) { + + this.rule = rule; + } + + public String getId() { + + return rule != null ? rule.getId() : id; + } + + public Rule getRule() throws ActionMgtException { + + if (rule != null) { + return rule; + } + + /* + If rule is not loaded, load it from the Rule Management Service. + This happens when multiple actions for an action type is loaded where the ActionRule is created + using the rule id referenced in actions data layer. + The need to load the rule for the action comes when executing actions in chain. + Thus, it's more efficient to load the rule lazily as rule execution depends on factors like + action status (active/inactive), returning state of the prior action in the chain, etc. + */ + rule = getRuleFromRuleManagementService(); + return rule; + } + + private Rule getRuleFromRuleManagementService() throws ActionMgtServerException { + + try { + return ActionMgtServiceComponentHolder.getInstance() + .getRuleManagementService() + .getRuleByRuleId(id, tenantDomain); + } catch (RuleManagementException e) { + throw new ActionMgtServerException("Error while retrieving the Rule.", e); + } + } + + /** + * Create an ActionRule object with the given Rule. + * + * @param rule Rule object. + * @return ActionRule object. + */ + public static ActionRule create(Rule rule) { + + return new ActionRule(rule); + } + + /** + * Create an ActionRule object with the given rule ID and tenant domain. + * rule ID is used to lazily load the Rule from the Rule Management Service. + * + * @param id Rule ID. Cannot be empty. + * @param tenantDomain Tenant domain. Cannot be empty. + * @return ActionRule object. + */ + public static ActionRule create(String id, String tenantDomain) { + + if (StringUtils.isBlank(id)) { + throw new IllegalArgumentException("Rule ID cannot be empty."); + } + if (StringUtils.isBlank(tenantDomain)) { + throw new IllegalArgumentException("Tenant domain cannot be empty."); + } + + return new ActionRule(id, tenantDomain); + } +} diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/service/impl/ActionManagementServiceImpl.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/service/impl/ActionManagementServiceImpl.java index aaa8168b30e0..b47d91a008f6 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/service/impl/ActionManagementServiceImpl.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/service/impl/ActionManagementServiceImpl.java @@ -437,6 +437,7 @@ private Action buildAction(String actionType, ActionDTO actionDTO) { .description(actionDTO.getDescription()) .status(actionDTO.getStatus()) .endpoint(actionDTO.getEndpoint()) + .rule(actionDTO.getActionRule()) .build(); } } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/util/ActionDTOBuilder.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/util/ActionDTOBuilder.java index 9e852ee1aee2..6f67b256098c 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/util/ActionDTOBuilder.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/util/ActionDTOBuilder.java @@ -23,6 +23,7 @@ import org.wso2.carbon.identity.action.management.exception.ActionMgtServerException; import org.wso2.carbon.identity.action.management.model.Action; import org.wso2.carbon.identity.action.management.model.ActionDTO; +import org.wso2.carbon.identity.action.management.model.ActionRule; import org.wso2.carbon.identity.action.management.model.Authentication; import org.wso2.carbon.identity.action.management.model.EndpointConfig; @@ -41,6 +42,7 @@ public class ActionDTOBuilder { private String description; private Action.Status status; private EndpointConfig endpoint; + private ActionRule rule; private Map properties; public ActionDTOBuilder() { @@ -55,6 +57,7 @@ public ActionDTOBuilder(ActionDTO actionDTO) { this.description = actionDTO.getDescription(); this.status = actionDTO.getStatus(); this.endpoint = actionDTO.getEndpoint(); + this.rule = actionDTO.getActionRule(); this.properties = actionDTO.getProperties(); } @@ -66,6 +69,7 @@ public ActionDTOBuilder(Action action) { this.description = action.getDescription(); this.status = action.getStatus(); this.endpoint = action.getEndpoint(); + this.rule = action.getActionRule(); } public ActionDTOBuilder id(String id) { @@ -134,6 +138,17 @@ public EndpointConfig getEndpoint() { return this.endpoint; } + public ActionDTOBuilder rule(ActionRule rule) { + + this.rule = rule; + return this; + } + + public ActionRule getActionRule() { + + return rule; + } + public ActionDTOBuilder setEndpointAndProperties(Map properties) throws ActionMgtException { @@ -201,6 +216,7 @@ public ActionDTO build() { .description(this.description) .status(this.status) .endpoint(this.endpoint) + .rule(this.rule) .build(); return new ActionDTO.Builder(action).properties(this.properties).build(); From c0264fe13e23a06f4a8f52eb5db1f12a6d203f93 Mon Sep 17 00:00:00 2001 From: malithie Date: Tue, 7 Jan 2025 15:06:41 +0530 Subject: [PATCH 2/5] Add unit tests for rule support in actions. --- .../dao/ActionManagementDAOFacadeTest.java | 376 ++++++++++++++---- .../dao/TestActionDTOModelResolver.java | 6 + .../ActionManagementServiceImplTest.java | 111 +++++- .../action/management/util/TestUtil.java | 24 ++ 4 files changed, 427 insertions(+), 90 deletions(-) diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/dao/ActionManagementDAOFacadeTest.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/dao/ActionManagementDAOFacadeTest.java index 8aa13fc9e089..a579156155b9 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/dao/ActionManagementDAOFacadeTest.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/dao/ActionManagementDAOFacadeTest.java @@ -40,6 +40,7 @@ import org.wso2.carbon.identity.action.management.internal.ActionMgtServiceComponentHolder; import org.wso2.carbon.identity.action.management.model.Action; import org.wso2.carbon.identity.action.management.model.ActionDTO; +import org.wso2.carbon.identity.action.management.model.ActionRule; import org.wso2.carbon.identity.action.management.model.Authentication; import org.wso2.carbon.identity.action.management.model.EndpointConfig; import org.wso2.carbon.identity.action.management.service.ActionDTOModelResolver; @@ -49,8 +50,10 @@ import org.wso2.carbon.identity.common.testng.WithCarbonHome; import org.wso2.carbon.identity.common.testng.WithH2Database; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; +import org.wso2.carbon.identity.rule.management.exception.RuleManagementException; +import org.wso2.carbon.identity.rule.management.model.Rule; +import org.wso2.carbon.identity.rule.management.service.RuleManagementService; import org.wso2.carbon.identity.secret.mgt.core.SecretManagerImpl; -import org.wso2.carbon.identity.secret.mgt.core.exception.SecretManagementException; import org.wso2.carbon.identity.secret.mgt.core.model.SecretType; import java.util.Collections; @@ -95,36 +98,26 @@ public class ActionManagementDAOFacadeTest { @Mock private ActionDTOModelResolver mockedActionDTOModelResolver; + @Mock + private RuleManagementService ruleManagementService; private TestActionDTOModelResolver testActionPropertyResolver; private MockedStatic actionPropertyResolverFactory; private MockedStatic identityTenantUtil; private ActionManagementDAOFacade daoFacade; - private ActionDTO creatingActionDTO; - private ActionDTO createdActionDTO; + private ActionDTO actionDTOToAddOrUpdate; + private ActionDTO actionDTORetrieved; @BeforeClass public void setUpClass() { daoFacade = new ActionManagementDAOFacade(new ActionManagementDAOImpl()); - creatingActionDTO = new ActionDTOBuilder() - .id(PRE_UPDATE_PASSWORD_ACTION_ID) - .type(Action.ActionTypes.PRE_UPDATE_PASSWORD) - .name(TEST_ACTION_NAME) - .description(TEST_ACTION_DESCRIPTION) - .endpoint(new EndpointConfig.EndpointConfigBuilder() - .uri(TEST_ACTION_URI) - .authentication(TestUtil.buildMockBasicAuthentication(TEST_USERNAME, TEST_PASSWORD)) - .build()) - .property(PASSWORD_SHARING_TYPE_PROPERTY_NAME, TEST_PASSWORD_SHARING_TYPE) - .property(CERTIFICATE_PROPERTY_NAME, - new Certificate.Builder().certificateContent(TEST_CERTIFICATE).build()) - .build(); + actionDTOToAddOrUpdate = createActionDTOForPasswordUpdateAction(); testActionPropertyResolver = new TestActionDTOModelResolver(); } @BeforeMethod - public void setUp() throws SecretManagementException { + public void setUp() throws Exception { SecretManagerImpl secretManager = mock(SecretManagerImpl.class); SecretType secretType = mock(SecretType.class); @@ -133,10 +126,11 @@ public void setUp() throws SecretManagementException { when(secretManager.getSecretType(any())).thenReturn(secretType); identityTenantUtil = mockStatic(IdentityTenantUtil.class); - identityTenantUtil.when(()-> IdentityTenantUtil.getTenantDomain(anyInt())).thenReturn(TENANT_DOMAIN); + identityTenantUtil.when(() -> IdentityTenantUtil.getTenantDomain(anyInt())).thenReturn(TENANT_DOMAIN); MockitoAnnotations.openMocks(this); actionPropertyResolverFactory = mockStatic(ActionDTOModelResolverFactory.class); + ActionMgtServiceComponentHolder.getInstance().setRuleManagementService(ruleManagementService); } @AfterMethod @@ -155,7 +149,7 @@ public void testAddActionWithActionPropertyResolverClientException() throws Acti .when(mockedActionDTOModelResolver).resolveForAddOperation(any(), any()); try { - daoFacade.addAction(creatingActionDTO, TENANT_ID); + daoFacade.addAction(actionDTOToAddOrUpdate, TENANT_ID); Assert.fail("Successful addition of the action without an exception is considered as a failure"); } catch (ActionMgtException e) { Assert.assertEquals(e.getClass(), ActionMgtClientException.class); @@ -173,7 +167,7 @@ public void testAddActionWithActionPropertyResolverServerException() throws Acti .when(mockedActionDTOModelResolver).resolveForAddOperation(any(), any()); try { - daoFacade.addAction(creatingActionDTO, TENANT_ID); + daoFacade.addAction(actionDTOToAddOrUpdate, TENANT_ID); Assert.fail("Successful addition of the action without an exception is considered as a failure"); } catch (ActionMgtException e) { Assert.assertEquals(e.getClass(), ActionMgtServerException.class); @@ -192,39 +186,15 @@ public void testAddAction() throws ActionMgtException { mockActionPropertyResolver(testActionPropertyResolver); try { - daoFacade.addAction(creatingActionDTO, TENANT_ID); + daoFacade.addAction(actionDTOToAddOrUpdate, TENANT_ID); } catch (Exception e) { Assert.fail(); } - createdActionDTO = daoFacade.getActionByActionId(PRE_UPDATE_PASSWORD_TYPE, PRE_UPDATE_PASSWORD_ACTION_ID, + actionDTORetrieved = daoFacade.getActionByActionId(PRE_UPDATE_PASSWORD_TYPE, PRE_UPDATE_PASSWORD_ACTION_ID, TENANT_ID); - Assert.assertEquals(createdActionDTO.getId(), creatingActionDTO.getId()); - Assert.assertEquals(createdActionDTO.getType(), creatingActionDTO.getType()); - Assert.assertEquals(createdActionDTO.getName(), creatingActionDTO.getName()); - Assert.assertEquals(createdActionDTO.getDescription(), creatingActionDTO.getDescription()); - Assert.assertEquals(createdActionDTO.getStatus(), Action.Status.ACTIVE); - Assert.assertEquals(createdActionDTO.getEndpoint().getUri(), creatingActionDTO.getEndpoint().getUri()); - - Authentication createdAuthentication = createdActionDTO.getEndpoint().getAuthentication(); - Assert.assertEquals(createdAuthentication.getType(), - creatingActionDTO.getEndpoint().getAuthentication().getType()); - Assert.assertEquals(createdAuthentication.getProperties().size(), - creatingActionDTO.getEndpoint().getAuthentication().getProperties().size()); - Assert.assertEquals(createdAuthentication.getProperty(Authentication.Property.USERNAME).getValue(), - TestUtil.buildSecretName(PRE_UPDATE_PASSWORD_ACTION_ID, Authentication.Type.BASIC, - Authentication.Property.USERNAME)); - Assert.assertEquals(createdAuthentication.getProperty(Authentication.Property.PASSWORD).getValue(), - TestUtil.buildSecretName(PRE_UPDATE_PASSWORD_ACTION_ID, Authentication.Type.BASIC, - Authentication.Property.PASSWORD)); - Assert.assertEquals(createdActionDTO.getProperties().size(), creatingActionDTO.getProperties().size()); - Assert.assertTrue(createdActionDTO.getProperties().containsKey(PASSWORD_SHARING_TYPE_PROPERTY_NAME)); - Assert.assertTrue(createdActionDTO.getProperties().containsKey(CERTIFICATE_PROPERTY_NAME)); - Assert.assertEquals(createdActionDTO.getProperty(PASSWORD_SHARING_TYPE_PROPERTY_NAME), - creatingActionDTO.getProperty(PASSWORD_SHARING_TYPE_PROPERTY_NAME)); - Assert.assertEquals(((Certificate) createdActionDTO.getProperty(CERTIFICATE_PROPERTY_NAME)) - .getCertificateContent(), TEST_CERTIFICATE); + verifyActionDTO(actionDTORetrieved, actionDTOToAddOrUpdate); } @Test(priority = 4) @@ -233,32 +203,8 @@ public void testGetActionsByType() throws ActionMgtException { mockActionPropertyResolver(testActionPropertyResolver); List actionDTOs = daoFacade.getActionsByActionType(PRE_UPDATE_PASSWORD_TYPE, TENANT_ID); ActionDTO result = actionDTOs.get(0); - Assert.assertEquals(result.getId(), createdActionDTO.getId()); - Assert.assertEquals(result.getType(), createdActionDTO.getType()); - Assert.assertEquals(result.getName(), createdActionDTO.getName()); - Assert.assertEquals(result.getDescription(), createdActionDTO.getDescription()); - Assert.assertEquals(result.getStatus(), createdActionDTO.getStatus()); - Assert.assertEquals(result.getEndpoint().getUri(), createdActionDTO.getEndpoint().getUri()); - - Authentication resultAuthentication = result.getEndpoint().getAuthentication(); - Assert.assertEquals(resultAuthentication.getType(), - createdActionDTO.getEndpoint().getAuthentication().getType()); - Assert.assertEquals(resultAuthentication.getProperties().size(), - createdActionDTO.getEndpoint().getAuthentication().getProperties().size()); - Assert.assertEquals(resultAuthentication.getProperty(Authentication.Property.USERNAME).getValue(), - createdActionDTO.getEndpoint().getAuthentication().getProperty(Authentication.Property.USERNAME) - .getValue()); - Assert.assertEquals(resultAuthentication.getProperty(Authentication.Property.PASSWORD).getValue(), - createdActionDTO.getEndpoint().getAuthentication().getProperty(Authentication.Property.PASSWORD) - .getValue()); - - Assert.assertEquals(result.getProperties().size(), createdActionDTO.getProperties().size()); - Assert.assertTrue(createdActionDTO.getProperties().containsKey(PASSWORD_SHARING_TYPE_PROPERTY_NAME)); - Assert.assertTrue(createdActionDTO.getProperties().containsKey(CERTIFICATE_PROPERTY_NAME)); - Assert.assertEquals(result.getProperty(PASSWORD_SHARING_TYPE_PROPERTY_NAME), - createdActionDTO.getProperty(PASSWORD_SHARING_TYPE_PROPERTY_NAME)); - Assert.assertEquals(((Certificate) result.getProperty(CERTIFICATE_PROPERTY_NAME)).getCertificateContent(), - ((Certificate) createdActionDTO.getProperty(CERTIFICATE_PROPERTY_NAME)).getCertificateContent()); + + verifyActionDTO(result, actionDTOToAddOrUpdate); } @Test(priority = 5) @@ -269,7 +215,7 @@ public void testUpdateActionPropertyResolverClientException() throws ActionDTOMo .when(mockedActionDTOModelResolver).resolveForUpdateOperation(any(), any(), any()); try { - daoFacade.updateAction(creatingActionDTO, createdActionDTO, TENANT_ID); + daoFacade.updateAction(actionDTOToAddOrUpdate, actionDTORetrieved, TENANT_ID); Assert.fail("Successful update of the actions without an exception is considered as a failure"); } catch (ActionMgtException e) { Assert.assertEquals(e.getClass(), ActionMgtClientException.class); @@ -288,7 +234,7 @@ public void testUpdateActionWithActionPropertyResolverServerException() throws A .resolveForUpdateOperation(any(), any(), any()); try { - daoFacade.updateAction(creatingActionDTO, createdActionDTO, TENANT_ID); + daoFacade.updateAction(actionDTOToAddOrUpdate, actionDTORetrieved, TENANT_ID); Assert.fail("Successful update of the actions without an exception is considered as a failure"); } catch (ActionMgtException e) { Assert.assertEquals(e.getClass(), ActionMgtServerException.class); @@ -308,7 +254,7 @@ public void testUpdateCompleteAction() throws ActionMgtException { mockActionPropertyResolver(testActionPropertyResolver); // Update action with certificate property deletion. ActionDTO updatingAction = new ActionDTOBuilder() - .id(createdActionDTO.getId()) + .id(actionDTORetrieved.getId()) .type(Action.ActionTypes.PRE_UPDATE_PASSWORD) .name(TEST_ACTION_NAME_UPDATED) .description(TEST_ACTION_DESCRIPTION_UPDATED) @@ -322,17 +268,17 @@ public void testUpdateCompleteAction() throws ActionMgtException { .build(); try { - daoFacade.updateAction(updatingAction, createdActionDTO, TENANT_ID); + daoFacade.updateAction(updatingAction, actionDTORetrieved, TENANT_ID); } catch (Exception e) { Assert.fail(); } ActionDTO result = daoFacade.getActionByActionId(PRE_UPDATE_PASSWORD_TYPE, updatingAction.getId(), TENANT_ID); - Assert.assertEquals(result.getId(), createdActionDTO.getId()); - Assert.assertEquals(result.getType(), createdActionDTO.getType()); + Assert.assertEquals(result.getId(), actionDTORetrieved.getId()); + Assert.assertEquals(result.getType(), actionDTORetrieved.getType()); Assert.assertEquals(result.getName(), updatingAction.getName()); Assert.assertEquals(result.getDescription(), updatingAction.getDescription()); - Assert.assertEquals(result.getStatus(), createdActionDTO.getStatus()); + Assert.assertEquals(result.getStatus(), actionDTORetrieved.getStatus()); Assert.assertEquals(result.getEndpoint().getUri(), updatingAction.getEndpoint().getUri()); Authentication updatedAuthentication = result.getEndpoint().getAuthentication(); @@ -352,22 +298,23 @@ public void testUpdateCompleteAction() throws ActionMgtException { Assert.assertEquals(result.getProperty(PASSWORD_SHARING_TYPE_PROPERTY_NAME), updatingAction.getProperty(PASSWORD_SHARING_TYPE_PROPERTY_NAME)); Assert.assertNull(result.getProperty(CERTIFICATE_PROPERTY_NAME)); - createdActionDTO = result; + actionDTORetrieved = result; } @Test(priority = 8) public void testDeactivateAction() throws ActionMgtException { - Assert.assertEquals(createdActionDTO.getStatus(), Action.Status.ACTIVE); - ActionDTO deactivatedActionDTO = daoFacade.deactivateAction(PRE_UPDATE_PASSWORD_TYPE, createdActionDTO.getId(), - TENANT_ID); + Assert.assertEquals(actionDTORetrieved.getStatus(), Action.Status.ACTIVE); + ActionDTO deactivatedActionDTO = + daoFacade.deactivateAction(PRE_UPDATE_PASSWORD_TYPE, actionDTORetrieved.getId(), + TENANT_ID); Assert.assertEquals(deactivatedActionDTO.getStatus(), Action.Status.INACTIVE); } @Test(priority = 9) public void testActivateAction() throws ActionMgtException { - ActionDTO activatedActionDTO = daoFacade.activateAction(PRE_UPDATE_PASSWORD_TYPE, createdActionDTO.getId(), + ActionDTO activatedActionDTO = daoFacade.activateAction(PRE_UPDATE_PASSWORD_TYPE, actionDTORetrieved.getId(), TENANT_ID); Assert.assertEquals(activatedActionDTO.getStatus(), Action.Status.ACTIVE); } @@ -385,7 +332,7 @@ public void testDeleteAction() throws ActionMgtException { mockActionPropertyResolver(testActionPropertyResolver); try { - daoFacade.deleteAction(createdActionDTO, TENANT_ID); + daoFacade.deleteAction(actionDTORetrieved, TENANT_ID); } catch (Exception e) { Assert.fail(); } @@ -394,10 +341,263 @@ public void testDeleteAction() throws ActionMgtException { Assert.assertEquals(daoFacade.getActionsCountPerType(TENANT_ID), Collections.emptyMap()); } + @Test(priority = 12) + public void testAddActionWithRule() throws Exception { + + Rule rule = mockRule(); + mockRuleManagementService(rule); + actionDTOToAddOrUpdate = createActionDTOForPasswordUpdateActionWithRule(rule); + mockActionPropertyResolver(testActionPropertyResolver); + + daoFacade.addAction(actionDTOToAddOrUpdate, TENANT_ID); + + actionDTORetrieved = daoFacade.getActionByActionId(PRE_UPDATE_PASSWORD_TYPE, PRE_UPDATE_PASSWORD_ACTION_ID, + TENANT_ID); + + verifyActionDTO(actionDTORetrieved, actionDTOToAddOrUpdate); + verifyActionDTORule(actionDTORetrieved, actionDTOToAddOrUpdate); + } + + @Test(priority = 13) + public void testFailureInAddActionWithRuleAtRuleManagementException() throws Exception { + + Rule rule = mockRule(); + when(ruleManagementService.addRule(rule, TENANT_DOMAIN)).thenThrow(new RuleManagementException("Error adding " + + "rule.")); + actionDTOToAddOrUpdate = createActionDTOForPasswordUpdateActionWithRule(rule); + + try { + daoFacade.addAction(actionDTOToAddOrUpdate, TENANT_ID); + } catch (ActionMgtException e) { + Assert.assertEquals(e.getMessage(), "Error while adding Action."); + Throwable cause = e.getCause(); + while (cause != null && !(cause instanceof ActionMgtServerException)) { + cause = cause.getCause(); + } + Assert.assertNotNull(cause, "Expected ActionMgtServerException was not found in the exception chain"); + Assert.assertEquals(cause.getMessage(), "Error while adding the Rule associated with the Action."); + } + } + + @Test(priority = 14) + public void testFailureInRetrievingActionWithRuleAtRuleManagementException() throws Exception { + + Rule rule = mockRule(); + when(ruleManagementService.getRuleByRuleId(rule.getId(), TENANT_DOMAIN)).thenThrow( + new RuleManagementException("Error retrieving rule.")); + + try { + daoFacade.getActionByActionId(PRE_UPDATE_PASSWORD_TYPE, PRE_UPDATE_PASSWORD_ACTION_ID, + TENANT_ID); + } catch (ActionMgtException e) { + Assert.assertEquals(e.getMessage(), "Error while retrieving Action by ID."); + Throwable cause = e.getCause(); + while (cause != null && !(cause instanceof ActionMgtServerException)) { + cause = cause.getCause(); + } + Assert.assertNotNull(cause, "Expected ActionMgtServerException was not found in the exception chain"); + Assert.assertEquals(cause.getMessage(), "Error while retrieving the Rule associated with the Action."); + } + } + + @Test(priority = 15) + public void testGetActionsWithRulesByType() throws Exception { + + Rule rule = mockRule(); + mockRuleManagementService(rule); + ActionDTO expectedActionDTO = createActionDTOForPasswordUpdateActionWithRule(rule); + mockActionPropertyResolver(testActionPropertyResolver); + + List actionDTOs = daoFacade.getActionsByActionType(PRE_UPDATE_PASSWORD_TYPE, TENANT_ID); + + ActionDTO result = actionDTOs.get(0); + verifyActionDTO(result, expectedActionDTO); + verifyActionDTORule(result, expectedActionDTO); + } + + @Test(priority = 16) + public void testUpdateActionUpdatingRule() throws Exception { + + Rule rule = mockRule(); + mockRuleManagementService(rule); + ActionDTO updatingActionDTO = createActionDTOForPasswordUpdateActionWithRule(rule); + mockActionPropertyResolver(testActionPropertyResolver); + + daoFacade.updateAction(updatingActionDTO, actionDTORetrieved, TENANT_ID); + + actionDTORetrieved = daoFacade.getActionByActionId(PRE_UPDATE_PASSWORD_TYPE, PRE_UPDATE_PASSWORD_ACTION_ID, + TENANT_ID); + + verifyActionDTO(actionDTORetrieved, updatingActionDTO); + verifyActionDTORule(actionDTORetrieved, updatingActionDTO); + } + + @Test(priority = 17) + public void testFailureInUpdateActionUpdatingRuleAtRuleManagementException() throws Exception { + + Rule rule = mockRule(); + when(ruleManagementService.updateRule(rule, TENANT_DOMAIN)).thenThrow(new RuleManagementException("Error " + + "updating rule.")); + ActionDTO updatingActionDTO = createActionDTOForPasswordUpdateActionWithRule(rule); + + try { + daoFacade.updateAction(updatingActionDTO, actionDTORetrieved, TENANT_ID); + } catch (ActionMgtException e) { + Assert.assertEquals(e.getMessage(), "Error while updating Action."); + Throwable cause = e.getCause(); + while (cause != null && !(cause instanceof ActionMgtServerException)) { + cause = cause.getCause(); + } + Assert.assertNotNull(cause, "Expected ActionMgtServerException was not found in the exception chain"); + Assert.assertEquals(cause.getMessage(), "Error while updating the Rule associated with the Action."); + } + } + + @Test(priority = 18) + public void testUpdateActionRemovingRule() throws Exception { + + ActionDTO updatingActionDTOWithoutRule = createActionDTOForPasswordUpdateAction(); + mockActionPropertyResolver(testActionPropertyResolver); + + daoFacade.updateAction(updatingActionDTOWithoutRule, actionDTORetrieved, TENANT_ID); + + actionDTORetrieved = daoFacade.getActionByActionId(PRE_UPDATE_PASSWORD_TYPE, PRE_UPDATE_PASSWORD_ACTION_ID, + TENANT_ID); + + verifyActionDTO(actionDTORetrieved, updatingActionDTOWithoutRule); + Assert.assertNull(actionDTORetrieved.getActionRule()); + } + + @Test(priority = 19) + public void testFailureInUpdateActionRemovingRuleAtRuleManagementException() throws Exception { + + doThrow(new RuleManagementException("Error updating rule.")).when(ruleManagementService) + .deleteRule(any(), any()); + ActionDTO updatingActionDTOWithoutRule = createActionDTOForPasswordUpdateAction(); + mockActionPropertyResolver(testActionPropertyResolver); + + try { + daoFacade.updateAction(updatingActionDTOWithoutRule, actionDTORetrieved, TENANT_ID); + } catch (ActionMgtException e) { + Assert.assertEquals(e.getMessage(), "Error while updating Action."); + Throwable cause = e.getCause(); + while (cause != null && !(cause instanceof ActionMgtServerException)) { + cause = cause.getCause(); + } + Assert.assertNotNull(cause, "Expected ActionMgtServerException was not found in the exception chain"); + Assert.assertEquals(cause.getMessage(), "Error while deleting the Rule associated with the Action."); + } + } + + @Test(priority = 20) + public void testUpdateActionAddingRule() throws Exception { + + Rule rule = mockRule(); + mockRuleManagementService(rule); + ActionDTO updatingActionDTO = createActionDTOForPasswordUpdateActionWithRule(rule); + mockActionPropertyResolver(testActionPropertyResolver); + + daoFacade.updateAction(updatingActionDTO, actionDTORetrieved, TENANT_ID); + + actionDTORetrieved = daoFacade.getActionByActionId(PRE_UPDATE_PASSWORD_TYPE, PRE_UPDATE_PASSWORD_ACTION_ID, + TENANT_ID); + + verifyActionDTO(actionDTORetrieved, updatingActionDTO); + verifyActionDTORule(actionDTORetrieved, updatingActionDTO); + } + + @Test(priority = 21) + public void testDeleteActionWithRule() throws ActionMgtException { + + mockActionPropertyResolver(testActionPropertyResolver); + + daoFacade.deleteAction(actionDTORetrieved, TENANT_ID); + + Assert.assertNull(daoFacade.getActionByActionId(PRE_UPDATE_PASSWORD_TYPE, PRE_UPDATE_PASSWORD_ACTION_ID, + TENANT_ID)); + Assert.assertEquals(daoFacade.getActionsCountPerType(TENANT_ID), Collections.emptyMap()); + } + private void mockActionPropertyResolver(ActionDTOModelResolver actionDTOModelResolver) { - actionPropertyResolverFactory.when( - () -> ActionDTOModelResolverFactory.getActionDTOModelResolver(Action.ActionTypes.PRE_UPDATE_PASSWORD)) + actionPropertyResolverFactory.when(() -> ActionDTOModelResolverFactory. + getActionDTOModelResolver(Action.ActionTypes.PRE_UPDATE_PASSWORD)) .thenReturn(actionDTOModelResolver); } + + private ActionDTO createActionDTOForPasswordUpdateAction() { + + return createActionDTOBuilderForPasswordUpdateAction().build(); + } + + private ActionDTO createActionDTOForPasswordUpdateActionWithRule(Rule rule) { + + return createActionDTOBuilderForPasswordUpdateAction().rule(ActionRule.create(rule)).build(); + } + + private ActionDTOBuilder createActionDTOBuilderForPasswordUpdateAction() { + + return new ActionDTOBuilder() + .id(PRE_UPDATE_PASSWORD_ACTION_ID) + .type(Action.ActionTypes.PRE_UPDATE_PASSWORD) + .name(TEST_ACTION_NAME) + .description(TEST_ACTION_DESCRIPTION) + .endpoint(new EndpointConfig.EndpointConfigBuilder() + .uri(TEST_ACTION_URI) + .authentication(TestUtil.buildMockBasicAuthentication(TEST_USERNAME, TEST_PASSWORD)) + .build()) + .property(PASSWORD_SHARING_TYPE_PROPERTY_NAME, TEST_PASSWORD_SHARING_TYPE) + .property(CERTIFICATE_PROPERTY_NAME, + new Certificate.Builder().certificateContent(TEST_CERTIFICATE).build()); + } + + private void verifyActionDTO(ActionDTO actualActionDTO, ActionDTO expectedActionDTO) { + + Assert.assertEquals(actualActionDTO.getId(), expectedActionDTO.getId()); + Assert.assertEquals(actualActionDTO.getType(), expectedActionDTO.getType()); + Assert.assertEquals(actualActionDTO.getName(), expectedActionDTO.getName()); + Assert.assertEquals(actualActionDTO.getDescription(), expectedActionDTO.getDescription()); + Assert.assertEquals(actualActionDTO.getStatus(), Action.Status.ACTIVE); + Assert.assertEquals(actualActionDTO.getEndpoint().getUri(), expectedActionDTO.getEndpoint().getUri()); + + Authentication createdAuthentication = actualActionDTO.getEndpoint().getAuthentication(); + Assert.assertEquals(createdAuthentication.getType(), + expectedActionDTO.getEndpoint().getAuthentication().getType()); + Assert.assertEquals(createdAuthentication.getProperties().size(), + expectedActionDTO.getEndpoint().getAuthentication().getProperties().size()); + Assert.assertEquals(createdAuthentication.getProperty(Authentication.Property.USERNAME).getValue(), + TestUtil.buildSecretName(PRE_UPDATE_PASSWORD_ACTION_ID, Authentication.Type.BASIC, + Authentication.Property.USERNAME)); + Assert.assertEquals(createdAuthentication.getProperty(Authentication.Property.PASSWORD).getValue(), + TestUtil.buildSecretName(PRE_UPDATE_PASSWORD_ACTION_ID, Authentication.Type.BASIC, + Authentication.Property.PASSWORD)); + + Assert.assertEquals(actualActionDTO.getProperties().size(), expectedActionDTO.getProperties().size()); + Assert.assertTrue(actualActionDTO.getProperties().containsKey(PASSWORD_SHARING_TYPE_PROPERTY_NAME)); + Assert.assertEquals(actualActionDTO.getProperty(PASSWORD_SHARING_TYPE_PROPERTY_NAME), + expectedActionDTO.getProperty(PASSWORD_SHARING_TYPE_PROPERTY_NAME)); + } + + private void verifyActionDTORule(ActionDTO actualActionDTO, ActionDTO expectedActionDTO) + throws ActionMgtException { + + Assert.assertNotNull(actualActionDTO.getActionRule()); + Assert.assertEquals(actualActionDTO.getActionRule().getId(), expectedActionDTO.getActionRule().getId()); + Assert.assertEquals(actualActionDTO.getActionRule().getRule(), expectedActionDTO.getActionRule().getRule()); + } + + private Rule mockRule() { + + Rule rule = mock(Rule.class); + when(rule.getId()).thenReturn("ruleId"); + when(rule.isActive()).thenReturn(true); + return rule; + } + + private void mockRuleManagementService(Rule rule) throws RuleManagementException { + + when(ruleManagementService.addRule(rule, TENANT_DOMAIN)).thenReturn(rule); + when(ruleManagementService.updateRule(rule, TENANT_DOMAIN)).thenReturn(rule); + when(ruleManagementService.getRuleByRuleId("ruleId", TENANT_DOMAIN)).thenReturn(rule); + } } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/dao/TestActionDTOModelResolver.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/dao/TestActionDTOModelResolver.java index 2b5424d40672..3e410280f9ed 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/dao/TestActionDTOModelResolver.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/dao/TestActionDTOModelResolver.java @@ -18,6 +18,7 @@ package org.wso2.carbon.identity.action.management.dao; +import org.apache.commons.lang.StringUtils; import org.wso2.carbon.identity.action.management.model.Action; import org.wso2.carbon.identity.action.management.model.ActionDTO; import org.wso2.carbon.identity.action.management.service.ActionDTOModelResolver; @@ -99,6 +100,11 @@ public ActionDTO resolveForUpdateOperation(ActionDTO updatingActionDTO, ActionDT Map properties = new HashMap<>(); properties.put(PASSWORD_SHARING_TYPE_PROPERTY_NAME, updatingActionDTO.getProperty(PASSWORD_SHARING_TYPE_PROPERTY_NAME)); + if (updatingActionDTO.getProperty(CERTIFICATE_PROPERTY_NAME) != null && + !StringUtils.EMPTY.equals(updatingActionDTO.getProperty( + CERTIFICATE_PROPERTY_NAME).toString())) { + properties.put(CERTIFICATE_PROPERTY_NAME, TestUtil.CERTIFICATE_ID); + } return new ActionDTO.Builder(updatingActionDTO).properties(properties).build(); } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/service/ActionManagementServiceImplTest.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/service/ActionManagementServiceImplTest.java index 1d159ec3bcde..5fcc6fc3b1ef 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/service/ActionManagementServiceImplTest.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/service/ActionManagementServiceImplTest.java @@ -35,6 +35,8 @@ import org.wso2.carbon.identity.common.testng.WithH2Database; import org.wso2.carbon.identity.common.testng.WithRealmService; import org.wso2.carbon.identity.core.internal.IdentityCoreServiceDataHolder; +import org.wso2.carbon.identity.rule.management.model.Rule; +import org.wso2.carbon.identity.rule.management.service.RuleManagementService; import org.wso2.carbon.identity.secret.mgt.core.SecretManagerImpl; import org.wso2.carbon.identity.secret.mgt.core.exception.SecretManagementException; import org.wso2.carbon.identity.secret.mgt.core.model.SecretType; @@ -75,21 +77,29 @@ public class ActionManagementServiceImplTest { private ActionManagementService actionManagementService; private Action sampleAction; + private Rule sampleRule; @BeforeClass public void setUpClass() { actionManagementService = new ActionManagementServiceImpl(); + sampleRule = TestUtil.buildMockRule("ruleId", true); } @BeforeMethod - public void setUp() throws SecretManagementException { + public void setUp() throws Exception { SecretManagerImpl secretManager = mock(SecretManagerImpl.class); SecretType secretType = mock(SecretType.class); ActionMgtServiceComponentHolder.getInstance().setSecretManager(secretManager); when(secretType.getId()).thenReturn(TestUtil.TEST_SECRET_TYPE_ID); when(secretManager.getSecretType(any())).thenReturn(secretType); + + RuleManagementService ruleManagementService = mock(RuleManagementService.class); + ActionMgtServiceComponentHolder.getInstance().setRuleManagementService(ruleManagementService); + when(ruleManagementService.getRuleByRuleId(any(), any())).thenReturn(sampleRule); + when(ruleManagementService.addRule(any(), any())).thenReturn(sampleRule); + when(ruleManagementService.updateRule(any(), any())).thenReturn(sampleRule); } @Test(priority = 1) @@ -124,6 +134,7 @@ public void testAddAction() throws ActionMgtException, SecretManagementException @Test(priority = 2, expectedExceptions = ActionMgtClientException.class, expectedExceptionsMessageRegExp = "Invalid request.") public void testAddActionWithInvalidData() throws ActionMgtException { + Action creatingAction = TestUtil.buildMockAction( TEST_INVALID_ACTION_NAME, TEST_ACTION_DESCRIPTION, @@ -136,6 +147,7 @@ public void testAddActionWithInvalidData() throws ActionMgtException { @Test(priority = 3, expectedExceptions = ActionMgtClientException.class, expectedExceptionsMessageRegExp = "Invalid request.") public void testAddActionWithEmptyData() throws ActionMgtException { + Action creatingAction = TestUtil.buildMockAction( StringUtils.EMPTY, TEST_ACTION_DESCRIPTION, @@ -259,7 +271,7 @@ public void testGetActionsCountPerType() throws ActionMgtException { Assert.assertNull(actionMap.get(Action.ActionTypes.PRE_UPDATE_PROFILE.getActionType())); Assert.assertNull(actionMap.get(Action.ActionTypes.PRE_REGISTRATION.getActionType())); Assert.assertNull(actionMap.get(Action.ActionTypes.AUTHENTICATION.getActionType())); - for (Map.Entry entry: actionMap.entrySet()) { + for (Map.Entry entry : actionMap.entrySet()) { Assert.assertEquals(Action.ActionTypes.PRE_ISSUE_ACCESS_TOKEN.getActionType(), entry.getKey()); Assert.assertEquals(entry.getValue().intValue(), 1); } @@ -320,6 +332,101 @@ public void testDeleteNonExistingAction() { } } + @Test(priority = 15) + public void testAddActionWithRule() throws ActionMgtException, SecretManagementException { + + Action creatingAction = TestUtil.buildMockActionWithRule( + TEST_ACTION_NAME, + TEST_ACTION_DESCRIPTION, + TEST_ACTION_URI, + TestUtil.buildMockBasicAuthentication(TEST_USERNAME, TEST_PASSWORD), + sampleRule); + sampleAction = actionManagementService.addAction(PRE_ISSUE_ACCESS_TOKEN_PATH, creatingAction, TENANT_DOMAIN); + + Assert.assertNotNull(sampleAction.getId()); + Assert.assertEquals(sampleAction.getName(), creatingAction.getName()); + Assert.assertEquals(sampleAction.getDescription(), creatingAction.getDescription()); + Assert.assertEquals(sampleAction.getStatus(), Action.Status.ACTIVE); + Assert.assertEquals(sampleAction.getType(), Action.ActionTypes.PRE_ISSUE_ACCESS_TOKEN); + Assert.assertEquals(sampleAction.getEndpoint().getUri(), creatingAction.getEndpoint().getUri()); + + Authentication sampleActionAuth = sampleAction.getEndpoint().getAuthentication(); + Authentication creatingActionAuth = creatingAction.getEndpoint().getAuthentication(); + Map secretProperties = resolveAuthPropertiesMap(creatingActionAuth, sampleAction.getId()); + + Assert.assertEquals(sampleActionAuth.getType(), creatingActionAuth.getType()); + Assert.assertEquals(sampleActionAuth.getProperties().size(), creatingActionAuth.getProperties().size()); + Assert.assertEquals(sampleActionAuth.getProperty(Authentication.Property.USERNAME).getValue(), + secretProperties.get(Authentication.Property.USERNAME.getName())); + Assert.assertEquals(sampleActionAuth.getProperty(Authentication.Property.PASSWORD).getValue(), + secretProperties.get(Authentication.Property.PASSWORD.getName())); + + Assert.assertNotNull(sampleAction.getActionRule()); + Assert.assertEquals(sampleAction.getActionRule().getId(), creatingAction.getActionRule().getId()); + Assert.assertEquals(sampleAction.getActionRule().getRule(), creatingAction.getActionRule().getRule()); + } + + @Test(priority = 16) + public void testGetActionsWithRulesByActionType() throws ActionMgtException { + + List actions = actionManagementService.getActionsByActionType(PRE_ISSUE_ACCESS_TOKEN_PATH, + TENANT_DOMAIN); + Assert.assertEquals(actions.size(), 1); + Action result = actions.get(0); + Assert.assertEquals(result.getId(), sampleAction.getId()); + Assert.assertEquals(result.getName(), sampleAction.getName()); + Assert.assertEquals(result.getDescription(), sampleAction.getDescription()); + Assert.assertEquals(result.getType().getActionType(), sampleAction.getType().getActionType()); + Assert.assertEquals(result.getStatus(), sampleAction.getStatus()); + Assert.assertEquals(result.getEndpoint().getUri(), sampleAction.getEndpoint().getUri()); + + Authentication resultActionAuth = result.getEndpoint().getAuthentication(); + Authentication sampleActionAuth = sampleAction.getEndpoint().getAuthentication(); + + Assert.assertEquals(resultActionAuth.getType(), sampleActionAuth.getType()); + Assert.assertEquals(resultActionAuth.getProperty(Authentication.Property.USERNAME).getValue(), + sampleActionAuth.getProperty(Authentication.Property.USERNAME).getValue()); + Assert.assertEquals(resultActionAuth.getProperty(Authentication.Property.PASSWORD).getValue(), + sampleActionAuth.getProperty(Authentication.Property.PASSWORD).getValue()); + + Assert.assertNotNull(result.getActionRule()); + Assert.assertEquals(result.getActionRule().getId(), sampleAction.getActionRule().getId()); + Assert.assertEquals(result.getActionRule().getRule(), sampleAction.getActionRule().getRule()); + } + + @Test(priority = 17) + public void testUpdateActionWithRule() throws ActionMgtException, SecretManagementException { + + Action updatingAction = TestUtil.buildMockActionWithRule( + TEST_ACTION_NAME_UPDATED, + TEST_ACTION_DESCRIPTION_UPDATED, + TEST_ACTION_URI, + TestUtil.buildMockAPIKeyAuthentication(TEST_API_KEY_HEADER, TEST_API_KEY_VALUE), sampleRule); + Action result = actionManagementService.updateAction(PRE_ISSUE_ACCESS_TOKEN_PATH, sampleAction.getId(), + updatingAction, TENANT_DOMAIN); + + Assert.assertEquals(result.getId(), sampleAction.getId()); + Assert.assertEquals(result.getName(), updatingAction.getName()); + Assert.assertEquals(result.getDescription(), updatingAction.getDescription()); + Assert.assertEquals(result.getType(), sampleAction.getType()); + Assert.assertEquals(result.getStatus(), sampleAction.getStatus()); + Assert.assertEquals(result.getEndpoint().getUri(), updatingAction.getEndpoint().getUri()); + + Authentication resultActionAuth = result.getEndpoint().getAuthentication(); + Authentication updatingActionAuth = updatingAction.getEndpoint().getAuthentication(); + Map secretProperties = resolveAuthPropertiesMap(updatingActionAuth, sampleAction.getId()); + + Assert.assertEquals(resultActionAuth.getType(), updatingActionAuth.getType()); + Assert.assertEquals(resultActionAuth.getProperty(Authentication.Property.HEADER).getValue(), + secretProperties.get(Authentication.Property.HEADER.getName())); + Assert.assertEquals(resultActionAuth.getProperty(Authentication.Property.VALUE).getValue(), + secretProperties.get(Authentication.Property.VALUE.getName())); + + Assert.assertNotNull(result.getActionRule()); + Assert.assertEquals(result.getActionRule().getId(), sampleAction.getActionRule().getId()); + Assert.assertEquals(result.getActionRule().getRule(), sampleAction.getActionRule().getRule()); + } + private Map resolveAuthPropertiesMap(Authentication authentication, String actionId) throws SecretManagementException { diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/util/TestUtil.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/util/TestUtil.java index a235877c7147..07493bb5ab46 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/util/TestUtil.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/util/TestUtil.java @@ -19,11 +19,16 @@ package org.wso2.carbon.identity.action.management.util; import org.wso2.carbon.identity.action.management.model.Action; +import org.wso2.carbon.identity.action.management.model.ActionRule; import org.wso2.carbon.identity.action.management.model.Authentication; import org.wso2.carbon.identity.action.management.model.EndpointConfig; +import org.wso2.carbon.identity.rule.management.model.Rule; import java.util.UUID; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + /** * Utility class for Action Management Tests. */ @@ -92,6 +97,17 @@ public static Action buildMockAction(String name, String description, String uri .build(); } + public static Action buildMockActionWithRule(String name, String description, String uri, + Authentication authentication, Rule rule) { + + return new Action.ActionRequestBuilder() + .name(name) + .description(description) + .endpoint(buildMockEndpointConfig(uri, authentication)) + .rule(ActionRule.create(rule)) + .build(); + } + public static String buildSecretName(String actionId, Authentication.Type authType, Authentication.Property authProperty) { @@ -124,4 +140,12 @@ public static EndpointConfig buildMockEndpointConfig(String uri, Authentication .authentication(authentication) .build(); } + + public static Rule buildMockRule(String ruleId, boolean isActive) { + + Rule rule = mock(Rule.class); + when(rule.getId()).thenReturn(ruleId); + when(rule.isActive()).thenReturn(isActive); + return rule; + } } From f8f90f228863d7508e64227121386ad92baf0d6f Mon Sep 17 00:00:00 2001 From: malithie Date: Wed, 8 Jan 2025 05:56:51 +0530 Subject: [PATCH 3/5] Apply minor refactoring suggestions. --- .../dao/impl/ActionManagementDAOFacade.java | 77 ++++++++++--------- .../ActionMgtServiceComponentHolder.java | 3 +- 2 files changed, 43 insertions(+), 37 deletions(-) diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/dao/impl/ActionManagementDAOFacade.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/dao/impl/ActionManagementDAOFacade.java index 2ea30739d6dd..21ca6361a734 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/dao/impl/ActionManagementDAOFacade.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/dao/impl/ActionManagementDAOFacade.java @@ -87,8 +87,8 @@ public void addAction(ActionDTO actionDTO, Integer tenantId) throws ActionMgtExc // Since exceptions thrown are wrapped with TransactionException, extracting the actual cause. handleActionPropertyResolverClientException(e.getCause()); LOG.debug("Error while creating the Action of Action Type: " + actionDTO.getType().getDisplayName() + - " in Tenant Domain: " + IdentityTenantUtil.getTenantDomain(tenantId) + - ". Rolling back created action information, authentication secrets and action properties."); + " in Tenant Domain: " + IdentityTenantUtil.getTenantDomain(tenantId) + + ". Rolling back created action information, authentication secrets and action properties."); throw ActionManagementExceptionHandler.handleServerException(ErrorMessage.ERROR_WHILE_ADDING_ACTION, e); } } @@ -289,8 +289,8 @@ private void deleteAuthenticationSecrets(ActionDTO deletingActionDTO) throws Act * Add the encrypted authentication secrets and replace the input authentication properties in the ActionDTOBuilder * object. * - * @param actionDTOBuilder ActionDTOBuilder object. - * @param encryptedProperties List of encrypted AuthProperty objects. + * @param actionDTOBuilder ActionDTOBuilder object. + * @param encryptedProperties List of encrypted AuthProperty objects. */ private void addEncryptedAuthSecretsToBuilder(ActionDTOBuilder actionDTOBuilder, List encryptedProperties) { @@ -299,39 +299,44 @@ private void addEncryptedAuthSecretsToBuilder(ActionDTOBuilder actionDTOBuilder, .collect(Collectors.toMap(AuthProperty::getName, AuthProperty::getValue)); actionDTOBuilder.endpoint(new EndpointConfig.EndpointConfigBuilder() - .uri(actionDTOBuilder.getEndpoint().getUri()) - .authentication(new Authentication.AuthenticationBuilder() - .type(actionDTOBuilder.getEndpoint().getAuthentication().getType()) - .properties(encryptedPropertyMap) - .build()) - .build()); + .uri(actionDTOBuilder.getEndpoint().getUri()) + .authentication(new Authentication.AuthenticationBuilder() + .type(actionDTOBuilder.getEndpoint().getAuthentication().getType()) + .properties(encryptedPropertyMap) + .build()) + .build()); } private void addActionRule(ActionDTOBuilder actionDTOBuilder, String tenantDomain) throws ActionMgtException { - if (actionDTOBuilder.getActionRule() != null) { - try { - ActionMgtServiceComponentHolder.getInstance() - .getRuleManagementService() - .addRule(actionDTOBuilder.getActionRule().getRule(), tenantDomain); - } catch (RuleManagementException e) { - throw new ActionMgtServerException("Error while adding the Rule associated with the Action.", e); - } + if (actionDTOBuilder.getActionRule() == null) { + return; } + + try { + ActionMgtServiceComponentHolder.getInstance() + .getRuleManagementService() + .addRule(actionDTOBuilder.getActionRule().getRule(), tenantDomain); + } catch (RuleManagementException e) { + throw new ActionMgtServerException("Error while adding the Rule associated with the Action.", e); + } + } private void loadActionRule(ActionDTOBuilder actionDTOBuilder, String tenantDomain) throws ActionMgtServerException { - if (actionDTOBuilder.getActionRule() != null) { - try { - ActionRule actionRule = ActionRule.create(ActionMgtServiceComponentHolder.getInstance() - .getRuleManagementService() - .getRuleByRuleId(actionDTOBuilder.getActionRule().getId(), tenantDomain)); - actionDTOBuilder.rule(actionRule); - } catch (RuleManagementException e) { - throw new ActionMgtServerException("Error while retrieving the Rule associated with the Action.", e); - } + if (actionDTOBuilder.getActionRule() == null) { + return; + } + + try { + ActionRule actionRule = ActionRule.create(ActionMgtServiceComponentHolder.getInstance() + .getRuleManagementService() + .getRuleByRuleId(actionDTOBuilder.getActionRule().getId(), tenantDomain)); + actionDTOBuilder.rule(actionRule); + } catch (RuleManagementException e) { + throw new ActionMgtServerException("Error while retrieving the Rule associated with the Action.", e); } } @@ -360,14 +365,16 @@ private void updateActionRule(ActionDTOBuilder updatingActionDTOBuilder, ActionD private void deleteActionRule(ActionDTO actionDTO, String tenantDomain) throws ActionMgtServerException { - if (actionDTO.getActionRule() != null) { - try { - ActionMgtServiceComponentHolder.getInstance() - .getRuleManagementService() - .deleteRule(actionDTO.getActionRule().getId(), tenantDomain); - } catch (RuleManagementException e) { - throw new ActionMgtServerException("Error while deleting the Rule associated with the Action.", e); - } + if (actionDTO.getActionRule() == null) { + return; + } + + try { + ActionMgtServiceComponentHolder.getInstance() + .getRuleManagementService() + .deleteRule(actionDTO.getActionRule().getId(), tenantDomain); + } catch (RuleManagementException e) { + throw new ActionMgtServerException("Error while deleting the Rule associated with the Action.", e); } } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/internal/ActionMgtServiceComponentHolder.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/internal/ActionMgtServiceComponentHolder.java index a5d73c12e881..da7ced3c5246 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/internal/ActionMgtServiceComponentHolder.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/internal/ActionMgtServiceComponentHolder.java @@ -102,8 +102,7 @@ public RuleManagementService getRuleManagementService() { * * @param ruleManagementService RuleManagementService instance. */ - public void setRuleManagementService( - RuleManagementService ruleManagementService) { + public void setRuleManagementService(RuleManagementService ruleManagementService) { this.ruleManagementService = ruleManagementService; } From 0d58c41b9c136189b8c1ef898c4a56c30f1e9f75 Mon Sep 17 00:00:00 2001 From: malithie Date: Wed, 8 Jan 2025 13:52:25 +0530 Subject: [PATCH 4/5] Fix updating action adding updating or removing rule. --- .../dao/impl/ActionManagementDAOFacade.java | 87 +++++++++++++------ .../dao/impl/ActionManagementDAOImpl.java | 18 ++-- .../action/management/model/ActionRule.java | 10 +++ .../dao/ActionManagementDAOFacadeTest.java | 21 ++++- .../ActionManagementServiceImplTest.java | 2 +- 5 files changed, 102 insertions(+), 36 deletions(-) diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/dao/impl/ActionManagementDAOFacade.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/dao/impl/ActionManagementDAOFacade.java index 21ca6361a734..a39c89eea433 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/dao/impl/ActionManagementDAOFacade.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/dao/impl/ActionManagementDAOFacade.java @@ -87,8 +87,8 @@ public void addAction(ActionDTO actionDTO, Integer tenantId) throws ActionMgtExc // Since exceptions thrown are wrapped with TransactionException, extracting the actual cause. handleActionPropertyResolverClientException(e.getCause()); LOG.debug("Error while creating the Action of Action Type: " + actionDTO.getType().getDisplayName() + - " in Tenant Domain: " + IdentityTenantUtil.getTenantDomain(tenantId) + - ". Rolling back created action information, authentication secrets and action properties."); + " in Tenant Domain: " + IdentityTenantUtil.getTenantDomain(tenantId) + + ". Rolling back created action information, authentication secrets and action properties."); throw ActionManagementExceptionHandler.handleServerException(ErrorMessage.ERROR_WHILE_ADDING_ACTION, e); } } @@ -289,8 +289,8 @@ private void deleteAuthenticationSecrets(ActionDTO deletingActionDTO) throws Act * Add the encrypted authentication secrets and replace the input authentication properties in the ActionDTOBuilder * object. * - * @param actionDTOBuilder ActionDTOBuilder object. - * @param encryptedProperties List of encrypted AuthProperty objects. + * @param actionDTOBuilder ActionDTOBuilder object. + * @param encryptedProperties List of encrypted AuthProperty objects. */ private void addEncryptedAuthSecretsToBuilder(ActionDTOBuilder actionDTOBuilder, List encryptedProperties) { @@ -299,17 +299,17 @@ private void addEncryptedAuthSecretsToBuilder(ActionDTOBuilder actionDTOBuilder, .collect(Collectors.toMap(AuthProperty::getName, AuthProperty::getValue)); actionDTOBuilder.endpoint(new EndpointConfig.EndpointConfigBuilder() - .uri(actionDTOBuilder.getEndpoint().getUri()) - .authentication(new Authentication.AuthenticationBuilder() - .type(actionDTOBuilder.getEndpoint().getAuthentication().getType()) - .properties(encryptedPropertyMap) - .build()) - .build()); + .uri(actionDTOBuilder.getEndpoint().getUri()) + .authentication(new Authentication.AuthenticationBuilder() + .type(actionDTOBuilder.getEndpoint().getAuthentication().getType()) + .properties(encryptedPropertyMap) + .build()) + .build()); } private void addActionRule(ActionDTOBuilder actionDTOBuilder, String tenantDomain) throws ActionMgtException { - if (actionDTOBuilder.getActionRule() == null) { + if (actionDTOBuilder.getActionRule() == null || actionDTOBuilder.getActionRule().getRule() == null) { return; } @@ -341,25 +341,56 @@ private void loadActionRule(ActionDTOBuilder actionDTOBuilder, String tenantDoma } private void updateActionRule(ActionDTOBuilder updatingActionDTOBuilder, ActionDTO existingActionDTO, - String tenantDomain) - throws ActionMgtException { - - if (existingActionDTO.getActionRule() == null && updatingActionDTOBuilder.getActionRule() != null) { - // This means a new action rule is added when updating the action. + String tenantDomain) throws ActionMgtException { + + /* + When updating an action, the action rule can be added, removed or updated. + When action rule is added, the Rule object is added to the ActionRule of the ActionDTO. + When action rule is removed, the Rule object is set as null in the ActionRule of the ActionDTO. + This happens as the API accepts the removal of the rule in an action update via an empty rule JSON object. + e.g., rule: {}. If rule is not present in the payload that means rule is not updated. + When action rule is updated, the Rule object is updated in the ActionRule of the ActionDTO. + */ + if (isAddingNewActionRule(updatingActionDTOBuilder, existingActionDTO)) { addActionRule(updatingActionDTOBuilder, tenantDomain); - } else if (existingActionDTO.getActionRule() != null && updatingActionDTOBuilder.getActionRule() == null) { - // This means the existing action rule is removed when updating the action. + } else if (isRemovingExistingActionRule(updatingActionDTOBuilder, existingActionDTO)) { deleteActionRule(existingActionDTO, tenantDomain); - } else if (existingActionDTO.getActionRule() != null && updatingActionDTOBuilder.getActionRule() != - null) { // This means the existing action rule is updated when updating the action. - try { - updatingActionDTOBuilder.getActionRule().getRule().setId(existingActionDTO.getActionRule().getId()); - ActionMgtServiceComponentHolder.getInstance() - .getRuleManagementService() - .updateRule(updatingActionDTOBuilder.getActionRule().getRule(), tenantDomain); - } catch (RuleManagementException e) { - throw new ActionMgtServerException("Error while updating the Rule associated with the Action.", e); - } + } else if (isUpdatingExistingActionRule(updatingActionDTOBuilder, existingActionDTO)) { + updateExistingActionRule(updatingActionDTOBuilder, existingActionDTO, tenantDomain); + } + } + + private boolean isAddingNewActionRule(ActionDTOBuilder updatingActionDTOBuilder, ActionDTO existingActionDTO) + throws ActionMgtException { + + return existingActionDTO.getActionRule() == null && updatingActionDTOBuilder.getActionRule() != null && + updatingActionDTOBuilder.getActionRule().getRule() != null; + } + + private boolean isRemovingExistingActionRule(ActionDTOBuilder updatingActionDTOBuilder, + ActionDTO existingActionDTO) throws ActionMgtException { + + return existingActionDTO.getActionRule() != null && updatingActionDTOBuilder.getActionRule() != null && + updatingActionDTOBuilder.getActionRule().getRule() == null; + } + + private boolean isUpdatingExistingActionRule(ActionDTOBuilder updatingActionDTOBuilder, + ActionDTO existingActionDTO) throws ActionMgtException { + + return existingActionDTO.getActionRule() != null && updatingActionDTOBuilder.getActionRule() != null && + updatingActionDTOBuilder.getActionRule().getRule() != null; + } + + private void updateExistingActionRule(ActionDTOBuilder updatingActionDTOBuilder, ActionDTO existingActionDTO, + String tenantDomain) throws ActionMgtException { + + try { + updatingActionDTOBuilder.getActionRule().getRule().setId(existingActionDTO.getActionRule().getId()); + ActionMgtServiceComponentHolder.getInstance() + .getRuleManagementService() + .updateRule(updatingActionDTOBuilder.getActionRule().getRule(), tenantDomain); + } catch (RuleManagementException e) { + throw new ActionMgtServerException("Error while updating the Rule associated with the Action.", e); } } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/dao/impl/ActionManagementDAOImpl.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/dao/impl/ActionManagementDAOImpl.java index 8026085a1e9b..61aa9800ada0 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/dao/impl/ActionManagementDAOImpl.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/dao/impl/ActionManagementDAOImpl.java @@ -481,12 +481,18 @@ private void addRuleReference(ActionDTO actionDTO, Integer tenantId) throws Acti private void updateRuleReference(ActionDTO updatingActionDTO, ActionDTO existingActionDTO, Integer tenantId) throws ActionMgtServerException { - if (existingActionDTO.getActionRule() == null && updatingActionDTO.getActionRule() != null) { - // This means a new action rule is added when updating the action. Add the rule reference. - addRuleReference(updatingActionDTO, tenantId); - } else if (existingActionDTO.getActionRule() != null && updatingActionDTO.getActionRule() == null) { - // This means the existing action rule is removed when updating the action. Remove the rule reference. - deleteRuleReference(updatingActionDTO, tenantId); + try { + if (existingActionDTO.getActionRule() == null && updatingActionDTO.getActionRule() != null && + updatingActionDTO.getActionRule().getRule() != null) { + // This means a new action rule is added when updating the action. Add the rule reference. + addRuleReference(updatingActionDTO, tenantId); + } else if (existingActionDTO.getActionRule() != null && updatingActionDTO.getActionRule() != null && + updatingActionDTO.getActionRule().getRule() == null) { + // This means the existing action rule is removed when updating the action. Remove the rule reference. + deleteRuleReference(updatingActionDTO, tenantId); + } + } catch (ActionMgtException e) { + throw new ActionMgtServerException("Error while updating the reference for the Rule in Action.", e); } } diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/model/ActionRule.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/model/ActionRule.java index e84902cc178e..6f463a8567ce 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/model/ActionRule.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/model/ActionRule.java @@ -71,6 +71,16 @@ action status (active/inactive), returning state of the prior action in the chai private Rule getRuleFromRuleManagementService() throws ActionMgtServerException { + if (StringUtils.isBlank(id) || StringUtils.isBlank(tenantDomain)) { + /* + This could happen if the ActionRule is created without the Rule object. + That means the rule value is explicitly set to null. + In such cases, loading the Rule from the Rule Management Service is not expected. + This scenario is used to let the user remove a Rule from an Action via the Action update API. + */ + return null; + } + try { return ActionMgtServiceComponentHolder.getInstance() .getRuleManagementService() diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/dao/ActionManagementDAOFacadeTest.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/dao/ActionManagementDAOFacadeTest.java index a579156155b9..fc569f20ff12 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/dao/ActionManagementDAOFacadeTest.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/dao/ActionManagementDAOFacadeTest.java @@ -456,7 +456,8 @@ public void testFailureInUpdateActionUpdatingRuleAtRuleManagementException() thr @Test(priority = 18) public void testUpdateActionRemovingRule() throws Exception { - ActionDTO updatingActionDTOWithoutRule = createActionDTOForPasswordUpdateAction(); + // In order to remove the rule from ActionRule create an ActionRule with a null Rule reference. + ActionDTO updatingActionDTOWithoutRule = createActionDTOForPasswordUpdateActionWithRule(null); mockActionPropertyResolver(testActionPropertyResolver); daoFacade.updateAction(updatingActionDTOWithoutRule, actionDTORetrieved, TENANT_ID); @@ -507,6 +508,24 @@ public void testUpdateActionAddingRule() throws Exception { } @Test(priority = 21) + public void testUpdateActionWithRuleWithoutUpdatingRule() throws Exception { + + Rule rule = mockRule(); + mockRuleManagementService(rule); + ActionDTO updatingActionDTO = createActionDTOForPasswordUpdateAction(); + ActionDTO expectedActionDTO = createActionDTOForPasswordUpdateActionWithRule(rule); + mockActionPropertyResolver(testActionPropertyResolver); + + daoFacade.updateAction(updatingActionDTO, actionDTORetrieved, TENANT_ID); + + actionDTORetrieved = daoFacade.getActionByActionId(PRE_UPDATE_PASSWORD_TYPE, PRE_UPDATE_PASSWORD_ACTION_ID, + TENANT_ID); + + verifyActionDTO(actionDTORetrieved, expectedActionDTO); + verifyActionDTORule(actionDTORetrieved, expectedActionDTO); + } + + @Test(priority = 22) public void testDeleteActionWithRule() throws ActionMgtException { mockActionPropertyResolver(testActionPropertyResolver); diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/service/ActionManagementServiceImplTest.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/service/ActionManagementServiceImplTest.java index 5fcc6fc3b1ef..d6f9bfe0e7e5 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/service/ActionManagementServiceImplTest.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/test/java/org/wso2/carbon/identity/action/management/service/ActionManagementServiceImplTest.java @@ -395,7 +395,7 @@ public void testGetActionsWithRulesByActionType() throws ActionMgtException { } @Test(priority = 17) - public void testUpdateActionWithRule() throws ActionMgtException, SecretManagementException { + public void testUpdateActionUpdatingRule() throws ActionMgtException, SecretManagementException { Action updatingAction = TestUtil.buildMockActionWithRule( TEST_ACTION_NAME_UPDATED, From a1a8a19401491bd4665fb6e0db7f5b9e233e6ec6 Mon Sep 17 00:00:00 2001 From: malithie Date: Wed, 8 Jan 2025 17:51:29 +0530 Subject: [PATCH 5/5] Remove redundant method. --- .../management/util/ActionDTOBuilder.java | 43 ------------------- 1 file changed, 43 deletions(-) diff --git a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/util/ActionDTOBuilder.java b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/util/ActionDTOBuilder.java index 6f67b256098c..36564eb8ea3c 100644 --- a/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/util/ActionDTOBuilder.java +++ b/components/action-mgt/org.wso2.carbon.identity.action.management/src/main/java/org/wso2/carbon/identity/action/management/util/ActionDTOBuilder.java @@ -18,18 +18,13 @@ package org.wso2.carbon.identity.action.management.util; -import org.wso2.carbon.identity.action.management.constant.ActionMgtConstants; -import org.wso2.carbon.identity.action.management.exception.ActionMgtException; -import org.wso2.carbon.identity.action.management.exception.ActionMgtServerException; import org.wso2.carbon.identity.action.management.model.Action; import org.wso2.carbon.identity.action.management.model.ActionDTO; import org.wso2.carbon.identity.action.management.model.ActionRule; -import org.wso2.carbon.identity.action.management.model.Authentication; import org.wso2.carbon.identity.action.management.model.EndpointConfig; import java.util.HashMap; import java.util.Map; -import java.util.stream.Collectors; /** * Internal Builder class for ActionDTO. @@ -149,44 +144,6 @@ public ActionRule getActionRule() { return rule; } - public ActionDTOBuilder setEndpointAndProperties(Map properties) throws - ActionMgtException { - - Authentication authentication; - Authentication.Type authnType = - Authentication.Type.valueOf(properties.remove(ActionMgtConstants.AUTHN_TYPE_PROPERTY)); - switch (authnType) { - case BASIC: - authentication = new Authentication.BasicAuthBuilder( - properties.remove(Authentication.Property.USERNAME.getName()), - properties.remove(Authentication.Property.PASSWORD.getName())).build(); - break; - case BEARER: - authentication = new Authentication.BearerAuthBuilder( - properties.remove(Authentication.Property.ACCESS_TOKEN.getName())).build(); - break; - case API_KEY: - authentication = new Authentication.APIKeyAuthBuilder( - properties.remove(Authentication.Property.HEADER.getName()), - properties.remove(Authentication.Property.VALUE.getName())).build(); - break; - case NONE: - authentication = new Authentication.NoneAuthBuilder().build(); - break; - default: - throw new ActionMgtServerException("Authentication type is not defined for the Action Endpoint."); - } - - this.endpoint = new EndpointConfig.EndpointConfigBuilder() - .uri(properties.remove(ActionMgtConstants.URI_PROPERTY)) - .authentication(authentication) - .build(); - // Add remaining properties as action properties. - this.properties = properties.entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - return this; - } - public ActionDTOBuilder properties(Map properties) { this.properties = properties;