Skip to content

Commit

Permalink
Issue 28579 add way to bulk reset permissions (#30352)
Browse files Browse the repository at this point in the history
New feature added to allow bulk reset permissions. The approach taken
was to create a new actionlet, this way now the user can create a step
and add a sub-action to it called "Reset Permissions". This subaction
doesn't need any parameter, and to execute it the user firing the action
should have edit permissions on the contentlet.

To be able to fire this action you should add it to a step as a
sub-action:



https://github.com/user-attachments/assets/1955851c-2f93-4c00-b007-d7c7bf389901


Then (and after adding any setting that you want) you can fire the step.

Here I show a case of an unlimited user, such as admin, in where I reset
the permission of several contentlets, first I show that the contentlets
have had their permissions modified.

**Case admin user**


https://github.com/user-attachments/assets/d915a00d-89e9-47cd-95a6-9c0c2971e5d3


Then there is the case in where the user doesn't have the permissions to
edit permissions in some of the contentlets, so here I select some
contentlets and show that they have only publish permissions to the
Publisher role and some others that have the edit permissions, these
last contentlets are going to be the ones that executes the action
successfully.

**Case limited user**


https://github.com/user-attachments/assets/4269bec3-7d2e-41ea-8c21-8a41ebc0b2d5



**Note***: Apart from the issue, an error was found and fixed. When
firing some action, **if the first contentlet throws a fail**, the flow
doesn't keep trying to execute the action in the remaining contentlets
because it was throwing an exception unrelated to the action itself,
here I show how was the error happening with another subaction (Unlock)



https://github.com/user-attachments/assets/8cde0540-c36b-4d1f-b6cb-4aa33434a6c5

---------

Co-authored-by: erickgonzalez <[email protected]>
  • Loading branch information
gortiz-dotcms and erickgonzalez authored Oct 17, 2024
1 parent 8d0762a commit e42386d
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,7 @@ public EventOutput fireBulkActions(@Context final HttpServletRequest request,
eventBuilder.name("failure");
eventBuilder.data(Map.class,
Map.of("failure", inode));
eventBuilder.mediaType(MediaType.APPLICATION_JSON_TYPE);
final OutboundEvent event = eventBuilder.build();
try {
eventOutput.write(event);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,7 @@ private void checkCopyPermissions(Permissionable permissionable, User user) thro
* @return boolean
* @throws DotDataException
*/
private boolean checkIfContentletTypeHasEditPermissions(final Permissionable permissionable, final User user) throws DotDataException {
public boolean checkIfContentletTypeHasEditPermissions(final Permissionable permissionable, final User user) throws DotDataException {

return permissionable instanceof Contentlet? // we can check if the content type has edit permissions
doesUserHavePermission(Contentlet.class.cast(permissionable).getContentType(), PermissionAPI.PERMISSION_EDIT_PERMISSIONS, user):false;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.dotmarketing.portlets.workflows.actionlet;

import com.dotmarketing.business.APILocator;
import com.dotmarketing.business.PermissionAPI;
import com.dotmarketing.business.PermissionBitAPIImpl;
import com.dotmarketing.business.Permissionable;
import com.dotmarketing.business.ajax.PermissionAjax;
import com.dotmarketing.exception.DotDataException;
import com.dotmarketing.exception.DotSecurityException;
import com.dotmarketing.portlets.workflows.model.WorkflowActionClassParameter;
import com.dotmarketing.portlets.workflows.model.WorkflowActionFailureException;
import com.dotmarketing.portlets.workflows.model.WorkflowActionletParameter;
import com.dotmarketing.portlets.workflows.model.WorkflowProcessor;
import com.dotmarketing.util.Logger;
import com.liferay.portal.model.User;

import java.util.List;
import java.util.Map;


/**
* This Actionlet allows the user to reset the permissions of a contentlet
* The user must have edit permissions to fire this action.
*/
public class ResetPermissionsActionlet extends WorkFlowActionlet{
@Override
public List<WorkflowActionletParameter> getParameters() {
return List.of();
}

@Override
public String getName() {
return "Reset Permissions";
}

@Override
public String getHowTo() {
return "This actionlet will reset permissions of the selected contentlets. It does not require any parameters.";
}

/*
* This method will reset the permissions of the contentlet after checking that the user has edit permissions to modify it.
* */
@Override
public void executeAction(WorkflowProcessor processor, Map<String, WorkflowActionClassParameter> params) throws WorkflowActionFailureException {

try {

User user = processor.getUser();

PermissionAPI permissionAPI = APILocator.getPermissionAPI();
PermissionBitAPIImpl api = (PermissionBitAPIImpl) APILocator.getPermissionAPI();
Permissionable asset = processor.getContentlet();
if (!user.isAdmin() && !api.doesUserHavePermission(asset, PermissionAPI.PERMISSION_EDIT_PERMISSIONS, user) &&
!api.checkIfContentletTypeHasEditPermissions(asset, user)) {

throw new DotSecurityException("User id: " + user.getUserId() + " does not have permission to alter permissions on asset " + asset.getPermissionId());
}
permissionAPI.removePermissions(asset);
} catch ( Exception e) {
Logger.debug(ResetPermissionsActionlet.class, e.getMessage());
throw new WorkflowActionFailureException(e.getMessage(), e);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,39 +71,7 @@
import com.dotmarketing.portlets.structure.model.Structure;
import com.dotmarketing.portlets.workflows.LargeMessageActionlet;
import com.dotmarketing.portlets.workflows.MessageActionlet;
import com.dotmarketing.portlets.workflows.actionlet.Actionlet;
import com.dotmarketing.portlets.workflows.actionlet.ArchiveContentActionlet;
import com.dotmarketing.portlets.workflows.actionlet.AsyncEmailActionlet;
import com.dotmarketing.portlets.workflows.actionlet.BatchAction;
import com.dotmarketing.portlets.workflows.actionlet.CheckURLAccessibilityActionlet;
import com.dotmarketing.portlets.workflows.actionlet.CheckinContentActionlet;
import com.dotmarketing.portlets.workflows.actionlet.CheckoutContentActionlet;
import com.dotmarketing.portlets.workflows.actionlet.CommentOnWorkflowActionlet;
import com.dotmarketing.portlets.workflows.actionlet.CopyActionlet;
import com.dotmarketing.portlets.workflows.actionlet.DeleteContentActionlet;
import com.dotmarketing.portlets.workflows.actionlet.DestroyContentActionlet;
import com.dotmarketing.portlets.workflows.actionlet.EmailActionlet;
import com.dotmarketing.portlets.workflows.actionlet.FourEyeApproverActionlet;
import com.dotmarketing.portlets.workflows.actionlet.MoveContentActionlet;
import com.dotmarketing.portlets.workflows.actionlet.MultipleApproverActionlet;
import com.dotmarketing.portlets.workflows.actionlet.NotifyAssigneeActionlet;
import com.dotmarketing.portlets.workflows.actionlet.NotifyUsersActionlet;
import com.dotmarketing.portlets.workflows.actionlet.PublishContentActionlet;
import com.dotmarketing.portlets.workflows.actionlet.PushNowActionlet;
import com.dotmarketing.portlets.workflows.actionlet.PushPublishActionlet;
import com.dotmarketing.portlets.workflows.actionlet.ReindexContentActionlet;
import com.dotmarketing.portlets.workflows.actionlet.ResetApproversActionlet;
import com.dotmarketing.portlets.workflows.actionlet.ResetTaskActionlet;
import com.dotmarketing.portlets.workflows.actionlet.SaveContentActionlet;
import com.dotmarketing.portlets.workflows.actionlet.SaveContentAsDraftActionlet;
import com.dotmarketing.portlets.workflows.actionlet.SendFormEmailActionlet;
import com.dotmarketing.portlets.workflows.actionlet.SetValueActionlet;
import com.dotmarketing.portlets.workflows.actionlet.TranslationActionlet;
import com.dotmarketing.portlets.workflows.actionlet.TwitterActionlet;
import com.dotmarketing.portlets.workflows.actionlet.UnarchiveContentActionlet;
import com.dotmarketing.portlets.workflows.actionlet.UnpublishContentActionlet;
import com.dotmarketing.portlets.workflows.actionlet.VelocityScriptActionlet;
import com.dotmarketing.portlets.workflows.actionlet.WorkFlowActionlet;
import com.dotmarketing.portlets.workflows.actionlet.*;
import com.dotmarketing.portlets.workflows.model.SystemActionWorkflowActionMapping;
import com.dotmarketing.portlets.workflows.model.WorkflowAction;
import com.dotmarketing.portlets.workflows.model.WorkflowActionClass;
Expand Down Expand Up @@ -264,6 +232,7 @@ public WorkflowAPIImpl() {
NotifyAssigneeActionlet.class,
UnarchiveContentActionlet.class,
ResetTaskActionlet.class,
ResetPermissionsActionlet.class,
MultipleApproverActionlet.class,
FourEyeApproverActionlet.class,
TwitterActionlet.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,11 @@
import static com.dotmarketing.portlets.workflows.util.WorkflowImportExportUtil.ACTION_ORDER;
import static com.dotmarketing.portlets.workflows.util.WorkflowImportExportUtil.STEP_ID;
import static com.dotmarketing.portlets.workflows.util.WorkflowImportExportUtil.getInstance;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.*;

import com.dotcms.contenttype.business.ContentTypeAPI;
import com.dotcms.contenttype.business.FieldAPI;
Expand All @@ -58,12 +52,9 @@
import com.dotcms.contenttype.model.field.WysiwygField;
import com.dotcms.contenttype.model.type.BaseContentType;
import com.dotcms.contenttype.model.type.ContentType;
import com.dotcms.datagen.CategoryDataGen;
import com.dotcms.datagen.RoleDataGen;
import com.dotcms.datagen.TestDataUtils;
import com.dotcms.datagen.TestUserUtils;
import com.dotcms.datagen.TestWorkflowUtils;
import com.dotcms.datagen.WorkflowDataGen;
import com.dotcms.contenttype.model.type.ContentTypeBuilder;
import com.dotcms.contenttype.model.type.SimpleContentType;
import com.dotcms.datagen.*;
import com.dotcms.mock.request.MockAttributeRequest;
import com.dotcms.mock.request.MockHeaderRequest;
import com.dotcms.mock.request.MockHttpRequestIntegrationTest;
Expand All @@ -79,38 +70,31 @@
import com.dotcms.rest.api.v1.authentication.ResponseUtil;
import com.dotcms.util.CollectionsUtils;
import com.dotcms.util.IntegrationTestInitService;
import com.dotcms.workflow.form.BulkActionForm;
import com.dotcms.workflow.form.FireActionForm;
import com.dotcms.workflow.form.FireBulkActionsForm;
import com.dotcms.workflow.form.WorkflowActionForm;
import com.dotcms.workflow.form.WorkflowActionStepForm;
import com.dotcms.workflow.form.WorkflowSchemeForm;
import com.dotcms.workflow.form.WorkflowSchemeImportObjectForm;
import com.dotcms.workflow.form.WorkflowStepUpdateForm;
import com.dotcms.workflow.form.*;
import com.dotcms.workflow.helper.WorkflowHelper;
import com.dotmarketing.beans.Host;
import com.dotmarketing.beans.Permission;
import com.dotmarketing.business.APILocator;
import com.dotmarketing.business.PermissionAPI;
import com.dotmarketing.business.Role;
import com.dotmarketing.business.RoleAPI;
import com.dotmarketing.business.*;
import com.dotmarketing.common.reindex.ReindexQueueAPI;
import com.dotmarketing.common.reindex.ReindexThread;
import com.dotmarketing.exception.AlreadyExistException;
import com.dotmarketing.exception.DotDataException;
import com.dotmarketing.exception.DotSecurityException;
import com.dotmarketing.portlets.categories.model.Category;
import com.dotmarketing.portlets.contentlet.business.ContentletAPI;
import com.dotmarketing.portlets.contentlet.business.DotContentletValidationException;
import com.dotmarketing.portlets.contentlet.business.HostAPI;
import com.dotmarketing.portlets.contentlet.model.Contentlet;
import com.dotmarketing.portlets.contentlet.model.ContentletDependencies;
import com.dotmarketing.portlets.contentlet.model.IndexPolicy;
import com.dotmarketing.portlets.folders.business.FolderAPI;
import com.dotmarketing.portlets.languagesmanager.business.LanguageAPI;
import com.dotmarketing.portlets.workflows.actionlet.MoveContentActionlet;
import com.dotmarketing.portlets.workflows.actionlet.ResetPermissionsActionlet;
import com.dotmarketing.portlets.workflows.business.BaseWorkflowIntegrationTest;
import com.dotmarketing.portlets.workflows.business.WorkflowAPI;
import com.dotmarketing.portlets.workflows.business.WorkflowAPI.SystemAction;
import com.dotmarketing.portlets.workflows.model.WorkflowAction;
import com.dotmarketing.portlets.workflows.model.WorkflowScheme;
import com.dotmarketing.portlets.workflows.model.WorkflowState;
import com.dotmarketing.portlets.workflows.model.WorkflowStep;
import com.dotmarketing.portlets.workflows.model.*;
import com.dotmarketing.portlets.workflows.util.WorkflowImportExportUtil;
import com.dotmarketing.util.DateUtil;
import com.dotmarketing.util.Logger;
Expand Down Expand Up @@ -147,6 +131,8 @@
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import com.liferay.portal.util.PortalUtil;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.commons.lang.RandomStringUtils;
Expand All @@ -155,6 +141,8 @@
import org.glassfish.jersey.media.multipart.ContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataBodyPart;
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.glassfish.jersey.media.sse.EventOutput;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
Expand All @@ -177,6 +165,7 @@ public class WorkflowResourceIntegrationTest extends BaseWorkflowIntegrationTest
private static Role systemRole;

static private WorkflowScheme testScheme;


@BeforeClass
public static void prepare() throws Exception {
Expand Down Expand Up @@ -2419,6 +2408,9 @@ public void testFireActionDefault_ContentWithCategories_Success() throws Excepti
}
}




private ContentType createCategoryFieldContentType(final String parentCategoryInode)
throws Exception {
ContentType contentType;
Expand Down Expand Up @@ -2605,4 +2597,7 @@ private static HttpServletRequest getHttpRequest() {

return request;
}

}


Loading

0 comments on commit e42386d

Please sign in to comment.