Skip to content

Commit

Permalink
XWIKI-21553: Do not override existing documents on user action (#2790)
Browse files Browse the repository at this point in the history
* Introduce a new listener to cancel any save that would delete the document.
* Add a unit test.
  • Loading branch information
michitux committed Jan 16, 2024
1 parent 560d747 commit c5efc1e
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.internal.document;

import javax.inject.Named;
import javax.inject.Singleton;

import org.xwiki.component.annotation.Component;
import org.xwiki.observation.AbstractEventListener;
import org.xwiki.observation.event.CancelableEvent;
import org.xwiki.observation.event.Event;

import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.internal.event.UserUpdatingDocumentEvent;

/**
* Cancel any save that tries to save a new document if the document already exists.
*
* <p>
* This can happen when the context document is set to an empty document because the user doesn't have view right,
* but the user still has, e.g., edit right. In this situation, the user would still be able to to save but it is
* hard to imagine a scenario where this would make sense. This listener therefore cancels the save in this
* situation.
* </p>
*
* @version $Id$
* @since 14.10.21
* @since 15.5.5
* @since 15.10.6
*/
@Component
@Singleton
@Named(DocumentOverrideListener.NAME)
public class DocumentOverrideListener extends AbstractEventListener
{
/**
* The unique identifier of the listener.
*/
public static final String NAME = "org.xwiki.internal.document.DocumentOverrideListener";

/**
* The default constructor.
*/
public DocumentOverrideListener()
{
super(NAME, new UserUpdatingDocumentEvent());
}

@Override
public void onEvent(Event event, Object source, Object data)
{
XWikiDocument document = (XWikiDocument) source;
XWikiDocument originalDocument = document.getOriginalDocument();

if (document.isNew() && originalDocument != null && !originalDocument.isNew()) {
((CancelableEvent) event).cancel(
"The document already exists but the document to be saved is marked as new.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -278,3 +278,4 @@ org.xwiki.security.authservice.script.AuthServiceScriptService
org.xwiki.model.validation.edit.XWikiDocumentLockEditConfirmationChecker
org.xwiki.evaluation.internal.DefaultObjectEvaluator
org.xwiki.evaluation.internal.VelocityObjectPropertyEvaluator
org.xwiki.internal.document.DocumentOverrideListener
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.internal.document;

import org.junit.jupiter.api.Test;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.observation.internal.DefaultObservationManager;
import org.xwiki.test.annotation.ComponentList;

import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.test.MockitoOldcore;
import com.xpn.xwiki.test.junit5.mockito.InjectMockitoOldcore;
import com.xpn.xwiki.test.junit5.mockito.OldcoreTest;
import com.xpn.xwiki.test.reference.ReferenceComponentList;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;

/**
* Unit tests for {@link DocumentOverrideListener}.
*
* @version $Id$
*/
@OldcoreTest(mockXWiki = false)
@ReferenceComponentList
@ComponentList({ DefaultObservationManager.class, DocumentOverrideListener.class })
class DocumentOverrideListenerTest
{
@InjectMockitoOldcore
private MockitoOldcore oldcore;

@Test
void savingNewDocument() throws Exception
{
DocumentReference documentReference = new DocumentReference("wiki", "space", "page");
XWikiDocument document = new XWikiDocument(documentReference);
this.oldcore.getSpyXWiki().checkSavingDocument(mock(), document, this.oldcore.getXWikiContext());
}

@Test
void savingExistingDocument() throws Exception
{
DocumentReference documentReference = new DocumentReference("wiki", "space", "page");
XWikiDocument document = new XWikiDocument(documentReference);
this.oldcore.getSpyXWiki().saveDocument(document, this.oldcore.getXWikiContext());
document = this.oldcore.getSpyXWiki().getDocument(documentReference, this.oldcore.getXWikiContext());
assertFalse(document.isNew());
this.oldcore.getSpyXWiki().checkSavingDocument(mock(), document, this.oldcore.getXWikiContext());
}

@Test
void savingOverridingExistingDocument() throws Exception
{
DocumentReference documentReference = new DocumentReference("wiki", "space", "page");
DocumentReference userReference = new DocumentReference("wiki", "XWiki", "user");
XWikiDocument document = new XWikiDocument(documentReference);
this.oldcore.getSpyXWiki().saveDocument(document, this.oldcore.getXWikiContext());
XWikiDocument newDocument = new XWikiDocument(documentReference);
XWikiException exception = assertThrows(XWikiException.class,
() -> this.oldcore.getSpyXWiki()
.checkSavingDocument(userReference, newDocument, this.oldcore.getXWikiContext()));
assertEquals("Error number 9001 in 9: User [wiki:XWiki.user] has been denied the right to save the "
+ "document [wiki:space.page]. Reason: [The document already exists but the document to be saved is marked "
+ "as new.]", exception.getMessage());
}
}

0 comments on commit c5efc1e

Please sign in to comment.