diff --git a/dotCMS/hotfix_tracking.md b/dotCMS/hotfix_tracking.md index 1a63a2852a48..568978c8b11f 100644 --- a/dotCMS/hotfix_tracking.md +++ b/dotCMS/hotfix_tracking.md @@ -150,4 +150,5 @@ This maintenance release includes the following code fixes: 143. https://github.com/dotCMS/core/issues/29162 : Slow performance with imports in some cases #29162 144. https://github.com/dotCMS/core/issues/28779 : IndexOutOfBoundsException in sitesearch portlet #28779 145. https://github.com/dotCMS/core/issues/29256 : Many to One Relationships Not Copied in Copy Site #29256 -146. https://github.com/dotCMS/core/issues/29392 : Saving folder names with special characters leads to incorrect encoding #29392 \ No newline at end of file +146. https://github.com/dotCMS/core/issues/29392 : Saving folder names with special characters leads to incorrect encoding #29392 +147. https://github.com/dotCMS/core/issues/29213 : Copying a contentlet without having Edit Permissions causes copy to have incorrect permissions. #29213 diff --git a/dotCMS/src/integration-test/java/com/dotcms/content/elasticsearch/business/ESContentletAPIImplTest.java b/dotCMS/src/integration-test/java/com/dotcms/content/elasticsearch/business/ESContentletAPIImplTest.java index 74cd451e32ad..0fb5054a4848 100644 --- a/dotCMS/src/integration-test/java/com/dotcms/content/elasticsearch/business/ESContentletAPIImplTest.java +++ b/dotCMS/src/integration-test/java/com/dotcms/content/elasticsearch/business/ESContentletAPIImplTest.java @@ -2801,4 +2801,47 @@ public void createContentWhichTextFieldOver255Chars_success(){ assertEquals(textOver255Chars,vanityURLCheckout.getURI()); assertEquals(textOver255Chars,vanityURLCheckout.getForwardTo()); } + + /** + * Method to test: {@link ESContentletAPIImpl#copyContentlet(Contentlet, User, boolean)} + * Given Scenario: + * Given an user with backend role that has permissions to publish, read and write contentlet (not edit permissions). + * Copying a contentlet with that user was throwing an error but copying the contentlet without the permissions previously established. + * ExpectedResult: Copy action should execute successfully with the correct permissions. + * + * @throws DotDataException + */ + @Test + public void test_copy_contentlet_without_permissions() throws DotDataException, DotSecurityException { + + final ContentletAPI contentletAPI1 = APILocator.getContentletAPIImpl(); + final List fields = new ArrayList<>(); + fields.add(new FieldDataGen().name("Title").velocityVarName("title").next()); + + final ContentType cType = createContentType("test"); + final Contentlet contentlet = new ContentletDataGen(cType.id()) + .host(APILocator.systemHost()) + .nextPersisted(); + + // create backend role + final Role backendRole = TestUserUtils.getBackendRole(); + + + // asign permissions on contentlet for backend role + addPermission(backendRole, contentlet, PermissionLevel.PUBLISH); + addPermission(backendRole, contentlet, PermissionLevel.READ); + addPermission(backendRole, contentlet, PermissionLevel.WRITE); + addPermission(backendRole, cType, PermissionLevel.WRITE); + + final User userWithBackendRole = TestUserUtils.getBackendUser(APILocator.systemHost()); + + final Contentlet respCont = contentletAPI1.copyContentlet(contentlet, userWithBackendRole, false); + + //check that the copied contentlet has publish permissions for the user with backend role + APILocator.getPermissionAPI().checkPermission(respCont, PermissionLevel.PUBLISH, userWithBackendRole); + + //check the copy + assertNotEquals(respCont.getIdentifier(), contentlet.getIdentifier()); + assertEquals(respCont.getHost(), APILocator.systemHost().getIdentifier()); + } } diff --git a/dotCMS/src/integration-test/java/com/dotcms/datagen/TestUserUtils.java b/dotCMS/src/integration-test/java/com/dotcms/datagen/TestUserUtils.java index a114c88e2dac..8a7db449fbd1 100644 --- a/dotCMS/src/integration-test/java/com/dotcms/datagen/TestUserUtils.java +++ b/dotCMS/src/integration-test/java/com/dotcms/datagen/TestUserUtils.java @@ -376,4 +376,13 @@ public static String getRandomUserId(final DotConnect dotConnect) throws DotData throw new IllegalStateException("dunno What Db 'Im running on"); } + @WrapInTransaction + public static User getBackendUser(final Host host) + throws DotDataException, DotSecurityException { + final String randEmail = RandomStringUtils.randomAlphabetic(10); + final String email = randEmail + "@dotcms.com"; + + return new UserDataGen().firstName(randEmail).lastName("Backend").emailAddress(email) + .password(randEmail).roles(getOrCreatePublisherRole(host), getFrontendRole(), getBackendRole()).nextPersisted(); + } } diff --git a/dotCMS/src/main/java/com/dotcms/content/elasticsearch/business/ESContentletAPIImpl.java b/dotCMS/src/main/java/com/dotcms/content/elasticsearch/business/ESContentletAPIImpl.java index 53618231be85..812f6c2d4281 100644 --- a/dotCMS/src/main/java/com/dotcms/content/elasticsearch/business/ESContentletAPIImpl.java +++ b/dotCMS/src/main/java/com/dotcms/content/elasticsearch/business/ESContentletAPIImpl.java @@ -5089,6 +5089,10 @@ private Contentlet checkin(final Contentlet contentletIn, this.pushSaveEvent(contentletOut, createNewVersion); } + if (contentletIn.getBoolProperty(Contentlet.IS_COPY)){ + contentletOut.setBoolProperty(Contentlet.IS_COPY, true); + } + return contentletOut; } finally { this.cleanup(contentletOut); @@ -9125,6 +9129,7 @@ public Contentlet copyContentlet(final Contentlet sourceContentlet, newContentlet.getMap().put(Contentlet.DISABLE_WORKFLOW, true); newContentlet.getMap().put(Contentlet.DONT_VALIDATE_ME, true); newContentlet.getMap().put(Contentlet.IS_COPY_CONTENTLET, true); + newContentlet.getMap().put(Contentlet.IS_COPY, true); newContentlet.setIndexPolicy(sourceContentlet.getIndexPolicy()); // Use the generated identifier if one version of this contentlet diff --git a/dotCMS/src/main/java/com/dotmarketing/business/PermissionBitAPIImpl.java b/dotCMS/src/main/java/com/dotmarketing/business/PermissionBitAPIImpl.java index f5e73e49cd79..320bac6f8b30 100644 --- a/dotCMS/src/main/java/com/dotmarketing/business/PermissionBitAPIImpl.java +++ b/dotCMS/src/main/java/com/dotmarketing/business/PermissionBitAPIImpl.java @@ -655,13 +655,7 @@ public void save(Permission permission, Permissionable permissionable, User user */ @WrapInTransaction private void save(Permission permission, Permissionable permissionable, User user, boolean respectFrontendRoles, boolean createEvent) throws DotDataException, DotSecurityException { - if(!doesUserHavePermission(permissionable, PermissionAPI.PERMISSION_EDIT_PERMISSIONS, user)) { - - if(!checkIfContentletTypeHasEditPermissions(permissionable, user)) { - throw new DotSecurityException("User id: " + user.getUserId() + " does not have permission to alter permissions on asset " + permissionable.getPermissionId()); - } - } - + checkCopyPermissions(permissionable, user); RoleAPI roleAPI = APILocator.getRoleAPI(); Role role = roleAPI.loadRoleById(permission.getRoleId()); @@ -700,6 +694,27 @@ private void save(Permission permission, Permissionable permissionable, User use } + private void checkCopyPermissions(Permissionable permissionable, User user) throws DotDataException, DotSecurityException { + + if (!isCopy(permissionable)){ + if (!doesUserHavePermission(permissionable, PermissionAPI.PERMISSION_EDIT_PERMISSIONS, user)) { + + if (!checkIfContentletTypeHasEditPermissions(permissionable, user)) { + throw new DotSecurityException("User id: " + user.getUserId() + " does not have permission to alter permissions on asset " + permissionable.getPermissionId()); + } + } + } + } + + public boolean isCopy(Permissionable permissionable){ + boolean isCopy = false; + if (permissionable instanceof Contentlet){ + Contentlet contentlet = (Contentlet) permissionable; + isCopy = contentlet.getBoolProperty(Contentlet.IS_COPY); + } + return isCopy; + } + /** * In case the permissionable is a contentlet, we try to check if the content type has edit permissions * This is applies when the doesUserHavePermission(permissionable, PermissionAPI.PERMISSION_EDIT_PERMISSIONS, user)) was called previously and has fail. diff --git a/dotCMS/src/main/java/com/dotmarketing/portlets/contentlet/model/Contentlet.java b/dotCMS/src/main/java/com/dotmarketing/portlets/contentlet/model/Contentlet.java index 658584786035..c65b1e9c1ee0 100644 --- a/dotCMS/src/main/java/com/dotmarketing/portlets/contentlet/model/Contentlet.java +++ b/dotCMS/src/main/java/com/dotmarketing/portlets/contentlet/model/Contentlet.java @@ -121,7 +121,8 @@ public class Contentlet implements Serializable, Permissionable, Categorizable, public static final String SORT_ORDER_KEY = "sortOrder"; public static final String DISABLED_WYSIWYG_KEY = "disabledWYSIWYG"; public static final String LANGUAGEID_KEY = "languageId"; - // End of reserved fields names + public static final String IS_COPY = "_is_being_copied"; + // End of reserved fields names private static final long serialVersionUID = 1L; public static final String HAS_TITLE_IMAGE_KEY = "hasTitleImage";