From ad5990cd9a44cf6a9160311ef2d80b364a105b43 Mon Sep 17 00:00:00 2001
From: Michael Hamann <michael.hamann@xwiki.com>
Date: Thu, 18 Jul 2024 16:44:26 +0200
Subject: [PATCH] XWIKI-20907: Introduce the notion of required rights

* Add a new flag to XWikiDocument if required rights shall be enforced.
* Add the new flag to the filter stream and XAR APIs, increase the XAR
  version and adapt tests.
* Add the new flag to the REST API.
* Add the new flag to the edit form to support updating it.
* Add a DocumentRequiredRightsManager API to allow getting the required
  rights that are set on a document.
* Add a DocumentAuthorizationManager to check rights using required
  rights.
* Restrict edit right to users that have all required rights.
* Add a test for DocumentRequiredRightsReader.
---
 xwiki-platform-core/pom.xml                   |  21 ++-
 .../org/xwiki/bridge/DocumentModelBridge.java |  12 ++
 .../event/model/WikiDocumentFilter.java       |   9 ++
 .../filter/xar/internal/XARDocumentModel.java |   2 +
 .../filter/xar/internal/XARFilterUtils.java   |   7 +-
 .../internal/input/DocumentLocaleReader.java  |   1 +
 .../internal/input/XARInputFilterStream.java  |  10 +-
 .../input/XARInputFilterStreamFactory.java    |  10 +-
 .../output/XAROutputFilterStream.java         |  14 +-
 .../output/XAROutputFilterStreamFactory.java  |  10 +-
 .../test/resources/document/class/class1.test |   4 +
 .../document/document/document1.test          |   7 +-
 .../document/document/document2.test          |   4 +
 .../documentattachmentwithoutcontent.test     |   7 +-
 .../document/documentwithnestedspaces1.test   |   7 +-
 .../document/documentwithunknownelements.test |   4 +
 .../document/document/emptydocument.test      |   4 +
 .../resources/document/document/legacy1.test  |   4 +
 .../document/document/legacytranslation1.test |   7 +-
 .../resources/document/document/new1.test     |   4 +
 .../resources/document/object/object1.test    |   7 +-
 .../document/object/objectwithoutclass.test   |   7 +-
 .../document/property/nullvalueproperty.test  |   2 +-
 .../test1/space/nestedspace/page.xml          |   3 +-
 .../packagefile/test1/space/page.xml          |   3 +-
 .../packagefile/xar1/space2/page2.xml         |   1 +
 .../resources/xar/legacymixed1.input.test     |  12 ++
 .../src/test/resources/xar/nested1.input.test |  12 ++
 .../src/test/resources/xar/nested2.input.test |   8 ++
 .../xar/test1-noforcedocument.input.test      |   8 ++
 .../resources/xar/test1-sourcetype.input.test |   8 ++
 .../src/test/resources/xar/test1.input.test   |   8 ++
 .../src/test/resources/xar/test1.output.test  |   4 +
 .../xar/testSkipFirstDocument.output.xml      |   4 +
 .../xar/testSkipLastSpace.output.xml          |   4 +
 .../src/test/resources/xar/xar1.input.test    |   8 ++
 .../main/java/com/xpn/xwiki/api/Document.java |  28 ++++
 .../com/xpn/xwiki/doc/LazyXWikiDocument.java  |   6 +
 .../java/com/xpn/xwiki/doc/XWikiDocument.java |  54 ++++++++
 .../XWikiDocumentLocaleEventGenerator.java    |   2 +
 .../XWikiDocumentOutputFilterStream.java      |   3 +
 .../main/java/com/xpn/xwiki/web/EditForm.java |  25 ++++
 .../src/main/resources/xwiki.db2.hbm.xml      |   2 +
 .../src/main/resources/xwiki.derby.hbm.xml    |   2 +
 .../src/main/resources/xwiki.hbm.xml          |   2 +
 .../src/main/resources/xwiki.mssql.hbm.xml    |   2 +
 .../src/main/resources/xwiki.oracle.hbm.xml   |   2 +
 .../main/resources/xwiki.postgresql.hbm.xml   |   2 +
 .../src/test/resources/filter/document1.xml   |  12 ++
 .../filter/documentwithattachment1content.xml |   4 +
 .../filter/documentwithattachment2content.xml |   4 +
 .../src/main/resources/xwiki.rest.model.xsd   |   1 +
 .../org/xwiki/rest/internal/ModelFactory.java |   6 +
 .../DefaultAuthorizationManager.java          |  25 +++-
 .../DocumentAuthorizationManager.java         |  76 +++++++++++
 .../DocumentRequiredRightsChecker.java        | 103 ++++++++++++++
 .../requiredrights/DocumentRequiredRight.java |  38 ++++++
 .../DocumentRequiredRights.java               |  42 ++++++
 .../DocumentRequiredRightsManager.java        |  47 +++++++
 .../main/resources/META-INF/components.txt    |   1 +
 ...ltAuthorizationManagerIntegrationTest.java |   6 +
 .../DefaultDocumentAuthorizationManager.java  | 122 +++++++++++++++++
 .../internal/RightsFilterListener.java        |  39 ++++++
 .../DefaultDocumentRequiredRightsManager.java |  71 ++++++++++
 .../DocumentRequiredRightsReader.java         | 103 ++++++++++++++
 .../main/resources/META-INF/components.txt    |   3 +
 .../internal/RightsFilterListenerTest.java    |  14 ++
 .../DocumentRequiredRightsReaderTest.java     | 128 ++++++++++++++++++
 .../testwikibuilding/LegacyTestWiki.java      |   6 +
 .../xar/internal/model/XarDocumentModel.java  |  18 ++-
 70 files changed, 1220 insertions(+), 26 deletions(-)
 create mode 100644 xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/DocumentAuthorizationManager.java
 create mode 100644 xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/internal/DocumentRequiredRightsChecker.java
 create mode 100644 xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/requiredrights/DocumentRequiredRight.java
 create mode 100644 xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/requiredrights/DocumentRequiredRights.java
 create mode 100644 xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/requiredrights/DocumentRequiredRightsManager.java
 create mode 100644 xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/authorization/internal/DefaultDocumentAuthorizationManager.java
 create mode 100644 xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/authorization/internal/requiredrights/DefaultDocumentRequiredRightsManager.java
 create mode 100644 xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/authorization/internal/requiredrights/DocumentRequiredRightsReader.java
 create mode 100644 xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/test/java/org/xwiki/security/authorization/internal/requiredrights/DocumentRequiredRightsReaderTest.java

diff --git a/xwiki-platform-core/pom.xml b/xwiki-platform-core/pom.xml
index 7bfe46023de..fe333ad95f2 100644
--- a/xwiki-platform-core/pom.xml
+++ b/xwiki-platform-core/pom.xml
@@ -138,10 +138,23 @@
 
                  Single justification example:
             -->
-            
-            
-            
-            
+
+            <revapi.differences>
+              <justification>Change in generated class of the REST model to add the
+                enforceRequiredRights property.</justification>
+              <criticality>allowed</criticality>
+              <differences>
+                <item>
+                  <code>java.annotation.attributeValueChanged</code>
+                  <old>class org.xwiki.rest.model.jaxb.Page</old>
+                  <new>class org.xwiki.rest.model.jaxb.Page</new>
+                  <annotationType>javax.xml.bind.annotation.XmlType</annotationType>
+                  <attribute>propOrder</attribute>
+                  <oldValue>{"language", "majorVersion", "minorVersion", "hidden", "created", "creator", "creatorName", "modified", "modifier", "modifierName", "originalMetadataAuthor", "originalMetadataAuthorName", "comment", "content", "clazz", "objects", "attachments", "hierarchy"}</oldValue>
+                  <newValue>{"language", "majorVersion", "minorVersion", "hidden", "enforceRequiredRights", "created", "creator", "creatorName", "modified", "modifier", "modifierName", "originalMetadataAuthor", "originalMetadataAuthorName", "comment", "content", "clazz", "objects", "attachments", "hierarchy"}</newValue>
+                </item>
+              </differences>
+            </revapi.differences>
           </analysisConfiguration>
         </configuration>
       </plugin>
diff --git a/xwiki-platform-core/xwiki-platform-bridge/src/main/java/org/xwiki/bridge/DocumentModelBridge.java b/xwiki-platform-core/xwiki-platform-bridge/src/main/java/org/xwiki/bridge/DocumentModelBridge.java
index dfb642810ab..535cadb0a54 100644
--- a/xwiki-platform-core/xwiki-platform-bridge/src/main/java/org/xwiki/bridge/DocumentModelBridge.java
+++ b/xwiki-platform-core/xwiki-platform-bridge/src/main/java/org/xwiki/bridge/DocumentModelBridge.java
@@ -221,4 +221,16 @@ default boolean isRestricted()
     {
         return false;
     }
+
+    /**
+     * @return {@code true} if required rights defined in a {@code XWiki.RequiredRightClass} object shall be
+     * enforced, meaning that editing will be limited to users with these rights and content of this document can't
+     * use more rights than defined in the object, {@code false} otherwise
+     * @since 16.10.0RC1
+     */
+    @Unstable
+    default boolean isEnforceRequiredRights()
+    {
+        return false;
+    }
 }
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-events/xwiki-platform-filter-event-model/src/main/java/org/xwiki/filter/event/model/WikiDocumentFilter.java b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-events/xwiki-platform-filter-event-model/src/main/java/org/xwiki/filter/event/model/WikiDocumentFilter.java
index f371bfc469e..5ffb495b5cb 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-events/xwiki-platform-filter-event-model/src/main/java/org/xwiki/filter/event/model/WikiDocumentFilter.java
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-events/xwiki-platform-filter-event-model/src/main/java/org/xwiki/filter/event/model/WikiDocumentFilter.java
@@ -26,6 +26,7 @@
 import org.xwiki.filter.FilterEventParameters;
 import org.xwiki.filter.FilterException;
 import org.xwiki.filter.annotation.Default;
+import org.xwiki.stability.Unstable;
 
 /**
  * Document related events.
@@ -197,6 +198,14 @@ public interface WikiDocumentFilter
      */
     String PARAMETER_REVISION_MINOR = "revision_minor";
 
+    // required rights
+    /**
+     * @type {@link Boolean}
+     * @since 16.10.0RC1
+     */
+    @Unstable
+    String PARAMETER_ENFORCE_REQUIRED_RIGHTS = "enforce_required_rights";
+
     // Events
 
     /**
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/XARDocumentModel.java b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/XARDocumentModel.java
index 42f2cc533bb..cc0eecfb769 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/XARDocumentModel.java
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/XARDocumentModel.java
@@ -72,6 +72,8 @@ public class XARDocumentModel extends XarDocumentModel
                 new EventParameter(XWikiWikiDocumentFilter.PARAMETER_REVISION_EFFECTIVEMETADATA_AUTHOR));
             put(ELEMENT_REVISION_ORIGINALMEDATAAUTHOR,
                 new EventParameter(XWikiWikiDocumentFilter.PARAMETER_REVISION_ORIGINALMETADATA_AUTHOR));
+            put(ELEMENT_ENFORCE_REQUIRED_RIGHTS,
+                new EventParameter(XWikiWikiDocumentFilter.PARAMETER_ENFORCE_REQUIRED_RIGHTS, Boolean.class));
             put(ELEMENT_REVISION_COMMENT, new EventParameter(XWikiWikiDocumentFilter.PARAMETER_REVISION_COMMENT));
             put(ELEMENT_REVISION_DATE, new EventParameter(XWikiWikiDocumentFilter.PARAMETER_REVISION_DATE, Date.class));
             put(ELEMENT_REVISION_MINOR,
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/XARFilterUtils.java b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/XARFilterUtils.java
index 3fcb53a6cf2..3a2f5802d06 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/XARFilterUtils.java
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/XARFilterUtils.java
@@ -76,10 +76,15 @@ public EventParameter(String name)
      */
     public static final String ROLEHINT_15 = ROLEHINT_PREFIX + XarDocumentModel.VERSION_15;
 
+    /**
+     * @since 16.10.0RC1
+     */
+    public static final String ROLEHINT_16 = ROLEHINT_PREFIX + XarDocumentModel.VERSION_16;
+
     /**
      * @since 7.2M1
      */
-    public static final String ROLEHINT_CURRENT = ROLEHINT_15;
+    public static final String ROLEHINT_CURRENT = ROLEHINT_16;
 
     /**
      * @since 6.2M1
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/input/DocumentLocaleReader.java b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/input/DocumentLocaleReader.java
index e526cea9742..3f355e08922 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/input/DocumentLocaleReader.java
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/input/DocumentLocaleReader.java
@@ -404,6 +404,7 @@ private void readDocument(XMLStreamReader xmlReader, Object filter, XARInputFilt
         // Initialize with a few defaults (thing that don't exist in old XAR format)
         this.currentDocumentRevisionParameters.put(XWikiWikiDocumentFilter.PARAMETER_SYNTAX, Syntax.XWIKI_1_0);
         this.currentDocumentRevisionParameters.put(XWikiWikiDocumentFilter.PARAMETER_HIDDEN, false);
+        this.currentDocumentRevisionParameters.put(XWikiWikiDocumentFilter.PARAMETER_ENFORCE_REQUIRED_RIGHTS, false);
 
         // Reference
         String referenceString = xmlReader.getAttributeValue(null, XARDocumentModel.ATTRIBUTE_DOCUMENT_REFERENCE);
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/input/XARInputFilterStream.java b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/input/XARInputFilterStream.java
index 36ad0c86334..394c948d9dc 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/input/XARInputFilterStream.java
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/input/XARInputFilterStream.java
@@ -44,8 +44,14 @@
  * @version $Id$
  * @since 6.2M1
  */
-@Component(hints = {XARFilterUtils.ROLEHINT_15, XARFilterUtils.ROLEHINT_14, XARFilterUtils.ROLEHINT_13,
-    XARFilterUtils.ROLEHINT_12, XARFilterUtils.ROLEHINT_11})
+@Component(hints = {
+    XARFilterUtils.ROLEHINT_16,
+    XARFilterUtils.ROLEHINT_15,
+    XARFilterUtils.ROLEHINT_14,
+    XARFilterUtils.ROLEHINT_13,
+    XARFilterUtils.ROLEHINT_12,
+    XARFilterUtils.ROLEHINT_11
+})
 @InstantiationStrategy(ComponentInstantiationStrategy.PER_LOOKUP)
 public class XARInputFilterStream extends AbstractBeanInputFilterStream<XARInputProperties, XARInputFilter>
 {
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/input/XARInputFilterStreamFactory.java b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/input/XARInputFilterStreamFactory.java
index f31841c318d..adbe2f295f3 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/input/XARInputFilterStreamFactory.java
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/input/XARInputFilterStreamFactory.java
@@ -33,8 +33,14 @@
  * @version $Id$
  * @since 6.2M1
  */
-@Component(hints = {XARFilterUtils.ROLEHINT_15, XARFilterUtils.ROLEHINT_14, XARFilterUtils.ROLEHINT_13,
-    XARFilterUtils.ROLEHINT_12, XARFilterUtils.ROLEHINT_11})
+@Component(hints = {
+    XARFilterUtils.ROLEHINT_16,
+    XARFilterUtils.ROLEHINT_15,
+    XARFilterUtils.ROLEHINT_14,
+    XARFilterUtils.ROLEHINT_13,
+    XARFilterUtils.ROLEHINT_12,
+    XARFilterUtils.ROLEHINT_11
+})
 @Singleton
 public class XARInputFilterStreamFactory
     extends AbstractBeanInputFilterStreamFactory<XARInputProperties, XARInputFilter>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/output/XAROutputFilterStream.java b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/output/XAROutputFilterStream.java
index 399ff9f4663..752f82ac1f8 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/output/XAROutputFilterStream.java
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/output/XAROutputFilterStream.java
@@ -69,8 +69,14 @@
  * @version $Id$
  * @since 6.2M1
  */
-@Component(hints = {XARFilterUtils.ROLEHINT_15, XARFilterUtils.ROLEHINT_14, XARFilterUtils.ROLEHINT_13,
-    XARFilterUtils.ROLEHINT_12, XARFilterUtils.ROLEHINT_11})
+@Component(hints = {
+    XARFilterUtils.ROLEHINT_16,
+    XARFilterUtils.ROLEHINT_15,
+    XARFilterUtils.ROLEHINT_14,
+    XARFilterUtils.ROLEHINT_13,
+    XARFilterUtils.ROLEHINT_12,
+    XARFilterUtils.ROLEHINT_11
+})
 @InstantiationStrategy(ComponentInstantiationStrategy.PER_LOOKUP)
 public class XAROutputFilterStream extends AbstractBeanOutputFilterStream<XAROutputProperties> implements XARFilter
 {
@@ -382,6 +388,10 @@ public void beginWikiDocumentRevision(String revision, FilterEventParameters par
                 this.writer.writeElement(XarDocumentModel.ELEMENT_HIDDEN,
                     toString(parameters.get(XWikiWikiDocumentFilter.PARAMETER_HIDDEN)));
             }
+            if (parameters.containsKey(XWikiWikiDocumentFilter.PARAMETER_ENFORCE_REQUIRED_RIGHTS)) {
+                this.writer.writeElement(XarDocumentModel.ELEMENT_ENFORCE_REQUIRED_RIGHTS,
+                    toString(parameters.get(XWikiWikiDocumentFilter.PARAMETER_ENFORCE_REQUIRED_RIGHTS)));
+            }
             if (parameters.containsKey(XWikiWikiDocumentFilter.PARAMETER_CONTENT)) {
                 this.writer.writeElement(XarDocumentModel.ELEMENT_CONTENT,
                     (String) parameters.get(XWikiWikiDocumentFilter.PARAMETER_CONTENT));
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/output/XAROutputFilterStreamFactory.java b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/output/XAROutputFilterStreamFactory.java
index 6d28e95627c..3a64f7484ad 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/output/XAROutputFilterStreamFactory.java
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/main/java/org/xwiki/filter/xar/internal/output/XAROutputFilterStreamFactory.java
@@ -34,8 +34,14 @@
  * @version $Id$
  * @since 6.2M1
  */
-@Component(hints = {XARFilterUtils.ROLEHINT_15, XARFilterUtils.ROLEHINT_14, XARFilterUtils.ROLEHINT_13,
-    XARFilterUtils.ROLEHINT_12, XARFilterUtils.ROLEHINT_11})
+@Component(hints = {
+    XARFilterUtils.ROLEHINT_16,
+    XARFilterUtils.ROLEHINT_15,
+    XARFilterUtils.ROLEHINT_14,
+    XARFilterUtils.ROLEHINT_13,
+    XARFilterUtils.ROLEHINT_12,
+    XARFilterUtils.ROLEHINT_11
+})
 @Singleton
 public class XAROutputFilterStreamFactory extends AbstractBeanOutputFilterStreamFactory<XAROutputProperties, XARFilter>
 {
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/class/class1.test b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/class/class1.test
index 33f5d88777d..f37e980d1f6 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/class/class1.test
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/class/class1.test
@@ -58,6 +58,10 @@
               <string>hidden</string>
               <boolean>false</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>false</boolean>
+            </entry>
           </parameters>
         </p>
         <wikiClass>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/document1.test b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/document1.test
index 78de35d8ba2..63b77d0c45b 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/document1.test
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/document1.test
@@ -2,7 +2,7 @@
 .inputexpect|xwiki+xar/1.4
 .#------------------------------------------------------------------------------
 <?xml version='1.1' encoding='UTF-8'?>
-<xwikidoc version="1.5" reference="space.page" locale="">
+<xwikidoc version="1.6" reference="space.page" locale="">
   <web>space</web>
   <name>page</name>
   <language/>
@@ -26,6 +26,7 @@
   <minorEdit>false</minorEdit>
   <syntaxId>syntax/1.0</syntaxId>
   <hidden>true</hidden>
+  <enforceRequiredRights>true</enforceRequiredRights>
   <content>content</content>
   <renderedcontent/>
   <attachment>
@@ -167,6 +168,10 @@
               <string>hidden</string>
               <boolean>true</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>true</boolean>
+            </entry>
             <entry>
               <string>parent_reference</string>
               <org.xwiki.model.reference.EntityReference>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/document2.test b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/document2.test
index 670b3aacc42..173c3b7b68f 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/document2.test
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/document2.test
@@ -165,6 +165,10 @@
               <string>hidden</string>
               <boolean>true</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>false</boolean>
+            </entry>
             <entry>
               <string>parent_reference</string>
               <org.xwiki.model.reference.EntityReference>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/documentattachmentwithoutcontent.test b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/documentattachmentwithoutcontent.test
index 334ed032575..1bc08f32de8 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/documentattachmentwithoutcontent.test
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/documentattachmentwithoutcontent.test
@@ -3,7 +3,7 @@
 .# Attachment with no content
 .#------------------------------------------------------------------------------
 <?xml version='1.1' encoding='UTF-8'?>
-<xwikidoc version="1.5" reference="space.page" locale="">
+<xwikidoc version="1.6" reference="space.page" locale="">
   <web>space</web>
   <name>page</name>
   <language/>
@@ -26,6 +26,7 @@
   <minorEdit>false</minorEdit>
   <syntaxId>syntax/1.0</syntaxId>
   <hidden>true</hidden>
+  <enforceRequiredRights>true</enforceRequiredRights>
   <content>content</content>
   <renderedcontent/>
   <attachment>
@@ -87,6 +88,10 @@
               <string>hidden</string>
               <boolean>true</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>true</boolean>
+            </entry>
             <entry>
               <string>parent_reference</string>
               <org.xwiki.model.reference.EntityReference>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/documentwithnestedspaces1.test b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/documentwithnestedspaces1.test
index 55a62b98a49..d411e20eee2 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/documentwithnestedspaces1.test
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/documentwithnestedspaces1.test
@@ -2,7 +2,7 @@
 .inputexpect|xwiki+xar/1.3
 .#------------------------------------------------------------------------------
 <?xml version='1.1' encoding='UTF-8'?>
-<xwikidoc version="1.5" reference="space1.space2.page" locale="">
+<xwikidoc version="1.6" reference="space1.space2.page" locale="">
   <web>space1.space2</web>
   <name>page</name>
   <language/>
@@ -25,6 +25,7 @@
   <minorEdit>false</minorEdit>
   <syntaxId>syntax/1.0</syntaxId>
   <hidden>true</hidden>
+  <enforceRequiredRights>true</enforceRequiredRights>
   <content>content</content>
   <renderedcontent/>
   <attachment>
@@ -165,6 +166,10 @@
                 <string>hidden</string>
                 <boolean>true</boolean>
               </entry>
+              <entry>
+                <string>enforce_required_rights</string>
+                <boolean>true</boolean>
+              </entry>
               <entry>
                 <string>parent_reference</string>
                 <org.xwiki.model.reference.EntityReference>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/documentwithunknownelements.test b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/documentwithunknownelements.test
index 093765872ff..42215e0a843 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/documentwithunknownelements.test
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/documentwithunknownelements.test
@@ -144,6 +144,10 @@
               <string>hidden</string>
               <boolean>true</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>false</boolean>
+            </entry>
             <entry>
               <string>parent_reference</string>
               <org.xwiki.model.reference.EntityReference>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/emptydocument.test b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/emptydocument.test
index a31e808f89d..552894aa5e3 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/emptydocument.test
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/emptydocument.test
@@ -39,6 +39,10 @@
               <string>hidden</string>
               <boolean>false</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>false</boolean>
+            </entry>
           </parameters>
         </p>
       </wikiDocumentRevision>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/legacy1.test b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/legacy1.test
index 607484f12dc..1646850cd60 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/legacy1.test
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/legacy1.test
@@ -165,6 +165,10 @@
               <string>hidden</string>
               <boolean>true</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>false</boolean>
+            </entry>
             <entry>
               <string>parent_reference</string>
               <org.xwiki.model.reference.EntityReference>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/legacytranslation1.test b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/legacytranslation1.test
index a5530e5459d..9f93e8f4ae9 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/legacytranslation1.test
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/legacytranslation1.test
@@ -3,7 +3,7 @@
 .# Make sure legacy XAR format is properly parsed
 .#------------------------------------------------------------------------------
 <?xml version='1.1' encoding='UTF-8'?>
-<xwikidoc version="1.5" reference="space.page" locale="fr">
+<xwikidoc version="1.6" reference="space.page" locale="fr">
   <web>space</web>
   <name>page</name>
   <language>fr</language>
@@ -26,6 +26,7 @@
   <minorEdit>false</minorEdit>
   <syntaxId>syntax/1.0</syntaxId>
   <hidden>true</hidden>
+  <enforceRequiredRights>true</enforceRequiredRights>
   <content>content</content>
   <renderedcontent/>
   <attachment>
@@ -165,6 +166,10 @@
               <string>hidden</string>
               <boolean>true</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>true</boolean>
+            </entry>
             <entry>
               <string>parent_reference</string>
               <org.xwiki.model.reference.EntityReference>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/new1.test b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/new1.test
index 1afa12a6bab..36cc0641264 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/new1.test
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/document/new1.test
@@ -176,6 +176,10 @@
               <string>hidden</string>
               <boolean>true</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>false</boolean>
+            </entry>
             <entry>
               <string>parent_reference</string>
               <org.xwiki.model.reference.EntityReference>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/object/object1.test b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/object/object1.test
index 7e8ea8b84dd..6478c224409 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/object/object1.test
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/object/object1.test
@@ -2,7 +2,7 @@
 .inputexpect|xwiki+xar/1.1
 .#------------------------------------------------------------------------------
 <?xml version='1.1' encoding='UTF-8'?>
-<xwikidoc version="1.5" reference="space.page" locale="">
+<xwikidoc version="1.6" reference="space.page" locale="">
   <web>space</web>
   <name>page</name>
   <language/>
@@ -11,6 +11,7 @@
   <version>1.1</version>
   <syntaxId>xwiki/1.0</syntaxId>
   <hidden>false</hidden>
+  <enforceRequiredRights>true</enforceRequiredRights>
   <object>
     <name>space.page</name>
     <number>0</number>
@@ -117,6 +118,10 @@
               <string>hidden</string>
               <boolean>false</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>true</boolean>
+            </entry>
           </parameters>
         </p>
         <wikiObject name="space.class[0]">
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/object/objectwithoutclass.test b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/object/objectwithoutclass.test
index 358e8cd8633..4e45c482962 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/object/objectwithoutclass.test
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/object/objectwithoutclass.test
@@ -2,7 +2,7 @@
 .inputexpect|xwiki+xar/1.1
 .#------------------------------------------------------------------------------
 <?xml version='1.1' encoding='UTF-8'?>
-<xwikidoc version="1.5" reference="space.page" locale="">
+<xwikidoc version="1.6" reference="space.page" locale="">
   <web>space</web>
   <name>page</name>
   <language/>
@@ -11,6 +11,7 @@
   <version>1.1</version>
   <syntaxId>xwiki/1.0</syntaxId>
   <hidden>false</hidden>
+  <enforceRequiredRights>true</enforceRequiredRights>
   <object>
     <name>space.page</name>
     <number>0</number>
@@ -52,6 +53,10 @@
               <string>hidden</string>
               <boolean>false</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>true</boolean>
+            </entry>
           </parameters>
         </p>
         <wikiObject name="space.class[0]">
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/property/nullvalueproperty.test b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/property/nullvalueproperty.test
index 0bd1bbd134c..17b95720939 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/property/nullvalueproperty.test
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/document/property/nullvalueproperty.test
@@ -2,7 +2,7 @@
 .expect|xwiki+xar/1.1
 .#------------------------------------------------------------------------------
 <?xml version='1.1' encoding='UTF-8'?>
-<xwikidoc version="1.5" reference="space.page" locale="">
+<xwikidoc version="1.6" reference="space.page" locale="">
   <web>space</web>
   <name>page</name>
   <language/>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/packagefile/test1/space/nestedspace/page.xml b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/packagefile/test1/space/nestedspace/page.xml
index ec48980eaf6..9711add6a39 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/packagefile/test1/space/nestedspace/page.xml
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/packagefile/test1/space/nestedspace/page.xml
@@ -1,5 +1,5 @@
 <?xml version='1.1' encoding='UTF-8'?>
-<xwikidoc version="1.5" reference="space.nestedspace.page" locale="">
+<xwikidoc version="1.6" reference="space.nestedspace.page" locale="">
   <web>space.nestedspace</web>
   <name>page</name>
   <language/>
@@ -19,6 +19,7 @@
   <validationScript/>
   <comment/>
   <minorEdit>false</minorEdit>
+  <enforceRequiredRights>true</enforceRequiredRights>
   <syntaxId>xwiki/2.0</syntaxId>
   <hidden>false</hidden>
   <content>content</content>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/packagefile/test1/space/page.xml b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/packagefile/test1/space/page.xml
index 1e4efba789a..bb0cb05bea7 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/packagefile/test1/space/page.xml
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/packagefile/test1/space/page.xml
@@ -1,5 +1,5 @@
 <?xml version='1.1' encoding='UTF-8'?>
-<xwikidoc version="1.5" reference="space.page" locale="">
+<xwikidoc version="1.6" reference="space.page" locale="">
   <web>space</web>
   <name>page</name>
   <language/>
@@ -21,6 +21,7 @@
   <minorEdit>false</minorEdit>
   <syntaxId>xwiki/2.0</syntaxId>
   <hidden>false</hidden>
+  <enforceRequiredRights>false</enforceRequiredRights>
   <content>content</content>
   <class>
     <name>space.page</name>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/packagefile/xar1/space2/page2.xml b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/packagefile/xar1/space2/page2.xml
index a7e2f81fc7c..e41700ea89d 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/packagefile/xar1/space2/page2.xml
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/packagefile/xar1/space2/page2.xml
@@ -20,5 +20,6 @@
   <minorEdit>false</minorEdit>
   <syntaxId>xwiki/2.0</syntaxId>
   <hidden>false</hidden>
+  <enforceRequiredRights>true</enforceRequiredRights>
   <content>content2</content>
 </xwikidoc>
\ No newline at end of file
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/legacymixed1.input.test b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/legacymixed1.input.test
index d36b6ca2c5d..a37e11ba3c7 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/legacymixed1.input.test
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/legacymixed1.input.test
@@ -47,6 +47,10 @@
               <string>hidden</string>
               <boolean>true</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>false</boolean>
+            </entry>
             <entry>
               <string>parent_reference</string>
               <org.xwiki.model.reference.EntityReference>
@@ -151,6 +155,10 @@
               <string>hidden</string>
               <boolean>true</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>false</boolean>
+            </entry>
             <entry>
               <string>parent_reference</string>
               <org.xwiki.model.reference.EntityReference>
@@ -255,6 +263,10 @@
               <string>hidden</string>
               <boolean>true</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>false</boolean>
+            </entry>
             <entry>
               <string>parent_reference</string>
               <org.xwiki.model.reference.EntityReference>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/nested1.input.test b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/nested1.input.test
index a028763a3c2..fe975a13cc6 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/nested1.input.test
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/nested1.input.test
@@ -43,6 +43,10 @@
               <string>hidden</string>
               <boolean>false</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>false</boolean>
+            </entry>
             <entry>
               <string>revision_author</string>
               <string>XWiki.author</string>
@@ -135,6 +139,10 @@
                     <string>hidden</string>
                     <boolean>false</boolean>
                   </entry>
+                  <entry>
+                    <string>enforce_required_rights</string>
+                    <boolean>false</boolean>
+                  </entry>
                   <entry>
                     <string>revision_author</string>
                     <string>XWiki.author</string>
@@ -227,6 +235,10 @@
               <string>hidden</string>
               <boolean>false</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>false</boolean>
+            </entry>
             <entry>
               <string>revision_author</string>
               <string>XWiki.author</string>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/nested2.input.test b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/nested2.input.test
index 01dd8e900e0..a0732fcc3d1 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/nested2.input.test
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/nested2.input.test
@@ -44,6 +44,10 @@
                 <string>hidden</string>
                 <boolean>false</boolean>
               </entry>
+              <entry>
+                <string>enforce_required_rights</string>
+                <boolean>false</boolean>
+              </entry>
               <entry>
                 <string>revision_author</string>
                 <string>XWiki.author</string>
@@ -137,6 +141,10 @@
                 <string>hidden</string>
                 <boolean>false</boolean>
               </entry>
+              <entry>
+                <string>enforce_required_rights</string>
+                <boolean>false</boolean>
+              </entry>
               <entry>
                 <string>revision_author</string>
                 <string>XWiki.author</string>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/test1-noforcedocument.input.test b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/test1-noforcedocument.input.test
index 7804a08b4fe..e2c2a43764d 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/test1-noforcedocument.input.test
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/test1-noforcedocument.input.test
@@ -43,6 +43,10 @@
                 <string>hidden</string>
                 <boolean>false</boolean>
               </entry>
+              <entry>
+                <string>enforce_required_rights</string>
+                <boolean>false</boolean>
+              </entry>
               <entry>
                 <string>revision_author</string>
                 <string>XWiki.author</string>
@@ -429,6 +433,10 @@
               <string>hidden</string>
               <boolean>false</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>false</boolean>
+            </entry>
             <entry>
               <string>revision_author</string>
               <string>XWiki.author</string>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/test1-sourcetype.input.test b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/test1-sourcetype.input.test
index a7beb10b8de..c2d7a0ed224 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/test1-sourcetype.input.test
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/test1-sourcetype.input.test
@@ -43,6 +43,10 @@
                 <string>hidden</string>
                 <boolean>false</boolean>
               </entry>
+              <entry>
+                <string>enforce_required_rights</string>
+                <boolean>false</boolean>
+              </entry>
               <entry>
                 <string>revision_author</string>
                 <string>XWiki.author</string>
@@ -429,6 +433,10 @@
               <string>hidden</string>
               <boolean>false</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>false</boolean>
+            </entry>
             <entry>
               <string>revision_author</string>
               <string>XWiki.author</string>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/test1.input.test b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/test1.input.test
index 2dcd3a50640..c02ae8e92aa 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/test1.input.test
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/test1.input.test
@@ -43,6 +43,10 @@
                 <string>hidden</string>
                 <boolean>false</boolean>
               </entry>
+              <entry>
+                <string>enforce_required_rights</string>
+                <boolean>false</boolean>
+              </entry>
               <entry>
                 <string>revision_author</string>
                 <string>XWiki.author</string>
@@ -429,6 +433,10 @@
               <string>hidden</string>
               <boolean>false</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>false</boolean>
+            </entry>
             <entry>
               <string>revision_author</string>
               <string>XWiki.author</string>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/test1.output.test b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/test1.output.test
index f9459a4cb15..9c641ca36c3 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/test1.output.test
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/test1.output.test
@@ -42,6 +42,10 @@
                 <string>hidden</string>
                 <boolean>false</boolean>
               </entry>
+              <entry>
+                <string>enforce_required_rights</string>
+                <boolean>true</boolean>
+              </entry>
               <entry>
                 <string>revision_author</string>
                 <string>XWiki.author</string>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/testSkipFirstDocument.output.xml b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/testSkipFirstDocument.output.xml
index b4004f3b04b..823bb94604f 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/testSkipFirstDocument.output.xml
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/testSkipFirstDocument.output.xml
@@ -39,6 +39,10 @@
               <string>hidden</string>
               <boolean>false</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>true</boolean>
+            </entry>
             <entry>
               <string>revision_author</string>
               <string>XWiki.author</string>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/testSkipLastSpace.output.xml b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/testSkipLastSpace.output.xml
index ec147da2d5b..4205194ad91 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/testSkipLastSpace.output.xml
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/testSkipLastSpace.output.xml
@@ -39,6 +39,10 @@
               <string>hidden</string>
               <boolean>false</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>false</boolean>
+            </entry>
             <entry>
               <string>revision_author</string>
               <string>XWiki.author</string>
diff --git a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/xar1.input.test b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/xar1.input.test
index 744c305db9f..900643e203a 100644
--- a/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/xar1.input.test
+++ b/xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-streams/xwiki-platform-filter-stream-xar/src/test/resources/xar/xar1.input.test
@@ -43,6 +43,10 @@
               <string>hidden</string>
               <boolean>false</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>false</boolean>
+            </entry>
             <entry>
               <string>revision_author</string>
               <string>XWiki.author</string>
@@ -134,6 +138,10 @@
               <string>hidden</string>
               <boolean>false</boolean>
             </entry>
+            <entry>
+              <string>enforce_required_rights</string>
+              <boolean>false</boolean>
+            </entry>
             <entry>
               <string>revision_author</string>
               <string>XWiki.author</string>
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/api/Document.java b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/api/Document.java
index a610a517951..fd0ab72c1fa 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/api/Document.java
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/api/Document.java
@@ -3263,6 +3263,34 @@ public void setHidden(boolean hidden)
         this.doc.setHidden(hidden);
     }
 
+    /**
+     * @return {@code true} if required rights defined in a {@code XWiki.RequiredRightClass} object shall be
+     * enforced, meaning that editing will be limited to users with these rights and content of this document can't
+     * use more rights than defined in the object, {@code false} otherwise
+     * @since 16.6.0RC1
+     */
+    @Unstable
+    public boolean isEnforceRequiredRights()
+    {
+        return this.doc.isEnforceRequiredRights();
+    }
+
+    /**
+     * @param enforceRequiredRights if required rights defined in a {@code XWiki.RequiredRightClass} object shall be
+     * enforced, meaning that editing will be limited to users with these rights and content of this document can't use
+     * more rights than defined in the object
+     * @since 16.6.0RC1
+     */
+    @Unstable
+    public void setEnforceRequiredRights(boolean enforceRequiredRights)
+    {
+        getDoc().setEnforceRequiredRights(enforceRequiredRights);
+
+        updateAuthor();
+
+        updateContentAuthor();
+    }
+
     /**
      * Drop permissions for the remainder of the rendering cycle. After this is called:
      * <ul>
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/doc/LazyXWikiDocument.java b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/doc/LazyXWikiDocument.java
index b5eea80f193..c9c5ffe19fa 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/doc/LazyXWikiDocument.java
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/doc/LazyXWikiDocument.java
@@ -286,6 +286,12 @@ public Boolean isHidden()
         return getDocument().isHidden();
     }
 
+    @Override
+    public boolean isEnforceRequiredRights()
+    {
+        return getDocument().isEnforceRequiredRights();
+    }
+
     @Override
     public XWikiDocumentArchive getDocumentArchive()
     {
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java
index 33e7c5ca07e..4b5272563d4 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java
@@ -518,6 +518,13 @@ private static Transformation getMacroTransformation()
      */
     private Syntax syntax;
 
+    /**
+     * If required rights that can be defined in a {@code XWiki.RequiredRightClass} shall be enforced. For
+     * backwards-compatibility reasons, this is {@code false} by default.
+     * @since 16.10.0RC1
+     */
+    private boolean enforceRequiredRights = false;
+
     /**
      * Is latest modification a minor edit.
      */
@@ -4189,6 +4196,11 @@ public void readDocMetaFromForm(EditForm eform, XWikiContext context) throws XWi
         if (eform.getHidden() != null) {
             setHidden("1".equals(eform.getHidden()));
         }
+
+        // Read the enforce required rights flag from the form
+        if (eform.getEnforceRequiredRights() != null) {
+            setEnforceRequiredRights("1".equals(eform.getEnforceRequiredRights()));
+        }
     }
 
     private Syntax resolveSyntax(String syntaxId)
@@ -4537,6 +4549,7 @@ public void clone(XWikiDocument document)
         setMinorEdit(document.isMinorEdit());
         setSyntax(document.getSyntax());
         setHidden(document.isHidden());
+        setEnforceRequiredRights(document.isEnforceRequiredRights());
 
         cloneXObjects(document);
         cloneAttachments(document);
@@ -4620,6 +4633,7 @@ private XWikiDocument cloneInternal(DocumentReference newDocumentReference, bool
             doc.setSyntax(getSyntax());
             doc.setHidden(isHidden());
             doc.setRestricted(isRestricted());
+            doc.setEnforceRequiredRights(isEnforceRequiredRights());
 
             if (this.xClass != null) {
                 doc.setXClass(this.xClass.clone());
@@ -4918,6 +4932,10 @@ public boolean equalsData(XWikiDocument otherDocument)
             return false;
         }
 
+        if (isEnforceRequiredRights() != otherDocument.isEnforceRequiredRights()) {
+            return false;
+        }
+
         // XClass
 
         if (!getXClass().equals(otherDocument.getXClass())) {
@@ -7084,6 +7102,11 @@ public List<MetaDataDiff> getMetaDataDiff(XWikiDocument fromDoc, XWikiDocument t
             list.add(new MetaDataDiff("hidden", fromDoc.isHidden(), toDoc.isHidden()));
         }
 
+        if (fromDoc.isEnforceRequiredRights() != toDoc.isEnforceRequiredRights()) {
+            list.add(new MetaDataDiff("enforceRequiredRights", fromDoc.isEnforceRequiredRights(),
+                toDoc.isEnforceRequiredRights()));
+        }
+
         return list;
     }
 
@@ -7700,6 +7723,32 @@ public void setSyntaxId(String syntaxId)
         setSyntax(syntax);
     }
 
+    /**
+     * @return {@code true} if required rights defined in a {@code XWiki.RequiredRightClass} object shall be
+     * enforced, meaning that editing will be limited to users with these rights and content of this document can't
+     * use more rights than defined in the object, {@code false} otherwise
+     * @since 16.10.0RC1
+     */
+    @Unstable
+    public boolean isEnforceRequiredRights()
+    {
+        return this.enforceRequiredRights;
+    }
+
+    /**
+     * @param enforceRequiredRights if required rights defined in a {@code XWiki.RequiredRightClass} object shall be
+     * enforced, meaning that editing will be limited to users with these rights and content of this document can't use
+     * more rights than defined in the object
+     * @since 16.10.0RC1
+     */
+    @Unstable
+    public void setEnforceRequiredRights(boolean enforceRequiredRights)
+    {
+        this.enforceRequiredRights = enforceRequiredRights;
+
+        setContentDirty(true);
+    }
+
     public Vector<BaseObject> getComments(boolean asc)
     {
         List<BaseObject> list = getXObjects(COMMENTSCLASS_REFERENCE);
@@ -9370,6 +9419,11 @@ public boolean apply(XWikiDocument document, boolean clean)
             modified = true;
         }
 
+        if (isEnforceRequiredRights() != document.isEnforceRequiredRights()) {
+            setEnforceRequiredRights(document.isEnforceRequiredRights());
+            modified = true;
+        }
+
         // /////////////////////////////////
         // XObjects
 
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/internal/filter/input/XWikiDocumentLocaleEventGenerator.java b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/internal/filter/input/XWikiDocumentLocaleEventGenerator.java
index 9fd24ad2678..35f2141bd7f 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/internal/filter/input/XWikiDocumentLocaleEventGenerator.java
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/internal/filter/input/XWikiDocumentLocaleEventGenerator.java
@@ -183,6 +183,8 @@ private void writeRevision(XWikiDocument document, Object filter, XWikiDocumentF
         }
         revisionParameters.put(WikiDocumentFilter.PARAMETER_SYNTAX, document.getSyntax());
         revisionParameters.put(WikiDocumentFilter.PARAMETER_HIDDEN, document.isHidden());
+        revisionParameters.put(XWikiWikiDocumentFilter.PARAMETER_ENFORCE_REQUIRED_RIGHTS,
+            document.isEnforceRequiredRights());
 
         revisionParameters.put(WikiDocumentFilter.PARAMETER_REVISION_EFFECTIVEMETADATA_AUTHOR, toString(document.getAuthors().getEffectiveMetadataAuthor()));
         revisionParameters.put(WikiDocumentFilter.PARAMETER_REVISION_ORIGINALMETADATA_AUTHOR, toString(document.getAuthors().getOriginalMetadataAuthor()));
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/internal/filter/output/XWikiDocumentOutputFilterStream.java b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/internal/filter/output/XWikiDocumentOutputFilterStream.java
index 90a066ce4e2..5db50c843e8 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/internal/filter/output/XWikiDocumentOutputFilterStream.java
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/internal/filter/output/XWikiDocumentOutputFilterStream.java
@@ -293,6 +293,9 @@ private void begin(FilterEventParameters parameters) throws FilterException
 
         this.entity.setMinorEdit(getBoolean(WikiDocumentFilter.PARAMETER_REVISION_MINOR, parameters, false));
 
+        this.entity.setEnforceRequiredRights(getBoolean(WikiDocumentFilter.PARAMETER_ENFORCE_REQUIRED_RIGHTS,
+            parameters, false));
+
         String revisions =
             getString(XWikiWikiDocumentFilter.PARAMETER_JRCSREVISIONS, this.currentLocaleParameters, null);
         if (revisions != null) {
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/EditForm.java b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/EditForm.java
index ce57090f11a..881ad496fc5 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/EditForm.java
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/EditForm.java
@@ -33,8 +33,10 @@
 import org.apache.commons.lang3.math.NumberUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.xwiki.stability.Unstable;
 import org.xwiki.store.TemporaryAttachmentSessionsManager;
 
+import com.xpn.xwiki.doc.XWikiDocument;
 import com.xpn.xwiki.util.Util;
 
 /**
@@ -97,6 +99,8 @@ public class EditForm extends XWikiForm
     private boolean convertSyntax;
 
     private String hidden;
+
+    private String enforceRequiredRights;
     
     private ObjectPolicyType objectPolicy;
 
@@ -129,6 +133,7 @@ public void readRequest()
         setSyntaxId(request.getParameter("syntaxId"));
         setConvertSyntax(Boolean.valueOf(request.getParameter("convertSyntax")));
         setHidden(request.getParameter("xhidden"));
+        setEnforceRequiredRights(request.getParameter("enforceRequiredRights"));
         setObjectPolicy(request.getParameter("objectPolicy"));
         setUpdateOrCreateMap(request);
         setObjectsToRemove(request.getParameterValues("deletedObjects"));
@@ -357,6 +362,26 @@ public void setHidden(String hidden)
         this.hidden = hidden;
     }
 
+    /**
+     * @return the enforce required rights flag, see {@link XWikiDocument#isEnforceRequiredRights()}
+     * @since 16.10.0RC1
+     */
+    @Unstable
+    public String getEnforceRequiredRights()
+    {
+        return this.enforceRequiredRights;
+    }
+
+    /**
+     * @param enforceRequiredRights the enforce required rights flag, see {@link XWikiDocument#isEnforceRequiredRights()}
+     * @since 16.10.0RC1
+     */
+    @Unstable
+    public void setEnforceRequiredRights(String enforceRequiredRights)
+    {
+        this.enforceRequiredRights = enforceRequiredRights;
+    }
+
     /**
      * Return the object policy given in the HTTP request. See {@link com.xpn.xwiki.web.ObjectPolicyType
      * ObjectPolicyType} for more information about what is an object policy.
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.db2.hbm.xml b/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.db2.hbm.xml
index f10d3cdbf5e..0b32367b840 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.db2.hbm.xml
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.db2.hbm.xml
@@ -75,6 +75,8 @@
         <property name="syntaxId" type="string" column="XWD_SYNTAX_ID" length="50" />
         <property name="hidden" type="boolean" column="XWD_HIDDEN" index="DOC_HIDDEN" not-null="true" />
         <property name="originalMetadataAuthorReference" type="string" column="XWD_ORIGINAL_METADATA_AUTHOR" length="255" index="DOC_ORIGINAL_METADATA_AUTHOR" />
+        <property name="enforceRequiredRights" type="boolean" column="XWD_ENFORCE_REQUIRED_RIGHTS"
+            index="DOC_ENFORCE_REQUIRED_RIGHTS" not-null="true"/>
     </class>
 
     <!-- com.xpn.xwiki.doc.XWikiSpace root -->
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.derby.hbm.xml b/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.derby.hbm.xml
index fd8297e2f9f..052d9339d3c 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.derby.hbm.xml
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.derby.hbm.xml
@@ -75,6 +75,8 @@
         <property name="syntaxId" type="string" column="XWD_SYNTAX_ID" length="50" />
         <property name="hidden" type="boolean" column="XWD_HIDDEN" index="DOC_HIDDEN" not-null="true" />
         <property name="originalMetadataAuthorReference" type="string" column="XWD_ORIGINAL_METADATA_AUTHOR" length="768" index="DOC_ORIGINAL_METADATA_AUTHOR" />
+        <property name="enforceRequiredRights" type="boolean" column="XWD_ENFORCE_REQUIRED_RIGHTS"
+            index="DOC_ENFORCE_REQUIRED_RIGHTS" not-null="true"/>
     </class>
 
     <!-- com.xpn.xwiki.doc.XWikiSpace root -->
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.hbm.xml b/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.hbm.xml
index 9fe3245b3e2..097b6726aff 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.hbm.xml
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.hbm.xml
@@ -70,6 +70,8 @@
         <property name="syntaxId" type="string" column="XWD_SYNTAX_ID" length="50" />
         <property name="hidden" type="boolean" column="XWD_HIDDEN" index="DOC_HIDDEN" not-null="true" />
         <property name="originalMetadataAuthorReference" type="string" column="XWD_ORIGINAL_METADATA_AUTHOR" length="768" index="DOC_ORIGINAL_METADATA_AUTHOR" />
+        <property name="enforceRequiredRights" type="boolean" column="XWD_ENFORCE_REQUIRED_RIGHTS"
+                  index="DOC_ENFORCE_REQUIRED_RIGHTS" not-null="true"/>
     </class>
 
     <!-- com.xpn.xwiki.doc.XWikiSpace root -->
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.mssql.hbm.xml b/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.mssql.hbm.xml
index 604665327a2..d94c1227f99 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.mssql.hbm.xml
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.mssql.hbm.xml
@@ -77,6 +77,8 @@
         <property name="syntaxId" type="string" column="XWD_SYNTAX_ID" length="50" />
         <property name="hidden" type="boolean" column="XWD_HIDDEN" index="DOC_HIDDEN" not-null="true" />
         <property name="originalMetadataAuthorReference" type="string" column="XWD_ORIGINAL_METADATA_AUTHOR" length="255" index="DOC_ORIGINAL_METADATA_AUTHOR" />
+        <property name="enforceRequiredRights" type="boolean" column="XWD_ENFORCE_REQUIRED_RIGHTS"
+            index="DOC_ENFORCE_REQUIRED_RIGHTS" not-null="true"/>
     </class>
 
     <!-- com.xpn.xwiki.doc.XWikiSpace root -->
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.oracle.hbm.xml b/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.oracle.hbm.xml
index e37c976d9af..ce2b676b2c2 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.oracle.hbm.xml
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.oracle.hbm.xml
@@ -83,6 +83,8 @@
         <property name="syntaxId" type="string" column="XWD_SYNTAX_ID" length="50" />
         <property name="hidden" type="boolean" column="XWD_HIDDEN" index="DOC_HIDDEN" not-null="true" />
         <property name="originalMetadataAuthorReference" type="string" column="XWD_ORIGINAL_METADATA" length="768" index="DOC_ORIGINAL_METADATA" />
+        <property name="enforceRequiredRights" type="boolean" column="XWD_ENFORCE_REQUIRED_RIGHTS"
+            index="DOC_ENFORCE_REQUIRED_RIGHTS" not-null="true"/>
     </class>
 
     <!-- com.xpn.xwiki.doc.XWikiSpace root -->
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.postgresql.hbm.xml b/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.postgresql.hbm.xml
index 81948bd7fa1..1e2f2d9b4d2 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.postgresql.hbm.xml
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/xwiki.postgresql.hbm.xml
@@ -79,6 +79,8 @@
         <property name="syntaxId" type="string" column="XWD_SYNTAX_ID" length="50" />
         <property name="hidden" type="boolean" column="XWD_HIDDEN" index="DOC_HIDDEN" not-null="true" />
         <property name="originalMetadataAuthorReference" type="string" column="XWD_ORIGINAL_METADATA_AUTHOR" length="768" index="DOC_ORIGINAL_METADATA_AUTHOR" />
+        <property name="enforceRequiredRights" type="boolean" column="XWD_ENFORCE_REQUIRED_RIGHTS"
+            index="DOC_ENFORCE_REQUIRED_RIGHTS" not-null="true"/>
     </class>
 
     <!-- com.xpn.xwiki.doc.XWikiSpace root -->
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/test/resources/filter/document1.xml b/xwiki-platform-core/xwiki-platform-oldcore/src/test/resources/filter/document1.xml
index 0f0bb271c52..f1f357da37e 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/test/resources/filter/document1.xml
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/test/resources/filter/document1.xml
@@ -48,6 +48,10 @@
                   <string>hidden</string>
                   <boolean>false</boolean>
                 </entry>
+                <entry>
+                  <string>enforce_required_rights</string>
+                  <boolean>false</boolean>
+                </entry>
                 <entry>
                   <string>revision_author</string>
                   <string>XWiki.author</string>
@@ -200,6 +204,10 @@
                   <string>hidden</string>
                   <boolean>true</boolean>
                 </entry>
+                <entry>
+                  <string>enforce_required_rights</string>
+                  <boolean>false</boolean>
+                </entry>
                 <entry>
                   <string>revision_author</string>
                   <string>XWiki.author</string>
@@ -530,6 +538,10 @@
                     <string>hidden</string>
                     <boolean>true</boolean>
                   </entry>
+                  <entry>
+                    <string>enforce_required_rights</string>
+                    <boolean>false</boolean>
+                  </entry>
                   <entry>
                     <string>revision_author</string>
                     <string>XWiki.author</string>
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/test/resources/filter/documentwithattachment1content.xml b/xwiki-platform-core/xwiki-platform-oldcore/src/test/resources/filter/documentwithattachment1content.xml
index 3d5891effb6..a107f777731 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/test/resources/filter/documentwithattachment1content.xml
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/test/resources/filter/documentwithattachment1content.xml
@@ -49,6 +49,10 @@
                   <string>hidden</string>
                   <boolean>false</boolean>
                 </entry>
+                <entry>
+                  <string>enforce_required_rights</string>
+                  <boolean>false</boolean>
+                </entry>
                 <entry>
                   <string>revision_author</string>
                   <string>XWiki.XWikiGuest</string>
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/test/resources/filter/documentwithattachment2content.xml b/xwiki-platform-core/xwiki-platform-oldcore/src/test/resources/filter/documentwithattachment2content.xml
index 77e524ef589..ffef321f09c 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/test/resources/filter/documentwithattachment2content.xml
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/test/resources/filter/documentwithattachment2content.xml
@@ -49,6 +49,10 @@
                   <string>hidden</string>
                   <boolean>false</boolean>
                 </entry>
+                <entry>
+                  <string>enforce_required_rights</string>
+                  <boolean>false</boolean>
+                </entry>
                 <entry>
                   <string>revision_author</string>
                   <string>XWiki.XWikiGuest</string>
diff --git a/xwiki-platform-core/xwiki-platform-rest/xwiki-platform-rest-model/src/main/resources/xwiki.rest.model.xsd b/xwiki-platform-core/xwiki-platform-rest/xwiki-platform-rest-model/src/main/resources/xwiki.rest.model.xsd
index 5b4b166e1a6..7d157e1d032 100644
--- a/xwiki-platform-core/xwiki-platform-rest/xwiki-platform-rest-model/src/main/resources/xwiki.rest.model.xsd
+++ b/xwiki-platform-core/xwiki-platform-rest/xwiki-platform-rest-model/src/main/resources/xwiki.rest.model.xsd
@@ -128,6 +128,7 @@
           <element name="majorVersion" type="int"></element>
           <element name="minorVersion" type="int"></element>
           <element name="hidden" type="boolean" default="false"></element>
+          <element name="enforceRequiredRights" type="boolean" minOccurs="0"/>
           <element name="created" type="dateTime"></element>
           <element name="creator" type="string"></element>
           <element name="creatorName" type="string"></element>
diff --git a/xwiki-platform-core/xwiki-platform-rest/xwiki-platform-rest-server/src/main/java/org/xwiki/rest/internal/ModelFactory.java b/xwiki-platform-core/xwiki-platform-rest/xwiki-platform-rest-server/src/main/java/org/xwiki/rest/internal/ModelFactory.java
index 12697d3140b..e74082acd0d 100644
--- a/xwiki-platform-core/xwiki-platform-rest/xwiki-platform-rest-server/src/main/java/org/xwiki/rest/internal/ModelFactory.java
+++ b/xwiki-platform-core/xwiki-platform-rest/xwiki-platform-rest-server/src/main/java/org/xwiki/rest/internal/ModelFactory.java
@@ -212,6 +212,11 @@ public boolean toDocument(Document doc, org.xwiki.rest.model.jaxb.Page restPage)
 
         doc.setHidden(restPage.isHidden());
 
+        if (restPage.isEnforceRequiredRights() != null) {
+            doc.setEnforceRequiredRights(restPage.isEnforceRequiredRights());
+            modified = true;
+        }
+
         // Set objects
         if (restPage.getObjects() != null) {
             Set<ObjectReference> newReferences = new HashSet<>();
@@ -761,6 +766,7 @@ public Page toRestPage(URI baseUri, URI self, Document doc, boolean useVersion,
         page.setMajorVersion(doc.getRCSVersion().at(0));
         page.setMinorVersion(doc.getRCSVersion().at(1));
         page.setHidden(doc.isHidden());
+        page.setEnforceRequiredRights(doc.isEnforceRequiredRights());
         page.setLanguage(doc.getLocale().toString());
         page.setCreator(doc.getCreator());
         if (withPrettyNames) {
diff --git a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/DefaultAuthorizationManager.java b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/DefaultAuthorizationManager.java
index 5366e259711..0b338d81c7b 100644
--- a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/DefaultAuthorizationManager.java
+++ b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/DefaultAuthorizationManager.java
@@ -38,6 +38,7 @@
 import org.xwiki.security.UserSecurityReference;
 import org.xwiki.security.authorization.cache.SecurityCache;
 import org.xwiki.security.authorization.cache.SecurityCacheLoader;
+import org.xwiki.security.authorization.internal.DocumentRequiredRightsChecker;
 import org.xwiki.security.internal.XWikiBridge;
 
 /**
@@ -79,6 +80,9 @@ public class DefaultAuthorizationManager implements AuthorizationManager
     @Inject
     private XWikiBridge xwikiBridge;
 
+    @Inject
+    private DocumentRequiredRightsChecker documentRequiredRightsChecker;
+
     /**
      * Check if the user is the super admin.
      *
@@ -148,9 +152,7 @@ private boolean hasSecurityAccess(Right right, DocumentReference userReference,
         }
 
         if (right == null || right == Right.ILLEGAL) {
-            if (check) {
-                logDeny(userReference, entityReference, right, "no such right");
-            }
+            logDenyIfCheck(right, userReference, entityReference, check, "no such right");
             return false;
         }
 
@@ -159,9 +161,26 @@ private boolean hasSecurityAccess(Right right, DocumentReference userReference,
             return false;
         }
 
+        // For edit right, check if the user has all required rights.
+        if (right == Right.EDIT && !this.documentRequiredRightsChecker.hasRequiredRights(userReference,
+            entityReference))
+        {
+            logDenyIfCheck(right, userReference, entityReference, check, "misses required right");
+
+            return false;
+        }
+
         return evaluateSecurityAccess(right, userReference, entityReference, check);
     }
 
+    private void logDenyIfCheck(Right right, DocumentReference userReference, EntityReference entityReference,
+        boolean check, String info)
+    {
+        if (check) {
+            logDeny(userReference, entityReference, right, info);
+        }
+    }
+
     private boolean evaluateSecurityAccess(Right right, DocumentReference userReference,
         EntityReference entityReference, boolean check)
         throws AuthorizationException
diff --git a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/DocumentAuthorizationManager.java b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/DocumentAuthorizationManager.java
new file mode 100644
index 00000000000..be32b7b671b
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/DocumentAuthorizationManager.java
@@ -0,0 +1,76 @@
+/*
+ * 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.security.authorization;
+
+import org.xwiki.component.annotation.Role;
+import org.xwiki.model.EntityType;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.stability.Unstable;
+
+/**
+ * Authorization manager that checks rights of document authors taking required rights into account.
+ *
+ * @version $Id$
+ * @since 16.10.0RC1
+ */
+@Unstable
+@Role
+public interface DocumentAuthorizationManager
+{
+    /**
+     * Tests if the given context author of the context document has the specified right at the given level, taking the
+     * required rights of the document into account.
+     *
+     * @param right the right to check
+     * @param level the level of the right, for example, for admin right, this could be {@link EntityType#DOCUMENT} for
+     * admin right directly on the document itself or {@link EntityType#WIKI} for admin right on the whole wiki.
+     * @param contextAuthor the author for which rights shall be checked
+     * @param contextDocument the document for which the rights shall be checked
+     * @return if the context author has the right
+     */
+    boolean hasAccess(Right right, EntityType level, DocumentReference contextAuthor,
+        DocumentReference contextDocument);
+
+    /**
+     * Check if the given document has the given required right.
+     *
+     * @param right the right to check
+     * @param level the level of the right
+     * @param contextDocument the reference of the context document
+     * @return if the document has the required right (independent of the actual author)
+     * @throws AuthorizationException if loading the required rights fails
+     */
+    boolean hasRequiredRight(Right right, EntityType level, DocumentReference contextDocument)
+        throws AuthorizationException;
+
+    /**
+     * Checks if the given context author of the context document has the specified right at the given level, taking the
+     * required rights of the document into account. This function should be used at security checkpoint.
+     *
+     * @param right the right to check
+     * @param level the level of the right, for example, for admin right, this could be {@link EntityType#DOCUMENT} for
+     * admin right directly on the document itself or {@link EntityType#WIKI} for admin right on the whole wiki.
+     * @param contextAuthor the author for which rights shall be checked
+     * @param contextDocument the document for which the rights shall be checked
+     * @throws AccessDeniedException if the context author does not have the right
+     */
+    void checkAccess(Right right, EntityType level, DocumentReference contextAuthor,
+        DocumentReference contextDocument) throws AccessDeniedException;
+}
diff --git a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/internal/DocumentRequiredRightsChecker.java b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/internal/DocumentRequiredRightsChecker.java
new file mode 100644
index 00000000000..3787136158d
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/internal/DocumentRequiredRightsChecker.java
@@ -0,0 +1,103 @@
+/*
+ * 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.security.authorization.internal;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+import org.xwiki.component.annotation.Component;
+import org.xwiki.model.EntityType;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.model.reference.EntityReference;
+import org.xwiki.security.authorization.AuthorizationException;
+import org.xwiki.security.authorization.AuthorizationManager;
+import org.xwiki.security.authorization.requiredrights.DocumentRequiredRight;
+import org.xwiki.security.authorization.requiredrights.DocumentRequiredRights;
+import org.xwiki.security.authorization.requiredrights.DocumentRequiredRightsManager;
+
+/**
+ * Check if a user has all required rights defined for an entity.
+ *
+ * @version $Id$
+ * @since 16.10.0RC1
+ */
+@Component(roles = DocumentRequiredRightsChecker.class)
+@Singleton
+public class DocumentRequiredRightsChecker
+{
+    @Inject
+    private Provider<AuthorizationManager> authorizationManagerProvider;
+
+    @Inject
+    private DocumentRequiredRightsManager documentRequiredRightsManager;
+
+    /**
+     * Check if a user has all required rights defined for an entity.
+     *
+     * @param userReference the user reference
+     * @param entityReference the entity reference
+     * @return true if the user has all required rights, false otherwise
+     * @throws AuthorizationException if an error occurs while loading the required rights
+     */
+    public boolean hasRequiredRights(DocumentReference userReference, EntityReference entityReference)
+        throws AuthorizationException
+    {
+        DocumentReference documentReference = getDocumentReference(entityReference);
+
+        if (documentReference == null) {
+            return true;
+        }
+
+        AuthorizationManager authorizationManager = this.authorizationManagerProvider.get();
+
+        DocumentRequiredRights documentRequiredRights =
+            this.documentRequiredRightsManager.getRequiredRights(documentReference)
+                .orElse(DocumentRequiredRights.EMPTY);
+        if (documentRequiredRights.enforce()) {
+            for (DocumentRequiredRight requiredRight : documentRequiredRights.rights()) {
+                if (!authorizationManager.hasAccess(requiredRight.right(), userReference,
+                    documentReference.extractReference(requiredRight.scope())))
+                {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    private DocumentReference getDocumentReference(EntityReference entityReference)
+    {
+        if (entityReference != null) {
+            if (entityReference instanceof DocumentReference documentReference) {
+                return documentReference;
+            }
+
+            EntityReference documentEntityReference = entityReference.extractReference(EntityType.DOCUMENT);
+
+            if (documentEntityReference != null) {
+                return new DocumentReference(documentEntityReference);
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/requiredrights/DocumentRequiredRight.java b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/requiredrights/DocumentRequiredRight.java
new file mode 100644
index 00000000000..595ed91f869
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/requiredrights/DocumentRequiredRight.java
@@ -0,0 +1,38 @@
+/*
+ * 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.security.authorization.requiredrights;
+
+import org.xwiki.model.EntityType;
+import org.xwiki.security.authorization.Right;
+import org.xwiki.stability.Unstable;
+
+/**
+ * A required right set on a document.
+ *
+ * @param right the right
+ * @param scope the scope of the right, e.g., wiki or document level
+ *
+ * @version $Id$
+ * @since 16.10.0RC1
+ */
+@Unstable
+public record DocumentRequiredRight(Right right, EntityType scope)
+{
+}
diff --git a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/requiredrights/DocumentRequiredRights.java b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/requiredrights/DocumentRequiredRights.java
new file mode 100644
index 00000000000..aadd0f253b7
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/requiredrights/DocumentRequiredRights.java
@@ -0,0 +1,42 @@
+/*
+ * 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.security.authorization.requiredrights;
+
+import java.util.Set;
+
+import org.xwiki.stability.Unstable;
+
+/**
+ * Represents the required rights that are configured on a document.
+ *
+ * @param enforce if the required rights shall be enforced
+ * @param rights the configured rights
+ *
+ * @version $Id$
+ * @since 16.10.0RC1
+ */
+@Unstable
+public record DocumentRequiredRights(boolean enforce, Set<DocumentRequiredRight> rights)
+{
+    /**
+     * Empty required rights.
+     */
+    public static final DocumentRequiredRights EMPTY = new DocumentRequiredRights(false, Set.of());
+}
diff --git a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/requiredrights/DocumentRequiredRightsManager.java b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/requiredrights/DocumentRequiredRightsManager.java
new file mode 100644
index 00000000000..64c68e45758
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/java/org/xwiki/security/authorization/requiredrights/DocumentRequiredRightsManager.java
@@ -0,0 +1,47 @@
+/*
+ * 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.security.authorization.requiredrights;
+
+import java.util.Optional;
+
+import org.xwiki.component.annotation.Role;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.security.authorization.AuthorizationException;
+import org.xwiki.stability.Unstable;
+
+/**
+ * Manager for document required rights.
+ *
+ * @version $Id$
+ * @since 16.10.0RC1
+ */
+@Unstable
+@Role
+public interface DocumentRequiredRightsManager
+{
+    /**
+     * Get the document required rights for the given document.
+     *
+     * @param documentReference the document to get the required rights for
+     * @return the document required rights if the document exists, empty otherwise
+     */
+    Optional<DocumentRequiredRights> getRequiredRights(DocumentReference documentReference)
+        throws AuthorizationException;
+}
diff --git a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/resources/META-INF/components.txt b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/resources/META-INF/components.txt
index f6e89bad263..0e25145cbe9 100644
--- a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/resources/META-INF/components.txt
+++ b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/main/resources/META-INF/components.txt
@@ -2,6 +2,7 @@ org.xwiki.security.DefaultSecurityReferenceFactory
 org.xwiki.security.authorization.cache.internal.DefaultSecurityCache
 org.xwiki.security.authorization.cache.internal.DefaultSecurityCacheLoader
 org.xwiki.security.authorization.internal.AuthorizationSettlerProvider
+org.xwiki.security.authorization.internal.DocumentRequiredRightsChecker
 org.xwiki.security.authorization.DefaultAuthorizationManager
 org.xwiki.security.authorization.internal.DefaultAuthorizationManagerConfiguration
 org.xwiki.security.authorization.internal.DefaultAuthorizationSettler
diff --git a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/test/java/org/xwiki/security/authorization/DefaultAuthorizationManagerIntegrationTest.java b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/test/java/org/xwiki/security/authorization/DefaultAuthorizationManagerIntegrationTest.java
index f87ed2f51fd..843801ce814 100644
--- a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/test/java/org/xwiki/security/authorization/DefaultAuthorizationManagerIntegrationTest.java
+++ b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-api/src/test/java/org/xwiki/security/authorization/DefaultAuthorizationManagerIntegrationTest.java
@@ -55,6 +55,7 @@
 import org.xwiki.security.authorization.cache.internal.TestCache;
 import org.xwiki.security.authorization.internal.AbstractSecurityRuleEntry;
 import org.xwiki.security.authorization.internal.DefaultAuthorizationSettler;
+import org.xwiki.security.authorization.internal.DocumentRequiredRightsChecker;
 import org.xwiki.security.authorization.testwikis.SecureTestEntity;
 import org.xwiki.security.authorization.testwikis.TestAccessRule;
 import org.xwiki.security.authorization.testwikis.TestDefinition;
@@ -137,6 +138,9 @@ class DefaultAuthorizationManagerIntegrationTest extends AbstractAuthorizationTe
     @MockComponent
     private SecurityCacheRulesInvalidator securityCacheRulesInvalidator;
 
+    @MockComponent
+    private DocumentRequiredRightsChecker documentRequiredRightsChecker;
+
     /** Mocked cache */
     private TestCache<Object> cache;
 
@@ -161,6 +165,8 @@ public EntityReference answer(InvocationOnMock invocation) throws Throwable
             });
 
         when(xWikiBridge.getMainWikiReference()).thenReturn(new WikiReference("xwiki"));
+
+        when(this.documentRequiredRightsChecker.hasRequiredRights(any(), any())).thenReturn(true);
     }
 
     @BeforeEach
diff --git a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/authorization/internal/DefaultDocumentAuthorizationManager.java b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/authorization/internal/DefaultDocumentAuthorizationManager.java
new file mode 100644
index 00000000000..e0966cf5599
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/authorization/internal/DefaultDocumentAuthorizationManager.java
@@ -0,0 +1,122 @@
+/*
+ * 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.security.authorization.internal;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.slf4j.Logger;
+import org.xwiki.component.annotation.Component;
+import org.xwiki.model.EntityType;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.model.reference.EntityReference;
+import org.xwiki.security.authorization.AccessDeniedException;
+import org.xwiki.security.authorization.AuthorizationException;
+import org.xwiki.security.authorization.AuthorizationManager;
+import org.xwiki.security.authorization.DocumentAuthorizationManager;
+import org.xwiki.security.authorization.Right;
+import org.xwiki.security.authorization.requiredrights.DocumentRequiredRights;
+import org.xwiki.security.authorization.requiredrights.DocumentRequiredRightsManager;
+
+/**
+ * Default implementation of {@link DocumentAuthorizationManager}.
+ *
+ * @version $Id$
+ * @since 16.10.0RC1
+ */
+@Component
+@Singleton
+public class DefaultDocumentAuthorizationManager implements DocumentAuthorizationManager
+{
+    @Inject
+    private AuthorizationManager authorizationManager;
+
+    @Inject
+    private DocumentRequiredRightsManager documentRequiredRightsManager;
+
+    @Inject
+    private Logger logger;
+
+    @Override
+    public boolean hasAccess(Right right, EntityType level, DocumentReference contextAuthor,
+        DocumentReference contextDocument)
+    {
+        EntityReference reference = contextDocument != null ? contextDocument.extractReference(level) : null;
+
+        try {
+            return hasRequiredRight(right, level, contextDocument)
+                && this.authorizationManager.hasAccess(right, contextAuthor, reference);
+        } catch (Exception e) {
+            this.logger.error("Failed to load required rights for user [{}] on [{}].",
+                contextAuthor, contextDocument, e);
+            return false;
+        }
+    }
+
+    @Override
+    public boolean hasRequiredRight(Right right, EntityType level, DocumentReference contextDocument)
+        throws AuthorizationException
+    {
+        DocumentRequiredRights documentRequiredRights =
+            this.documentRequiredRightsManager.getRequiredRights(contextDocument)
+                .orElse(DocumentRequiredRights.EMPTY);
+
+        boolean hasAccess;
+
+        if (documentRequiredRights.enforce()) {
+            hasAccess = documentRequiredRights.rights().stream()
+                .filter(requiredRight -> level.isAllowedAncestor(requiredRight.scope()))
+                .anyMatch(requiredRight ->
+                    requiredRight.right().equals(right) || requiredRight.right()
+                        .getImpliedRights()
+                        // TODO: this is wrong. Not all rights are implied on all levels. For example, if there was
+                        //  no script right on space level, admin right on space level wouldn't imply script right.
+                        // Need to investigate what's the proper solution here but either a) check enabled rights per
+                        // level or b) raise the input level to the first level where the input right is defined.
+                        .contains(right));
+        } else {
+            hasAccess = true;
+        }
+
+        return hasAccess;
+    }
+
+    @Override
+    public void checkAccess(Right right, EntityType level, DocumentReference contextAuthor,
+        DocumentReference contextDocument) throws AccessDeniedException
+    {
+        EntityReference reference = contextDocument != null ? contextDocument.extractReference(level) : null;
+
+        try {
+            if (hasRequiredRight(right, level, contextDocument)) {
+                this.authorizationManager.checkAccess(right, contextAuthor, reference);
+            } else {
+                this.logger.info("[{}] right has been denied to user [{}] on entity [{}] based on required rights"
+                        + " of document [{}] on level [{}]: security checkpoint", right, contextAuthor, reference,
+                    contextDocument, level);
+                throw new AccessDeniedException(right, contextAuthor, reference);
+            }
+        } catch (AccessDeniedException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new AccessDeniedException(right, contextAuthor, contextDocument, e);
+        }
+    }
+}
diff --git a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/authorization/internal/RightsFilterListener.java b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/authorization/internal/RightsFilterListener.java
index 722c52c26f8..9d61582a07c 100644
--- a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/authorization/internal/RightsFilterListener.java
+++ b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/authorization/internal/RightsFilterListener.java
@@ -32,6 +32,9 @@
 import org.xwiki.observation.AbstractEventListener;
 import org.xwiki.observation.event.CancelableEvent;
 import org.xwiki.observation.event.Event;
+import org.xwiki.security.authorization.internal.requiredrights.DocumentRequiredRightsReader;
+import org.xwiki.security.authorization.requiredrights.DocumentRequiredRight;
+import org.xwiki.security.authorization.requiredrights.DocumentRequiredRights;
 import org.xwiki.security.authorization.AccessDeniedException;
 import org.xwiki.security.authorization.AuthorizationManager;
 import org.xwiki.security.authorization.Right;
@@ -64,9 +67,15 @@ public class RightsFilterListener extends AbstractEventListener
      */
     public static final String NAME = "org.xwiki.security.authorization.internal.RightsFilterListener";
 
+    private static final LocalDocumentReference REQUIRED_RIGHT_CLASS =
+        new LocalDocumentReference("XWiki", "RequiredRightClass");
+
     @Inject
     private AuthorizationManager authorization;
 
+    @Inject
+    private DocumentRequiredRightsReader documentRequiredRightsReader;
+
     /**
      * The default constructor.
      */
@@ -89,6 +98,36 @@ public void onEvent(Event event, Object source, Object data)
         // Check global rights
         checkModifiedRights(userEvent.getUserReference(), document,
             XWikiGlobalRightsDocumentInitializer.CLASS_REFERENCE, (CancelableEvent) event);
+
+        // Check the required rights.
+        checkModifiedRequiredRights(userEvent.getUserReference(), document, (CancelableEvent) event);
+    }
+
+    private void checkModifiedRequiredRights(DocumentReference user, XWikiDocument document,
+        CancelableEvent event)
+    {
+        XWikiDocument originalDocument = document.getOriginalDocument();
+        DocumentRequiredRights originalRequiredRights =
+            this.documentRequiredRightsReader.readRequiredRights(originalDocument);
+        DocumentRequiredRights requiredRights = this.documentRequiredRightsReader.readRequiredRights(document);
+
+        if (!originalRequiredRights.equals(requiredRights) && requiredRights.enforce()) {
+            // We can assume that the current user has all existing required rights as otherwise editing would be
+            // denied.
+            // Therefore, only check if the user has all rights specified in the updated document that could either
+            // have changed required rights or where enforcing has just been enabled.
+            for (DocumentRequiredRight requiredRight : requiredRights.rights()) {
+                try {
+                    this.authorization.checkAccess(requiredRight.right(), user,
+                        document.getDocumentReference().extractReference(requiredRight.scope()));
+                } catch (AccessDeniedException e) {
+                    event.cancel(
+                        "The author doesn't have the right [%s] on the [%s] level that has been specified as required."
+                            .formatted(requiredRight.right().getName(), requiredRight.scope().getLowerCase()));
+                    break;
+                }
+            }
+        }
     }
 
     private void checkModifiedRights(DocumentReference user, XWikiDocument document,
diff --git a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/authorization/internal/requiredrights/DefaultDocumentRequiredRightsManager.java b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/authorization/internal/requiredrights/DefaultDocumentRequiredRightsManager.java
new file mode 100644
index 00000000000..c5c5465ccab
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/authorization/internal/requiredrights/DefaultDocumentRequiredRightsManager.java
@@ -0,0 +1,71 @@
+/*
+ * 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.security.authorization.internal.requiredrights;
+
+import java.util.Optional;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+import org.xwiki.component.annotation.Component;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.security.authorization.AuthorizationException;
+import org.xwiki.security.authorization.requiredrights.DocumentRequiredRights;
+import org.xwiki.security.authorization.requiredrights.DocumentRequiredRightsManager;
+
+import com.xpn.xwiki.XWikiContext;
+import com.xpn.xwiki.XWikiException;
+import com.xpn.xwiki.doc.XWikiDocument;
+
+/**
+ * Default implementation of {@link DocumentRequiredRightsManager}.
+ *
+ * @version $Id$
+ */
+@Component
+@Singleton
+public class DefaultDocumentRequiredRightsManager implements DocumentRequiredRightsManager
+{
+    @Inject
+    private Provider<XWikiContext> contextProvider;
+
+    @Inject
+    private DocumentRequiredRightsReader documentRequiredRightsReader;
+
+    @Override
+    public Optional<DocumentRequiredRights> getRequiredRights(DocumentReference documentReference)
+        throws AuthorizationException
+    {
+        XWikiContext context = this.contextProvider.get();
+
+        // Load the document.
+        try {
+            XWikiDocument document = context.getWiki().getDocument(documentReference, context);
+            if (!document.isNew()) {
+                return Optional.of(this.documentRequiredRightsReader.readRequiredRights(document));
+            }
+        } catch (XWikiException e) {
+            throw new AuthorizationException("Failed to load the document", e);
+        }
+
+        return Optional.empty();
+    }
+}
diff --git a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/authorization/internal/requiredrights/DocumentRequiredRightsReader.java b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/authorization/internal/requiredrights/DocumentRequiredRightsReader.java
new file mode 100644
index 00000000000..c8daa1ea347
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/authorization/internal/requiredrights/DocumentRequiredRightsReader.java
@@ -0,0 +1,103 @@
+/*
+ * 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.security.authorization.internal.requiredrights;
+
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.inject.Singleton;
+
+import org.apache.commons.lang3.StringUtils;
+import org.xwiki.component.annotation.Component;
+import org.xwiki.model.EntityType;
+import org.xwiki.model.reference.EntityReference;
+import org.xwiki.model.reference.LocalDocumentReference;
+import org.xwiki.security.authorization.requiredrights.DocumentRequiredRight;
+import org.xwiki.security.authorization.requiredrights.DocumentRequiredRights;
+import org.xwiki.security.authorization.Right;
+
+import com.xpn.xwiki.doc.XWikiDocument;
+import com.xpn.xwiki.objects.BaseObject;
+
+/**
+ * Read the required rights from a document.
+ *
+ * @version $Id$
+ * @since 16.6.0RC1
+ */
+@Component(roles = DocumentRequiredRightsReader.class)
+@Singleton
+public class DocumentRequiredRightsReader
+{
+    private static final LocalDocumentReference CLASS_REFERENCE =
+        new LocalDocumentReference("XWiki", "RequiredRightClass");
+
+    private static final String PROPERTY_NAME = "level";
+
+    /**
+     * Read the required rights from a document.
+     *
+     * @param document the document to read the required rights from
+     * @return the required rights
+     */
+    public DocumentRequiredRights readRequiredRights(XWikiDocument document)
+    {
+        return new DocumentRequiredRights(document.isEnforceRequiredRights(),
+            document.getXObjects(CLASS_REFERENCE).stream()
+                .filter(Objects::nonNull)
+                .map(this::readRequiredRight)
+                // Don't allow edit right/edit right implies no extra right.
+                .filter(requiredRight -> !Right.EDIT.equals(requiredRight.right()))
+                .collect(Collectors.toUnmodifiableSet()));
+    }
+
+    private DocumentRequiredRight readRequiredRight(BaseObject object)
+    {
+        String value = object.getStringValue(PROPERTY_NAME);
+        EntityType entityType = EntityType.DOCUMENT;
+        Right right = Right.toRight(value);
+        if (right.equals(Right.ILLEGAL)) {
+            String[] levelRight = StringUtils.split(value, "_", 2);
+            if (levelRight.length == 2) {
+                right = Right.toRight(levelRight[1]);
+                entityType = EntityType.valueOf(levelRight[0].toUpperCase());
+            }
+        }
+
+        // TODO: add some handling for invalid values.
+        Set<EntityType> targetedEntityTypes = right.getTargetedEntityType();
+        if (targetedEntityTypes == null) {
+            // This means the right targets only the farm level, which is null.
+            entityType = null;
+        } else {
+            EntityReference entityReference = object.getDocumentReference().extractReference(entityType);
+            // Try to get the highest level where this right can be assigned. This is done to ensure that, e.g.,
+            // programming right can imply admin right on the wiki level even if programming right is only specified on
+            // the document level.
+            while (!targetedEntityTypes.contains(entityType) && entityReference != null) {
+                entityType = entityReference.getType();
+                entityReference = entityReference.getParent();
+            }
+        }
+
+        return new DocumentRequiredRight(right, entityType);
+    }
+}
diff --git a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/resources/META-INF/components.txt b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/resources/META-INF/components.txt
index f9d7d702026..fbfbca70d58 100644
--- a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/resources/META-INF/components.txt
+++ b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/resources/META-INF/components.txt
@@ -1,8 +1,11 @@
 500:org.xwiki.security.authorization.internal.BridgeAuthorizationManager
 org.xwiki.security.authorization.internal.DefaultContextualAuthorizationManager
+org.xwiki.security.authorization.internal.DefaultDocumentAuthorizationManager
 org.xwiki.security.authorization.internal.DefaultSecurityCacheRulesInvalidator
 org.xwiki.security.authorization.internal.DefaultSecurityCacheRulesInvalidatorListener
 org.xwiki.security.authorization.internal.DefaultSecurityEntryReader
+org.xwiki.security.authorization.internal.requiredrights.DefaultDocumentRequiredRightsManager
+org.xwiki.security.authorization.internal.requiredrights.DocumentRequiredRightsReader
 org.xwiki.security.authorization.internal.RightsFilterListener
 org.xwiki.security.internal.DefaultUserBridge
 org.xwiki.security.internal.DefaultXWikiBridge
diff --git a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/test/java/org/xwiki/security/authorization/internal/RightsFilterListenerTest.java b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/test/java/org/xwiki/security/authorization/internal/RightsFilterListenerTest.java
index 9681754a839..52bfc41a47c 100644
--- a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/test/java/org/xwiki/security/authorization/internal/RightsFilterListenerTest.java
+++ b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/test/java/org/xwiki/security/authorization/internal/RightsFilterListenerTest.java
@@ -19,11 +19,14 @@
  */
 package org.xwiki.security.authorization.internal;
 
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.xwiki.model.reference.DocumentReference;
 import org.xwiki.security.authorization.AccessDeniedException;
 import org.xwiki.security.authorization.AuthorizationManager;
 import org.xwiki.security.authorization.Right;
+import org.xwiki.security.authorization.internal.requiredrights.DocumentRequiredRightsReader;
+import org.xwiki.security.authorization.requiredrights.DocumentRequiredRights;
 import org.xwiki.test.junit5.mockito.InjectMockComponents;
 import org.xwiki.test.junit5.mockito.MockComponent;
 
@@ -40,7 +43,9 @@
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.when;
 
 /**
  * Validate {@link RightsFilterListener}.
@@ -54,12 +59,21 @@ public class RightsFilterListenerTest
     @MockComponent
     private AuthorizationManager authorization;
 
+    @MockComponent
+    private DocumentRequiredRightsReader requiredRightsReader;
+
     @InjectMockComponents
     private RightsFilterListener listener;
 
     @InjectMockitoOldcore
     private MockitoOldcore oldcore;
 
+    @BeforeEach
+    void setUp()
+    {
+        when(this.requiredRightsReader.readRequiredRights(any())).thenReturn(DocumentRequiredRights.EMPTY);
+    }
+
     @Test
     public void noRight()
     {
diff --git a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/test/java/org/xwiki/security/authorization/internal/requiredrights/DocumentRequiredRightsReaderTest.java b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/test/java/org/xwiki/security/authorization/internal/requiredrights/DocumentRequiredRightsReaderTest.java
new file mode 100644
index 00000000000..bedfdcb46db
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/test/java/org/xwiki/security/authorization/internal/requiredrights/DocumentRequiredRightsReaderTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.security.authorization.internal.requiredrights;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.xwiki.model.EntityType;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.model.reference.LocalDocumentReference;
+import org.xwiki.security.authorization.Right;
+import org.xwiki.security.authorization.requiredrights.DocumentRequiredRight;
+import org.xwiki.security.authorization.requiredrights.DocumentRequiredRights;
+import org.xwiki.test.junit5.mockito.ComponentTest;
+import org.xwiki.test.junit5.mockito.InjectMockComponents;
+
+import com.xpn.xwiki.doc.XWikiDocument;
+import com.xpn.xwiki.objects.BaseObject;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Unit test for {@link DocumentRequiredRightsReader}.
+ *
+ * @version $Id$
+ */
+@ComponentTest
+class DocumentRequiredRightsReaderTest
+{
+    private static final LocalDocumentReference CLASS_REFERENCE =
+        new LocalDocumentReference("XWiki", "RequiredRightClass");
+
+    private static final DocumentReference DOCUMENT_REFERENCE =
+        new DocumentReference("wiki", "Space", "Page");
+
+    @InjectMockComponents
+    private DocumentRequiredRightsReader documentRequiredRightsReader;
+
+    @ParameterizedTest
+    @MethodSource("getArguments")
+    void readRequiredRights(boolean enforce, List<String> values, DocumentRequiredRights expected)
+    {
+        XWikiDocument document = mock();
+        when(document.isEnforceRequiredRights()).thenReturn(enforce);
+        List<BaseObject> objects = values.stream()
+            .map(value -> {
+                if (value == null) {
+                    return null;
+                } else {
+                    BaseObject object = mock();
+                    when(object.getStringValue("level")).thenReturn(value);
+                    when(object.getDocumentReference()).thenReturn(DOCUMENT_REFERENCE);
+                    return object;
+                }
+            })
+            .toList();
+        when(document.getXObjects(CLASS_REFERENCE)).thenReturn(objects);
+
+        DocumentRequiredRights actual = this.documentRequiredRightsReader.readRequiredRights(document);
+
+        assertEquals(expected, actual);
+    }
+
+    static Stream<Arguments> getArguments()
+    {
+        return Stream.of(
+            Arguments.of(
+                false,
+                List.of(),
+                DocumentRequiredRights.EMPTY
+            ),
+            Arguments.of(
+                true,
+                List.of(),
+                new DocumentRequiredRights(true, Set.of())
+            ),
+            Arguments.of(
+                true,
+                List.of("programming"),
+                new DocumentRequiredRights(true, Set.of(new DocumentRequiredRight(Right.PROGRAM, null)))
+            ),
+            Arguments.of(
+                true,
+                List.of("edit"),
+                new DocumentRequiredRights(true, Set.of())
+            ),
+            Arguments.of(
+                true,
+                List.of("wiki_admin"),
+                new DocumentRequiredRights(true, Set.of(new DocumentRequiredRight(Right.ADMIN, EntityType.WIKI)))
+            ),
+            Arguments.of(
+                true,
+                Arrays.asList(null, "script"),
+                new DocumentRequiredRights(true, Set.of(new DocumentRequiredRight(Right.SCRIPT, EntityType.DOCUMENT)))
+            ),
+            Arguments.of(
+                true,
+                Arrays.asList(null, "admin", null),
+                new DocumentRequiredRights(true, Set.of(new DocumentRequiredRight(Right.ADMIN, EntityType.SPACE)))
+            )
+        );
+    }
+}
diff --git a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/test/java/org/xwiki/security/authorization/testwikibuilding/LegacyTestWiki.java b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/test/java/org/xwiki/security/authorization/testwikibuilding/LegacyTestWiki.java
index 0a03bc4596a..e493946830e 100644
--- a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/test/java/org/xwiki/security/authorization/testwikibuilding/LegacyTestWiki.java
+++ b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/test/java/org/xwiki/security/authorization/testwikibuilding/LegacyTestWiki.java
@@ -1073,6 +1073,12 @@ public Object invoke(Invocation invocation)
                     allowing(mockedDocument)
                         .getXObjects(XWikiGroupsDocumentInitializer.XWIKI_GROUPS_DOCUMENT_REFERENCE);
                     will(returnValue(Collections.emptyList()));
+                    // TODO: implement support for returning other values to add test cases for required rights.
+                    allowing(mockedDocument).isEnforceRequiredRights();
+                    will(returnValue(false));
+                    allowing(mockedDocument)
+                        .getXObjects(with(equal(new LocalDocumentReference("XWiki", "RequiredRightClass"))));
+                    will(returnValue(List.of()));
                 }
             });
 
diff --git a/xwiki-platform-core/xwiki-platform-xar/xwiki-platform-xar-model/src/main/java/org/xwiki/xar/internal/model/XarDocumentModel.java b/xwiki-platform-core/xwiki-platform-xar/xwiki-platform-xar-model/src/main/java/org/xwiki/xar/internal/model/XarDocumentModel.java
index a49b99ceb5a..703905e136f 100644
--- a/xwiki-platform-core/xwiki-platform-xar/xwiki-platform-xar-model/src/main/java/org/xwiki/xar/internal/model/XarDocumentModel.java
+++ b/xwiki-platform-core/xwiki-platform-xar/xwiki-platform-xar-model/src/main/java/org/xwiki/xar/internal/model/XarDocumentModel.java
@@ -19,6 +19,8 @@
  */
 package org.xwiki.xar.internal.model;
 
+import org.xwiki.stability.Unstable;
+
 /**
  * @version $Id$
  * @since 5.4M1
@@ -61,12 +63,20 @@ public class XarDocumentModel
      */
     public static final String VERSION_15 = "1.5";
 
+    /**
+     * Introduce the concept of required right enforcement.
+     *
+     * @since 16.10.0RC1
+     */
+    @Unstable
+    public static final String VERSION_16 = "1.6";
+
     /**
      * The current version.
      * 
      * @since 9.0RC1
      */
-    public static final String VERSION_CURRENT = VERSION_15;
+    public static final String VERSION_CURRENT = VERSION_16;
 
     public static final String ELEMENT_DOCUMENT = "xwikidoc";
 
@@ -163,6 +173,12 @@ public class XarDocumentModel
      */
     public static final String ELEMENT_REVISION_ORIGINALMEDATAAUTHOR = "originalMetadataAuthor";
 
+    /**
+     * @since 16.10.0RC1
+     */
+    @Unstable
+    public static final String ELEMENT_ENFORCE_REQUIRED_RIGHTS = "enforceRequiredRights";
+
     public static final String ELEMENT_REVISION_COMMENT = "comment";
 
     public static final String ELEMENT_REVISION_MINOR = "minorEdit";
-- 
GitLab