CVE-2023-26056: Incorrect Authorization (CWE-863)
value: XWiki Platform is a generic wiki platform. Starting in version 3.0-milestone-1, it's possible to execute a script with the right of another user, provided the target user does not have programming right. The problem has been patched in XWiki 14.8-rc-1, 14.4.5, and 13.10.10. There are no known workarounds for this issue.
CWE description: The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check. This allows attackers to bypass intended access restrictions.
CVSS v3 base score: 5.4
Published: 2023-03-02T19:15Z
Repository: https://github.com/xwiki/xwiki-platform
Language: Java
Files changed: xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-api/src/main/java/org/xwiki/rendering/async/internal/block/BlockAsyncRendererConfiguration.java, xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-api/src/main/java/org/xwiki/rendering/async/internal/block/DefaultBlockAsyncRenderer.java, xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-macro/pom.xml, xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-macro/src/main/java/org/xwiki/rendering/async/internal/AbstractExecutedContentMacro.java, xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-macro/src/main/java/org/xwiki/rendering/async/internal/AsyncMacro.java, xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/pom.xml, xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/main/java/org/xwiki/rendering/internal/macro/context/ContextMacro.java, xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/test/java/org/xwiki/rendering/internal/macro/context/ContextMacroTest.java, xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/test/java/org/xwiki/rendering/macro/context/IntegrationTests.java
Commit date: 2022-09-09 10:48:51 +0200
Commit message: XWIKI-19856: Macro execution as any user without programming rights through the context macro

Diff: diff --git a/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-api/src/main/java/org/xwiki/rendering/async/internal/block/BlockAsyncRendererConfiguration.java b/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-api/src/main/java/org/xwiki/rendering/async/internal/block/BlockAsyncRendererConfiguration.java
index df7456a7d6fd..3edb17a2ffe7 100644
--- a/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-api/src/main/java/org/xwiki/rendering/async/internal/block/BlockAsyncRendererConfiguration.java
+++ b/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-api/src/main/java/org/xwiki/rendering/async/internal/block/BlockAsyncRendererConfiguration.java
@@ -29,10 +29,11 @@
 import org.xwiki.component.descriptor.ComponentRole;
 import org.xwiki.component.descriptor.DefaultComponentRole;
 import org.xwiki.model.reference.EntityReference;
 import org.xwiki.rendering.async.internal.AsyncRendererConfiguration;
 import org.xwiki.rendering.block.Block;
+import org.xwiki.rendering.block.XDOM;
 import org.xwiki.rendering.syntax.Syntax;
 import org.xwiki.rendering.transformation.TransformationContext;
 
 /**
  * Configuration to pass to {@link BlockAsyncRendererExecutor}.
@@ -44,10 +45,12 @@ public class BlockAsyncRendererConfiguration extends AsyncRendererConfiguration
 {
     private List<String> id;
 
     private Block block;
 
+    private XDOM xdom;
+
     private boolean asyncAllowed;
 
     private boolean cacheAllowed;
 
     private Syntax defaultSyntax;
@@ -68,12 +71,16 @@ public class BlockAsyncRendererConfiguration extends AsyncRendererConfiguration
      * @param idElements the id used as prefix (concatenated with contextual information) for the actual job identifier
      * @param block the block to transform
      */
     public BlockAsyncRendererConfiguration(List<?> idElements, Block block)
     {
-        this.id = new ArrayList<>(idElements.size());
-        addElements(idElements);
+        if (idElements != null) {
+            this.id = new ArrayList<>(idElements.size());
+            addElements(idElements);
+        } else {
+            this.id = new ArrayList<>();
+        }
         this.block = block;
 
         // Enabled by default
         this.asyncAllowed = true;
     }
@@ -111,10 +118,32 @@ public void setId(List<String> id)
     public Block getBlock()
     {
         return this.block;
     }
 
+    /**
+     * @return the XDOM to use in the transformation context
+     * @since 14.8RC1
+     * @since 14.4.5
+     * @since 13.10.10
+     */
+    public XDOM getXDOM()
+    {
+        return this.xdom;
+    }
+
+    /**
+     * @param xdom the XDOM to use in the transformation context
+     * @since 14.8RC1
+     * @since 14.4.5
+     * @since 13.10.10
+     */
+    public void setXDOM(XDOM xdom)
+    {
+        this.xdom = xdom;
+    }
+
     /**
      * @return true if the execution should be asynchronous if possible
      */
     public boolean isAsyncAllowed()
     {
diff --git a/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-api/src/main/java/org/xwiki/rendering/async/internal/block/DefaultBlockAsyncRenderer.java b/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-api/src/main/java/org/xwiki/rendering/async/internal/block/DefaultBlockAsyncRenderer.java
index 2991ae4d9061..d9e1328a2685 100644
--- a/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-api/src/main/java/org/xwiki/rendering/async/internal/block/DefaultBlockAsyncRenderer.java
+++ b/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-api/src/main/java/org/xwiki/rendering/async/internal/block/DefaultBlockAsyncRenderer.java
@@ -117,15 +117,13 @@ public Block execute(boolean async, boolean cached) throws RenderingException
             // Make sure the source is inline if needed
             if (this.configuration.isInline()) {
                 block = PARSERUTILS.convertToInline(block, true);
             }
 
-            // Create a XDOM instance for the transformation context
-            XDOM xdom;
-            if (block instanceof XDOM) {
-                xdom = (XDOM) block;
-            } else {
+            // Get the XDOM instance for the transformation context
+            XDOM xdom = this.configuration.getXDOM();
+            if (xdom == null) {
                 Block rootBlock = block.getRoot();
 
                 if (rootBlock instanceof XDOM) {
                     xdom = (XDOM) rootBlock;
                 } else {
diff --git a/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-macro/pom.xml b/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-macro/pom.xml
index 6a1080992498..a2ef02cab166 100644
--- a/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-macro/pom.xml
+++ b/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-macro/pom.xml
@@ -35,11 +35,11 @@
     <xwiki.jacoco.instructionRatio>0.00</xwiki.jacoco.instructionRatio>
     </properties>
   <dependencies>
     <dependency>
       <groupId>org.xwiki.platform</groupId>
-      <artifactId>xwiki-platform-rendering-async-default</artifactId>
+      <artifactId>xwiki-platform-rendering-async-api</artifactId>
       <version>${project.version}</version>
     </dependency>
     <dependency>
       <groupId>org.xwiki.platform</groupId>
       <artifactId>xwiki-platform-bridge</artifactId>
@@ -50,10 +50,16 @@
       <artifactId>xwiki-platform-rendering-xwiki</artifactId>
       <version>${project.version}</version>
     </dependency>
 
     <!-- Testing dependencies -->
+    <dependency>
+      <groupId>org.xwiki.platform</groupId>
+      <artifactId>xwiki-platform-rendering-async-default</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
     <dependency>
       <groupId>org.xwiki.rendering</groupId>
       <artifactId>xwiki-rendering-syntax-xwiki21</artifactId>
       <version>${rendering.version}</version>
       <scope>test</scope>
@@ -79,26 +85,12 @@
     <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>javax.servlet-api</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.mortbay.jasper</groupId>
+      <artifactId>apache-el</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-checkstyle-plugin</artifactId>
-        <executions>
-          <execution>
-            <!-- Specify the "default" execution id so that the "blocker" one is always executed -->
-            <id>default</id>
-            <configuration>
-              <excludes>
-                org/xwiki/rendering/internal/macro/display/DisplayMacro.java
-              </excludes>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-    </plugins>
-  </build>
 </project>
diff --git a/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-macro/src/main/java/org/xwiki/rendering/async/internal/AbstractExecutedContentMacro.java b/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-macro/src/main/java/org/xwiki/rendering/async/internal/AbstractExecutedContentMacro.java
new file mode 100644
index 000000000000..8a40f51ca5eb
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-macro/src/main/java/org/xwiki/rendering/async/internal/AbstractExecutedContentMacro.java
@@ -0,0 +1,147 @@
+/*
+ * 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.rendering.async.internal;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.xwiki.bridge.DocumentAccessBridge;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.model.reference.DocumentReferenceResolver;
+import org.xwiki.rendering.async.internal.block.BlockAsyncRendererConfiguration;
+import org.xwiki.rendering.async.internal.block.BlockAsyncRendererExecutor;
+import org.xwiki.rendering.block.Block;
+import org.xwiki.rendering.block.Block.Axes;
+import org.xwiki.rendering.block.MacroBlock;
+import org.xwiki.rendering.block.MetaDataBlock;
+import org.xwiki.rendering.block.match.MetadataBlockMatcher;
+import org.xwiki.rendering.listener.MetaData;
+import org.xwiki.rendering.macro.AbstractMacro;
+import org.xwiki.rendering.macro.Macro;
+import org.xwiki.rendering.macro.MacroContentParser;
+import org.xwiki.rendering.macro.descriptor.ContentDescriptor;
+import org.xwiki.rendering.transformation.MacroTransformationContext;
+import org.xwiki.rendering.transformation.RenderingContext;
+
+/**
+ * Base class to implement a macro which have content to execute.
+ * 
+ * @param <P> the type of the macro parameters bean
+ * @version $Id$
+ * @since 14.8RC1
+ * @since 14.4.5
+ * @since 13.10.10
+ */
+public abstract class AbstractExecutedContentMacro<P> extends AbstractMacro<P>
+{
+    @Inject
+    protected BlockAsyncRendererExecutor executor;
+
+    @Inject
+    protected MacroContentParser parser;
+
+    @Inject
+    protected DocumentAccessBridge documentAccessBridge;
+
+    @Inject
+    protected DocumentReferenceResolver<String> resolver;
+
+    @Inject
+    protected RenderingContext renderingContext;
+
+    /**
+     * Creates a new {@link Macro} instance.
+     *
+     * @param name the name of the macro (eg "Table Of Contents" for the TOC macro)
+     * @param description string describing this macro.
+     * @param contentDescriptor the {@link ContentDescriptor} describing the content of this macro.
+     * @param parametersBeanClass class of the parameters bean.
+     */
+    protected AbstractExecutedContentMacro(String name, String description, ContentDescriptor contentDescriptor,
+        Class<?> parametersBeanClass)
+    {
+        super(name, description, contentDescriptor, parametersBeanClass);
+    }
+
+    @Override
+    public boolean supportsInlineMode()
+    {
+        return true;
+    }
+
+    protected String getCurrentSource(MacroTransformationContext context)
+    {
+        String currentSource = null;
+
+        if (context != null) {
+            currentSource =
+                context.getTransformationContext() != null ? context.getTransformationContext().getId() : null;
+
+            MacroBlock currentMacroBlock = context.getCurrentMacroBlock();
+
+            if (currentMacroBlock != null) {
+                MetaDataBlock metaDataBlock =
+                    currentMacroBlock.getFirstBlock(new MetadataBlockMatcher(MetaData.SOURCE), Axes.ANCESTOR_OR_SELF);
+
+                if (metaDataBlock != null) {
+                    currentSource = (String) metaDataBlock.getMetaData().getMetaData(MetaData.SOURCE);
+                }
+            }
+        }
+
+        return currentSource;
+    }
+
+    protected BlockAsyncRendererConfiguration createBlockAsyncRendererConfiguration(List<?> idElements, Block content,
+        MacroTransformationContext context)
+    {
+        return createBlockAsyncRendererConfiguration(idElements, content, getCurrentSource(context), context);
+    }
+
+    protected BlockAsyncRendererConfiguration createBlockAsyncRendererConfiguration(List<?> idElements, Block content,
+        String source, MacroTransformationContext context)
+    {
+        BlockAsyncRendererConfiguration configuration = new BlockAsyncRendererConfiguration(idElements, content);
+
+        // Set author
+        if (source != null) {
+            DocumentReference sourceReference = this.resolver.resolve(source);
+            configuration.setSecureReference(sourceReference, this.documentAccessBridge.getCurrentAuthorReference());
+
+            // Invalidate the cache when the document containing the macro call is modified
+            configuration.useEntity(sourceReference);
+        }
+
+        // Indicate if the result should be inline or not
+        configuration.setInline(context.isInline());
+
+        // Indicate the syntax of the content
+        configuration.setDefaultSyntax(this.parser.getCurrentSyntax(context));
+
+        // Indicate the target syntax
+        configuration.setTargetSyntax(this.renderingContext.getTargetSyntax());
+
+        // Set the transformation id
+        configuration.setTransformationId(context.getTransformationContext().getId());
+
+        return configuration;
+    }
+}
diff --git a/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-macro/src/main/java/org/xwiki/rendering/async/internal/AsyncMacro.java b/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-macro/src/main/java/org/xwiki/rendering/async/internal/AsyncMacro.java
index acaca7e46475..a658b24d5e4b 100644
--- a/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-macro/src/main/java/org/xwiki/rendering/async/internal/AsyncMacro.java
+++ b/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-macro/src/main/java/org/xwiki/rendering/async/internal/AsyncMacro.java
@@ -26,66 +26,40 @@
 
 import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
 
-import org.xwiki.bridge.DocumentAccessBridge;
 import org.xwiki.component.annotation.Component;
-import org.xwiki.model.reference.DocumentReference;
-import org.xwiki.model.reference.DocumentReferenceResolver;
 import org.xwiki.rendering.async.AsyncContext;
 import org.xwiki.rendering.async.AsyncMacroParameters;
 import org.xwiki.rendering.async.internal.block.BlockAsyncRendererConfiguration;
-import org.xwiki.rendering.async.internal.block.BlockAsyncRendererExecutor;
 import org.xwiki.rendering.block.Block;
-import org.xwiki.rendering.block.Block.Axes;
-import org.xwiki.rendering.block.MacroBlock;
 import org.xwiki.rendering.block.MetaDataBlock;
 import org.xwiki.rendering.block.XDOM;
-import org.xwiki.rendering.block.match.MetadataBlockMatcher;
-import org.xwiki.rendering.listener.MetaData;
-import org.xwiki.rendering.macro.AbstractMacro;
-import org.xwiki.rendering.macro.MacroContentParser;
 import org.xwiki.rendering.macro.MacroExecutionException;
 import org.xwiki.rendering.macro.descriptor.DefaultContentDescriptor;
 import org.xwiki.rendering.transformation.MacroTransformationContext;
-import org.xwiki.rendering.transformation.RenderingContext;
 
 /**
  * Asynchronous and cached execution of wiki content.
  * 
  * @version $Id$
  * @since 11.6RC1
  */
 @Component
 @Named("async")
 @Singleton
-public class AsyncMacro extends AbstractMacro<AsyncMacroParameters>
+public class AsyncMacro extends AbstractExecutedContentMacro<AsyncMacroParameters>
 {
     /**
      * The description of the macro.
      */
     private static final String DESCRIPTION = "Asynchronous and cached execution of wiki content.";
 
-    @Inject
-    private BlockAsyncRendererExecutor executor;
-
-    @Inject
-    private MacroContentParser parser;
-
-    @Inject
-    private DocumentAccessBridge documentAccessBridge;
-
-    @Inject
-    private DocumentReferenceResolver<String> resolver;
-
     @Inject
     private AsyncContext asyncContext;
 
-    @Inject
-    private RenderingContext renderingContext;
-
     /**
      * Default constructor.
      */
     public AsyncMacro()
     {
@@ -93,39 +67,10 @@ public AsyncMacro()
             AsyncMacroParameters.class);
 
         setDefaultCategories(Set.of(DEFAULT_CATEGORY_CONTENT));
     }
 
-    @Override
-    public boolean supportsInlineMode()
-    {
-        return true;
-    }
-
-    private String getCurrentSource(MacroTransformationContext context)
-    {
-        String currentSource = null;
-
-        if (context != null) {
-            currentSource =
-                context.getTransformationContext() != null ? context.getTransformationContext().getId() : null;
-
-            MacroBlock currentMacroBlock = context.getCurrentMacroBlock();
-
-            if (currentMacroBlock != null) {
-                MetaDataBlock metaDataBlock =
-                    currentMacroBlock.getFirstBlock(new MetadataBlockMatcher(MetaData.SOURCE), Axes.ANCESTOR_OR_SELF);
-
-                if (metaDataBlock != null) {
-                    currentSource = (String) metaDataBlock.getMetaData().getMetaData(MetaData.SOURCE);
-                }
-            }
-        }
-
-        return currentSource;
-    }
-
     @Override
     public List<Block> execute(AsyncMacroParameters parameters, String content, MacroTransformationContext context)
         throws MacroExecutionException
     {
         XDOM xdom = this.parser.parse(content, context, false, context.isInline());
@@ -154,46 +99,26 @@ public List<Block> execute(AsyncMacroParameters parameters, String content, Macr
             if (index != -1) {
                 idElements.add(index);
             }
         }
 
-        BlockAsyncRendererConfiguration configuration = new BlockAsyncRendererConfiguration(idElements, xdom);
-
-        // Set author
-        if (source != null) {
-            DocumentReference sourceReference = this.resolver.resolve(source);
-            configuration.setSecureReference(sourceReference, this.documentAccessBridge.getCurrentAuthorReference());
-
-            // Invalidate the cache when the document containing the macro call is modified
-            configuration.useEntity(sourceReference);
-        }
-
-        // Indicate if the result should be inline or not
-        configuration.setInline(context.isInline());
+        BlockAsyncRendererConfiguration configuration =
+            createBlockAsyncRendererConfiguration(idElements, xdom, source, context);
 
         // Enable/disable async
         configuration.setAsyncAllowed(parameters.isAsync());
         // Enable/disable caching
         configuration.setCacheAllowed(parameters.isCached());
         // Indicate context entries
         configuration.setContextEntries(parameters.getContext());
 
-        // Indicate the syntax of the content
-        configuration.setDefaultSyntax(this.parser.getCurrentSyntax(context));
-
-        // Indicate the target syntax
-        configuration.setTargetSyntax(this.renderingContext.getTargetSyntax());
-
-        // Set the transformation id
-        configuration.setTransformationId(context.getTransformationContext().getId());
-
         try {
             Block result = this.executor.execute(configuration);
 
             // Indicate the content is not transformed if the current execution is not async
             if (!parameters.isAsync() || !this.asyncContext.isEnabled()) {
-                result = new MetaDataBlock(Collections.singletonList(result), this.getNonGeneratedContentMetaData());
+                result = new MetaDataBlock(Collections.singletonList(result), getNonGeneratedContentMetaData());
             }
 
             return Collections.singletonList(result);
         } catch (Exception e) {
             throw new MacroExecutionException("Failed start the execution of the macro", e);
diff --git a/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/pom.xml b/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/pom.xml
index 8e4ada8d7882..a97bd43f5007 100644
--- a/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/pom.xml
+++ b/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/pom.xml
@@ -36,23 +36,40 @@
     <xwiki.extension.name>Context Macro</xwiki.extension.name>
   </properties>
   <dependencies>
     <dependency>
       <groupId>org.xwiki.platform</groupId>
-      <artifactId>xwiki-platform-bridge</artifactId>
+      <artifactId>xwiki-platform-rendering-async-macro</artifactId>
       <version>${project.version}</version>
     </dependency>
     <dependency>
       <groupId>org.xwiki.platform</groupId>
       <artifactId>xwiki-platform-rendering-transformation-macro</artifactId>
       <version>${project.version}</version>
       <scope>runtime</scope>
     </dependency>
+
     <!-- Test Dependencies -->
+    <dependency>
+      <groupId>org.xwiki.platform</groupId>
+      <artifactId>xwiki-platform-rendering-async-default</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
     <dependency>
       <groupId>org.xwiki.rendering</groupId>
       <artifactId>xwiki-rendering-macro-toc</artifactId>
       <version>${rendering.version}</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mortbay.jasper</groupId>
+      <artifactId>apache-el</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 </project>
diff --git a/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/main/java/org/xwiki/rendering/internal/macro/context/ContextMacro.java b/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/main/java/org/xwiki/rendering/internal/macro/context/ContextMacro.java
index f7881db31e8c..03ae81837dba 100644
--- a/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/main/java/org/xwiki/rendering/internal/macro/context/ContextMacro.java
+++ b/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/main/java/org/xwiki/rendering/internal/macro/context/ContextMacro.java
@@ -18,48 +18,51 @@
  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  */
 package org.xwiki.rendering.internal.macro.context;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
 
-import org.xwiki.bridge.DocumentAccessBridge;
 import org.xwiki.bridge.DocumentModelBridge;
 import org.xwiki.component.annotation.Component;
 import org.xwiki.model.reference.DocumentReference;
 import org.xwiki.model.reference.DocumentReferenceResolver;
+import org.xwiki.rendering.async.internal.AbstractExecutedContentMacro;
+import org.xwiki.rendering.async.internal.block.BlockAsyncRendererConfiguration;
 import org.xwiki.rendering.block.Block;
 import org.xwiki.rendering.block.MetaDataBlock;
 import org.xwiki.rendering.block.XDOM;
 import org.xwiki.rendering.listener.MetaData;
-import org.xwiki.rendering.macro.AbstractMacro;
-import org.xwiki.rendering.macro.MacroContentParser;
 import org.xwiki.rendering.macro.MacroExecutionException;
 import org.xwiki.rendering.macro.context.ContextMacroParameters;
 import org.xwiki.rendering.macro.context.TransformationContextMode;
 import org.xwiki.rendering.macro.descriptor.DefaultContentDescriptor;
 import org.xwiki.rendering.transformation.MacroTransformationContext;
 import org.xwiki.rendering.transformation.TransformationContext;
 import org.xwiki.rendering.transformation.TransformationManager;
+import org.xwiki.security.authorization.AccessDeniedException;
+import org.xwiki.security.authorization.AuthorizationManager;
+import org.xwiki.security.authorization.Right;
 
 /**
  * Execute the macro's content in the context of another document's reference.
  * 
  * @version $Id$
  * @since 3.0M1
  */
 @Component
 @Named("context")
 @Singleton
-public class ContextMacro extends AbstractMacro<ContextMacroParameters>
+public class ContextMacro extends AbstractExecutedContentMacro<ContextMacroParameters>
 {
     /**
      * The description of the macro.
      */
     private static final String DESCRIPTION = "Executes content in the context of the passed document";
@@ -67,31 +70,19 @@ public class ContextMacro extends AbstractMacro<ContextMacroParameters>
     /**
      * The description of the macro content.
      */
     private static final String CONTENT_DESCRIPTION = "The content to execute";
 
-    /**
-     * Used to set the current document in the context (old way) and check rights.
-     */
     @Inject
-    private DocumentAccessBridge documentAccessBridge;
+    private AuthorizationManager authorizationManager;
 
-    /**
-     * The parser used to parse macro content.
-     */
     @Inject
-    private MacroContentParser contentParser;
+    private TransformationManager transformationManager;
 
-    /**
-     * Used to transform document links into absolute references.
-     */
     @Inject
     @Named("macro")
-    private DocumentReferenceResolver<String> macroDocumentReferenceResolver;
-
-    @Inject
-    private TransformationManager transformationManager;
+    private DocumentReferenceResolver<String> macroReferenceResolver;
 
     /**
      * Create and initialize the descriptor of the macro.
      */
     public ContextMacro()
@@ -103,14 +94,52 @@ public ContextMacro()
         // for other macros (TOC macro, etc). Make it the same priority as the Include macro.
         setPriority(10);
         setDefaultCategories(Set.of(DEFAULT_CATEGORY_DEVELOPMENT));
     }
 
-    @Override
-    public boolean supportsInlineMode()
+    private void checkAccess(DocumentReference currentAuthor, DocumentReference referencedDocReference)
+        throws MacroExecutionException
     {
-        return true;
+        // Current author must have view right on the target document to use it as context document
+        try {
+            this.authorizationManager.checkAccess(Right.VIEW, currentAuthor, referencedDocReference);
+        } catch (AccessDeniedException e) {
+            throw new MacroExecutionException("Author [" + currentAuthor
+                + "] is not allowed to access target document [" + referencedDocReference + "]", e);
+        }
+    }
+
+    private XDOM getXDOM(DocumentReference referencedDocReference, ContextMacroParameters parameters)
+        throws MacroExecutionException
+    {
+        try {
+            if (parameters.getTransformationContext() == TransformationContextMode.DOCUMENT
+                || parameters.getTransformationContext() == TransformationContextMode.TRANSFORMATIONS) {
+                // Apply the transformations but with a Transformation Context having the XDOM of the passed
+                // document so that macros execute on the passed document's XDOM (e.g. the TOC macro will generate
+                // the toc for the passed document instead of the current document).
+                DocumentModelBridge referencedDoc =
+                    this.documentAccessBridge.getTranslatedDocumentInstance(referencedDocReference);
+                XDOM referencedXDOM = referencedDoc.getXDOM();
+
+                if (parameters.getTransformationContext() == TransformationContextMode.TRANSFORMATIONS) {
+                    // Get the XDOM from the referenced doc but with Transformations applied so that all macro are
+                    // executed and contribute XDOM elements.
+                    // 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());
+                    this.transformationManager.performTransformations(referencedXDOM, referencedTxContext);
+                }
+
+                return referencedXDOM;
+            }
+        } catch (Exception e) {
+            throw new MacroExecutionException("Failed to resolve the XDOM to use in the transformation", e);
+        }
+
+        return null;
     }
 
     @Override
     public List<Block> execute(ContextMacroParameters parameters, String content, MacroTransformationContext context)
         throws MacroExecutionException
@@ -118,77 +147,47 @@ public List<Block> execute(ContextMacroParameters parameters, String content, Ma
         if (parameters.getDocument() == null) {
             throw new MacroExecutionException("You must specify a 'document' parameter pointing to the document to "
                 + "set in the context as the current document.");
         }
 
+        DocumentReference currentAuthor = this.documentAccessBridge.getCurrentAuthorReference();
         DocumentReference referencedDocReference =
-            this.macroDocumentReferenceResolver.resolve(parameters.getDocument(), context.getCurrentMacroBlock());
+            this.macroReferenceResolver.resolve(parameters.getDocument(), context.getCurrentMacroBlock());
 
-        boolean currentContextHasProgrammingRights = this.documentAccessBridge.hasProgrammingRights();
+        // Make sure the author is allowed to use the target document
+        checkAccess(currentAuthor, referencedDocReference);
 
-        List<Block> result;
-        try {
-            Map<String, Object> backupObjects = new HashMap<>();
-            try {
-                this.documentAccessBridge.pushDocumentInContext(backupObjects, referencedDocReference);
-
-                // The current document is now the passed document. Check for programming rights for it. If it has
-                // programming rights then the initial current document also needs programming right, else throw an
-                // error since it would be a security breach otherwise.
-                if (this.documentAccessBridge.hasProgrammingRights() && !currentContextHasProgrammingRights) {
-                    throw new MacroExecutionException("Current document must have programming rights since the "
-                        + "context document provided [" + parameters.getDocument() + "] has programming rights.");
-                }
+        MetaData metadata = new MetaData();
+        metadata.addMetaData(MetaData.SOURCE, parameters.getDocument());
+        metadata.addMetaData(MetaData.BASE, parameters.getDocument());
 
-                MetaData metadata = new MetaData();
-                metadata.addMetaData(MetaData.SOURCE, parameters.getDocument());
-                metadata.addMetaData(MetaData.BASE, parameters.getDocument());
-
-                XDOM xdom = this.contentParser.parse(content, context, false, metadata, false);
-
-                // Configure the  Transformation Context depending on the mode asked.
-                if (parameters.getTransformationContext() == TransformationContextMode.DOCUMENT
-                    || parameters.getTransformationContext() == TransformationContextMode.TRANSFORMATIONS)
-                {
-                    // Apply the transformations but with a Transformation Context having the XDOM of the passed
-                    // document so that macros execute on the passed document's XDOM (e.g. the TOC macro will generate
-                    // the toc for the passed document instead of the current document).
-                    DocumentModelBridge referencedDoc =
-                        this.documentAccessBridge.getTranslatedDocumentInstance(referencedDocReference);
-                    XDOM referencedXDOM = referencedDoc.getXDOM();
-
-                    if (parameters.getTransformationContext() == TransformationContextMode.TRANSFORMATIONS) {
-                        // Get the XDOM from the referenced doc but with Transformations applied so that all macro are
-                        // executed and contribute XDOM elements.
-                        // 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());
-                        this.transformationManager.performTransformations(referencedXDOM, referencedTxContext);
-                    }
-
-                    // Now execute transformation on the context macro content but with the referenced XDOM in the
-                    // Transformation context!
-                    TransformationContext txContext =
-                        new TransformationContext(referencedXDOM, referencedDoc.getSyntax());
-                    this.transformationManager.performTransformations(xdom, txContext);
-                }
+        XDOM xdom = this.parser.parse(content, context, false, metadata, context.isInline());
 
-                // Keep metadata so that the result stay associated to context properties when inserted in the parent
-                // XDOM
-                result = Arrays.asList((Block) new MetaDataBlock(xdom.getChildren(), xdom.getMetaData()));
+        if (xdom.getChildren().isEmpty()) {
+            return Collections.emptyList();
+        }
 
-            } finally {
-                this.documentAccessBridge.popDocumentFromContext(backupObjects);
-            }
+        // Reuse the very generic async rendering framework (even if we don't do async and caching) since it's taking
+        // care of many other things
+        BlockAsyncRendererConfiguration configuration = createBlockAsyncRendererConfiguration(null, xdom, context);
+
+        // Configure the Transformation Context XDOM depending on the mode asked.
+        configuration.setXDOM(getXDOM(referencedDocReference, parameters));
+
+        Map<String, Object> backupObjects = new HashMap<>();
+        try {
+            // Switch the context document
+            this.documentAccessBridge.pushDocumentInContext(backupObjects, referencedDocReference);
+
+            // Execute the content
+            Block result = this.executor.execute(configuration);
+
+            // Keep metadata so that the result stay associated to context properties when inserted in the parent XDOM
+            return Arrays.asList((Block) new MetaDataBlock(result.getChildren(), xdom.getMetaData()));
         } catch (Exception e) {
-            if (e instanceof MacroExecutionException) {
-                throw (MacroExecutionException) e;
-            } else {
-                throw new MacroExecutionException(
-                    String.format("Failed to render page in the context of [%s]", referencedDocReference), e);
-            }
+            throw new MacroExecutionException("Failed start the execution of the macro", e);
+        } finally {
+            // Restore the context document
+            this.documentAccessBridge.popDocumentFromContext(backupObjects);
         }
-
-        return result;
     }
 }
diff --git a/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/test/java/org/xwiki/rendering/internal/macro/context/ContextMacroTest.java b/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/test/java/org/xwiki/rendering/internal/macro/context/ContextMacroTest.java
index 887777d36913..860ad849cb84 100644
--- a/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/test/java/org/xwiki/rendering/internal/macro/context/ContextMacroTest.java
+++ b/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/test/java/org/xwiki/rendering/internal/macro/context/ContextMacroTest.java
@@ -20,180 +20,169 @@
 package org.xwiki.rendering.internal.macro.context;
 
 import java.util.Arrays;
 import java.util.Collections;
 
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
 import org.xwiki.bridge.DocumentAccessBridge;
 import org.xwiki.bridge.DocumentModelBridge;
 import org.xwiki.model.reference.DocumentReference;
 import org.xwiki.model.reference.DocumentReferenceResolver;
 import org.xwiki.properties.BeanDescriptor;
 import org.xwiki.properties.BeanManager;
-import org.xwiki.rendering.block.Block;
-import org.xwiki.rendering.block.LinkBlock;
+import org.xwiki.rendering.async.internal.block.BlockAsyncRendererConfiguration;
+import org.xwiki.rendering.async.internal.block.BlockAsyncRendererExecutor;
 import org.xwiki.rendering.block.MacroBlock;
-import org.xwiki.rendering.block.ParagraphBlock;
+import org.xwiki.rendering.block.WordBlock;
 import org.xwiki.rendering.block.XDOM;
 import org.xwiki.rendering.listener.MetaData;
-import org.xwiki.rendering.listener.reference.ResourceReference;
-import org.xwiki.rendering.listener.reference.ResourceType;
 import org.xwiki.rendering.macro.MacroContentParser;
 import org.xwiki.rendering.macro.MacroExecutionException;
 import org.xwiki.rendering.macro.context.ContextMacroParameters;
 import org.xwiki.rendering.syntax.Syntax;
 import org.xwiki.rendering.transformation.MacroTransformationContext;
-import org.xwiki.test.mockito.MockitoComponentMockingRule;
-
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.*;
+import org.xwiki.security.authorization.AccessDeniedException;
+import org.xwiki.security.authorization.AuthorizationManager;
+import org.xwiki.security.authorization.Right;
+import org.xwiki.test.TestEnvironment;
+import org.xwiki.test.annotation.ComponentList;
+import org.xwiki.test.junit5.mockito.ComponentTest;
+import org.xwiki.test.junit5.mockito.InjectMockComponents;
+import org.xwiki.test.junit5.mockito.MockComponent;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 /**
  * Unit tests for {@link ContextMacro}.
  *
  * @version $Id$
  * @since 8.3RC1
  */
-public class ContextMacroTest
+@ComponentTest
+@ComponentList(TestEnvironment.class)
+class ContextMacroTest
 {
-    @Rule
-    public MockitoComponentMockingRule<ContextMacro> mocker = new MockitoComponentMockingRule<>(ContextMacro.class);
+    private static final DocumentReference AUTHOR = new DocumentReference("wiki", "XWiki", "author");
+
+    private static final DocumentReference TARGET_REFERENCE = new DocumentReference("wiki", "space", "target");
+
+    private static final DocumentReference SOURCE_REFERENCE = new DocumentReference("wiki", "space", "source");
+
+    @Inject
+    protected BlockAsyncRendererExecutor executor;
+
+    @MockComponent
+    private DocumentAccessBridge dab;
+
+    @MockComponent
+    private BeanManager beanManager;
+
+    @MockComponent
+    private MacroContentParser parser;
+
+    @MockComponent
+    private AuthorizationManager authorization;
+
+    @MockComponent
+    @Named("macro")
+    private DocumentReferenceResolver<String> macroReferenceResolver;
+
+    @MockComponent
+    protected DocumentReferenceResolver<String> resolver;
 
-    @Before
-    public void setUp() throws Exception
+    @InjectMockComponents
+    private ContextMacro macro;
+
+    @BeforeEach
+    public void beforeEach() throws Exception
     {
         // Macro Descriptor set up
-        BeanManager beanManager = this.mocker.getInstance(BeanManager.class);
         BeanDescriptor descriptor = mock(BeanDescriptor.class);
         when(descriptor.getProperties()).thenReturn(Collections.emptyList());
-        when(beanManager.getBeanDescriptor(any())).thenReturn(descriptor);
+        when(this.beanManager.getBeanDescriptor(any())).thenReturn(descriptor);
+
+        when(this.dab.getCurrentAuthorReference()).thenReturn(AUTHOR);
+
+        when(this.macroReferenceResolver.resolve(eq("target"), any())).thenReturn(TARGET_REFERENCE);
+        when(this.resolver.resolve("source")).thenReturn(SOURCE_REFERENCE);
     }
 
     @Test
-    public void executeWhenNoDocumentSpecified() throws Exception
+    void executeWhenNoDocumentSpecified() throws Exception
     {
         ContextMacroParameters parameters = new ContextMacroParameters();
 
         try {
-            this.mocker.getComponentUnderTest().execute(parameters, "", new MacroTransformationContext());
+            this.macro.execute(parameters, "", new MacroTransformationContext());
             fail("Should have thrown an exception");
         } catch (MacroExecutionException expected) {
-            Assert.assertEquals("You must specify a 'document' parameter pointing to the document to set in the "
+            assertEquals("You must specify a 'document' parameter pointing to the document to set in the "
                 + "context as the current document.", expected.getMessage());
         }
     }
 
     @Test
-    public void executeWithReferencedDocumentHavingProgrammingRightsButNotTheCallingDocument() throws Exception
+    void executeWithReferencedDocumentNotViewableByTheAuthor() throws Exception
     {
         MacroTransformationContext macroContext = new MacroTransformationContext();
         MacroBlock macroBlock = new MacroBlock("context", Collections.emptyMap(), false);
         macroContext.setCurrentMacroBlock(macroBlock);
 
-        DocumentReferenceResolver<String> resolver =
-            this.mocker.getInstance(DocumentReferenceResolver.TYPE_STRING, "macro");
-        when(resolver.resolve("wiki:space.page", macroBlock)).thenReturn(
-            new DocumentReference("wiki", "space", "page"));
-
-        DocumentAccessBridge dab = this.mocker.getInstance(DocumentAccessBridge.class);
-        when(dab.hasProgrammingRights()).thenReturn(false).thenReturn(true);
+        doThrow(AccessDeniedException.class).when(this.authorization).checkAccess(Right.VIEW, AUTHOR, TARGET_REFERENCE);
 
         ContextMacroParameters parameters = new ContextMacroParameters();
-        parameters.setDocument("wiki:space.page");
+        parameters.setDocument("target");
 
         try {
-            this.mocker.getComponentUnderTest().execute(parameters, "", macroContext);
+            this.macro.execute(parameters, "", macroContext);
             fail("Should have thrown an exception");
         } catch (MacroExecutionException expected) {
-            assertEquals("Current document must have programming rights since the context document provided ["
-                + "wiki:space.page] has programming rights.", expected.getMessage());
+            assertEquals("Author [wiki:XWiki.author] is not allowed to access target document [wiki:space.target]",
+                expected.getMessage());
         }
     }
 
     @Test
-    public void executeWithReferencedDocumentHavingProgrammingRightsAndCallingDocumentToo() throws Exception
-    {
-        MacroBlock macroBlock = new MacroBlock("context", Collections.<String, String>emptyMap(), false);
-        MacroTransformationContext macroContext = new MacroTransformationContext();
-        macroContext.setSyntax(Syntax.XWIKI_2_0);
-        macroContext.setCurrentMacroBlock(macroBlock);
-
-        DocumentReferenceResolver<String> resolver =
-            this.mocker.getInstance(DocumentReferenceResolver.TYPE_STRING, "macro");
-        DocumentReference referencedDocumentReference = new DocumentReference("wiki", "space", "page");
-        when(resolver.resolve("wiki:space.page", macroBlock)).thenReturn(referencedDocumentReference);
-
-        DocumentAccessBridge dab = this.mocker.getInstance(DocumentAccessBridge.class);
-        when(dab.hasProgrammingRights()).thenReturn(true).thenReturn(true);
-        DocumentModelBridge dmb = mock(DocumentModelBridge.class);
-        when(dab.getTranslatedDocumentInstance(referencedDocumentReference)).thenReturn(dmb);
-
-        MacroContentParser parser = this.mocker.getInstance(MacroContentParser.class);
-        when(parser.parse(eq(""), same(macroContext), eq(false), any(MetaData.class), eq(false))).thenReturn(
-            new XDOM(Collections.emptyList()));
-
-        ContextMacroParameters parameters = new ContextMacroParameters();
-        parameters.setDocument("wiki:space.page");
-
-        this.mocker.getComponentUnderTest().execute(parameters, "", macroContext);
-    }
-
-    @Test
-    public void executeOk() throws Exception
+    void executeOk() throws Exception
     {
         MacroBlock macroBlock = new MacroBlock("context", Collections.<String, String>emptyMap(), false);
+        MetaData metadata = new MetaData();
+        metadata.addMetaData(MetaData.SOURCE, "source");
+        XDOM xdom = new XDOM(Arrays.asList(macroBlock), metadata);
         MacroTransformationContext macroContext = new MacroTransformationContext();
         macroContext.setSyntax(Syntax.XWIKI_2_0);
         macroContext.setCurrentMacroBlock(macroBlock);
 
-        DocumentReferenceResolver<String> resolver =
-            this.mocker.getInstance(DocumentReferenceResolver.TYPE_STRING, "macro");
-        DocumentReference referencedDocumentReference = new DocumentReference("wiki", "space", "page");
-        when(resolver.resolve("wiki:space.page", macroBlock)).thenReturn(referencedDocumentReference);
-
-        DocumentAccessBridge dab = this.mocker.getInstance(DocumentAccessBridge.class);
         DocumentModelBridge dmb = mock(DocumentModelBridge.class);
-        when(dab.getTranslatedDocumentInstance(referencedDocumentReference)).thenReturn(dmb);
+        when(this.dab.getTranslatedDocumentInstance(TARGET_REFERENCE)).thenReturn(dmb);
 
-        MacroContentParser parser = this.mocker.getInstance(MacroContentParser.class);
-        XDOM xdom = new XDOM(Arrays.asList((Block) new ParagraphBlock(Arrays.asList((Block) new LinkBlock(
-            Collections.emptyList(), new ResourceReference("", ResourceType.DOCUMENT), false)))));
-        when(parser.parse(eq(""), same(macroContext), eq(false), any(MetaData.class), eq(false))).thenReturn(xdom);
+        when(this.parser.parse(eq(""), same(macroContext), eq(false), any(MetaData.class), eq(false))).thenReturn(xdom);
 
         ContextMacroParameters parameters = new ContextMacroParameters();
-        parameters.setDocument("wiki:space.page");
-
-        // Note: we're not testing the returned value here since this is done in integation tests.
-        this.mocker.getComponentUnderTest().execute(parameters, "", macroContext);
-    }
+        parameters.setDocument("target");
 
-    @Test
-    public void executeWithRelativeDocumentReferenceParameter() throws Exception
-    {
-        MacroBlock macroBlock = new MacroBlock("context", Collections.<String, String>emptyMap(), false);
+        when(this.executor.execute(any())).thenReturn(new WordBlock("result"));
 
-        MacroTransformationContext macroContext = new MacroTransformationContext();
-        macroContext.setSyntax(Syntax.XWIKI_2_0);
-        macroContext.setCurrentMacroBlock(macroBlock);
+        this.macro.execute(parameters, "", macroContext);
 
-        DocumentReferenceResolver<String> resolver =
-            this.mocker.getInstance(DocumentReferenceResolver.TYPE_STRING, "macro");
-        DocumentReference referencedDocumentReference = new DocumentReference("basewiki", "basespace", "page");
-        when(resolver.resolve("page", macroBlock)).thenReturn(referencedDocumentReference);
-
-        DocumentAccessBridge dab = this.mocker.getInstance(DocumentAccessBridge.class);
-        DocumentModelBridge dmb = mock(DocumentModelBridge.class);
-        when(dab.getTranslatedDocumentInstance(referencedDocumentReference)).thenReturn(dmb);
-
-        MacroContentParser parser = this.mocker.getInstance(MacroContentParser.class);
-        when(parser.parse(eq(""), same(macroContext), eq(false), any(MetaData.class), eq(false))).thenReturn(
-            new XDOM(Collections.emptyList()));
-
-        ContextMacroParameters parameters = new ContextMacroParameters();
-        parameters.setDocument("page");
+        ArgumentCaptor<BlockAsyncRendererConfiguration> configurationCaptor =
+            ArgumentCaptor.forClass(BlockAsyncRendererConfiguration.class);
+        verify(this.executor).execute(configurationCaptor.capture());
 
-        this.mocker.getComponentUnderTest().execute(parameters, "", macroContext);
+        BlockAsyncRendererConfiguration configuration = configurationCaptor.getValue();
+        assertEquals(AUTHOR, configuration.getSecureAuthorReference());
+        assertEquals(SOURCE_REFERENCE, configuration.getSecureDocumentReference());
     }
 }
diff --git a/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/test/java/org/xwiki/rendering/macro/context/IntegrationTests.java b/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/test/java/org/xwiki/rendering/macro/context/IntegrationTests.java
index 619befc00798..cb3ca5597deb 100644
--- a/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/test/java/org/xwiki/rendering/macro/context/IntegrationTests.java
+++ b/xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/test/java/org/xwiki/rendering/macro/context/IntegrationTests.java
@@ -19,41 +19,63 @@
  */
 package org.xwiki.rendering.macro.context;
 
 import java.io.StringReader;
 
-import org.junit.runner.RunWith;
 import org.xwiki.bridge.DocumentAccessBridge;
 import org.xwiki.bridge.DocumentModelBridge;
+import org.xwiki.component.manager.ComponentManager;
 import org.xwiki.component.util.DefaultParameterizedType;
 import org.xwiki.model.reference.DocumentReference;
 import org.xwiki.model.reference.DocumentReferenceResolver;
+import org.xwiki.query.QueryManager;
+import org.xwiki.refactoring.internal.ModelBridge;
+import org.xwiki.refactoring.internal.ReferenceUpdater;
 import org.xwiki.rendering.block.MacroBlock;
 import org.xwiki.rendering.block.XDOM;
 import org.xwiki.rendering.parser.Parser;
-import org.xwiki.rendering.test.integration.RenderingTestSuite;
+import org.xwiki.rendering.test.integration.junit5.RenderingTests;
+import org.xwiki.security.authorization.AuthorizationManager;
+import org.xwiki.security.authorization.ContextualAuthorizationManager;
+import org.xwiki.template.TemplateManager;
+import org.xwiki.test.TestEnvironment;
 import org.xwiki.test.annotation.AllComponents;
+import org.xwiki.test.annotation.ComponentList;
 import org.xwiki.test.mockito.MockitoComponentManager;
 
-import static org.mockito.Mockito.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 /**
  * Run all tests found in {@code *.test} files located in the classpath. These {@code *.test} files must follow the
  * conventions described in {@link org.xwiki.rendering.test.integration.TestDataParser}.
  *
  * @version $Id$
  * @since 8.3RC1
  */
-@RunWith(RenderingTestSuite.class)
 @AllComponents
-public class IntegrationTests
+@ComponentList(TestEnvironment.class)
+public class IntegrationTests implements RenderingTests
 {
-    @RenderingTestSuite.Initialized
+    @RenderingTests.Initialized
     public void initialize(MockitoComponentManager componentManager) throws Exception
     {
+        // Replace the environment by a test compatible one
+        componentManager.registerComponent(TestEnvironment.class);
+        
         // For performance reasons we mock some components to avoid having to draw all oldcore components
 
+        // Some components we don't really use and which trigger a lot of dependencies
+        componentManager.registerMockComponent(TemplateManager.class);
+        componentManager.registerMockComponent(ModelBridge.class);
+        componentManager.registerMockComponent(QueryManager.class);
+        componentManager.registerMockComponent(ReferenceUpdater.class);
+        componentManager.registerMockComponent(AuthorizationManager.class);
+        componentManager.registerMockComponent(ContextualAuthorizationManager.class);
+
         // Macro Reference Resolver
         DocumentReferenceResolver<String> macroResolver = componentManager.registerMockComponent(
             new DefaultParameterizedType(null, DocumentReferenceResolver.class, String.class), "macro");
         DocumentReference referencedDocumentReference = new DocumentReference("Wiki", "Space", "Page");
         when(macroResolver.resolve(eq("Space.Page"), any(MacroBlock.class))).thenReturn(referencedDocumentReference);
@@ -65,7 +87,10 @@ public void initialize(MockitoComponentManager componentManager) throws Exceptio
         when(dab.getTranslatedDocumentInstance(referencedDocumentReference)).thenReturn(dmb);
 
         Parser parser = componentManager.getInstance(Parser.class, "xwiki/2.1");
         XDOM xdom = parser.parse(new StringReader("= heading1 =\n==heading2=="));
         when(dmb.getXDOM()).thenReturn(xdom);
+
+        // Replace the context component manager
+        componentManager.registerComponent(ComponentManager.class, "context", componentManager);
     }
 }

Vulnerable code:         this.id = new ArrayList<>(idElements.size());
        addElements(idElements);
            // Create a XDOM instance for the transformation context
            XDOM xdom;
            if (block instanceof XDOM) {
                xdom = (XDOM) block;
            } else {
      <artifactId>xwiki-platform-rendering-async-default</artifactId>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-checkstyle-plugin</artifactId>
        <executions>
          <execution>
            <!-- Specify the "default" execution id so that the "blocker" one is always executed -->
            <id>default</id>
            <configuration>
              <excludes>
                org/xwiki/rendering/internal/macro/display/DisplayMacro.java
              </excludes>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
import org.xwiki.bridge.DocumentAccessBridge;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.DocumentReferenceResolver;
import org.xwiki.rendering.async.internal.block.BlockAsyncRendererExecutor;
import org.xwiki.rendering.block.Block.Axes;
import org.xwiki.rendering.block.MacroBlock;
import org.xwiki.rendering.block.match.MetadataBlockMatcher;
import org.xwiki.rendering.listener.MetaData;
import org.xwiki.rendering.macro.AbstractMacro;
import org.xwiki.rendering.macro.MacroContentParser;
import org.xwiki.rendering.transformation.RenderingContext;
public class AsyncMacro extends AbstractMacro<AsyncMacroParameters>
    @Inject
    private BlockAsyncRendererExecutor executor;

    @Inject
    private MacroContentParser parser;

    @Inject
    private DocumentAccessBridge documentAccessBridge;

    @Inject
    private DocumentReferenceResolver<String> resolver;

    @Inject
    private RenderingContext renderingContext;

    @Override
    public boolean supportsInlineMode()
    {
        return true;
    }

    private String getCurrentSource(MacroTransformationContext context)
    {
        String currentSource = null;

        if (context != null) {
            currentSource =
                context.getTransformationContext() != null ? context.getTransformationContext().getId() : null;

            MacroBlock currentMacroBlock = context.getCurrentMacroBlock();

            if (currentMacroBlock != null) {
                MetaDataBlock metaDataBlock =
                    currentMacroBlock.getFirstBlock(new MetadataBlockMatcher(MetaData.SOURCE), Axes.ANCESTOR_OR_SELF);

                if (metaDataBlock != null) {
                    currentSource = (String) metaDataBlock.getMetaData().getMetaData(MetaData.SOURCE);
                }
            }
        }

        return currentSource;
    }

        BlockAsyncRendererConfiguration configuration = new BlockAsyncRendererConfiguration(idElements, xdom);

        // Set author
        if (source != null) {
            DocumentReference sourceReference = this.resolver.resolve(source);
            configuration.setSecureReference(sourceReference, this.documentAccessBridge.getCurrentAuthorReference());

            // Invalidate the cache when the document containing the macro call is modified
            configuration.useEntity(sourceReference);
        }

        // Indicate if the result should be inline or not
        configuration.setInline(context.isInline());
        // Indicate the syntax of the content
        configuration.setDefaultSyntax(this.parser.getCurrentSyntax(context));

        // Indicate the target syntax
        configuration.setTargetSyntax(this.renderingContext.getTargetSyntax());

        // Set the transformation id
        configuration.setTransformationId(context.getTransformationContext().getId());

                result = new MetaDataBlock(Collections.singletonList(result), this.getNonGeneratedContentMetaData());
      <artifactId>xwiki-platform-bridge</artifactId>
import org.xwiki.bridge.DocumentAccessBridge;
import org.xwiki.rendering.macro.AbstractMacro;
import org.xwiki.rendering.macro.MacroContentParser;
public class ContextMacro extends AbstractMacro<ContextMacroParameters>
    /**
     * Used to set the current document in the context (old way) and check rights.
     */
    private DocumentAccessBridge documentAccessBridge;
    /**
     * The parser used to parse macro content.
     */
    private MacroContentParser contentParser;
    /**
     * Used to transform document links into absolute references.
     */
    private DocumentReferenceResolver<String> macroDocumentReferenceResolver;

    @Inject
    private TransformationManager transformationManager;
    @Override
    public boolean supportsInlineMode()
        return true;
            this.macroDocumentReferenceResolver.resolve(parameters.getDocument(), context.getCurrentMacroBlock());
        boolean currentContextHasProgrammingRights = this.documentAccessBridge.hasProgrammingRights();
        List<Block> result;
        try {
            Map<String, Object> backupObjects = new HashMap<>();
            try {
                this.documentAccessBridge.pushDocumentInContext(backupObjects, referencedDocReference);

                // The current document is now the passed document. Check for programming rights for it. If it has
                // programming rights then the initial current document also needs programming right, else throw an
                // error since it would be a security breach otherwise.
                if (this.documentAccessBridge.hasProgrammingRights() && !currentContextHasProgrammingRights) {
                    throw new MacroExecutionException("Current document must have programming rights since the "
                        + "context document provided [" + parameters.getDocument() + "] has programming rights.");
                }
                MetaData metadata = new MetaData();
                metadata.addMetaData(MetaData.SOURCE, parameters.getDocument());
                metadata.addMetaData(MetaData.BASE, parameters.getDocument());

                XDOM xdom = this.contentParser.parse(content, context, false, metadata, false);

                // Configure the  Transformation Context depending on the mode asked.
                if (parameters.getTransformationContext() == TransformationContextMode.DOCUMENT
                    || parameters.getTransformationContext() == TransformationContextMode.TRANSFORMATIONS)
                {
                    // Apply the transformations but with a Transformation Context having the XDOM of the passed
                    // document so that macros execute on the passed document's XDOM (e.g. the TOC macro will generate
                    // the toc for the passed document instead of the current document).
                    DocumentModelBridge referencedDoc =
                        this.documentAccessBridge.getTranslatedDocumentInstance(referencedDocReference);
                    XDOM referencedXDOM = referencedDoc.getXDOM();

                    if (parameters.getTransformationContext() == TransformationContextMode.TRANSFORMATIONS) {
                        // Get the XDOM from the referenced doc but with Transformations applied so that all macro are
                        // executed and contribute XDOM elements.
                        // 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());
                        this.transformationManager.performTransformations(referencedXDOM, referencedTxContext);
                    }

                    // Now execute transformation on the context macro content but with the referenced XDOM in the
                    // Transformation context!
                    TransformationContext txContext =
                        new TransformationContext(referencedXDOM, referencedDoc.getSyntax());
                    this.transformationManager.performTransformations(xdom, txContext);
                }
                // Keep metadata so that the result stay associated to context properties when inserted in the parent
                // XDOM
                result = Arrays.asList((Block) new MetaDataBlock(xdom.getChildren(), xdom.getMetaData()));
            } finally {
                this.documentAccessBridge.popDocumentFromContext(backupObjects);
            }
            if (e instanceof MacroExecutionException) {
                throw (MacroExecutionException) e;
            } else {
                throw new MacroExecutionException(
                    String.format("Failed to render page in the context of [%s]", referencedDocReference), e);
            }

        return result;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.xwiki.rendering.block.Block;
import org.xwiki.rendering.block.LinkBlock;
import org.xwiki.rendering.block.ParagraphBlock;
import org.xwiki.rendering.listener.reference.ResourceReference;
import org.xwiki.rendering.listener.reference.ResourceType;
import org.xwiki.test.mockito.MockitoComponentMockingRule;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
public class ContextMacroTest
    @Rule
    public MockitoComponentMockingRule<ContextMacro> mocker = new MockitoComponentMockingRule<>(ContextMacro.class);
    @Before
    public void setUp() throws Exception
        BeanManager beanManager = this.mocker.getInstance(BeanManager.class);
        when(beanManager.getBeanDescriptor(any())).thenReturn(descriptor);
    public void executeWhenNoDocumentSpecified() throws Exception
            this.mocker.getComponentUnderTest().execute(parameters, "", new MacroTransformationContext());
            Assert.assertEquals("You must specify a 'document' parameter pointing to the document to set in the "
    public void executeWithReferencedDocumentHavingProgrammingRightsButNotTheCallingDocument() throws Exception
        DocumentReferenceResolver<String> resolver =
            this.mocker.getInstance(DocumentReferenceResolver.TYPE_STRING, "macro");
        when(resolver.resolve("wiki:space.page", macroBlock)).thenReturn(
            new DocumentReference("wiki", "space", "page"));

        DocumentAccessBridge dab = this.mocker.getInstance(DocumentAccessBridge.class);
        when(dab.hasProgrammingRights()).thenReturn(false).thenReturn(true);
        parameters.setDocument("wiki:space.page");
            this.mocker.getComponentUnderTest().execute(parameters, "", macroContext);
            assertEquals("Current document must have programming rights since the context document provided ["
                + "wiki:space.page] has programming rights.", expected.getMessage());
    public void executeWithReferencedDocumentHavingProgrammingRightsAndCallingDocumentToo() throws Exception
    {
        MacroBlock macroBlock = new MacroBlock("context", Collections.<String, String>emptyMap(), false);
        MacroTransformationContext macroContext = new MacroTransformationContext();
        macroContext.setSyntax(Syntax.XWIKI_2_0);
        macroContext.setCurrentMacroBlock(macroBlock);

        DocumentReferenceResolver<String> resolver =
            this.mocker.getInstance(DocumentReferenceResolver.TYPE_STRING, "macro");
        DocumentReference referencedDocumentReference = new DocumentReference("wiki", "space", "page");
        when(resolver.resolve("wiki:space.page", macroBlock)).thenReturn(referencedDocumentReference);

        DocumentAccessBridge dab = this.mocker.getInstance(DocumentAccessBridge.class);
        when(dab.hasProgrammingRights()).thenReturn(true).thenReturn(true);
        DocumentModelBridge dmb = mock(DocumentModelBridge.class);
        when(dab.getTranslatedDocumentInstance(referencedDocumentReference)).thenReturn(dmb);

        MacroContentParser parser = this.mocker.getInstance(MacroContentParser.class);
        when(parser.parse(eq(""), same(macroContext), eq(false), any(MetaData.class), eq(false))).thenReturn(
            new XDOM(Collections.emptyList()));

        ContextMacroParameters parameters = new ContextMacroParameters();
        parameters.setDocument("wiki:space.page");

        this.mocker.getComponentUnderTest().execute(parameters, "", macroContext);
    }

    @Test
    public void executeOk() throws Exception
        DocumentReferenceResolver<String> resolver =
            this.mocker.getInstance(DocumentReferenceResolver.TYPE_STRING, "macro");
        DocumentReference referencedDocumentReference = new DocumentReference("wiki", "space", "page");
        when(resolver.resolve("wiki:space.page", macroBlock)).thenReturn(referencedDocumentReference);

        DocumentAccessBridge dab = this.mocker.getInstance(DocumentAccessBridge.class);
        when(dab.getTranslatedDocumentInstance(referencedDocumentReference)).thenReturn(dmb);
        MacroContentParser parser = this.mocker.getInstance(MacroContentParser.class);
        XDOM xdom = new XDOM(Arrays.asList((Block) new ParagraphBlock(Arrays.asList((Block) new LinkBlock(
            Collections.emptyList(), new ResourceReference("", ResourceType.DOCUMENT), false)))));
        when(parser.parse(eq(""), same(macroContext), eq(false), any(MetaData.class), eq(false))).thenReturn(xdom);
        parameters.setDocument("wiki:space.page");

        // Note: we're not testing the returned value here since this is done in integation tests.
        this.mocker.getComponentUnderTest().execute(parameters, "", macroContext);
    }
    @Test
    public void executeWithRelativeDocumentReferenceParameter() throws Exception
    {
        MacroBlock macroBlock = new MacroBlock("context", Collections.<String, String>emptyMap(), false);
        MacroTransformationContext macroContext = new MacroTransformationContext();
        macroContext.setSyntax(Syntax.XWIKI_2_0);
        macroContext.setCurrentMacroBlock(macroBlock);
        DocumentReferenceResolver<String> resolver =
            this.mocker.getInstance(DocumentReferenceResolver.TYPE_STRING, "macro");
        DocumentReference referencedDocumentReference = new DocumentReference("basewiki", "basespace", "page");
        when(resolver.resolve("page", macroBlock)).thenReturn(referencedDocumentReference);

        DocumentAccessBridge dab = this.mocker.getInstance(DocumentAccessBridge.class);
        DocumentModelBridge dmb = mock(DocumentModelBridge.class);
        when(dab.getTranslatedDocumentInstance(referencedDocumentReference)).thenReturn(dmb);

        MacroContentParser parser = this.mocker.getInstance(MacroContentParser.class);
        when(parser.parse(eq(""), same(macroContext), eq(false), any(MetaData.class), eq(false))).thenReturn(
            new XDOM(Collections.emptyList()));

        ContextMacroParameters parameters = new ContextMacroParameters();
        parameters.setDocument("page");
        this.mocker.getComponentUnderTest().execute(parameters, "", macroContext);
import org.junit.runner.RunWith;
import org.xwiki.rendering.test.integration.RenderingTestSuite;
import static org.mockito.Mockito.*;
@RunWith(RenderingTestSuite.class)
public class IntegrationTests
    @RenderingTestSuite.Initialized
Fixed code: import org.xwiki.rendering.block.XDOM;
    private XDOM xdom;

        if (idElements != null) {
            this.id = new ArrayList<>(idElements.size());
            addElements(idElements);
        } else {
            this.id = new ArrayList<>();
        }
    /**
     * @return the XDOM to use in the transformation context
     * @since 14.8RC1
     * @since 14.4.5
     * @since 13.10.10
     */
    public XDOM getXDOM()
    {
        return this.xdom;
    }

    /**
     * @param xdom the XDOM to use in the transformation context
     * @since 14.8RC1
     * @since 14.4.5
     * @since 13.10.10
     */
    public void setXDOM(XDOM xdom)
    {
        this.xdom = xdom;
    }

            // Get the XDOM instance for the transformation context
            XDOM xdom = this.configuration.getXDOM();
            if (xdom == null) {
      <artifactId>xwiki-platform-rendering-async-api</artifactId>
    <dependency>
      <groupId>org.xwiki.platform</groupId>
      <artifactId>xwiki-platform-rendering-async-default</artifactId>
      <version>${project.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.mortbay.jasper</groupId>
      <artifactId>apache-el</artifactId>
      <scope>test</scope>
    </dependency>
/*
 * 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.rendering.async.internal;

import java.util.List;

import javax.inject.Inject;

import org.xwiki.bridge.DocumentAccessBridge;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.DocumentReferenceResolver;
import org.xwiki.rendering.async.internal.block.BlockAsyncRendererConfiguration;
import org.xwiki.rendering.async.internal.block.BlockAsyncRendererExecutor;
import org.xwiki.rendering.block.Block;
import org.xwiki.rendering.block.Block.Axes;
import org.xwiki.rendering.block.MacroBlock;
import org.xwiki.rendering.block.MetaDataBlock;
import org.xwiki.rendering.block.match.MetadataBlockMatcher;
import org.xwiki.rendering.listener.MetaData;
import org.xwiki.rendering.macro.AbstractMacro;
import org.xwiki.rendering.macro.Macro;
import org.xwiki.rendering.macro.MacroContentParser;
import org.xwiki.rendering.macro.descriptor.ContentDescriptor;
import org.xwiki.rendering.transformation.MacroTransformationContext;
import org.xwiki.rendering.transformation.RenderingContext;

/**
 * Base class to implement a macro which have content to execute.
 * 
 * @param <P> the type of the macro parameters bean
 * @version $Id$
 * @since 14.8RC1
 * @since 14.4.5
 * @since 13.10.10
 */
public abstract class AbstractExecutedContentMacro<P> extends AbstractMacro<P>
{
    @Inject
    protected BlockAsyncRendererExecutor executor;

    @Inject
    protected MacroContentParser parser;

    @Inject
    protected DocumentAccessBridge documentAccessBridge;

    @Inject
    protected DocumentReferenceResolver<String> resolver;

    @Inject
    protected RenderingContext renderingContext;

    /**
     * Creates a new {@link Macro} instance.
     *
     * @param name the name of the macro (eg "Table Of Contents" for the TOC macro)
     * @param description string describing this macro.
     * @param contentDescriptor the {@link ContentDescriptor} describing the content of this macro.
     * @param parametersBeanClass class of the parameters bean.
     */
    protected AbstractExecutedContentMacro(String name, String description, ContentDescriptor contentDescriptor,
        Class<?> parametersBeanClass)
    {
        super(name, description, contentDescriptor, parametersBeanClass);
    }

    @Override
    public boolean supportsInlineMode()
    {
        return true;
    }

    protected String getCurrentSource(MacroTransformationContext context)
    {
        String currentSource = null;

        if (context != null) {
            currentSource =
                context.getTransformationContext() != null ? context.getTransformationContext().getId() : null;

            MacroBlock currentMacroBlock = context.getCurrentMacroBlock();

            if (currentMacroBlock != null) {
                MetaDataBlock metaDataBlock =
                    currentMacroBlock.getFirstBlock(new MetadataBlockMatcher(MetaData.SOURCE), Axes.ANCESTOR_OR_SELF);

                if (metaDataBlock != null) {
                    currentSource = (String) metaDataBlock.getMetaData().getMetaData(MetaData.SOURCE);
                }
            }
        }

        return currentSource;
    }

    protected BlockAsyncRendererConfiguration createBlockAsyncRendererConfiguration(List<?> idElements, Block content,
        MacroTransformationContext context)
    {
        return createBlockAsyncRendererConfiguration(idElements, content, getCurrentSource(context), context);
    }

    protected BlockAsyncRendererConfiguration createBlockAsyncRendererConfiguration(List<?> idElements, Block content,
        String source, MacroTransformationContext context)
    {
        BlockAsyncRendererConfiguration configuration = new BlockAsyncRendererConfiguration(idElements, content);

        // Set author
        if (source != null) {
            DocumentReference sourceReference = this.resolver.resolve(source);
            configuration.setSecureReference(sourceReference, this.documentAccessBridge.getCurrentAuthorReference());

            // Invalidate the cache when the document containing the macro call is modified
            configuration.useEntity(sourceReference);
        }

        // Indicate if the result should be inline or not
        configuration.setInline(context.isInline());

        // Indicate the syntax of the content
        configuration.setDefaultSyntax(this.parser.getCurrentSyntax(context));

        // Indicate the target syntax
        configuration.setTargetSyntax(this.renderingContext.getTargetSyntax());

        // Set the transformation id
        configuration.setTransformationId(context.getTransformationContext().getId());

        return configuration;
    }
}
public class AsyncMacro extends AbstractExecutedContentMacro<AsyncMacroParameters>
        BlockAsyncRendererConfiguration configuration =
            createBlockAsyncRendererConfiguration(idElements, xdom, source, context);
                result = new MetaDataBlock(Collections.singletonList(result), getNonGeneratedContentMetaData());
      <artifactId>xwiki-platform-rendering-async-macro</artifactId>

    <dependency>
      <groupId>org.xwiki.platform</groupId>
      <artifactId>xwiki-platform-rendering-async-default</artifactId>
      <version>${project.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.mortbay.jasper</groupId>
      <artifactId>apache-el</artifactId>
      <scope>test</scope>
    </dependency>
import java.util.Collections;
import org.xwiki.rendering.async.internal.AbstractExecutedContentMacro;
import org.xwiki.rendering.async.internal.block.BlockAsyncRendererConfiguration;
import org.xwiki.security.authorization.AccessDeniedException;
import org.xwiki.security.authorization.AuthorizationManager;
import org.xwiki.security.authorization.Right;
public class ContextMacro extends AbstractExecutedContentMacro<ContextMacroParameters>
    private AuthorizationManager authorizationManager;
    private TransformationManager transformationManager;
    private DocumentReferenceResolver<String> macroReferenceResolver;
    private void checkAccess(DocumentReference currentAuthor, DocumentReference referencedDocReference)
        throws MacroExecutionException
        // Current author must have view right on the target document to use it as context document
        try {
            this.authorizationManager.checkAccess(Right.VIEW, currentAuthor, referencedDocReference);
        } catch (AccessDeniedException e) {
            throw new MacroExecutionException("Author [" + currentAuthor
                + "] is not allowed to access target document [" + referencedDocReference + "]", e);
        }
    }

    private XDOM getXDOM(DocumentReference referencedDocReference, ContextMacroParameters parameters)
        throws MacroExecutionException
    {
        try {
            if (parameters.getTransformationContext() == TransformationContextMode.DOCUMENT
                || parameters.getTransformationContext() == TransformationContextMode.TRANSFORMATIONS) {
                // Apply the transformations but with a Transformation Context having the XDOM of the passed
                // document so that macros execute on the passed document's XDOM (e.g. the TOC macro will generate
                // the toc for the passed document instead of the current document).
                DocumentModelBridge referencedDoc =
                    this.documentAccessBridge.getTranslatedDocumentInstance(referencedDocReference);
                XDOM referencedXDOM = referencedDoc.getXDOM();

                if (parameters.getTransformationContext() == TransformationContextMode.TRANSFORMATIONS) {
                    // Get the XDOM from the referenced doc but with Transformations applied so that all macro are
                    // executed and contribute XDOM elements.
                    // 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());
                    this.transformationManager.performTransformations(referencedXDOM, referencedTxContext);
                }

                return referencedXDOM;
            }
        } catch (Exception e) {
            throw new MacroExecutionException("Failed to resolve the XDOM to use in the transformation", e);
        }

        return null;
        DocumentReference currentAuthor = this.documentAccessBridge.getCurrentAuthorReference();
            this.macroReferenceResolver.resolve(parameters.getDocument(), context.getCurrentMacroBlock());
        // Make sure the author is allowed to use the target document
        checkAccess(currentAuthor, referencedDocReference);
        MetaData metadata = new MetaData();
        metadata.addMetaData(MetaData.SOURCE, parameters.getDocument());
        metadata.addMetaData(MetaData.BASE, parameters.getDocument());
        XDOM xdom = this.parser.parse(content, context, false, metadata, context.isInline());
        if (xdom.getChildren().isEmpty()) {
            return Collections.emptyList();
        }
        // Reuse the very generic async rendering framework (even if we don't do async and caching) since it's taking
        // care of many other things
        BlockAsyncRendererConfiguration configuration = createBlockAsyncRendererConfiguration(null, xdom, context);

        // Configure the Transformation Context XDOM depending on the mode asked.
        configuration.setXDOM(getXDOM(referencedDocReference, parameters));

        Map<String, Object> backupObjects = new HashMap<>();
        try {
            // Switch the context document
            this.documentAccessBridge.pushDocumentInContext(backupObjects, referencedDocReference);

            // Execute the content
            Block result = this.executor.execute(configuration);

            // Keep metadata so that the result stay associated to context properties when inserted in the parent XDOM
            return Arrays.asList((Block) new MetaDataBlock(result.getChildren(), xdom.getMetaData()));
            throw new MacroExecutionException("Failed start the execution of the macro", e);
        } finally {
            // Restore the context document
            this.documentAccessBridge.popDocumentFromContext(backupObjects);
import javax.inject.Inject;
import javax.inject.Named;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.xwiki.rendering.async.internal.block.BlockAsyncRendererConfiguration;
import org.xwiki.rendering.async.internal.block.BlockAsyncRendererExecutor;
import org.xwiki.rendering.block.WordBlock;
import org.xwiki.security.authorization.AccessDeniedException;
import org.xwiki.security.authorization.AuthorizationManager;
import org.xwiki.security.authorization.Right;
import org.xwiki.test.TestEnvironment;
import org.xwiki.test.annotation.ComponentList;
import org.xwiki.test.junit5.mockito.ComponentTest;
import org.xwiki.test.junit5.mockito.InjectMockComponents;
import org.xwiki.test.junit5.mockito.MockComponent;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ComponentTest
@ComponentList(TestEnvironment.class)
class ContextMacroTest
    private static final DocumentReference AUTHOR = new DocumentReference("wiki", "XWiki", "author");

    private static final DocumentReference TARGET_REFERENCE = new DocumentReference("wiki", "space", "target");

    private static final DocumentReference SOURCE_REFERENCE = new DocumentReference("wiki", "space", "source");

    @Inject
    protected BlockAsyncRendererExecutor executor;

    @MockComponent
    private DocumentAccessBridge dab;

    @MockComponent
    private BeanManager beanManager;

    @MockComponent
    private MacroContentParser parser;

    @MockComponent
    private AuthorizationManager authorization;

    @MockComponent
    @Named("macro")
    private DocumentReferenceResolver<String> macroReferenceResolver;

    @MockComponent
    protected DocumentReferenceResolver<String> resolver;
    @InjectMockComponents
    private ContextMacro macro;

    @BeforeEach
    public void beforeEach() throws Exception
        when(this.beanManager.getBeanDescriptor(any())).thenReturn(descriptor);

        when(this.dab.getCurrentAuthorReference()).thenReturn(AUTHOR);

        when(this.macroReferenceResolver.resolve(eq("target"), any())).thenReturn(TARGET_REFERENCE);
        when(this.resolver.resolve("source")).thenReturn(SOURCE_REFERENCE);
    void executeWhenNoDocumentSpecified() throws Exception
            this.macro.execute(parameters, "", new MacroTransformationContext());
            assertEquals("You must specify a 'document' parameter pointing to the document to set in the "
    void executeWithReferencedDocumentNotViewableByTheAuthor() throws Exception
        doThrow(AccessDeniedException.class).when(this.authorization).checkAccess(Right.VIEW, AUTHOR, TARGET_REFERENCE);
        parameters.setDocument("target");
            this.macro.execute(parameters, "", macroContext);
            assertEquals("Author [wiki:XWiki.author] is not allowed to access target document [wiki:space.target]",
                expected.getMessage());
    void executeOk() throws Exception
        MetaData metadata = new MetaData();
        metadata.addMetaData(MetaData.SOURCE, "source");
        XDOM xdom = new XDOM(Arrays.asList(macroBlock), metadata);
        when(this.dab.getTranslatedDocumentInstance(TARGET_REFERENCE)).thenReturn(dmb);
        when(this.parser.parse(eq(""), same(macroContext), eq(false), any(MetaData.class), eq(false))).thenReturn(xdom);
        parameters.setDocument("target");
        when(this.executor.execute(any())).thenReturn(new WordBlock("result"));
        this.macro.execute(parameters, "", macroContext);
        ArgumentCaptor<BlockAsyncRendererConfiguration> configurationCaptor =
            ArgumentCaptor.forClass(BlockAsyncRendererConfiguration.class);
        verify(this.executor).execute(configurationCaptor.capture());
        BlockAsyncRendererConfiguration configuration = configurationCaptor.getValue();
        assertEquals(AUTHOR, configuration.getSecureAuthorReference());
        assertEquals(SOURCE_REFERENCE, configuration.getSecureDocumentReference());
import org.xwiki.component.manager.ComponentManager;
import org.xwiki.query.QueryManager;
import org.xwiki.refactoring.internal.ModelBridge;
import org.xwiki.refactoring.internal.ReferenceUpdater;
import org.xwiki.rendering.test.integration.junit5.RenderingTests;
import org.xwiki.security.authorization.AuthorizationManager;
import org.xwiki.security.authorization.ContextualAuthorizationManager;
import org.xwiki.template.TemplateManager;
import org.xwiki.test.TestEnvironment;
import org.xwiki.test.annotation.ComponentList;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ComponentList(TestEnvironment.class)
public class IntegrationTests implements RenderingTests
    @RenderingTests.Initialized
        // Replace the environment by a test compatible one
        componentManager.registerComponent(TestEnvironment.class);
        
        // Some components we don't really use and which trigger a lot of dependencies
        componentManager.registerMockComponent(TemplateManager.class);
        componentManager.registerMockComponent(ModelBridge.class);
        componentManager.registerMockComponent(QueryManager.class);
        componentManager.registerMockComponent(ReferenceUpdater.class);
        componentManager.registerMockComponent(AuthorizationManager.class);
        componentManager.registerMockComponent(ContextualAuthorizationManager.class);


        // Replace the context component manager
        componentManager.registerComponent(ComponentManager.class, "context", componentManager);