diff --git a/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-test/xwiki-platform-flamingo-skin-test-docker/src/test/it/org/xwiki/flamingo/test/docker/ViewersIT.java b/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-test/xwiki-platform-flamingo-skin-test-docker/src/test/it/org/xwiki/flamingo/test/docker/ViewersIT.java index 02e979b11ea2..467446ce8cb8 100644 --- a/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-test/xwiki-platform-flamingo-skin-test-docker/src/test/it/org/xwiki/flamingo/test/docker/ViewersIT.java +++ b/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-test/xwiki-platform-flamingo-skin-test-docker/src/test/it/org/xwiki/flamingo/test/docker/ViewersIT.java @@ -28,9 +28,13 @@ import org.xwiki.flamingo.skin.test.po.SiblingsPage; import org.xwiki.livedata.test.po.TableLayoutElement; import org.xwiki.model.reference.DocumentReference; +import org.xwiki.repository.test.SolrTestUtils; +import org.xwiki.test.docker.junit5.TestConfiguration; import org.xwiki.test.docker.junit5.TestReference; import org.xwiki.test.docker.junit5.UITest; import org.xwiki.test.ui.TestUtils; +import org.xwiki.test.ui.po.CopyOrRenameOrDeleteStatusPage; +import org.xwiki.test.ui.po.RenamePage; import static org.hamcrest.CoreMatchers.hasItem; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -106,7 +110,8 @@ void siblings(TestUtils testUtils, TestReference testReference) */ @Test @Order(2) - void children(TestUtils testUtils, TestReference testReference) + void children(TestUtils testUtils, TestReference testReference, TestConfiguration testConfiguration) + throws Exception { DocumentReference childADocumentReference = new DocumentReference("ChildA", testReference.getLastSpaceReference()); @@ -120,7 +125,7 @@ void children(TestUtils testUtils, TestReference testReference) testUtils.createPage(childBDocumentReference, "", "ChildB"); testUtils.createUser(EDIT_USER, EDIT_USER, null); testUtils.loginAsSuperAdmin(); - testUtils.setRights(testReference, null, EDIT_USER, "edit", true); + testUtils.setRightsOnSpace(testReference.getLastSpaceReference(), null, EDIT_USER, "edit,delete", true); testUtils.forceGuestUser(); testUtils.gotoPage(testReference); @@ -147,7 +152,8 @@ void children(TestUtils testUtils, TestReference testReference) TableLayoutElement adminTableLayoutElement = ChildrenPage.goToPage(testReference).getLiveData().getTableLayout(); - assertEquals(2, adminTableLayoutElement.countRows()); + // WebPreferences is visible by superadmin + assertEquals(3, adminTableLayoutElement.countRows()); adminTableLayoutElement .assertCellWithLink(ChildrenPage.LIVE_DATA_TITLE, "ChildA", testUtils.getURL(childADocumentReference)); adminTableLayoutElement @@ -166,13 +172,15 @@ void children(TestUtils testUtils, TestReference testReference) assertTrue(childrenPage.hasTabs()); PinnedChildPagesTab pinnedChildPagesTab = childrenPage.openPinnedChildPagesTab(); - assertEquals(List.of("ChildA", "ChildB"), pinnedChildPagesTab.getNavigationTree().getTopLevelPages()); + assertEquals(List.of("ChildA", "ChildB"), + pinnedChildPagesTab.getNavigationTree().getTopLevelPages()); assertFalse(pinnedChildPagesTab.isPinned("ChildA")); assertFalse(pinnedChildPagesTab.isPinned("ChildB")); pinnedChildPagesTab.dragBefore("ChildB", "ChildA"); assertTrue(pinnedChildPagesTab.isPinned("ChildB")); pinnedChildPagesTab.save(); + childrenPage = ChildrenPage.goToPage(testReference); pinnedChildPagesTab = childrenPage.openPinnedChildPagesTab(); assertEquals(List.of("ChildB", "ChildA"), pinnedChildPagesTab.getNavigationTree().getTopLevelPages()); @@ -187,5 +195,34 @@ void children(TestUtils testUtils, TestReference testReference) assertEquals(List.of("ChildA", "ChildB"), pinnedChildPagesTab.getNavigationTree().getTopLevelPages()); assertFalse(pinnedChildPagesTab.isPinned("ChildA")); assertFalse(pinnedChildPagesTab.isPinned("ChildB")); + + pinnedChildPagesTab.pinPage("ChildB"); + assertTrue(pinnedChildPagesTab.isPinned("ChildA")); + assertTrue(pinnedChildPagesTab.isPinned("ChildB")); + pinnedChildPagesTab.save(); + + testUtils.loginAsSuperAdmin(); + new SolrTestUtils(testUtils, testConfiguration.getServletEngine()).waitEmptyQueue(); + childrenPage = ChildrenPage.goToPage(testReference); + pinnedChildPagesTab = childrenPage.openPinnedChildPagesTab(); + assertEquals(List.of("ChildA", "ChildB", "WebPreferences"), + pinnedChildPagesTab.getNavigationTree().getTopLevelPages()); + assertTrue(pinnedChildPagesTab.isPinned("ChildA")); + assertTrue(pinnedChildPagesTab.isPinned("ChildB")); + assertFalse(pinnedChildPagesTab.isPinned("WebPreferences")); + + testUtils.login(EDIT_USER, EDIT_USER); + // Check that pinned page are preserved after a move + RenamePage renamePage = testUtils.gotoPage(testReference).rename(); + renamePage.setPreserveChildren(true); + renamePage.getDocumentPicker().setTitle("ChildrenRenamed"); + CopyOrRenameOrDeleteStatusPage statusPage = renamePage.clickRenameButton().waitUntilFinished(); + statusPage.gotoNewPage(); + childrenPage = ChildrenPage.clickOnChildrenMenu(); + + pinnedChildPagesTab = childrenPage.openPinnedChildPagesTab(); + assertEquals(List.of("ChildA", "ChildB"), pinnedChildPagesTab.getNavigationTree().getTopLevelPages()); + assertTrue(pinnedChildPagesTab.isPinned("ChildA")); + assertTrue(pinnedChildPagesTab.isPinned("ChildB")); } } diff --git a/xwiki-platform-core/xwiki-platform-refactoring/xwiki-platform-refactoring-api/src/main/java/org/xwiki/refactoring/internal/job/AbstractCopyOrMoveJob.java b/xwiki-platform-core/xwiki-platform-refactoring/xwiki-platform-refactoring-api/src/main/java/org/xwiki/refactoring/internal/job/AbstractCopyOrMoveJob.java index 419d32fd19f0..917a14813fb7 100644 --- a/xwiki-platform-core/xwiki-platform-refactoring/xwiki-platform-refactoring-api/src/main/java/org/xwiki/refactoring/internal/job/AbstractCopyOrMoveJob.java +++ b/xwiki-platform-core/xwiki-platform-refactoring/xwiki-platform-refactoring-api/src/main/java/org/xwiki/refactoring/internal/job/AbstractCopyOrMoveJob.java @@ -21,9 +21,7 @@ import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.function.BiConsumer; -import java.util.stream.Collectors; import org.xwiki.model.EntityType; import org.xwiki.model.reference.DocumentReference; @@ -309,16 +307,36 @@ protected void process(final SpaceReference source, final SpaceReference destina protected boolean checkAllRights(DocumentReference oldReference, DocumentReference newReference) throws Exception { - if (!hasAccess(Right.VIEW, oldReference)) { - this.logger.error("You don't have sufficient permissions over the source document [{}].", oldReference); - return false; - } else if (!hasAccess(Right.EDIT, newReference) - || (this.modelBridge.exists(newReference) && !hasAccess(Right.DELETE, newReference))) { - this.logger.error("You don't have sufficient permissions over the destination document [{}].", - newReference); - return false; + boolean isWebPreferences = + isSpacePreferencesReference(oldReference) && isSpacePreferencesReference(newReference); + DocumentReference oldHomeReference = getSpaceHomeReference(oldReference); + boolean result = true; + // if we're trying to move a WebPreferences not as part of moving a WebHome we treat it with standard rights + // like we do for other pages. + if (!isWebPreferences || !this.concernedEntities.containsKey(oldHomeReference)) { + if (!hasAccess(Right.VIEW, oldReference)) { + this.logger.error("You don't have sufficient permissions over the source document [{}].", oldReference); + result = false; + } else if (!hasAccess(Right.EDIT, newReference) + || (this.modelBridge.exists(newReference) && !hasAccess(Right.DELETE, newReference))) { + this.logger.error("You don't have sufficient permissions over the destination document [{}].", + newReference); + result = false; + } + // else if we're moving a WebHome as part of moving its WebHome we check rights on the space itself. + // Note that we don't perform other checks regarding overwriting because WebPreferences is checked last: + // - if there's only a WebPreferences document alone in the destination then it's ok to overwrite it as + // we're creating a new space (since we move WebHome) + // - if we're overwriting also WebHome then we already performed the check if it's allowed to overwrite it + } else { + if (!hasAccess(Right.EDIT, oldHomeReference)) { + this.logger.error("You don't have sufficient permissions over the home document of WebPreferences " + + "[{}].", + newReference); + result = false; + } } - return true; + return result; } protected void maybePerformRefactoring(DocumentReference oldReference, DocumentReference newReference) @@ -352,14 +370,14 @@ protected boolean copyOrMove(DocumentReference oldReference, DocumentReference n // Step 2: Delete the destination document if needed. this.progressManager.startStep(this); - if (this.modelBridge.exists(newReference)) { - if (this.request.isInteractive() && !this.modelBridge.canOverwriteSilently(newReference) - && !confirmOverwrite(oldReference, newReference)) { - this.logger.warn( - "Skipping [{}] because [{}] already exists and the user doesn't want to overwrite it.", - oldReference, newReference); - return false; - } + if (this.modelBridge.exists(newReference) + && this.request.isInteractive() + && !this.modelBridge.canOverwriteSilently(newReference) + && !confirmOverwrite(oldReference, newReference)) { + this.logger.warn( + "Skipping [{}] because [{}] already exists and the user doesn't want to overwrite it.", + oldReference, newReference); + return false; } this.progressManager.endStep(this); @@ -416,19 +434,6 @@ protected EntityReference getCommonParent() return getCommonParent(entityReferences); } - /** - * @return the list of references that have been selected to be refactored. - * @since 16.10.0RC1 - */ - public Map getSelectedEntities() - { - return this.concernedEntities.values().stream() - .filter(EntitySelection::isSelected) - .filter(entity -> entity.getTargetEntityReference().isPresent()) - .collect(Collectors.toMap(EntitySelection::getEntityReference, - entity -> entity.getTargetEntityReference().get())); - } - /** * Atomic operation to perform: should be a rename for Rename/Move and copy for Copy. * @param source the source reference diff --git a/xwiki-platform-core/xwiki-platform-refactoring/xwiki-platform-refactoring-api/src/main/java/org/xwiki/refactoring/internal/job/AbstractEntityJob.java b/xwiki-platform-core/xwiki-platform-refactoring/xwiki-platform-refactoring-api/src/main/java/org/xwiki/refactoring/internal/job/AbstractEntityJob.java index 9d3196a97569..693caf3056d9 100644 --- a/xwiki-platform-core/xwiki-platform-refactoring/xwiki-platform-refactoring-api/src/main/java/org/xwiki/refactoring/internal/job/AbstractEntityJob.java +++ b/xwiki-platform-core/xwiki-platform-refactoring/xwiki-platform-refactoring-api/src/main/java/org/xwiki/refactoring/internal/job/AbstractEntityJob.java @@ -69,9 +69,9 @@ public interface Visitor void visit(T node); } - private static final JobGroupPath ROOT_GROUP = new JobGroupPath(RefactoringJobs.GROUP, null); + protected static final String PREFERENCES_DOCUMENT_NAME = "WebPreferences"; - private static final String PREFERENCES_DOCUMENT_NAME = "WebPreferences"; + private static final JobGroupPath ROOT_GROUP = new JobGroupPath(RefactoringJobs.GROUP, null); /** * The component used to access the XWiki model and to perform low level operations on it. @@ -261,7 +261,13 @@ protected boolean isSpaceHomeReference(DocumentReference documentReference) .equals(this.defaultEntityReferenceProvider.getDefaultReference(documentReference.getType()).getName()); } - private boolean isSpacePreferencesReference(EntityReference entityReference) + protected DocumentReference getSpaceHomeReference(DocumentReference reference) + { + String referenceName = this.defaultEntityReferenceProvider.getDefaultReference(EntityType.DOCUMENT).getName(); + return new DocumentReference(referenceName, reference.getLastSpaceReference()); + } + + protected boolean isSpacePreferencesReference(EntityReference entityReference) { return entityReference.getType() == EntityType.DOCUMENT && PREFERENCES_DOCUMENT_NAME.equals(entityReference.getName()); diff --git a/xwiki-platform-core/xwiki-platform-refactoring/xwiki-platform-refactoring-api/src/main/java/org/xwiki/refactoring/internal/job/AbstractEntityJobWithChecks.java b/xwiki-platform-core/xwiki-platform-refactoring/xwiki-platform-refactoring-api/src/main/java/org/xwiki/refactoring/internal/job/AbstractEntityJobWithChecks.java index 5bbf26f9daf3..0c03ddff5002 100644 --- a/xwiki-platform-core/xwiki-platform-refactoring/xwiki-platform-refactoring-api/src/main/java/org/xwiki/refactoring/internal/job/AbstractEntityJobWithChecks.java +++ b/xwiki-platform-core/xwiki-platform-refactoring/xwiki-platform-refactoring-api/src/main/java/org/xwiki/refactoring/internal/job/AbstractEntityJobWithChecks.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.stream.Collectors; import org.xwiki.bridge.event.DocumentsDeletingEvent; import org.xwiki.model.reference.DocumentReference; @@ -63,6 +64,8 @@ protected void runInternal() throws Exception // Process progressManager.startStep(this); setContextUser(); + + // FIXME: this should probably be the selected entities only... process(entityReferences); } } finally { @@ -70,6 +73,21 @@ protected void runInternal() throws Exception } } + /** + * @return the list of references that have been selected to be refactored. + * @since 17.2.0RC1 + * @since 16.10.5 + * @since 16.4.7 + */ + public Map getSelectedEntities() + { + return this.concernedEntities.values().stream() + .filter(EntitySelection::isSelected) + .filter(entity -> entity.getTargetEntityReference().isPresent()) + .collect(Collectors.toMap(EntitySelection::getEntityReference, + entity -> entity.getTargetEntityReference().get())); + } + protected void getEntities(Collection entityReferences) { this.progressManager.pushLevelProgress(entityReferences.size(), this); diff --git a/xwiki-platform-core/xwiki-platform-refactoring/xwiki-platform-refactoring-api/src/main/java/org/xwiki/refactoring/internal/job/MoveJob.java b/xwiki-platform-core/xwiki-platform-refactoring/xwiki-platform-refactoring-api/src/main/java/org/xwiki/refactoring/internal/job/MoveJob.java index a5f2513b5c9e..8edf27c46a5f 100644 --- a/xwiki-platform-core/xwiki-platform-refactoring/xwiki-platform-refactoring-api/src/main/java/org/xwiki/refactoring/internal/job/MoveJob.java +++ b/xwiki-platform-core/xwiki-platform-refactoring/xwiki-platform-refactoring-api/src/main/java/org/xwiki/refactoring/internal/job/MoveJob.java @@ -77,7 +77,20 @@ protected void getEntities(Collection entityReferences) @Override protected boolean checkAllRights(DocumentReference oldReference, DocumentReference newReference) throws Exception { - if (!hasAccess(Right.DELETE, oldReference)) { + boolean isWebPreferences = + isSpacePreferencesReference(oldReference) && isSpacePreferencesReference(newReference); + DocumentReference oldHomeReference = getSpaceHomeReference(oldReference); + // if it's a WebPreferences document and the operation concerns also its WebHome then we check the rights of the + // WebHome only. + if (isWebPreferences && this.concernedEntities.containsKey(oldHomeReference)) { + if (!hasAccess(Right.DELETE, oldHomeReference)) { + this.logger.error("You don't have sufficient permissions over the home document of WebPreferences " + + "[{}].", + newReference); + return false; + } + return super.checkAllRights(oldReference, newReference); + } else if (!hasAccess(Right.DELETE, oldReference)) { // The move operation is implemented as Copy + Delete. this.logger.error("You are not allowed to delete [{}].", oldReference); return false; diff --git a/xwiki-platform-core/xwiki-platform-refactoring/xwiki-platform-refactoring-api/src/test/java/org/xwiki/refactoring/internal/job/MoveJobTest.java b/xwiki-platform-core/xwiki-platform-refactoring/xwiki-platform-refactoring-api/src/test/java/org/xwiki/refactoring/internal/job/MoveJobTest.java index 6f611010bbaa..b9b099afb6f0 100644 --- a/xwiki-platform-core/xwiki-platform-refactoring/xwiki-platform-refactoring-api/src/test/java/org/xwiki/refactoring/internal/job/MoveJobTest.java +++ b/xwiki-platform-core/xwiki-platform-refactoring/xwiki-platform-refactoring-api/src/test/java/org/xwiki/refactoring/internal/job/MoveJobTest.java @@ -58,6 +58,7 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mockConstruction; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -491,6 +492,205 @@ otherDocFromSpace, new EntitySelection(otherDocFromSpace, targetDoc2) assertEquals("Skipping [chess:A.B.C.WebHome] because it doesn't exist.", getLogCapture().getMessage(0)); } + /* + * This test scenario checks performing a move of the space chess:A.B.C that contains docs WebHome, X et Y and a + * WebPreference to wiki tennis. + * The resulting references are tennis:C.X and tennis:C.Y. Note that it's a space move because the request is using + * deep flag set to true. + * Contrarily to previous test, this test checks the needed rights for the move and in particular for + * WebPreferences. + */ + @Test + void moveSpaceHomeDeepWithPreferences() throws Throwable + { + DocumentReference spaceHome = new DocumentReference("chess", List.of("A", "B", "C"), "WebHome"); + DocumentReference docFromSpace = new DocumentReference("X", spaceHome.getLastSpaceReference()); + DocumentReference otherDocFromSpace = new DocumentReference("Y", spaceHome.getLastSpaceReference()); + DocumentReference prefFromSpace = new DocumentReference("WebPreferences", spaceHome.getLastSpaceReference()); + when(this.modelBridge.getDocumentReferences(spaceHome.getLastSpaceReference())) + .thenReturn(List.of(spaceHome, docFromSpace, otherDocFromSpace, prefFromSpace)); + when(this.modelBridge.exists(spaceHome)).thenReturn(true); + when(this.modelBridge.exists(docFromSpace)).thenReturn(true); + when(this.modelBridge.exists(otherDocFromSpace)).thenReturn(true); + when(this.modelBridge.exists(prefFromSpace)).thenReturn(true); + + DocumentReference authorReference = new DocumentReference("xwiki", "XWiki", "Author"); + DocumentReference userReference = new DocumentReference("xwiki", "XWiki", "User"); + + // We check needed rights, but we don't need specific rights for the WebPreferences as we're checking right + // of the space home. + when(this.authorization.hasAccess(Right.VIEW, authorReference, spaceHome)).thenReturn(true); + when(this.authorization.hasAccess(Right.VIEW, authorReference, docFromSpace)).thenReturn(true); + when(this.authorization.hasAccess(Right.VIEW, authorReference, otherDocFromSpace)).thenReturn(true); + when(this.authorization.hasAccess(Right.VIEW, authorReference, prefFromSpace)).thenReturn(false); + + when(this.authorization.hasAccess(Right.VIEW, userReference, spaceHome)).thenReturn(true); + when(this.authorization.hasAccess(Right.VIEW, userReference, docFromSpace)).thenReturn(true); + when(this.authorization.hasAccess(Right.VIEW, userReference, otherDocFromSpace)).thenReturn(true); + when(this.authorization.hasAccess(Right.VIEW, userReference, prefFromSpace)).thenReturn(false); + + when(this.authorization.hasAccess(Right.DELETE, authorReference, spaceHome)).thenReturn(true); + when(this.authorization.hasAccess(Right.DELETE, authorReference, docFromSpace)).thenReturn(true); + when(this.authorization.hasAccess(Right.DELETE, authorReference, otherDocFromSpace)).thenReturn(true); + when(this.authorization.hasAccess(Right.DELETE, authorReference, prefFromSpace)).thenReturn(false); + + when(this.authorization.hasAccess(Right.DELETE, userReference, spaceHome)).thenReturn(true); + when(this.authorization.hasAccess(Right.DELETE, userReference, docFromSpace)).thenReturn(true); + when(this.authorization.hasAccess(Right.DELETE, userReference, otherDocFromSpace)).thenReturn(true); + when(this.authorization.hasAccess(Right.DELETE, userReference, prefFromSpace)).thenReturn(false); + + DocumentReference targetDoc1 = new DocumentReference("tennis", "C", "X"); + DocumentReference targetDoc2 = new DocumentReference("tennis", "C", "Y"); + DocumentReference targetDoc3 = new DocumentReference("tennis", "C", "WebPreferences"); + DocumentReference targetDocSpaceHome = new DocumentReference("tennis", "C", "WebHome"); + + when(this.authorization.hasAccess(Right.EDIT, authorReference, spaceHome)).thenReturn(true); + when(this.authorization.hasAccess(Right.EDIT, authorReference, targetDocSpaceHome)).thenReturn(true); + when(this.authorization.hasAccess(Right.EDIT, authorReference, targetDoc1)).thenReturn(true); + when(this.authorization.hasAccess(Right.EDIT, authorReference, targetDoc2)).thenReturn(true); + when(this.authorization.hasAccess(Right.EDIT, authorReference, targetDoc3)).thenReturn(false); + + when(this.authorization.hasAccess(Right.EDIT, userReference, spaceHome)).thenReturn(true); + when(this.authorization.hasAccess(Right.EDIT, userReference, targetDocSpaceHome)).thenReturn(true); + when(this.authorization.hasAccess(Right.EDIT, userReference, targetDoc1)).thenReturn(true); + when(this.authorization.hasAccess(Right.EDIT, userReference, targetDoc2)).thenReturn(true); + when(this.authorization.hasAccess(Right.EDIT, userReference, targetDoc3)).thenReturn(false); + + WikiReference newWiki = new WikiReference("tennis"); + + MoveRequest request = createRequest(spaceHome, newWiki); + request.setCheckRights(true); + request.setCheckAuthorRights(true); + request.setDeep(true); + request.setAuthorReference(authorReference); + request.setUserReference(userReference); + + run(request); + + verify(this.modelBridge).rename(docFromSpace, targetDoc1); + verify(this.modelBridge).rename(otherDocFromSpace, targetDoc2); + verify(this.modelBridge).rename(prefFromSpace, targetDoc3); + verify(this.modelBridge).rename(spaceHome, targetDocSpaceHome); + + verify(this.authorization).hasAccess(Right.VIEW, authorReference, spaceHome); + verify(this.authorization).hasAccess(Right.VIEW, authorReference, docFromSpace); + verify(this.authorization).hasAccess(Right.VIEW, authorReference, otherDocFromSpace); + verify(this.authorization, never()).hasAccess(Right.VIEW, authorReference, prefFromSpace); + + verify(this.authorization).hasAccess(Right.VIEW, userReference, spaceHome); + verify(this.authorization).hasAccess(Right.VIEW, userReference, docFromSpace); + verify(this.authorization).hasAccess(Right.VIEW, userReference, otherDocFromSpace); + verify(this.authorization, never()).hasAccess(Right.VIEW, userReference, prefFromSpace); + + verify(this.authorization, times(2)).hasAccess(Right.DELETE, authorReference, spaceHome); + verify(this.authorization).hasAccess(Right.DELETE, authorReference, docFromSpace); + verify(this.authorization).hasAccess(Right.DELETE, authorReference, otherDocFromSpace); + verify(this.authorization, never()).hasAccess(Right.DELETE, authorReference, prefFromSpace); + + verify(this.authorization, times(2)).hasAccess(Right.DELETE, userReference, spaceHome); + verify(this.authorization).hasAccess(Right.DELETE, userReference, docFromSpace); + verify(this.authorization).hasAccess(Right.DELETE, userReference, otherDocFromSpace); + verify(this.authorization, never()).hasAccess(Right.DELETE, userReference, prefFromSpace); + + verify(this.authorization).hasAccess(Right.EDIT, authorReference, spaceHome); + verify(this.authorization).hasAccess(Right.EDIT, authorReference, targetDocSpaceHome); + verify(this.authorization).hasAccess(Right.EDIT, authorReference, targetDoc1); + verify(this.authorization).hasAccess(Right.EDIT, authorReference, targetDoc2); + verify(this.authorization, never()).hasAccess(Right.EDIT, authorReference, targetDoc3); + + verify(this.authorization).hasAccess(Right.EDIT, userReference, spaceHome); + verify(this.authorization).hasAccess(Right.EDIT, userReference, targetDocSpaceHome); + verify(this.authorization).hasAccess(Right.EDIT, userReference, targetDoc1); + verify(this.authorization).hasAccess(Right.EDIT, userReference, targetDoc2); + verify(this.authorization, never()).hasAccess(Right.EDIT, userReference, targetDoc3); + + verify(this.observationManager).notify(any(DocumentsDeletingEvent.class), any(MoveJob.class), + eq(Map.of( + docFromSpace, new EntitySelection(docFromSpace, targetDoc1), + otherDocFromSpace, new EntitySelection(otherDocFromSpace, targetDoc2), + prefFromSpace, new EntitySelection(prefFromSpace, targetDoc3), + spaceHome, new EntitySelection(spaceHome, targetDocSpaceHome) + ))); + } + + @Test + void moveWebPreferencesWithoutHome() throws Throwable + { + DocumentReference spaceHome = new DocumentReference("chess", List.of("A", "B", "C"), "WebHome"); + DocumentReference docFromSpace = new DocumentReference("X", spaceHome.getLastSpaceReference()); + DocumentReference prefFromSpace = new DocumentReference("WebPreferences", spaceHome.getLastSpaceReference()); + when(this.modelBridge.getDocumentReferences(spaceHome.getLastSpaceReference())) + .thenReturn(List.of(docFromSpace, prefFromSpace)); + when(this.modelBridge.exists(spaceHome)).thenReturn(false); + when(this.modelBridge.exists(docFromSpace)).thenReturn(true); + when(this.modelBridge.exists(prefFromSpace)).thenReturn(true); + + DocumentReference authorReference = new DocumentReference("xwiki", "XWiki", "Author"); + DocumentReference userReference = new DocumentReference("xwiki", "XWiki", "User"); + + when(this.authorization.hasAccess(Right.VIEW, authorReference, docFromSpace)).thenReturn(true); + when(this.authorization.hasAccess(Right.VIEW, authorReference, prefFromSpace)).thenReturn(false); + + when(this.authorization.hasAccess(Right.VIEW, userReference, docFromSpace)).thenReturn(true); + when(this.authorization.hasAccess(Right.VIEW, userReference, prefFromSpace)).thenReturn(false); + + when(this.authorization.hasAccess(Right.DELETE, authorReference, docFromSpace)).thenReturn(true); + when(this.authorization.hasAccess(Right.DELETE, authorReference, prefFromSpace)).thenReturn(false); + + when(this.authorization.hasAccess(Right.DELETE, userReference, docFromSpace)).thenReturn(true); + when(this.authorization.hasAccess(Right.DELETE, userReference, prefFromSpace)).thenReturn(false); + + DocumentReference targetDoc1 = new DocumentReference("tennis", "C", "X"); + DocumentReference targetDoc3 = new DocumentReference("tennis", "C", "WebPreferences"); + + when(this.authorization.hasAccess(Right.EDIT, authorReference, spaceHome)).thenReturn(true); + when(this.authorization.hasAccess(Right.EDIT, authorReference, targetDoc1)).thenReturn(true); + when(this.authorization.hasAccess(Right.EDIT, authorReference, targetDoc3)).thenReturn(false); + + when(this.authorization.hasAccess(Right.EDIT, userReference, spaceHome)).thenReturn(true); + when(this.authorization.hasAccess(Right.EDIT, userReference, targetDoc1)).thenReturn(true); + when(this.authorization.hasAccess(Right.EDIT, userReference, targetDoc3)).thenReturn(false); + + WikiReference newWiki = new WikiReference("tennis"); + + MoveRequest request = createRequest(spaceHome, newWiki); + request.setCheckRights(true); + request.setCheckAuthorRights(true); + request.setDeep(true); + request.setAuthorReference(authorReference); + request.setUserReference(userReference); + + run(request); + + verify(this.modelBridge).rename(docFromSpace, targetDoc1); + verify(this.modelBridge, never()).rename(prefFromSpace, targetDoc3); + + verify(this.authorization).hasAccess(Right.VIEW, authorReference, docFromSpace); + verify(this.authorization, never()).hasAccess(Right.VIEW, authorReference, prefFromSpace); + + verify(this.authorization).hasAccess(Right.VIEW, userReference, docFromSpace); + verify(this.authorization, never()).hasAccess(Right.VIEW, userReference, prefFromSpace); + + verify(this.authorization).hasAccess(Right.DELETE, authorReference, docFromSpace); + verify(this.authorization, never()).hasAccess(Right.DELETE, authorReference, prefFromSpace); + + verify(this.authorization).hasAccess(Right.DELETE, userReference, docFromSpace); + verify(this.authorization).hasAccess(Right.DELETE, userReference, prefFromSpace); + + verify(this.authorization).hasAccess(Right.EDIT, authorReference, targetDoc1); + verify(this.authorization, never()).hasAccess(Right.EDIT, authorReference, targetDoc3); + + verify(this.authorization).hasAccess(Right.EDIT, userReference, targetDoc1); + verify(this.authorization, never()).hasAccess(Right.EDIT, userReference, targetDoc3); + + verify(this.observationManager).notify(any(DocumentsDeletingEvent.class), any(MoveJob.class), + eq(Map.of( + docFromSpace, new EntitySelection(docFromSpace, targetDoc1) + ))); + assertEquals(1, getLogCapture().size()); + assertEquals("You are not allowed to delete [chess:A.B.C.WebPreferences].", getLogCapture().getMessage(0)); + } + @Test void moveSpaceToSpaceHome() throws Throwable { diff --git a/xwiki-platform-core/xwiki-platform-test/xwiki-platform-test-ui/src/main/java/org/xwiki/test/ui/TestUtils.java b/xwiki-platform-core/xwiki-platform-test/xwiki-platform-test-ui/src/main/java/org/xwiki/test/ui/TestUtils.java index c4657406594c..a8973b65d95d 100644 --- a/xwiki-platform-core/xwiki-platform-test/xwiki-platform-test-ui/src/main/java/org/xwiki/test/ui/TestUtils.java +++ b/xwiki-platform-core/xwiki-platform-test/xwiki-platform-test-ui/src/main/java/org/xwiki/test/ui/TestUtils.java @@ -1257,7 +1257,7 @@ public String executeWiki(String wikiContent, Syntax wikiSyntax, Map