diff --git a/src/main/java/com/marklogic/appdeployer/command/security/DeployRolesCommand.java b/src/main/java/com/marklogic/appdeployer/command/security/DeployRolesCommand.java index 0749c6845..c1c4148ea 100644 --- a/src/main/java/com/marklogic/appdeployer/command/security/DeployRolesCommand.java +++ b/src/main/java/com/marklogic/appdeployer/command/security/DeployRolesCommand.java @@ -6,6 +6,7 @@ import com.marklogic.appdeployer.command.CommandContext; import com.marklogic.appdeployer.command.SortOrderConstants; import com.marklogic.appdeployer.command.SupportsCmaCommand; +import com.marklogic.mgmt.PayloadParser; import com.marklogic.mgmt.SaveReceipt; import com.marklogic.mgmt.api.configuration.Configuration; import com.marklogic.mgmt.api.configuration.Configurations; @@ -19,8 +20,7 @@ import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; +import java.util.*; /** * As of 3.15.0, this no longer deploys roles in two phases. This is due to the new sorting class, which uses a @@ -34,6 +34,7 @@ public class DeployRolesCommand extends AbstractResourceCommand implements SupportsCmaCommand { private ObjectNodesSorter objectNodesSorter = new RoleObjectNodesSorter(); + private Set defaultRolesToNotUndeploy; public DeployRolesCommand() { setExecuteSortOrder(SortOrderConstants.DEPLOY_ROLES); @@ -42,6 +43,10 @@ public DeployRolesCommand() { setSupportsResourceMerging(true); setResourceIdPropertyName("role-name"); setResourceClassType(Role.class); + + defaultRolesToNotUndeploy = new HashSet<>(); + // "admin" is the main one to never delete, throwing in a couple other sensible ones too + defaultRolesToNotUndeploy.addAll(Arrays.asList("admin", "manage-admin", "security")); } /** @@ -179,8 +184,28 @@ protected ResourceManager getResourceManager(CommandContext context) { return new RoleManager(context.getManageClient()); } + @Override + protected String adjustPayloadBeforeDeletingResource(ResourceManager mgr, CommandContext context, File f, String payload) { + String roleName = new PayloadParser().getPayloadFieldValue(payload, "role-name", false); + + if (roleName != null && defaultRolesToNotUndeploy != null && defaultRolesToNotUndeploy.contains(roleName)) { + logger.info(format("Not undeploying role '%s' because it's in the list of role names to not undeploy", roleName)); + return null; + } + + return super.adjustPayloadBeforeDeletingResource(mgr, context, f, payload); + } + public void setObjectNodesSorter(ObjectNodesSorter objectNodesSorter) { this.objectNodesSorter = objectNodesSorter; } + + public Set getDefaultRolesToNotUndeploy() { + return defaultRolesToNotUndeploy; + } + + public void setDefaultRolesToNotUndeploy(Set defaultRolesToNotUndeploy) { + this.defaultRolesToNotUndeploy = defaultRolesToNotUndeploy; + } } diff --git a/src/test/java/com/marklogic/appdeployer/command/security/DontUndeployCertainRolesTest.java b/src/test/java/com/marklogic/appdeployer/command/security/DontUndeployCertainRolesTest.java new file mode 100644 index 000000000..9134d2b45 --- /dev/null +++ b/src/test/java/com/marklogic/appdeployer/command/security/DontUndeployCertainRolesTest.java @@ -0,0 +1,52 @@ +package com.marklogic.appdeployer.command.security; + +import com.marklogic.appdeployer.AbstractAppDeployerTest; +import com.marklogic.mgmt.resource.security.RoleManager; +import com.marklogic.mgmt.resource.security.UserManager; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +public class DontUndeployCertainRolesTest extends AbstractAppDeployerTest { + + @Test + public void test() { + final String testRole = "ml-app-deployer-test-role"; + final String adminRole = "admin"; + + appConfig.getFirstConfigDir().setBaseDir(new File("src/test/resources/sample-app/users-to-not-undeploy")); + initializeAppDeployer(new DeployRolesCommand()); + + RoleManager mgr = new RoleManager(manageClient); + assertFalse(mgr.exists(testRole)); + assertTrue(mgr.exists(adminRole)); + + deploySampleApp(); + + try { + assertTrue(mgr.exists(testRole)); + assertTrue(mgr.exists(adminRole)); + } finally { + undeploySampleApp(); + + assertFalse(mgr.exists(testRole)); + assertTrue(mgr.exists(adminRole), "The 'admin' role should not have been deleted since it's in the list of " + + "roles to not undeploy"); + } + } + + @Test + public void verifySetOfDefaultRoles() { + Set roles = new DeployRolesCommand().getDefaultRolesToNotUndeploy(); + assertEquals(3, roles.size(), "The main role we don't want to delete is admin, but manage-admin and " + + "security are included as well just to be safe, as those two roles together can allow for any other " + + "role to be recreated"); + assertTrue(roles.contains("admin")); + assertTrue(roles.contains("manage-admin")); + assertTrue(roles.contains("security")); + } + +} diff --git a/src/test/resources/sample-app/users-to-not-undeploy/security/roles/admin.json b/src/test/resources/sample-app/users-to-not-undeploy/security/roles/admin.json new file mode 100644 index 000000000..83d6d5195 --- /dev/null +++ b/src/test/resources/sample-app/users-to-not-undeploy/security/roles/admin.json @@ -0,0 +1,4 @@ +{ + "role-name": "admin", + "description": "Not clear why someone would try to modify this role, but just in case, we don't want to delete it on undeploy" +} diff --git a/src/test/resources/sample-app/users-to-not-undeploy/security/roles/testRole.json b/src/test/resources/sample-app/users-to-not-undeploy/security/roles/testRole.json new file mode 100644 index 000000000..cf941fb5d --- /dev/null +++ b/src/test/resources/sample-app/users-to-not-undeploy/security/roles/testRole.json @@ -0,0 +1,4 @@ +{ + "role-name": "ml-app-deployer-test-role", + "description": "This is here to make sure we can delete it" +}