You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by an...@apache.org on 2021/07/21 07:52:31 UTC

[jackrabbit-oak] branch trunk updated (023f632 -> 5f241a4)

This is an automated email from the ASF dual-hosted git repository.

angela pushed a change to branch trunk
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git.


    from 023f632  OAK-9501 don't trim stack traces in unit and integration tests (#328)
     new 7d4d1f9  OAK-9494 : Check if a privilege name is included in a set/array of Privileges obtained from AccessControlManager.getPrivileges
     new 5f241a4  OAK-9494 : Check if a privilege name is included in a set/array of Privileges obtained from AccessControlManager.getPrivileges

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../impl/PrincipalBasedAccessControlManager.java   |   5 +
 .../jackrabbit/oak/benchmark/BenchmarkOptions.java |   5 +
 .../jackrabbit/oak/benchmark/BenchmarkRunner.java  |   6 +
 .../GetPrivilegeCollectionIncludeNamesTest.java    | 133 ++++++++++++++
 .../accesscontrol/AccessControlManagerImpl.java    |  13 +-
 .../security/privilege/PrivilegeManagerImpl.java   |  33 +---
 .../security/JackrabbitAccessControlManager.java   |  49 ++++++
 .../authorization/PrivilegeCollection.java         | 107 +++++++++++
 .../api/security/authorization/package-info.java   |   2 +-
 .../jackrabbit/api/security/package-info.java      |   2 +-
 .../JackrabbitAccessControlManagerDelegator.java   |  23 +++
 .../JackrabbitAccessControlManagerTest.java        |  91 ++++++++++
 .../PrivilegeCollectionDefaultTest.java            |  72 ++++++++
 .../AbstractAccessControlManager.java              |  76 +++++++-
 .../authorization/accesscontrol/package-info.java  |   2 +-
 .../security/privilege/PrivilegeBitsProvider.java  |  42 ++++-
 .../oak/spi/security/privilege/PrivilegeUtil.java  |  50 ++++++
 .../oak/spi/security/privilege/package-info.java   |   2 +-
 .../AbstractAccessControlManagerTest.java          | 195 ++++++++++++---------
 .../privilege/PrivilegeBitsProviderTest.java       |  48 +++++
 .../spi/security/privilege/PrivilegeUtilTest.java  |  54 ++++++
 21 files changed, 870 insertions(+), 140 deletions(-)
 create mode 100644 oak-benchmarks/src/main/java/org/apache/jackrabbit/oak/benchmark/authorization/GetPrivilegeCollectionIncludeNamesTest.java
 create mode 100644 oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/authorization/PrivilegeCollection.java
 create mode 100644 oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/authorization/JackrabbitAccessControlManagerTest.java
 create mode 100644 oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/authorization/PrivilegeCollectionDefaultTest.java

[jackrabbit-oak] 02/02: OAK-9494 : Check if a privilege name is included in a set/array of Privileges obtained from AccessControlManager.getPrivileges

Posted by an...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

angela pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git

commit 5f241a44263ae5b07d1d2e2966e5d2d7b0b5dfee
Author: angela <an...@adobe.com>
AuthorDate: Wed Jul 21 09:52:02 2021 +0200

    OAK-9494 : Check if a privilege name is included in a set/array of Privileges obtained from AccessControlManager.getPrivileges
---
 oak-benchmarks/run_GetPrivilegesIncludeNames.sh | 61 -------------------------
 1 file changed, 61 deletions(-)

diff --git a/oak-benchmarks/run_GetPrivilegesIncludeNames.sh b/oak-benchmarks/run_GetPrivilegesIncludeNames.sh
deleted file mode 100755
index b863bcf..0000000
--- a/oak-benchmarks/run_GetPrivilegesIncludeNames.sh
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/bin/sh
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-TITLE=GetPrivilegeCollectionIncludeNamesTest_ACCESSCONTORL_MANAGER_HAS_PRIVILEGES
-BENCH="GetPrivilegeCollectionIncludeNamesTest"
-ITEMS_TO_READ="10000"
-ACE_CNT="1 5 10"
-GROUP_CNT="1 10 100"
-RUNTIME=5
-FIXS="Oak-Segment-Tar"
-THREADS="1,10,20" #,50" #"1,2,4,8,10,15,20,50"
-PROFILE=true
-EVAL_TYPE=ACCESSCONTORL_MANAGER_HAS_PRIVILEGES #ACCESSCONTORL_MANAGER_GET_PRIVILEGE_COLLECTION, PRIVILEGE_MANAGER_INCLUDES, JCR_PRIVILEGE_NAME_AGGREGATION, ACCESSCONTORL_MANAGER_HAS_PRIVILEGES
-REPORT=false
-
-LOG=$TITLE"_$(date +'%Y%m%d_%H%M%S').csv"
-echo "Benchmarks: $BENCH" > $LOG
-echo "Fixture: $FIXS" >> $LOG
-echo "Runtime: $RUNTIME" >> $LOG
-echo "Concurrency: $THREADS" >> $LOG
-echo "Profiling: $PROFILE" >> $LOG
-echo "EvalType: $EVAL_TYPE" >> $LOG
-
-echo "Items to Read: $ITEMS_TO_READ" >> $LOG
-echo "Number of ACEs per Group Principal: $ACE_CNT" >> $LOG
-echo "Number of Groups: $GROUP_CNT" >> $LOG
-
-echo "--------------------------------------" >> $LOG
-
-for bm in $BENCH
-    do
-    for aceCnt in $ACE_CNT
-        do
-        for groupCnt in $GROUP_CNT
-        do  
-            echo "EvalType = $EVAL_TYPE with $aceCnt initial ACEs per principal and $groupCnt principals per subject" | tee -a $LOG
-            echo "-----------------------------------------------------------" | tee -a $LOG
-            rm -rf target/Jackrabbit-* target/Oak-Tar-*
-            cmd="java -Xmx2048m -Dprofile=$PROFILE -Druntime=$RUNTIME -Dwarmup=1 -jar target/oak-benchmarks-*-SNAPSHOT.jar benchmark --itemsToRead $ITEMS_TO_READ --numberOfInitialAce $aceCnt --numberOfGroups $groupCnt --csvFile $LOG --concurrency $THREADS --report $REPORT --evalType $EVAL_TYPE $bm $FIXS"
-            echo $cmd
-            $cmd
-        done
-    done
-done
-echo "-----------------------------------------"
-echo "Benchmark completed. see $LOG for details:"
-cat $LOG

[jackrabbit-oak] 01/02: OAK-9494 : Check if a privilege name is included in a set/array of Privileges obtained from AccessControlManager.getPrivileges

Posted by an...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

angela pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git

commit 7d4d1f9314244db91d2d4f4468727e876e3c92f1
Author: angela <an...@adobe.com>
AuthorDate: Wed Jul 21 09:50:33 2021 +0200

    OAK-9494 : Check if a privilege name is included in a set/array of Privileges obtained from AccessControlManager.getPrivileges
---
 .../impl/PrincipalBasedAccessControlManager.java   |   5 +
 oak-benchmarks/run_GetPrivilegesIncludeNames.sh    |  61 +++++++
 .../jackrabbit/oak/benchmark/BenchmarkOptions.java |   5 +
 .../jackrabbit/oak/benchmark/BenchmarkRunner.java  |   6 +
 .../GetPrivilegeCollectionIncludeNamesTest.java    | 133 ++++++++++++++
 .../accesscontrol/AccessControlManagerImpl.java    |  13 +-
 .../security/privilege/PrivilegeManagerImpl.java   |  33 +---
 .../security/JackrabbitAccessControlManager.java   |  49 ++++++
 .../authorization/PrivilegeCollection.java         | 107 +++++++++++
 .../api/security/authorization/package-info.java   |   2 +-
 .../jackrabbit/api/security/package-info.java      |   2 +-
 .../JackrabbitAccessControlManagerDelegator.java   |  23 +++
 .../JackrabbitAccessControlManagerTest.java        |  91 ++++++++++
 .../PrivilegeCollectionDefaultTest.java            |  72 ++++++++
 .../AbstractAccessControlManager.java              |  76 +++++++-
 .../authorization/accesscontrol/package-info.java  |   2 +-
 .../security/privilege/PrivilegeBitsProvider.java  |  42 ++++-
 .../oak/spi/security/privilege/PrivilegeUtil.java  |  50 ++++++
 .../oak/spi/security/privilege/package-info.java   |   2 +-
 .../AbstractAccessControlManagerTest.java          | 195 ++++++++++++---------
 .../privilege/PrivilegeBitsProviderTest.java       |  48 +++++
 .../spi/security/privilege/PrivilegeUtilTest.java  |  54 ++++++
 22 files changed, 931 insertions(+), 140 deletions(-)

diff --git a/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalBasedAccessControlManager.java b/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalBasedAccessControlManager.java
index 8b67389..f98a37f 100644
--- a/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalBasedAccessControlManager.java
+++ b/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalBasedAccessControlManager.java
@@ -109,6 +109,11 @@ class PrincipalBasedAccessControlManager extends AbstractAccessControlManager im
         this.filterProvider = filterProvider;
         filter = filterProvider.getFilter(mgrProvider.getSecurityProvider(), mgrProvider.getRoot(), mgrProvider.getNamePathMapper());
     }
+    
+    @Override
+    protected @NotNull PrivilegeBitsProvider getPrivilegeBitsProvider() {
+        return mgrProvider.getPrivilegeBitsProvider();
+    }
 
     //-------------------------------------< JackrabbitAccessControlManager >---
 
diff --git a/oak-benchmarks/run_GetPrivilegesIncludeNames.sh b/oak-benchmarks/run_GetPrivilegesIncludeNames.sh
new file mode 100755
index 0000000..b863bcf
--- /dev/null
+++ b/oak-benchmarks/run_GetPrivilegesIncludeNames.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+TITLE=GetPrivilegeCollectionIncludeNamesTest_ACCESSCONTORL_MANAGER_HAS_PRIVILEGES
+BENCH="GetPrivilegeCollectionIncludeNamesTest"
+ITEMS_TO_READ="10000"
+ACE_CNT="1 5 10"
+GROUP_CNT="1 10 100"
+RUNTIME=5
+FIXS="Oak-Segment-Tar"
+THREADS="1,10,20" #,50" #"1,2,4,8,10,15,20,50"
+PROFILE=true
+EVAL_TYPE=ACCESSCONTORL_MANAGER_HAS_PRIVILEGES #ACCESSCONTORL_MANAGER_GET_PRIVILEGE_COLLECTION, PRIVILEGE_MANAGER_INCLUDES, JCR_PRIVILEGE_NAME_AGGREGATION, ACCESSCONTORL_MANAGER_HAS_PRIVILEGES
+REPORT=false
+
+LOG=$TITLE"_$(date +'%Y%m%d_%H%M%S').csv"
+echo "Benchmarks: $BENCH" > $LOG
+echo "Fixture: $FIXS" >> $LOG
+echo "Runtime: $RUNTIME" >> $LOG
+echo "Concurrency: $THREADS" >> $LOG
+echo "Profiling: $PROFILE" >> $LOG
+echo "EvalType: $EVAL_TYPE" >> $LOG
+
+echo "Items to Read: $ITEMS_TO_READ" >> $LOG
+echo "Number of ACEs per Group Principal: $ACE_CNT" >> $LOG
+echo "Number of Groups: $GROUP_CNT" >> $LOG
+
+echo "--------------------------------------" >> $LOG
+
+for bm in $BENCH
+    do
+    for aceCnt in $ACE_CNT
+        do
+        for groupCnt in $GROUP_CNT
+        do  
+            echo "EvalType = $EVAL_TYPE with $aceCnt initial ACEs per principal and $groupCnt principals per subject" | tee -a $LOG
+            echo "-----------------------------------------------------------" | tee -a $LOG
+            rm -rf target/Jackrabbit-* target/Oak-Tar-*
+            cmd="java -Xmx2048m -Dprofile=$PROFILE -Druntime=$RUNTIME -Dwarmup=1 -jar target/oak-benchmarks-*-SNAPSHOT.jar benchmark --itemsToRead $ITEMS_TO_READ --numberOfInitialAce $aceCnt --numberOfGroups $groupCnt --csvFile $LOG --concurrency $THREADS --report $REPORT --evalType $EVAL_TYPE $bm $FIXS"
+            echo $cmd
+            $cmd
+        done
+    done
+done
+echo "-----------------------------------------"
+echo "Benchmark completed. see $LOG for details:"
+cat $LOG
diff --git a/oak-benchmarks/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkOptions.java b/oak-benchmarks/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkOptions.java
index e5f96fc..8657bf1 100644
--- a/oak-benchmarks/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkOptions.java
+++ b/oak-benchmarks/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkOptions.java
@@ -94,6 +94,7 @@ public class BenchmarkOptions {
     private final OptionSpec<String> nonOption;
     private final OptionSpec<?> help;
     private final OptionSpec<Boolean> useAggregationFilter;
+    private final OptionSpec<String> evalType;
     private final OptionSpec<String> elasticHost;
     private final OptionSpec<String> elasticScheme;
     private final OptionSpec<Integer> elasticPort;
@@ -359,6 +360,8 @@ public class BenchmarkOptions {
     public OptionSpec<Boolean> getUseAggregationFilter() {
         return useAggregationFilter;
     }
+    
+    public OptionSpec<String> getEvalutionType() { return evalType; }
 
 
     public BenchmarkOptions(OptionParser parser) {
@@ -443,6 +446,8 @@ public class BenchmarkOptions {
         useAggregationFilter = parser.accepts("useAggregationFilter", "Run principal-based tests with 'AggregationFilter'")
                 .withOptionalArg().ofType(Boolean.class)
                 .defaultsTo(Boolean.FALSE);
+        evalType = parser.accepts("evalType", "Allows to switch between different evaluation types within a single benchmark")
+                .withOptionalArg().ofType(String.class);
         batchSize = parser.accepts("batchSize", "Batch size before persisting operations.")
                 .withOptionalArg().ofType(Integer.class).defaultsTo(AddMembersTest.DEFAULT_BATCH_SIZE);
         importBehavior = parser.accepts("importBehavior", "Protected Item Import Behavior")
diff --git a/oak-benchmarks/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java b/oak-benchmarks/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java
index 2878e32..5b0584e 100644
--- a/oak-benchmarks/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java
+++ b/oak-benchmarks/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java
@@ -36,6 +36,7 @@ import org.apache.jackrabbit.oak.benchmark.authentication.external.SyncAllUsersT
 import org.apache.jackrabbit.oak.benchmark.authentication.external.SyncExternalUsersTest;
 import org.apache.jackrabbit.oak.benchmark.authorization.AceCreationTest;
 import org.apache.jackrabbit.oak.benchmark.authorization.CanReadNonExisting;
+import org.apache.jackrabbit.oak.benchmark.authorization.GetPrivilegeCollectionIncludeNamesTest;
 import org.apache.jackrabbit.oak.benchmark.authorization.SaveHasItemGetItemTest;
 import org.apache.jackrabbit.oak.benchmark.authorization.HasPermissionHasItemGetItemTest;
 import org.apache.jackrabbit.oak.benchmark.authorization.HasPrivilegesHasItemGetItemTest;
@@ -290,6 +291,11 @@ public class BenchmarkRunner {
                                 benchmarkOptions.getNumberOfInitialAce().value(options),
                                 benchmarkOptions.getNumberOfGroups().value(options),
                                 benchmarkOptions.getReport().value(options)),
+                        new GetPrivilegeCollectionIncludeNamesTest(benchmarkOptions.getItemsToRead().value(options),
+                                benchmarkOptions.getNumberOfInitialAce().value(options),
+                                benchmarkOptions.getNumberOfGroups().value(options),
+                                benchmarkOptions.getReport().value(options), 
+                                benchmarkOptions.getEvalutionType().value(options)),
                         new ConcurrentReadDeepTreeTest(
                                 benchmarkOptions.getRunAsAdmin().value(options),
                                 benchmarkOptions.getItemsToRead().value(options),
diff --git a/oak-benchmarks/src/main/java/org/apache/jackrabbit/oak/benchmark/authorization/GetPrivilegeCollectionIncludeNamesTest.java b/oak-benchmarks/src/main/java/org/apache/jackrabbit/oak/benchmark/authorization/GetPrivilegeCollectionIncludeNamesTest.java
new file mode 100644
index 0000000..ebbfb43
--- /dev/null
+++ b/oak-benchmarks/src/main/java/org/apache/jackrabbit/oak/benchmark/authorization/GetPrivilegeCollectionIncludeNamesTest.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.benchmark.authorization;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+import joptsimple.internal.Strings;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
+import org.apache.jackrabbit.api.security.authorization.PrivilegeCollection;
+import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBits;
+import org.jetbrains.annotations.NotNull;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.Privilege;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Stream;
+
+public class GetPrivilegeCollectionIncludeNamesTest extends AbstractHasItemGetItemTest {
+    
+    private enum EvaluationType {
+        ACCESSCONTORL_MANAGER_GET_PRIVILEGE_COLLECTION,
+        JCR_PRIVILEGE_NAME_AGGREGATION,
+        ACCESSCONTORL_MANAGER_HAS_PRIVILEGES
+    } 
+    
+    private static final List<String> ALL_PRIVILEGE_NAMES = ImmutableList.copyOf(PrivilegeBits.BUILT_IN.keySet());
+    
+    private final EvaluationType evalType;
+    
+    public GetPrivilegeCollectionIncludeNamesTest(int itemsToRead, int numberOfACEs, int numberOfGroups, boolean doReport, String evalType) {
+        super(itemsToRead, numberOfACEs, numberOfGroups, doReport);
+        this.evalType = (Strings.isNullOrEmpty(evalType)) ? EvaluationType.ACCESSCONTORL_MANAGER_GET_PRIVILEGE_COLLECTION : EvaluationType.valueOf(evalType);
+    }
+
+    @Override
+    @NotNull String additionalMethodName() {
+        return evalType.name();
+    }
+
+    @Override
+    void additionalOperations(@NotNull String path, @NotNull Session s, @NotNull AccessControlManager acMgr) {
+        try {
+            List<String> privNames = ImmutableList.of(getRandom(ALL_PRIVILEGE_NAMES), getRandom(ALL_PRIVILEGE_NAMES), getRandom(ALL_PRIVILEGE_NAMES), getRandom(ALL_PRIVILEGE_NAMES));
+            String accessControlledPath = getAccessControlledPath(path);
+            if (EvaluationType.ACCESSCONTORL_MANAGER_GET_PRIVILEGE_COLLECTION == evalType) {
+                PrivilegeCollection pc = ((JackrabbitAccessControlManager) acMgr).getPrivilegeCollection(accessControlledPath);
+                for (String toTest : privNames) {
+                    boolean includes = pc.includes(toTest);
+                    if (doReport) {
+                        System.out.println("PrivilegeCollection.includes('"+toTest+"') : "+includes);
+                    }
+                }
+            } else if (EvaluationType.JCR_PRIVILEGE_NAME_AGGREGATION == evalType) {
+                // evaluation using regular JCR Privilege API 
+                Privilege[] privileges = acMgr.getPrivileges(accessControlledPath);
+                Set<String> privilegeNames = Sets.newHashSet(AccessControlUtils.namesFromPrivileges(privileges));
+                Stream.of(privileges).filter(Privilege::isAggregate).forEach(privilege -> Collections.addAll(privilegeNames, AccessControlUtils.namesFromPrivileges(privilege.getAggregatePrivileges())));
+                for (String toTest : privNames) {
+                    boolean includes = privilegeNames.contains(toTest);
+                    if (doReport) {
+                        System.out.println("Privileges '"+Arrays.toString(privileges)+"' include '"+toTest+"' : "+includes);
+                    }
+                }
+            } else {
+                // evaluation using separate AccessControlManager.hasPrivileges
+                for (String toTest : privNames) {
+                    boolean hasPrivilege = acMgr.hasPrivileges(accessControlledPath, AccessControlUtils.privilegesFromNames(acMgr, toTest)); 
+                    if (doReport) {
+                        System.out.println("AccessControlManager.hasPrivileges('"+accessControlledPath+"','"+toTest+"') : "+hasPrivilege);
+                    }
+                }
+            }
+        } catch (RepositoryException e) {
+            if (doReport) {
+                e.printStackTrace(System.out);
+            }
+        }
+    }
+
+    @Override
+    protected void randomRead(Session testSession, List<String> allPaths, int cnt) throws RepositoryException {
+        boolean logout = false;
+        if (testSession == null) {
+            testSession = getTestSession();
+            logout = true;
+        }
+        try {
+            int nodeCnt = 0;
+            int propertyCnt = 0;
+            int addCnt = 0;
+            int noAccess = 0;
+            int size = allPaths.size();
+
+            AccessControlManager acMgr = testSession.getAccessControlManager();
+
+            long start = System.currentTimeMillis();
+            for (int i = 0; i < cnt; i++) {
+                double rand = size * Math.random();
+                int index = (int) Math.floor(rand);
+                String path = allPaths.get(index);
+                additionalOperations(path, testSession, acMgr);
+            }
+            long end = System.currentTimeMillis();
+            if (doReport) {
+                System.out.println("Session " + testSession.getUserID() + " reading " + cnt + " (Nodes: "+ nodeCnt +"; Properties: "+propertyCnt+"; no access: "+noAccess+"; "+ additionalMethodName()+": "+addCnt+") completed in " + (end - start));
+            }
+        } finally {
+            if (logout) {
+                logout(testSession);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/AccessControlManagerImpl.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/AccessControlManagerImpl.java
index f1e0a6c..0523345 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/AccessControlManagerImpl.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/AccessControlManagerImpl.java
@@ -80,7 +80,6 @@ import org.apache.jackrabbit.oak.spi.security.authorization.restriction.Restrict
 import org.apache.jackrabbit.oak.spi.security.principal.PrincipalConfiguration;
 import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
 import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBits;
-import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBitsProvider;
 import org.apache.jackrabbit.oak.spi.xml.ImportBehavior;
 import org.apache.jackrabbit.util.ISO9075;
 import org.apache.jackrabbit.util.Text;
@@ -100,7 +99,6 @@ public class AccessControlManagerImpl extends AbstractAccessControlManager imple
 
     private static final Logger log = LoggerFactory.getLogger(AccessControlManagerImpl.class);
 
-    private final PrivilegeBitsProvider bitsProvider;
     private final ReadOnlyNodeTypeManager ntMgr;
 
     private final PrincipalManager principalManager;
@@ -113,7 +111,6 @@ public class AccessControlManagerImpl extends AbstractAccessControlManager imple
                                     @NotNull SecurityProvider securityProvider) {
         super(root, namePathMapper, securityProvider);
 
-        bitsProvider = new PrivilegeBitsProvider(root);
         ntMgr = ReadOnlyNodeTypeManager.getInstance(root, namePathMapper);
 
         principalManager = securityProvider.getConfiguration(PrincipalConfiguration.class).getPrincipalManager(root, namePathMapper);
@@ -318,7 +315,7 @@ public class AccessControlManagerImpl extends AbstractAccessControlManager imple
 
             Tree aceNode = TreeUtil.addChild(aclTree, nodeName, ntName);
             aceNode.setProperty(REP_PRINCIPAL_NAME, ace.getPrincipal().getName());
-            aceNode.setProperty(REP_PRIVILEGES, bitsProvider.getPrivilegeNames(ace.getPrivilegeBits()), Type.NAMES);
+            aceNode.setProperty(REP_PRIVILEGES, getPrivilegeBitsProvider().getPrivilegeNames(ace.getPrivilegeBits()), Type.NAMES);
             Set<Restriction> restrictions = ace.getRestrictions();
             restrictionProvider.writeRestrictions(oakPath, aceNode, restrictions);
         }
@@ -533,7 +530,7 @@ public class AccessControlManagerImpl extends AbstractAccessControlManager imple
         boolean isAllow = NT_REP_GRANT_ACE.equals(TreeUtil.getPrimaryTypeName(aceTree));
         Set<Restriction> restrictions = restrictionProvider.readRestrictions(oakPath, aceTree);
         Iterable<String> privNames = checkNotNull(TreeUtil.getStrings(aceTree, REP_PRIVILEGES));
-        return new Entry(getPrincipal(aceTree, principalMap), bitsProvider.getBits(privNames), isAllow, restrictions, getNamePathMapper());
+        return new Entry(getPrincipal(aceTree, principalMap), getPrivilegeBitsProvider().getBits(privNames), isAllow, restrictions, getNamePathMapper());
     }
 
     @NotNull
@@ -648,7 +645,7 @@ public class AccessControlManagerImpl extends AbstractAccessControlManager imple
         @Override
         @NotNull
         PrivilegeBits getPrivilegeBits(@NotNull Privilege[] privileges) {
-            return bitsProvider.getBits(privileges, getNamePathMapper());
+            return getPrivilegeBitsProvider().getBits(privileges, getNamePathMapper());
         }
 
         @Override
@@ -718,7 +715,7 @@ public class AccessControlManagerImpl extends AbstractAccessControlManager imple
         @Override
         @NotNull
         PrivilegeBits getPrivilegeBits(@NotNull Privilege[] privileges) {
-            return bitsProvider.getBits(privileges, getNamePathMapper());
+            return getPrivilegeBitsProvider().getBits(privileges, getNamePathMapper());
         }
 
         @Override
@@ -776,7 +773,7 @@ public class AccessControlManagerImpl extends AbstractAccessControlManager imple
         @Override
         public Privilege[] getPrivileges() {
             Set<Privilege> privileges = new HashSet<>();
-            for (String name : bitsProvider.getPrivilegeNames(getPrivilegeBits())) {
+            for (String name : getPrivilegeBitsProvider().getPrivilegeNames(getPrivilegeBits())) {
                 try {
                     privileges.add(getPrivilegeManager().getPrivilege(getNamePathMapper().getJcrName(name)));
                 } catch (RepositoryException e) {
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeManagerImpl.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeManagerImpl.java
index 6a6ac1f..a40df3e 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeManagerImpl.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/privilege/PrivilegeManagerImpl.java
@@ -17,7 +17,6 @@
 package org.apache.jackrabbit.oak.security.privilege;
 
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
@@ -32,6 +31,7 @@ import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeDefinition;
 import org.apache.jackrabbit.oak.spi.security.privilege.ImmutablePrivilegeDefinition;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeUtil;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
@@ -67,7 +67,7 @@ class PrivilegeManagerImpl implements PrivilegeManager {
     @NotNull
     @Override
     public Privilege getPrivilege(@NotNull String privilegeName) throws RepositoryException {
-        PrivilegeDefinition def = getPrivilegeDefinition(getOakName(privilegeName));
+        PrivilegeDefinition def = getPrivilegeDefinition(PrivilegeUtil.getOakName(privilegeName, namePathMapper));
         if (def == null) {
             throw new AccessControlException("No such privilege " + privilegeName);
         } else {
@@ -85,7 +85,7 @@ class PrivilegeManagerImpl implements PrivilegeManager {
         if (Strings.isNullOrEmpty(privilegeName)) {
             throw new RepositoryException("Invalid privilege name " + privilegeName);
         }
-        PrivilegeDefinition definition = new ImmutablePrivilegeDefinition(getOakName(privilegeName), isAbstract, getOakNames(declaredAggregateNames));
+        PrivilegeDefinition definition = new ImmutablePrivilegeDefinition(PrivilegeUtil.getOakName(privilegeName, namePathMapper), isAbstract, PrivilegeUtil.getOakNames(declaredAggregateNames, namePathMapper));
         PrivilegeDefinitionWriter writer = new PrivilegeDefinitionWriter(getWriteRoot());
         writer.writeDefinition(definition);
 
@@ -101,33 +101,6 @@ class PrivilegeManagerImpl implements PrivilegeManager {
     }
 
     @NotNull
-    private Set<String> getOakNames(@Nullable String[] jcrNames) throws RepositoryException {
-        Set<String> oakNames;
-        if (jcrNames == null || jcrNames.length == 0) {
-            oakNames = Collections.emptySet();
-        } else {
-            oakNames = new HashSet<>(jcrNames.length);
-            for (String jcrName : jcrNames) {
-                String oakName = getOakName(jcrName);
-                oakNames.add(oakName);
-            }
-        }
-        return oakNames;
-    }
-
-    @NotNull
-    private String getOakName(@Nullable String jcrName) throws RepositoryException {
-        if (jcrName == null) {
-            throw new AccessControlException("Invalid privilege name 'null'");
-        }
-        String oakName = namePathMapper.getOakNameOrNull(jcrName);
-        if (oakName == null) {
-        	throw new AccessControlException("Cannot resolve privilege name " + jcrName);
-        }
-        return oakName;
-    }
-
-    @NotNull
     private Privilege getPrivilege(@NotNull PrivilegeDefinition definition) {
         return new PrivilegeImpl(definition);
     }
diff --git a/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/JackrabbitAccessControlManager.java b/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/JackrabbitAccessControlManager.java
index 1bf9bd2..135ee74 100644
--- a/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/JackrabbitAccessControlManager.java
+++ b/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/JackrabbitAccessControlManager.java
@@ -28,6 +28,7 @@ import javax.jcr.security.Privilege;
 import java.security.Principal;
 import java.util.Set;
 
+import org.apache.jackrabbit.api.security.authorization.PrivilegeCollection;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.osgi.annotation.versioning.ProviderType;
@@ -171,4 +172,52 @@ public interface JackrabbitAccessControlManager extends AccessControlManager {
     @NotNull
     public Privilege[] getPrivileges(@Nullable String absPath, @NotNull Set<Principal> principals)
             throws PathNotFoundException, AccessDeniedException, RepositoryException;
+
+    /**
+     * <p>Returns the {@link PrivilegeCollection} for editing session at the given absolute path, which
+     * must be an existing node. This is equivalent to {@link #getPrivileges(String)} and 
+     * {@link #hasPrivileges(String, Privilege[])} but allows for easy resolution of aggregated privileges 
+     * (like e.g. jcr:all) and repeated evaluation if the editing session has privileges granted 
+     * at the given target node.</p>
+     * 
+     * Note: For backwards compatibility this method comes with a default implementation that computes the {@link PrivilegeCollection}
+     * using regular JCR/Jackrabbit API, which might not be efficient. Implementations of {@link JackrabbitAccessControlManager} 
+     * are therefore expected to overwrite the default.
+     * 
+     * @param absPath An absolute path to an existing JCR node.
+     * @return A {@link PrivilegeCollection} wrapping around the privileges granted for the editing session at absPath.
+     * @throws PathNotFoundException if no node at <code>absPath</code> exists or the session does not have sufficient 
+     * access to retrieve a node at that location.
+     * @throws RepositoryException If another error occurs.
+     * @since Oak 1.42.0
+     */
+    @NotNull
+    default PrivilegeCollection getPrivilegeCollection(@Nullable String absPath) throws RepositoryException {
+        return new PrivilegeCollection.Default(getPrivileges(absPath), this);
+    }
+
+    /**
+     * <p>Returns the {@link PrivilegeCollection} for the given set of principals at the given absolute path, which
+     * must be an existing node. This is equivalent to {@link #getPrivileges(String,Set)} and 
+     * {@link #hasPrivileges(String, Set, Privilege[])} but allows for easy resolution of aggregated privileges 
+     * (like e.g. jcr:all) and repeated evaluation if the editing session has privileges granted 
+     * at the given target node.</p>
+     *
+     * Note: For backwards compatibility this method comes with a default implementation that computes the {@link PrivilegeCollection}
+     * using regular JCR/Jackrabbit API, which might not be efficient. Implementations of {@link JackrabbitAccessControlManager}
+     * are therefore expected to overwrite the default.
+     * 
+     * @param absPath An absolute path to an existing JCR node.
+     * @param principals A set of principals for which the {@link PrivilegeCollection} should be created.
+     * @return A {@link PrivilegeCollection} wrapping around the privileges granted for the editing session at absPath.
+     * @throws PathNotFoundException if no node at <code>absPath</code> exists or the session does not have sufficient 
+     * access to retrieve a node at that location.
+     * @throws AccessDeniedException if the session lacks <code>READ_ACCESS_CONTROL</code> privilege for the <code>absPath</code> node.
+     * @throws RepositoryException If another error occurs.
+     * @since Oak 1.42.0
+     */
+    @NotNull
+    default PrivilegeCollection getPrivilegeCollection(@Nullable String absPath, @NotNull Set<Principal> principals) throws RepositoryException {
+        return new PrivilegeCollection.Default(getPrivileges(absPath, principals), this);
+    }
 }
\ No newline at end of file
diff --git a/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/authorization/PrivilegeCollection.java b/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/authorization/PrivilegeCollection.java
new file mode 100644
index 0000000..9f2ea4b
--- /dev/null
+++ b/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/authorization/PrivilegeCollection.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.api.security.authorization;
+
+import org.jetbrains.annotations.NotNull;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.security.AccessControlException;
+import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.Privilege;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Stream;
+
+public interface PrivilegeCollection {
+
+    /**
+     * Return the underlying privilege array.
+     * 
+     * @return the privilege array for which this instance has been created.
+     * @throws RepositoryException If an error occurs.
+     */
+    Privilege[] getPrivileges() throws RepositoryException;
+
+    /**
+     * Tests whether the given JCR {@code privilegeNames} are contained in the privileges for which this instance of 
+     * {@code PrivilegeEvaluation} has been created such as e.g. through 
+     * {@link org.apache.jackrabbit.api.security.JackrabbitAccessControlManager#getPrivilegeCollection(String)} or  
+     * {@link org.apache.jackrabbit.api.security.JackrabbitAccessControlManager#getPrivilegeCollection(String, Set)}. 
+     * The inclusion can either be direct or through privilege aggregation.
+     * 
+     * @param privilegeNames The JCR names of privileges to be tested. They can be passed in expanded form 
+     * (like e.g. {@link Privilege#JCR_READ)} or in qualified form (i.e. 'jcr:read' if 'jcr' was the prefixed defined for 
+     * the 'http://www.jcp.org/jcr/1.0' namespace.
+     * @return {@code true} if the underlying {@code privileges} include all specified privilege names either directly 
+     * or by means of aggregation; {@code false} if one or multiple privileges are not included. If {@code jcr:all} privilege 
+     * is part of this collection or if no privilege names are specified this method will return {@code true}. 
+     * If no privileges are granted {@code false} is returned.
+     * @throws AccessControlException If any of the given privilege names is invalid i.e. no such privilege exists.
+     * @throws RepositoryException If another error occurs.
+     * @since Oak 1.42.0
+     */
+    boolean includes(@NotNull String... privilegeNames) throws RepositoryException;
+
+    /**
+     * Default implementation of the {@link PrivilegeCollection} interface.
+     * 
+     * Note that this implementation uses JCR API to resolve privilege aggregation, which is expected to be inefficient.
+     * Implementations of {@link org.apache.jackrabbit.api.security.JackrabbitAccessControlManager#getPrivilegeCollection(String)} 
+     * and {@link org.apache.jackrabbit.api.security.JackrabbitAccessControlManager#getPrivilegeCollection(String, Set)} 
+     * therefore should provide improved implementations of the {@code PrivilegeCollection} interface.
+     * 
+     * @since Oak 1.42.0
+     */
+    class Default implements PrivilegeCollection {
+        
+        private final Privilege[] privileges;
+        private final AccessControlManager accessControlManager;
+        
+        public Default(@NotNull Privilege[] privileges, @NotNull AccessControlManager accessControlManager) {
+            this.privileges = privileges;
+            this.accessControlManager = accessControlManager;
+        }
+
+        @Override
+        public Privilege[] getPrivileges() {
+            return privileges;
+        }
+
+        @Override
+        public boolean includes(@NotNull String... privilegeNames) throws RepositoryException {
+            if (privilegeNames.length == 0) {
+                return true;
+            }
+            if (privileges.length == 0) {
+                return false;
+            }
+            Set<Privilege> toTest = new HashSet<>(privilegeNames.length);
+            for (String pName : privilegeNames) {
+                toTest.add(accessControlManager.privilegeFromName(pName));
+            }
+            Set<Privilege> privilegeSet = new HashSet<>(Arrays.asList(privileges));
+            if (privilegeSet.containsAll(toTest)) {
+                return true;
+            } else {
+                Stream.of(privileges).filter(Privilege::isAggregate).forEach(privilege -> Collections.addAll(privilegeSet, privilege.getAggregatePrivileges()));
+                return privilegeSet.containsAll(toTest);
+            }
+        }
+    }
+}
diff --git a/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/authorization/package-info.java b/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/authorization/package-info.java
index d71b3f6..132dc73 100644
--- a/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/authorization/package-info.java
+++ b/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/authorization/package-info.java
@@ -18,5 +18,5 @@
 /**
  * Jackrabbit extensions for authorization.
  */
-@org.osgi.annotation.versioning.Version("2.4.2")
+@org.osgi.annotation.versioning.Version("2.5.0")
 package org.apache.jackrabbit.api.security.authorization;
diff --git a/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/package-info.java b/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/package-info.java
index 8fef895..4e7c077 100644
--- a/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/package-info.java
+++ b/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/package-info.java
@@ -18,5 +18,5 @@
 /**
  * Jackrabbit extensions for access control.
  */
-@org.osgi.annotation.versioning.Version("2.4.1")
+@org.osgi.annotation.versioning.Version("2.5.0")
 package org.apache.jackrabbit.api.security;
diff --git a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/JackrabbitAccessControlManagerDelegator.java b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/JackrabbitAccessControlManagerDelegator.java
index f6840e4..f842c5e 100644
--- a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/JackrabbitAccessControlManagerDelegator.java
+++ b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/JackrabbitAccessControlManagerDelegator.java
@@ -29,6 +29,7 @@ import javax.jcr.security.Privilege;
 
 import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
 import org.apache.jackrabbit.api.security.JackrabbitAccessControlPolicy;
+import org.apache.jackrabbit.api.security.authorization.PrivilegeCollection;
 import org.apache.jackrabbit.oak.jcr.session.operation.SessionOperation;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -114,6 +115,28 @@ public class JackrabbitAccessControlManagerDelegator implements JackrabbitAccess
             }
         });
     }
+    
+    @Override
+    public @NotNull PrivilegeCollection getPrivilegeCollection(@Nullable String absPath) throws RepositoryException {
+        return delegate.perform(new SessionOperation<PrivilegeCollection>("getPrivilegeCollection") {
+            @NotNull
+            @Override
+            public PrivilegeCollection perform() throws RepositoryException {
+                return jackrabbitACManager.getPrivilegeCollection(absPath);
+            }
+        });
+    }
+
+    @Override
+    public @NotNull PrivilegeCollection getPrivilegeCollection(@Nullable String absPath, @NotNull Set<Principal> principals) throws RepositoryException {
+        return delegate.perform(new SessionOperation<PrivilegeCollection>("getPrivilegeCollection") {
+            @NotNull
+            @Override
+            public PrivilegeCollection perform() throws RepositoryException {
+                return jackrabbitACManager.getPrivilegeCollection(absPath, principals);
+            }
+        });
+    }
 
     @Override
     public Privilege[] getSupportedPrivileges(String absPath) throws RepositoryException {
diff --git a/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/authorization/JackrabbitAccessControlManagerTest.java b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/authorization/JackrabbitAccessControlManagerTest.java
new file mode 100644
index 0000000..a7b1a66
--- /dev/null
+++ b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/authorization/JackrabbitAccessControlManagerTest.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.jcr.security.authorization;
+
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
+import org.apache.jackrabbit.api.security.authorization.PrivilegeCollection;
+import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+import org.apache.jackrabbit.test.NotExecutableException;
+
+import javax.jcr.AccessDeniedException;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.security.Privilege;
+
+import java.util.Collections;
+
+import static org.junit.Assert.assertArrayEquals;
+
+public class JackrabbitAccessControlManagerTest extends AbstractEvaluationTest {
+    
+    private JackrabbitAccessControlManager jrAcMgr;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        if (!(acMgr instanceof JackrabbitAccessControlManager)) {
+            throw new NotExecutableException("JackrabbitAccessControlManager expected");
+        }
+        jrAcMgr = (JackrabbitAccessControlManager) acMgr;
+    }
+    
+    public void testGetPrivilegeCollection() throws Exception {
+        PrivilegeCollection pc = jrAcMgr.getPrivilegeCollection(path);
+        assertTrue(pc.includes(Privilege.JCR_ALL));
+        assertTrue(pc.includes(Privilege.JCR_READ, PrivilegeConstants.REP_READ_PROPERTIES));
+        assertArrayEquals(privilegesFromName(Privilege.JCR_ALL), pc.getPrivileges());
+    }
+    
+    public void testGetPrivilegeCollectionInvalidPath() throws Exception {
+        try {
+            jrAcMgr.getPrivilegeCollection("/invalid");
+            fail();
+        } catch (PathNotFoundException e) {
+            // success
+        }
+    }
+
+    public void testGetPrivilegeCollectionEmptyPrincipalSet() throws Exception {
+        PrivilegeCollection pc = jrAcMgr.getPrivilegeCollection(path, Collections.emptySet());
+        assertFalse(pc.includes(Privilege.JCR_ALL));
+        assertFalse(pc.includes(Privilege.JCR_READ, PrivilegeConstants.REP_READ_PROPERTIES));
+        assertArrayEquals(new Privilege[0], pc.getPrivileges());
+    }
+
+    public void testGetPrivilegeCollectionPrincipalSet() throws Exception {
+        PrivilegeCollection pc = jrAcMgr.getPrivilegeCollection(path, Collections.singleton(EveryonePrincipal.getInstance()));
+        assertFalse(pc.includes(Privilege.JCR_ALL));
+        assertTrue(pc.includes(Privilege.JCR_READ, PrivilegeConstants.REP_READ_PROPERTIES));
+        assertArrayEquals(privilegesFromName(Privilege.JCR_READ), pc.getPrivileges());
+    }
+    
+    public void testGetPrivilegeCollectionTestSession() throws Exception {
+        PrivilegeCollection pc = ((JackrabbitAccessControlManager) testAcMgr).getPrivilegeCollection(path);
+        assertFalse(pc.includes(Privilege.JCR_ALL));
+        assertTrue(pc.includes(Privilege.JCR_READ, PrivilegeConstants.REP_READ_PROPERTIES));
+        assertArrayEquals(privilegesFromName(Privilege.JCR_READ), pc.getPrivileges());
+    }
+
+    public void testGetPrivilegeCollectionTestSessionPrincipalSet() throws Exception {
+        try {
+            ((JackrabbitAccessControlManager) testAcMgr).getPrivilegeCollection(path, Collections.singleton(EveryonePrincipal.getInstance()));
+            fail("AccessDeniedException expected");
+        } catch (AccessDeniedException e) {
+            // success
+        }
+    }
+}
\ No newline at end of file
diff --git a/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/authorization/PrivilegeCollectionDefaultTest.java b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/authorization/PrivilegeCollectionDefaultTest.java
new file mode 100644
index 0000000..85e44c6
--- /dev/null
+++ b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/authorization/PrivilegeCollectionDefaultTest.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.jcr.security.authorization;
+
+import org.apache.jackrabbit.api.security.authorization.PrivilegeCollection;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+
+import javax.jcr.security.Privilege;
+
+import static org.junit.Assert.assertArrayEquals;
+
+public class PrivilegeCollectionDefaultTest extends AbstractEvaluationTest {
+    
+    public void testEmptyCollection() throws Exception {
+        PrivilegeCollection pc = new PrivilegeCollection.Default(new Privilege[0], acMgr);
+        assertArrayEquals(new Privilege[0], pc.getPrivileges());
+        assertTrue(pc.includes());
+        assertFalse(pc.includes(Privilege.JCR_ALL));
+        assertFalse(pc.includes(PrivilegeConstants.JCR_READ));
+        assertFalse(pc.includes(Privilege.JCR_MODIFY_ACCESS_CONTROL));
+    }
+    
+    public void testJcrAllCollection() throws Exception {
+        Privilege[] jcrAll = privilegesFromName(Privilege.JCR_ALL);
+        PrivilegeCollection pc = new PrivilegeCollection.Default(jcrAll, acMgr);
+        assertArrayEquals(jcrAll, pc.getPrivileges());
+        assertTrue(pc.includes());
+        assertTrue(pc.includes(Privilege.JCR_ALL));
+        assertTrue(pc.includes(PrivilegeConstants.REP_READ_NODES));
+        assertTrue(pc.includes(Privilege.JCR_LIFECYCLE_MANAGEMENT));
+        assertTrue(pc.includes(Privilege.JCR_WRITE));
+    }
+    
+    public void testNonAggregated() throws Exception {
+        Privilege[] privs = privilegesFromNames(new String[] {Privilege.JCR_NODE_TYPE_MANAGEMENT, Privilege.JCR_REMOVE_NODE, Privilege.JCR_READ_ACCESS_CONTROL});
+        PrivilegeCollection pc = new PrivilegeCollection.Default(privs, acMgr);
+        assertArrayEquals(privs, pc.getPrivileges());
+        assertTrue(pc.includes());
+        assertFalse(pc.includes(Privilege.JCR_ALL));
+        assertFalse(pc.includes(PrivilegeConstants.REP_READ_PROPERTIES));
+        assertFalse(pc.includes(Privilege.JCR_REMOVE_CHILD_NODES));
+        assertTrue(pc.includes(Privilege.JCR_NODE_TYPE_MANAGEMENT, Privilege.JCR_REMOVE_NODE, Privilege.JCR_READ_ACCESS_CONTROL));
+        assertTrue(pc.includes(Privilege.JCR_REMOVE_NODE));
+    }
+
+    public void testAggregated() throws Exception {
+        Privilege[] privs = privilegesFromNames(new String[] {Privilege.JCR_READ, Privilege.JCR_MODIFY_PROPERTIES, PrivilegeConstants.REP_WRITE});
+        PrivilegeCollection pc = new PrivilegeCollection.Default(privs, acMgr);
+        assertArrayEquals(privs, pc.getPrivileges());
+        assertTrue(pc.includes());
+        assertFalse(pc.includes(Privilege.JCR_ALL));
+        assertFalse(pc.includes(PrivilegeConstants.JCR_WORKSPACE_MANAGEMENT));
+        assertTrue(pc.includes(Privilege.JCR_READ, Privilege.JCR_MODIFY_PROPERTIES, PrivilegeConstants.REP_WRITE));
+        assertTrue(pc.includes(PrivilegeConstants.REP_READ_PROPERTIES, PrivilegeConstants.REP_READ_NODES));
+        assertTrue(pc.includes(PrivilegeConstants.REP_ADD_PROPERTIES, PrivilegeConstants.REP_REMOVE_PROPERTIES));
+        assertTrue(pc.includes(Privilege.JCR_REMOVE_CHILD_NODES, Privilege.JCR_MODIFY_PROPERTIES, Privilege.JCR_NODE_TYPE_MANAGEMENT));
+    }
+}
\ No newline at end of file
diff --git a/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/AbstractAccessControlManager.java b/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/AbstractAccessControlManager.java
index 118f57d..0ff23a6 100644
--- a/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/AbstractAccessControlManager.java
+++ b/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/AbstractAccessControlManager.java
@@ -17,6 +17,7 @@
 package org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol;
 
 import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
+import org.apache.jackrabbit.api.security.authorization.PrivilegeCollection;
 import org.apache.jackrabbit.api.security.authorization.PrivilegeManager;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.Tree;
@@ -27,7 +28,10 @@ import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfigu
 import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionAware;
 import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider;
 import org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBits;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBitsProvider;
 import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConfiguration;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeUtil;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
@@ -58,6 +62,7 @@ public abstract class AbstractAccessControlManager implements JackrabbitAccessCo
     private final PrivilegeManager privilegeManager;
 
     private PermissionProvider permissionProvider;
+    private PrivilegeBitsProvider privilegeBitsProvider;
     private boolean doRefresh = false;
 
     protected AbstractAccessControlManager(@NotNull Root root,
@@ -118,6 +123,21 @@ public abstract class AbstractAccessControlManager implements JackrabbitAccessCo
         }
     }
 
+    @Override
+    public @NotNull PrivilegeCollection getPrivilegeCollection(@Nullable String absPath) throws RepositoryException {
+        return getPrivilegeCollection(absPath, getPermissionProvider(), Permissions.NO_PERMISSION);
+    }
+
+    @Override
+    public @NotNull PrivilegeCollection getPrivilegeCollection(@Nullable String absPath, @NotNull Set<Principal> principals) throws RepositoryException {
+        if (getPrincipals().equals(principals)) {
+            return getPrivilegeCollection(absPath);
+        } else {
+            PermissionProvider provider = config.getPermissionProvider(root, workspaceName, principals);
+            return getPrivilegeCollection(absPath, provider, Permissions.READ_ACCESS_CONTROL);
+        }
+    }
+
     //----------------------------------------------------------< protected >---
     @NotNull
     protected AuthorizationConfiguration getConfig() {
@@ -143,6 +163,14 @@ public abstract class AbstractAccessControlManager implements JackrabbitAccessCo
     protected PrivilegeManager getPrivilegeManager() {
         return privilegeManager;
     }
+    
+    @NotNull
+    protected PrivilegeBitsProvider getPrivilegeBitsProvider() {
+        if (privilegeBitsProvider == null) {
+            privilegeBitsProvider = new PrivilegeBitsProvider(root);
+        }
+        return privilegeBitsProvider;
+    }
 
     @Nullable
     protected String getOakPath(@Nullable String jcrPath) throws RepositoryException {
@@ -210,9 +238,7 @@ public abstract class AbstractAccessControlManager implements JackrabbitAccessCo
     }
 
     @NotNull
-    private Privilege[] getPrivileges(@Nullable String absPath,
-                                      @NotNull PermissionProvider provider,
-                                      long permissions) throws RepositoryException {
+    private Set<String> getPrivilegeNames(@Nullable String absPath, @NotNull PermissionProvider provider, long permissions) throws RepositoryException {
         Tree tree;
         if (absPath == null) {
             tree = null;
@@ -222,17 +248,28 @@ public abstract class AbstractAccessControlManager implements JackrabbitAccessCo
         } else {
             tree = getTree(getOakPath(absPath), permissions, false);
         }
-        Set<String> pNames = provider.getPrivileges(tree);
-        if (pNames.isEmpty()) {
+        return provider.getPrivileges(tree);
+    }
+
+    @NotNull
+    private Privilege[] getPrivileges(@NotNull Set<String> privilegeNames) throws RepositoryException {
+        if (privilegeNames.isEmpty()) {
             return new Privilege[0];
         } else {
-            Set<Privilege> privileges = new HashSet<>(pNames.size());
-            for (String name : pNames) {
+            Set<Privilege> privileges = new HashSet<>(privilegeNames.size());
+            for (String name : privilegeNames) {
                 privileges.add(privilegeManager.getPrivilege(namePathMapper.getJcrName(name)));
             }
             return privileges.toArray(new Privilege[0]);
         }
     }
+    
+    @NotNull
+    private Privilege[] getPrivileges(@Nullable String absPath,
+                                      @NotNull PermissionProvider provider,
+                                      long permissions) throws RepositoryException {
+        return getPrivileges(getPrivilegeNames(absPath, provider, permissions));
+    }
 
     private boolean hasPrivileges(@Nullable String absPath, @Nullable Privilege[] privileges,
                                   @NotNull PermissionProvider provider, long permissions,
@@ -258,4 +295,29 @@ public abstract class AbstractAccessControlManager implements JackrabbitAccessCo
             return provider.hasPrivileges(tree, privilegeNames.toArray(new String[0]));
         }
     }
+
+    @NotNull
+    private PrivilegeCollection getPrivilegeCollection(@Nullable String absPath, @NotNull PermissionProvider provider, long permissions) throws RepositoryException {
+        Set<String> pNames = getPrivilegeNames(absPath, provider, permissions);
+        return new PrivilegeCollection() {
+            @Override
+            public Privilege[] getPrivileges() throws RepositoryException {
+                return AbstractAccessControlManager.this.getPrivileges(pNames);
+            }
+
+            @Override
+            public boolean includes(@NotNull String... privilegeNames) throws RepositoryException {
+                if (privilegeNames.length == 0) {
+                    return true;
+                }
+                if (pNames.isEmpty()) {
+                    return false;
+                }
+                PrivilegeBitsProvider pbp = getPrivilegeBitsProvider();
+                PrivilegeBits toTest = pbp.getBits(PrivilegeUtil.getOakNames(privilegeNames, getNamePathMapper()), true);
+                PrivilegeBits bits = pbp.getBits(pNames);
+                return bits.includes(toTest);
+            }
+        };
+    }
 }
diff --git a/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/package-info.java b/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/package-info.java
index 35003f7..8bdef7e 100644
--- a/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/package-info.java
+++ b/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/package-info.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("1.7.0")
+@Version("1.8.0")
 package org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol;
 
 import org.osgi.annotation.versioning.Version;
diff --git a/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBitsProvider.java b/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBitsProvider.java
index 587999b..7219c68 100644
--- a/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBitsProvider.java
+++ b/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBitsProvider.java
@@ -22,6 +22,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import javax.jcr.security.AccessControlException;
 import javax.jcr.security.Privilege;
 
 import com.google.common.base.Function;
@@ -84,7 +85,7 @@ public final class PrivilegeBitsProvider implements PrivilegeConstants {
     }
 
     /**
-     * Returns the bits for the given privilege names.
+     * Returns the bits for the given privilege names. Note, that any invalid privilege names will be ignored.
      * 
      * @param privilegeNames the names
      * @return the privilege bits representing the given privilege names.
@@ -95,8 +96,40 @@ public final class PrivilegeBitsProvider implements PrivilegeConstants {
             return PrivilegeBits.EMPTY;
         }
 
-        Tree privilegesTree = null;
         PrivilegeBits bits = PrivilegeBits.getInstance();
+        collectBits(privilegeNames, bits);
+        return bits.unmodifiable();
+    }
+
+    /**
+     * Returns the bits for the given privilege names with the option to verify that all privilege names point to a valid,
+     * registered privilege.
+     * 
+     * @param privilegeNames An iterable of privilege names.
+     * @param validateNames If set to {@code true} this method will throw an AccessControlException if an invalid privilege 
+     * name is found (i.e. one that doesn't represent a registered privilege). If set to {@code false} invalid privilege 
+     * names will be ignored i.e. making this method equivalent to {@link #getBits(String...)}.
+     * @return the privilege bits representing the given privilege names.
+     * @throws AccessControlException If {@code validateNames} is {@code true} and the any of the specified privilege names is invalid.
+     */
+    @NotNull
+    public PrivilegeBits getBits(@NotNull Iterable<String> privilegeNames, boolean validateNames) throws AccessControlException {
+        if (!validateNames) {
+            return getBits(privilegeNames);
+        }
+        if (Iterables.isEmpty(privilegeNames)) {
+            return PrivilegeBits.EMPTY;
+        }
+        PrivilegeBits bits = PrivilegeBits.getInstance();
+        if (!collectBits(privilegeNames, bits)) {
+            throw new AccessControlException("Invalid privilege name contained in " + privilegeNames);
+        }
+        return bits.unmodifiable();
+    }
+    
+    private boolean collectBits(@NotNull Iterable<String> privilegeNames, @NotNull PrivilegeBits bits) {
+        Tree privilegesTree = null;
+        boolean allNamesValid = true;
         for (String privilegeName : privilegeNames) {
             PrivilegeBits builtIn = PrivilegeBits.BUILT_IN.get(privilegeName);
             if (builtIn != null) {
@@ -113,11 +146,12 @@ public final class PrivilegeBitsProvider implements PrivilegeConstants {
                     nameToBits.put(privilegeName, bitsFromDefTree);
                     bits.add(bitsFromDefTree);
                 } else {
-                    log.debug("Ignoring privilege name {}", privilegeName);
+                    log.debug("Invalid privilege name {}", privilegeName);
+                    allNamesValid = false;
                 }
             }
         }
-        return bits.unmodifiable();
+        return allNamesValid;
     }
 
     /**
diff --git a/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeUtil.java b/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeUtil.java
index e53e406..294010f 100644
--- a/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeUtil.java
+++ b/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeUtil.java
@@ -20,8 +20,15 @@ import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.jcr.security.AccessControlException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * Privilege management related utility methods.
@@ -60,4 +67,47 @@ public final class PrivilegeUtil implements PrivilegeConstants {
         }
         return new ImmutablePrivilegeDefinition(name, isAbstract, declAggrNames);
     }
+
+    /**
+     * Convert the given JCR privilege names to Oak names.
+     * 
+     * @param jcrNames The JCR names of privileges
+     * @param namePathMapper The {@link NamePathMapper} to use for the conversion.
+     * @return A set of Oak names
+     * @throws AccessControlException If the given JCR names cannot be converted.
+     */
+    @NotNull
+    public static Set<String> getOakNames(@Nullable String[] jcrNames, @NotNull NamePathMapper namePathMapper) throws AccessControlException {
+        Set<String> oakNames;
+        if (jcrNames == null || jcrNames.length == 0) {
+            oakNames = Collections.emptySet();
+        } else {
+            oakNames = new HashSet<>(jcrNames.length);
+            for (String jcrName : jcrNames) {
+                String oakName = getOakName(jcrName, namePathMapper);
+                oakNames.add(oakName);
+            }
+        }
+        return oakNames;
+    }
+
+    /**
+     * Convert the given JCR privilege name to an Oak name.
+     * 
+     * @param jcrName The JCR name of a privilege.
+     * @param namePathMapper The {@link NamePathMapper} to use for the conversion.
+     * @return the Oak name of the given privilege.
+     * @throws AccessControlException If the specified name is null or cannot be resolved to an Oak name.
+     */
+    @NotNull
+    public static String getOakName(@Nullable String jcrName, @NotNull NamePathMapper namePathMapper) throws AccessControlException {
+        if (jcrName == null) {
+            throw new AccessControlException("Invalid privilege name 'null'");
+        }
+        String oakName = namePathMapper.getOakNameOrNull(jcrName);
+        if (oakName == null) {
+            throw new AccessControlException("Cannot resolve privilege name " + jcrName);
+        }
+        return oakName;
+    }
 }
diff --git a/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/package-info.java b/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/package-info.java
index ee2fb29..5c1e647 100644
--- a/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/package-info.java
+++ b/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/privilege/package-info.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("1.4.2")
+@Version("1.5.0")
 package org.apache.jackrabbit.oak.spi.security.privilege;
 
 import org.osgi.annotation.versioning.Version;
diff --git a/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/AbstractAccessControlManagerTest.java b/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/AbstractAccessControlManagerTest.java
index e1d45a5..c352516 100644
--- a/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/AbstractAccessControlManagerTest.java
+++ b/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/AbstractAccessControlManagerTest.java
@@ -16,23 +16,9 @@
  */
 package org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol;
 
-import java.security.Principal;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import javax.jcr.AccessDeniedException;
-import javax.jcr.PathNotFoundException;
-import javax.jcr.RepositoryException;
-import javax.jcr.security.AccessControlException;
-import javax.jcr.security.AccessControlPolicy;
-import javax.jcr.security.AccessControlPolicyIterator;
-import javax.jcr.security.Privilege;
-
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
-import org.apache.jackrabbit.api.security.JackrabbitAccessControlPolicy;
+import org.apache.jackrabbit.api.security.authorization.PrivilegeCollection;
 import org.apache.jackrabbit.api.security.authorization.PrivilegeManager;
 import org.apache.jackrabbit.oak.api.ContentSession;
 import org.apache.jackrabbit.oak.api.Root;
@@ -46,13 +32,31 @@ import org.apache.jackrabbit.oak.spi.security.authorization.permission.EmptyPerm
 import org.apache.jackrabbit.oak.spi.security.authorization.permission.OpenPermissionProvider;
 import org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions;
 import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBits;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBitsProvider;
 import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConfiguration;
 import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
 import org.jetbrains.annotations.NotNull;
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
 
+import javax.jcr.AccessDeniedException;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.RepositoryException;
+import javax.jcr.security.AccessControlException;
+import javax.jcr.security.Privilege;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import static org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants.JCR_ADD_CHILD_NODES;
+import static org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants.JCR_ALL;
+import static org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants.JCR_READ;
+import static org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants.REP_BITS;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -62,10 +66,13 @@ import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.withSettings;
 
 public class AbstractAccessControlManagerTest extends AbstractAccessControlTest {
 
@@ -90,43 +97,52 @@ public class AbstractAccessControlManagerTest extends AbstractAccessControlTest
 
     @Before
     public void before() throws Exception {
-        testPrivileges = new Privilege[] {mockPrivilege("priv1"), mockPrivilege("priv2")};
-        allPrivileges = new Privilege[] {mockPrivilege(PrivilegeConstants.JCR_ALL)};
+        testPrivileges = new Privilege[] {mockPrivilege(JCR_READ), mockPrivilege(JCR_ADD_CHILD_NODES)};
+        allPrivileges = new Privilege[] {mockPrivilege(JCR_ALL)};
 
-        cs = Mockito.mock(ContentSession.class);
+        cs = mock(ContentSession.class);
         when(cs.getWorkspaceName()).thenReturn(WSP_NAME);
         when(cs.getAuthInfo()).thenReturn(new AuthInfoImpl(null, ImmutableMap.of(), testPrincipals));
 
         when(root.getContentSession()).thenReturn(cs);
 
-        Tree nonExistingTree = Mockito.mock(Tree.class);
+        Tree nonExistingTree = mock(Tree.class);
         when(nonExistingTree.exists()).thenReturn(false);
         when(root.getTree(nonExistingPath)).thenReturn(nonExistingTree);
 
-        Tree existingTree = Mockito.mock(Tree.class);
+        Tree existingTree = mock(Tree.class);
         when(existingTree.exists()).thenReturn(true);
         when(root.getTree(testPath)).thenReturn(existingTree);
 
-        Tree rootTree = Mockito.mock(Tree.class);
+        Tree rootTree = mock(Tree.class);
         when(rootTree.exists()).thenReturn(true);
         when(root.getTree("/")).thenReturn(rootTree);
-
-        privilegeManager = Mockito.mock(PrivilegeManager.class);
+        
+        Tree jcrAllTree = mock(Tree.class);
+        PrivilegeBits pb = PrivilegeBits.getInstance(PrivilegeBits.BUILT_IN.get(JCR_READ), PrivilegeBits.BUILT_IN.get(JCR_ADD_CHILD_NODES));
+        when(jcrAllTree.getProperty(REP_BITS)).thenReturn(pb.asPropertyState(REP_BITS));
+        Tree privilegesTree = mock(Tree.class);
+        when(privilegesTree.exists()).thenReturn(true);
+        when(privilegesTree.hasChild(JCR_ALL)).thenReturn(true);
+        when(privilegesTree.getChild(JCR_ALL)).thenReturn(jcrAllTree);
+        when(root.getTree(PrivilegeConstants.PRIVILEGES_PATH)).thenReturn(privilegesTree);
+
+        privilegeManager = mock(PrivilegeManager.class);
         when(privilegeManager.getRegisteredPrivileges()).thenReturn(testPrivileges);
-        when(privilegeManager.getPrivilege("priv1")).thenReturn(testPrivileges[0]);
-        when(privilegeManager.getPrivilege("priv2")).thenReturn(testPrivileges[1]);
-        when(privilegeManager.getPrivilege(PrivilegeConstants.JCR_ALL)).thenReturn(allPrivileges[0]);
+        when(privilegeManager.getPrivilege(JCR_READ)).thenReturn(testPrivileges[0]);
+        when(privilegeManager.getPrivilege(JCR_ADD_CHILD_NODES)).thenReturn(testPrivileges[1]);
+        when(privilegeManager.getPrivilege(JCR_ALL)).thenReturn(allPrivileges[0]);
 
-        PrivilegeConfiguration privilegeConfiguration = Mockito.mock(PrivilegeConfiguration.class);
+        PrivilegeConfiguration privilegeConfiguration = mock(PrivilegeConfiguration.class);
         when(privilegeConfiguration.getPrivilegeManager(root, getNamePathMapper())).thenReturn(privilegeManager);
 
-        authorizationConfiguration = Mockito.mock(AuthorizationConfiguration.class);
+        authorizationConfiguration = mock(AuthorizationConfiguration.class);
         when(authorizationConfiguration.getPermissionProvider(root, WSP_NAME, getEveryonePrincipalSet())).thenReturn(EmptyPermissionProvider.getInstance());
         when(authorizationConfiguration.getPermissionProvider(root, WSP_NAME, testPrincipals)).thenReturn(OpenPermissionProvider.getInstance());
         when(authorizationConfiguration.getPermissionProvider(root, WSP_NAME, ImmutableSet.of())).thenReturn(EmptyPermissionProvider.getInstance());
         when(authorizationConfiguration.getContext()).thenReturn(Context.DEFAULT);
 
-        securityProvider = Mockito.mock(SecurityProvider.class);
+        securityProvider = mock(SecurityProvider.class);
         when(securityProvider.getConfiguration(PrivilegeConfiguration.class)).thenReturn(privilegeConfiguration);
         when(securityProvider.getConfiguration(AuthorizationConfiguration.class)).thenReturn(authorizationConfiguration);
 
@@ -134,7 +150,7 @@ public class AbstractAccessControlManagerTest extends AbstractAccessControlTest
     }
 
     private AbstractAccessControlManager createAccessControlManager(@NotNull Root root, @NotNull NamePathMapper namePathMapper) {
-        return new TestAcMgr(root, namePathMapper, securityProvider);
+        return mock(AbstractAccessControlManager.class, withSettings().useConstructor(root, namePathMapper, securityProvider).defaultAnswer(InvocationOnMock::callRealMethod));
     }
 
     private static List<String> getInvalidPaths() {
@@ -148,13 +164,13 @@ public class AbstractAccessControlManagerTest extends AbstractAccessControlTest
     }
 
     private static Privilege mockPrivilege(@NotNull String name) {
-        Privilege p = Mockito.mock(Privilege.class);
+        Privilege p = mock(Privilege.class);
         when(p.getName()).thenReturn(name);
         return p;
     }
 
     private static Set<Principal> getEveryonePrincipalSet() {
-        return ImmutableSet.<Principal>of(EveryonePrincipal.getInstance());
+        return ImmutableSet.of(EveryonePrincipal.getInstance());
     }
 
     //--------------------------------------------------- protected methods >---
@@ -212,7 +228,7 @@ public class AbstractAccessControlManagerTest extends AbstractAccessControlTest
     }
 
     @Test(expected = PathNotFoundException.class)
-    public void testGetTreeNonExstingPath() throws Exception {
+    public void testGetTreeNonExistingPath() throws Exception {
         acMgr.getTree(nonExistingPath, Permissions.NO_PERMISSION, false);
     }
 
@@ -247,6 +263,13 @@ public class AbstractAccessControlManagerTest extends AbstractAccessControlTest
         mgr.getTree(testPath, Permissions.ALL, true);
     }
 
+    @Test
+    public void testGetPrivilegeBitsProvider() {
+        PrivilegeBitsProvider pbp = acMgr.getPrivilegeBitsProvider();
+        assertNotNull(pbp);
+        assertSame(pbp, acMgr.getPrivilegeBitsProvider());
+    }
+    
     //---------------------------------------------< getSupportedPrivileges >---
     @Test
     public void testGetSupportedPrivileges() throws Exception {
@@ -279,7 +302,7 @@ public class AbstractAccessControlManagerTest extends AbstractAccessControlTest
     //--------------------------------------------------< privilegeFromName >---
     @Test
     public void testPrivilegeFromName() throws Exception {
-        List<Privilege> allPrivileges = Arrays.asList(privilegeManager.getRegisteredPrivileges());
+        Privilege[] allPrivileges = privilegeManager.getRegisteredPrivileges();
         for (Privilege privilege : allPrivileges) {
             Privilege p = acMgr.privilegeFromName(privilege.getName());
             assertEquals(privilege, p);
@@ -309,7 +332,7 @@ public class AbstractAccessControlManagerTest extends AbstractAccessControlTest
 
     @Test(expected = PathNotFoundException.class)
     public void testHasPrivilegesNonExistingNodePathEmptyPrincipalSet() throws Exception {
-        acMgr.hasPrivileges(nonExistingPath, ImmutableSet.<Principal>of(), testPrivileges);
+        acMgr.hasPrivileges(nonExistingPath, ImmutableSet.of(), testPrivileges);
     }
 
     @Test
@@ -338,7 +361,7 @@ public class AbstractAccessControlManagerTest extends AbstractAccessControlTest
     public void testHasPrivilegesInvalidPathsEveryoneSet() {
         for (String path : getInvalidPaths()) {
             try {
-                acMgr.hasPrivileges(path, ImmutableSet.<Principal>of(EveryonePrincipal.getInstance()), testPrivileges);
+                acMgr.hasPrivileges(path, ImmutableSet.of(EveryonePrincipal.getInstance()), testPrivileges);
                 fail("AccessControlManager#hasPrivileges for node that doesn't exist should fail.");
             } catch (RepositoryException e) {
                 // success
@@ -358,7 +381,7 @@ public class AbstractAccessControlManagerTest extends AbstractAccessControlTest
 
     @Test
     public void testHasRepoPrivilegesEmptyPrincipalSet() throws Exception {
-        assertFalse(acMgr.hasPrivileges(null, ImmutableSet.<Principal>of(), testPrivileges));
+        assertFalse(acMgr.hasPrivileges(null, ImmutableSet.of(), testPrivileges));
     }
 
     //------------------------------------------------------< getPrivileges >---
@@ -369,7 +392,7 @@ public class AbstractAccessControlManagerTest extends AbstractAccessControlTest
 
     @Test(expected = PathNotFoundException.class)
     public void testGetPrivilegesNonExistingNodePathEmptyPrincipalSet() throws Exception {
-        acMgr.getPrivileges(nonExistingPath, ImmutableSet.<Principal>of());
+        acMgr.getPrivileges(nonExistingPath, ImmutableSet.of());
     }
 
     @Test
@@ -394,7 +417,7 @@ public class AbstractAccessControlManagerTest extends AbstractAccessControlTest
 
         for (String path : getInvalidPaths()) {
             try {
-                acMgr.getPrivileges(path, ImmutableSet.<Principal>of());
+                acMgr.getPrivileges(path, ImmutableSet.of());
                 fail("AccessControlManager#getPrivileges  for node that doesn't exist should fail.");
             } catch (RepositoryException e) {
                 // success
@@ -408,13 +431,13 @@ public class AbstractAccessControlManagerTest extends AbstractAccessControlTest
     }
 
     @Test
-    public void testGetPrivilegesEveronePrincipalSet() throws Exception {
+    public void testGetPrivilegesEveryonePrincipalSet() throws Exception {
         assertArrayEquals(new Privilege[0], acMgr.getPrivileges(testPath, getEveryonePrincipalSet()));
     }
 
     @Test
     public void testGetPrivilegesEmptyPrincipalSet() throws Exception {
-        assertArrayEquals(new Privilege[0], acMgr.getPrivileges(testPath, ImmutableSet.<Principal>of()));
+        assertArrayEquals(new Privilege[0], acMgr.getPrivileges(testPath, ImmutableSet.of()));
     }
 
     @Test
@@ -440,56 +463,54 @@ public class AbstractAccessControlManagerTest extends AbstractAccessControlTest
 
     @Test
     public void testGetRepoPrivilegesEmptyPrincipalSet() throws Exception {
-        assertArrayEquals(new Privilege[0], acMgr.getPrivileges(null, ImmutableSet.<Principal>of()));
+        assertArrayEquals(new Privilege[0], acMgr.getPrivileges(null, ImmutableSet.of()));
     }
 
-    private static class TestAcMgr extends AbstractAccessControlManager {
-
-        protected TestAcMgr(@NotNull Root root, @NotNull NamePathMapper namePathMapper, @NotNull SecurityProvider securityProvider) {
-            super(root, namePathMapper, securityProvider);
-        }
-
-        @NotNull
-        @Override
-        public JackrabbitAccessControlPolicy[] getApplicablePolicies(@NotNull Principal principal) {
-            throw new UnsupportedOperationException();
-        }
-
-        @NotNull
-        @Override
-        public JackrabbitAccessControlPolicy[] getPolicies(@NotNull Principal principal) {
-            throw new UnsupportedOperationException();
-        }
-
-        @NotNull
-        @Override
-        public AccessControlPolicy[] getEffectivePolicies(@NotNull Set<Principal> set) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public AccessControlPolicy[] getPolicies(String absPath)  {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public AccessControlPolicy[] getEffectivePolicies(String absPath) {
-            throw new UnsupportedOperationException();
+    //------------------------------------------------------< getPrivilegeCollection >---
+    @Test
+    public void testGetPrivilegeCollection() throws Exception {
+        PrivilegeCollection pe = acMgr.getPrivilegeCollection(testPath);
+        assertArrayEquals(allPrivileges, pe.getPrivileges());
+        
+        assertTrue(pe.includes(Arrays.stream(testPrivileges).map(Privilege::getName).distinct().toArray(String[]::new)));
+        assertTrue(pe.includes());
+        assertFalse(pe.includes(PrivilegeConstants.JCR_WORKSPACE_MANAGEMENT));
+        try {
+            pe.includes("invalid");
+            fail();
+        } catch (AccessControlException e) {
+            // success
         }
+    }
 
-        @Override
-        public AccessControlPolicyIterator getApplicablePolicies(String absPath) {
-            throw new UnsupportedOperationException();
-        }
+    @Test
+    public void testGetPrivilegeCollectionSamePrincipalSet() throws Exception {
+        PrivilegeCollection pc = acMgr.getPrivilegeCollection(testPath, testPrincipals);
+        assertArrayEquals(allPrivileges, pc.getPrivileges());
+        
+        verify(acMgr).getPrivilegeCollection(testPath);
+        verify(acMgr).getPrivilegeCollection(testPath, testPrincipals);
+    }
 
-        @Override
-        public void setPolicy(String absPath, AccessControlPolicy policy) {
-            throw new UnsupportedOperationException();
-        }
+    @Test
+    public void testGetPrivilegeCollectionEmptyPrincipalSet() throws Exception {
+        PrivilegeCollection pc = acMgr.getPrivilegeCollection(testPath, Collections.emptySet());
+        assertArrayEquals(new Privilege[0], pc.getPrivileges());
+        assertFalse(pc.includes(JCR_READ));
+        assertTrue(pc.includes());
 
-        @Override
-        public void removePolicy(String absPath, AccessControlPolicy policy) {
-            throw new UnsupportedOperationException();
-        }
+        verify(acMgr, never()).getPrivilegeCollection(testPath);
+        verify(acMgr).getPrivilegeCollection(testPath, Collections.emptySet());
+    }
+    
+    @Test
+    public void testGetPrivilegeCollectionPrincipalSet() throws Exception {
+        PrivilegeCollection pc = acMgr.getPrivilegeCollection(testPath, getEveryonePrincipalSet());
+        assertArrayEquals(new Privilege[0], pc.getPrivileges());
+        assertFalse(pc.includes(Arrays.stream(testPrivileges).map(Privilege::getName).distinct().toArray(String[]::new)));
+        assertTrue(pc.includes());
+        
+        verify(acMgr).getPrivilegeCollection(testPath, getEveryonePrincipalSet());
+        verify(acMgr, never()).getPrivilegeCollection(testPath);
     }
 }
diff --git a/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBitsProviderTest.java b/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBitsProviderTest.java
index 8f6b3c4..a0595f6 100644
--- a/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBitsProviderTest.java
+++ b/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeBitsProviderTest.java
@@ -31,7 +31,9 @@ import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
 
+import javax.jcr.security.AccessControlException;
 import javax.jcr.security.Privilege;
+import java.util.Collections;
 import java.util.Set;
 
 import static org.junit.Assert.assertEquals;
@@ -41,6 +43,7 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -229,6 +232,51 @@ public class PrivilegeBitsProviderTest implements PrivilegeConstants {
     }
 
     @Test
+    public void testBitsValidateEmpty() throws Exception {
+        PrivilegeBits bits = bitsProvider.getBits(Collections.emptySet(), true);
+        assertSame(PrivilegeBits.EMPTY, bits);
+
+        bits = bitsProvider.getBits(Collections.emptySet(), false);
+        assertSame(PrivilegeBits.EMPTY, bits);
+
+        verify(root, never()).getTree(PRIVILEGES_PATH);
+        verify(privTree, never()).getChild(anyString());
+    }
+    
+    @Test
+    public void testGetBitsValidateTwiceBuitInKnown() throws Exception {
+        Iterable<String> names = ImmutableList.of(KNOWN_PRIV_NAME, JCR_ADD_CHILD_NODES);
+        PrivilegeBits bits1 = bitsProvider.getBits(names, true);
+        PrivilegeBits bits2 = bitsProvider.getBits(names, false);
+
+        assertNotSame(bits1, bits2);
+        assertEquals(bits1, bits2);
+
+        verify(root, times(1)).getTree(PRIVILEGES_PATH);
+        verify(privTree, times(1)).getChild(KNOWN_PRIV_NAME);
+    }
+    
+    @Test(expected = AccessControlException.class)
+    public void testBitsValidateNonExistingTree() throws Exception {
+        Iterable<String> names = ImmutableList.of(KNOWN_PRIV_NAME, JCR_ADD_CHILD_NODES);
+
+        when(privTree.exists()).thenReturn(true);
+        when(privTree.hasChild(KNOWN_PRIV_NAME)).thenReturn(false);
+        // privilegesTree has no child for KNOWN_PRIV_NAME -> must fail
+        bitsProvider.getBits(names, true);
+    }
+
+    public void testBitsValidateFalseNonExistingTree() throws Exception {
+        Iterable<String> names = ImmutableList.of(KNOWN_PRIV_NAME, JCR_ADD_CHILD_NODES);
+
+        when(privTree.exists()).thenReturn(true);
+        when(privTree.hasChild(KNOWN_PRIV_NAME)).thenReturn(false);
+        
+        // validation is disabled => same as getBits(Iterable)
+        assertEquals(bitsProvider.getBits(names), bitsProvider.getBits(names, false));
+    }
+
+    @Test
     public void testGetBitsFromEmptyPrivileges() {
         assertSame(PrivilegeBits.EMPTY, bitsProvider.getBits(new Privilege[0], NamePathMapper.DEFAULT));
     }
diff --git a/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeUtilTest.java b/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeUtilTest.java
index 9af8854..764be10 100644
--- a/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeUtilTest.java
+++ b/oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/privilege/PrivilegeUtilTest.java
@@ -21,17 +21,28 @@ import com.google.common.collect.Iterables;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 import org.junit.Test;
 
+import javax.jcr.RepositoryException;
+import javax.jcr.security.AccessControlException;
+import javax.jcr.security.Privilege;
+
+import static org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants.JCR_LIFECYCLE_MANAGEMENT;
 import static org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants.JCR_READ;
 import static org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants.PRIVILEGES_PATH;
 import static org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants.REP_AGGREGATES;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 public class PrivilegeUtilTest {
@@ -65,6 +76,49 @@ public class PrivilegeUtilTest {
         PrivilegeDefinition def = PrivilegeUtil.readDefinition(defTree);
         assertEquals("name", def.getName());
         assertTrue(Iterables.elementsEqual(aggregateNames, PrivilegeUtil.readDefinition(defTree).getDeclaredAggregateNames()));
+    }
+    
+    @Test
+    public void testGetPrivilegeOakNameDefaultMapper() throws Exception {
+        assertEquals(JCR_READ, PrivilegeUtil.getOakName(JCR_READ, NamePathMapper.DEFAULT));
+        assertEquals(Privilege.JCR_ADD_CHILD_NODES, PrivilegeUtil.getOakName(Privilege.JCR_ADD_CHILD_NODES, NamePathMapper.DEFAULT));
+        assertEquals("anystring", PrivilegeUtil.getOakName("anystring", NamePathMapper.DEFAULT));
+    }
+
+    @Test
+    public void testGetPrivilegeOakName() throws Exception {
+        NamePathMapper mapper = mock(NamePathMapper.class);
+        when(mapper.getOakNameOrNull(Privilege.JCR_LIFECYCLE_MANAGEMENT)).thenReturn(JCR_LIFECYCLE_MANAGEMENT);
+        assertEquals(JCR_LIFECYCLE_MANAGEMENT, PrivilegeUtil.getOakName(Privilege.JCR_LIFECYCLE_MANAGEMENT, mapper));
+        verify(mapper).getOakNameOrNull(Privilege.JCR_LIFECYCLE_MANAGEMENT);
+        verifyNoMoreInteractions(mapper);
+    }
+    
+    @Test(expected = AccessControlException.class)
+    public void testGetPrivilegeOakNameFromNull() throws RepositoryException {
+        PrivilegeUtil.getOakName(null, NamePathMapper.DEFAULT);
+    }
 
+    @Test(expected = AccessControlException.class)
+    public void testGetPrivilegeOakNameResolvesToNull() throws RepositoryException {
+        PrivilegeUtil.getOakName(Privilege.JCR_READ, new NamePathMapper.Default() {
+            public @Nullable String getOakNameOrNull(@NotNull String jcrName) {
+                return null;
+            }
+        });
+    }
+    
+    @Test
+    public void testGetPrivilegeOakNamesFromNull() throws Exception {
+        NamePathMapper mapper = mock(NamePathMapper.class);
+        assertTrue(PrivilegeUtil.getOakNames(null, mapper).isEmpty());
+        verifyNoInteractions(mapper);
+    }
+    
+    @Test
+    public void testGetPrivilegeOakNamesFromEmptyArray() throws Exception {
+        NamePathMapper mapper = mock(NamePathMapper.class);
+        assertTrue(PrivilegeUtil.getOakNames(new String[0], mapper).isEmpty());
+        verifyNoInteractions(mapper);
     }
 }
\ No newline at end of file