Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import java.io.Reader;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Properties;

Expand All @@ -31,6 +32,8 @@
import org.xwiki.display.internal.DisplayConfiguration;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.rendering.syntax.Syntax;
import org.xwiki.rendering.transformation.RenderingContext;
import org.xwiki.security.authorization.Right;
import org.xwiki.test.annotation.AllComponents;
import org.xwiki.test.junit5.mockito.InjectComponentManager;
import org.xwiki.test.mockito.MockitoComponentManager;
Expand All @@ -47,6 +50,8 @@
import com.xpn.xwiki.test.junit5.mockito.InjectMockitoOldcore;
import com.xpn.xwiki.test.junit5.mockito.OldcoreTest;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
Expand Down Expand Up @@ -134,7 +139,14 @@ public void setup() throws Exception
this.oldcore.getXWikiContext().put("isInRenderingEngine", true);

when(this.oldcore.getMockAuthorizationManager().hasAccess(any(), any(), any())).thenReturn(true);
when(this.oldcore.getMockContextualAuthorizationManager().hasAccess(any())).thenReturn(true);
when(this.oldcore.getMockContextualAuthorizationManager().hasAccess(any())).thenAnswer(invocationOnMock -> {
if (List.of(Right.SCRIPT, Right.PROGRAM).contains(invocationOnMock.getArgument(0))) {
RenderingContext renderingContext = this.oldcore.getMocker().getInstance(RenderingContext.class);
return !renderingContext.isRestricted();
} else {
return true;
}
});
when(this.xwiki.getRightService().hasProgrammingRights(any())).thenReturn(true);

this.componentManager
Expand Down Expand Up @@ -174,6 +186,16 @@ public void getRenderedTitleWhenTitleIsSet()
assertEquals("title", this.document.getRenderedTitle(Syntax.XHTML_1_0, this.oldcore.getXWikiContext()));
}

@Test
void getRenderedTitleRestricted()
{
this.document.setRestricted(true);
// Title with velocity that shouldn't be evaluated
String title = "#set($key = \"title\")$key";
this.document.setTitle(title);
assertEquals(title, this.document.getRenderedTitle(Syntax.XHTML_1_0, this.oldcore.getXWikiContext()));
}

@Test
public void getRenderedTitleInHTMLWhenExtractedFromContent()
{
Expand Down Expand Up @@ -208,6 +230,19 @@ public void getRenderedTitleInHTMLWhenExtractedFromContent()
assertEquals("Page", this.document.getRenderedTitle(Syntax.XHTML_1_0, this.oldcore.getXWikiContext()));
}

@Test
void getRenderedTitleWhenRestricted()
{
// Configure XWiki to extract title from content
this.oldcore.getConfigurationSource().setProperty("xwiki.title.compatibility", "1");
this.document.setRestricted(true);

this.document.setContent("content not in section\n"
+ "= {{groovy}}print \"value\"{{/groovy}}=\nheader 1 content\n" + "== header 2==\nheader 2 content");
assertThat(this.document.getRenderedTitle(Syntax.XHTML_1_0, this.oldcore.getXWikiContext()),
startsWith("<span class=\"xwikirenderingerror\">Failed to execute the [groovy] macro."));
}

@Test
public void getRenderedTitleInPlainWhenExtractedFromContent()
{
Expand Down Expand Up @@ -347,6 +382,46 @@ public void getRenderedContentIsForcingCurrentDocumentAsTheSecurityDocument() th
assertEquals("<p>Space.Page</p>", this.document.getRenderedContent(this.oldcore.getXWikiContext()));
}

@Test
void getRenderedContentSetsRestrictedRendering() throws Exception
{
XWikiDocument otherDocument = new XWikiDocument(new DocumentReference("otherwiki", "otherspace", "otherpage"));
otherDocument.setContentAuthorReference(new DocumentReference("otherwiki", "XWiki", "othercontentauthor"));
XWikiDocument sdoc = new XWikiDocument(new DocumentReference("callerwiki", "callerspace", "callerpage"));
Document apiDocument = this.document.newDocument(this.oldcore.getXWikiContext());

String content = "{{velocity}}test{{/velocity}}";

this.document.setRestricted(true);
this.document.setContent(content);
this.oldcore.getXWikiContext().setDoc(null);

// Verify that the Velocity macro is not executed.
assertThat(this.document.getRenderedContent(this.oldcore.getXWikiContext()),
startsWith("<div class=\"xwikirenderingerror\">Failed to execute the [velocity] macro."));

this.document.setRestricted(false);

assertEquals("<p>test</p>", this.document.getRenderedContent(this.oldcore.getXWikiContext()));

this.oldcore.getXWikiContext().setDoc(otherDocument);

assertEquals("<p>test</p>", apiDocument.getRenderedContent(content, Syntax.XWIKI_2_1.toIdString()));

otherDocument.setRestricted(true);

assertThat(apiDocument.getRenderedContent(content, Syntax.XWIKI_2_1.toIdString()),
startsWith("<div class=\"xwikirenderingerror\">Failed to execute the [velocity] macro."));

this.oldcore.getXWikiContext().put("sdoc", sdoc);
assertEquals("<p>test</p>", apiDocument.getRenderedContent(content, Syntax.XWIKI_2_1.toIdString()));

sdoc.setRestricted(true);

assertThat(apiDocument.getRenderedContent(content, Syntax.XWIKI_2_1.toIdString()),
startsWith("<div class=\"xwikirenderingerror\">Failed to execute the [velocity] macro."));
}

@Test
public void getRenderedContentTextWithSourceSyntaxSpecified()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,8 @@ public Void answer(InvocationOnMock invocation) throws Throwable
document.setStore(getMockStore());

XWikiDocument savedDocument = document.clone();
// Make sure the saved version is not restricted.
savedDocument.setRestricted(false);

documents.put(document.getDocumentReferenceWithLocale(), savedDocument);

Expand Down Expand Up @@ -832,6 +834,9 @@ public Void answer(InvocationOnMock invocation) throws Throwable
}

XWikiDocument savedDocument = document.clone();
// Make sure the saved version is not restricted.
savedDocument.setRestricted(false);

documents.put(document.getDocumentReferenceWithLocale(), savedDocument);

if (isNew) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@ private List<Block> executeContext(XDOM xdom, ContextMacroParameters parameters,
// IMPORTANT: This can be dangerous since it means executing macros, and thus also script macros
// defined in the referenced document. To be used with caution.
TransformationContext referencedTxContext =
new TransformationContext(referencedXDOM, referencedDoc.getSyntax());
new TransformationContext(referencedXDOM, referencedDoc.getSyntax(),
referencedDoc.isRestricted());
this.transformationManager.performTransformations(referencedXDOM, referencedTxContext);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,9 @@ private EntityReference getFullReference(EntityReference reference)
private boolean checkPreAccess(Right right)
{
if (CONTENT_AUTHOR_RIGHTS.contains(right)) {
if (this.renderingContext.isRestricted()) {
return false;
} else if (right == Right.PROGRAM && this.xcontextProvider.get().hasDroppedPermissions()) {
return false;
}
XWikiDocument doc = getProgrammingDocument();
boolean restricted = this.renderingContext.isRestricted() || (doc != null && doc.isRestricted());
return !(restricted || (right == Right.PROGRAM && this.xcontextProvider.get().hasDroppedPermissions()));
}

return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,36 @@
*/
package org.xwiki.security.authorization.internal;

import java.util.stream.Stream;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.LocalDocumentReference;
import org.xwiki.model.reference.WikiReference;
import org.xwiki.rendering.transformation.RenderingContext;
import org.xwiki.security.authorization.AuthorizationManager;
import org.xwiki.security.authorization.Right;
import org.xwiki.test.junit5.mockito.InjectMockComponents;
import org.xwiki.test.junit5.mockito.MockComponent;

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.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
* Validate {@link DefaultContextualAuthorizationManager}.
Expand All @@ -51,6 +62,9 @@ class DefaultContextualAuthorizationManagerTest
@MockComponent
private AuthorizationManager authorizationManager;

@MockComponent
private RenderingContext renderingContext;

@InjectMockComponents
private DefaultContextualAuthorizationManager contextualAuthorizationManager;

Expand Down Expand Up @@ -89,4 +103,38 @@ void hasAccess()
verify(this.authorizationManager).hasAccess(same(Right.VIEW), isNull(),
eq(new DocumentReference(localReference, this.currentWikiReference)));
}

@ParameterizedTest
@MethodSource("contentRightsSource")
void contentAuthorRightPreAccess(Right right)
{
when(this.authorizationManager.hasAccess(eq(right), any(), any())).thenReturn(true);

assertTrue(this.contextualAuthorizationManager.hasAccess(right));

// Check restricted rendering context (once).
when(this.renderingContext.isRestricted()).thenReturn(true).thenReturn(false);
assertFalse(this.contextualAuthorizationManager.hasAccess(right));

XWikiDocument contextDocument = mock(XWikiDocument.class);
this.oldcore.getXWikiContext().setDoc(contextDocument);
assertTrue(this.contextualAuthorizationManager.hasAccess(right));
verify(contextDocument).isRestricted();
// Check restricted document denies script right.
when(contextDocument.isRestricted()).thenReturn(true).thenReturn(false);
assertFalse(this.contextualAuthorizationManager.hasAccess(right));

// Check dropping permissions keeps script but not programming right
this.oldcore.getXWikiContext().dropPermissions();
if (right == Right.PROGRAM) {
assertFalse(this.contextualAuthorizationManager.hasAccess(right));
} else {
assertTrue(this.contextualAuthorizationManager.hasAccess(right));
}
}

static Stream<Right> contentRightsSource()
{
return Stream.of(Right.SCRIPT, Right.PROGRAM);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,21 @@ public ViewPage rollbackToVersion(String version)
return new ViewPage();
}

/**
* View the document at the given version.
*
* @param version the version to view
* @return the viewpage
* @since 14.10.7
* @since 15.2RC1
*/
public ViewPage viewVersion(String version)
{
this.pane.findElement(By.xpath(".//table//tr//td[position()=3]/a[contains(., '" + version + "')]")).click();

return new ViewPage();
}

public HistoryPane deleteVersion(String version)
{
getDriver().makeConfirmDialogSilent(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@
$escapetool.xml($services.localization.render('web.history.changes.failedToCompute'))
</div>
#else
#if (($origdoc.isRestricted() || $newdoc.isRestricted()) && ($services.user.allProperties.type == 'ADVANCED'
|| $stringtool.contains($htmlDiff, 'xwikirenderingerror')))
#warning($escapetool.xml($services.localization.render('web.history.changes.restrictedInfo')))
#end
$htmlDiff
#end
#end
Expand Down