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:32 UTC
[jackrabbit-oak] 01/02: OAK-9494 : Check if a privilege name is
included in a set/array of Privileges obtained from
AccessControlManager.getPrivileges
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