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/27 12:53:25 UTC
[jackrabbit-oak] branch trunk updated: OAK-9514 : Add
RestrictionPattern.matches(@NotNull String path, boolean isProperty)
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
The following commit(s) were added to refs/heads/trunk by this push:
new 65ef656 OAK-9514 : Add RestrictionPattern.matches(@NotNull String path, boolean isProperty)
65ef656 is described below
commit 65ef6569aa395fa62368d7b763b194bf03951147
Author: angela <an...@adobe.com>
AuthorDate: Tue Jul 27 14:53:11 2021 +0200
OAK-9514 : Add RestrictionPattern.matches(@NotNull String path, boolean isProperty)
---
.../principalbased/impl/EntryCache.java | 11 +-
.../principalbased/impl/EntryPredicate.java | 22 +-
.../principalbased/impl/PermissionEntry.java | 2 +
.../impl/PrincipalBasedPermissionProvider.java | 6 +-
.../principalbased/impl/EntryPredicateTest.java | 130 +++++-------
.../authorization/permission/AllPermissions.java | 5 +
.../permission/CompiledPermissionImpl.java | 8 +-
.../permission/CompiledPermissions.java | 12 ++
.../authorization/permission/EntryPredicate.java | 20 +-
.../authorization/permission/NoPermissions.java | 5 +
.../authorization/permission/PermissionEntry.java | 6 +-
.../permission/EntryPredicateTest.java | 225 +++++++++++++++++++--
.../permission/PermissionEntryTest.java | 30 ++-
.../restriction/CompositePattern.java | 10 +
.../restriction/RestrictionPattern.java | 27 ++-
.../authorization/restriction/package-info.java | 2 +-
16 files changed, 403 insertions(+), 118 deletions(-)
diff --git a/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/EntryCache.java b/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/EntryCache.java
index 200a1e0..329dc48 100644
--- a/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/EntryCache.java
+++ b/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/EntryCache.java
@@ -81,7 +81,7 @@ class EntryCache implements Constants {
private final String effectivePath;
private final PrivilegeBits privilegeBits;
- private RestrictionPattern pattern;
+ private final RestrictionPattern pattern;
private PermissionEntryImpl(@NotNull Tree entryTree) {
effectivePath = Strings.emptyToNull(TreeUtil.getString(entryTree, REP_EFFECTIVE_PATH));
@@ -105,8 +105,13 @@ class EntryCache implements Constants {
}
@Override
- public boolean matches(@NotNull String treePath) {
- return pattern.matches(treePath);
+ public boolean matches(@NotNull String oakPath) {
+ return pattern.matches(oakPath);
+ }
+
+ @Override
+ public boolean matches(@NotNull String oakPath, boolean isProperty) {
+ return pattern.matches(oakPath, isProperty);
}
@Override
diff --git a/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/EntryPredicate.java b/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/EntryPredicate.java
index c207f9c..11cc53a 100644
--- a/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/EntryPredicate.java
+++ b/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/EntryPredicate.java
@@ -30,12 +30,18 @@ final class EntryPredicate {
private EntryPredicate() {}
@NotNull
- static Predicate<PermissionEntry> create(@Nullable String oakPath) {
- if (oakPath == null) {
- return permissionEntry -> permissionEntry.matches();
- } else {
- return permissionEntry -> permissionEntry.matches(oakPath);
- }
+ static Predicate<PermissionEntry> create() {
+ return permissionEntry -> permissionEntry.matches();
+ }
+
+ @NotNull
+ static Predicate<PermissionEntry> create(@NotNull String oakPath) {
+ return permissionEntry -> permissionEntry.matches(oakPath);
+ }
+
+ @NotNull
+ static Predicate<PermissionEntry> create(@NotNull String oakPath, boolean isProperty) {
+ return permissionEntry -> permissionEntry.matches(oakPath, isProperty);
}
@NotNull
@@ -44,7 +50,7 @@ final class EntryPredicate {
// target node does not exist (anymore) in this workspace
// use best effort calculation based on the item path.
String predicatePath = (property == null) ? tree.getPath() : PathUtils.concat(tree.getPath(), property.getName());
- return create(predicatePath);
+ return create(predicatePath, property != null);
} else {
return permissionEntry -> permissionEntry.matches(tree, property);
}
@@ -61,7 +67,7 @@ final class EntryPredicate {
return permissionEntry -> permissionEntry.appliesTo(parentTree.getPath()) && permissionEntry.matches(parentTree, null);
} else {
String parentPath = PathUtils.getParentPath(treePath);
- return permissionEntry -> permissionEntry.appliesTo(parentPath) && permissionEntry.matches(parentPath);
+ return permissionEntry -> permissionEntry.appliesTo(parentPath) && permissionEntry.matches(parentPath, false);
}
}
diff --git a/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PermissionEntry.java b/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PermissionEntry.java
index 5a2457f..b0ff318 100644
--- a/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PermissionEntry.java
+++ b/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PermissionEntry.java
@@ -29,6 +29,8 @@ interface PermissionEntry {
boolean appliesTo(@NotNull String path);
boolean matches(@NotNull String oakPath);
+
+ boolean matches(@NotNull String oakPath, boolean isProperty);
boolean matches(@NotNull Tree tree, @Nullable PropertyState property);
diff --git a/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalBasedPermissionProvider.java b/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalBasedPermissionProvider.java
index 8ff91fe..18895f3 100644
--- a/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalBasedPermissionProvider.java
+++ b/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalBasedPermissionProvider.java
@@ -341,7 +341,7 @@ class PrincipalBasedPermissionProvider implements AggregatedPermissionProvider,
Predicate<PermissionEntry> predicate;
if (readOnly == null) {
oakPath = REPOSITORY_PERMISSION_PATH;
- predicate = EntryPredicate.create(null);
+ predicate = EntryPredicate.create();
} else {
oakPath = readOnly.getPath();
predicate = EntryPredicate.create(readOnly, null);
@@ -366,7 +366,7 @@ class PrincipalBasedPermissionProvider implements AggregatedPermissionProvider,
if (effectivePath == null) {
return PrivilegeBits.EMPTY;
} else {
- return modAcBits.modifiable().diff(getGrantedPrivilegeBits(effectivePath, EntryPredicate.create(effectivePath)));
+ return modAcBits.modifiable().diff(getGrantedPrivilegeBits(effectivePath, EntryPredicate.create(effectivePath, false)));
}
}
@@ -430,7 +430,7 @@ class PrincipalBasedPermissionProvider implements AggregatedPermissionProvider,
private long getGranted() {
if (grantedPermissions == -1) {
- PrivilegeBits pb = getGrantedPrivilegeBits(REPOSITORY_PERMISSION_PATH, EntryPredicate.create(null));
+ PrivilegeBits pb = getGrantedPrivilegeBits(REPOSITORY_PERMISSION_PATH, EntryPredicate.create());
grantedPermissions = PrivilegeBits.calculatePermissions(pb, PrivilegeBits.EMPTY, true);
}
return grantedPermissions;
diff --git a/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/EntryPredicateTest.java b/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/EntryPredicateTest.java
index 4578b7d..d0a923b 100644
--- a/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/EntryPredicateTest.java
+++ b/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/EntryPredicateTest.java
@@ -23,17 +23,18 @@ import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
import org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import org.mockito.Mockito;
import static org.apache.jackrabbit.oak.spi.security.authorization.principalbased.impl.MockUtility.mockTree;
import static org.junit.Assert.assertSame;
-import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
public class EntryPredicateTest {
@@ -50,39 +51,56 @@ public class EntryPredicateTest {
public void before() {
tree = mockTree(TREE_PATH, false);
}
+
+ @After
+ public void after() {
+ reset(pe, tree);
+ }
@Test
public void testCreateNullPath() {
- Predicate<PermissionEntry> predicate = EntryPredicate.create(null);
+ Predicate<PermissionEntry> predicate = EntryPredicate.create();
predicate.apply(pe);
verify(pe, times(1)).matches();
- verify(pe, never()).matches(Mockito.anyString());
- verify(pe, never()).matches(Mockito.any(Tree.class), Mockito.any(PropertyState.class));
- verify(pe, never()).getPrivilegeBits();
+ verifyNoMoreInteractions(pe);
}
@Test
public void testCreatePath() {
- Predicate<PermissionEntry> predicate = EntryPredicate.create(Mockito.anyString());
+ Predicate<PermissionEntry> predicate = EntryPredicate.create("/path");
+ predicate.apply(pe);
+
+ verify(pe, times(1)).matches("/path");
+ verifyNoMoreInteractions(pe);
+ }
+
+ @Test
+ public void testCreatePathNotIsProperty() {
+ Predicate<PermissionEntry> predicate = EntryPredicate.create("/path", false);
+ predicate.apply(pe);
+
+ verify(pe, times(1)).matches("/path", false);
+ verifyNoMoreInteractions(pe);
+ }
+
+ @Test
+ public void testCreatePathIsProperty() {
+ Predicate<PermissionEntry> predicate = EntryPredicate.create("/path", true);
predicate.apply(pe);
- verify(pe, never()).matches();
- verify(pe, times(1)).matches(Mockito.anyString());
- verify(pe, never()).matches(Mockito.any(Tree.class), Mockito.any(PropertyState.class));
- verify(pe, never()).getPrivilegeBits();
+ verify(pe, times(1)).matches("/path", true);
+ verifyNoMoreInteractions(pe);
}
@Test
public void testCreateNonExistingTree() {
+ doReturn(false).when(tree).exists();
Predicate<PermissionEntry> predicate = EntryPredicate.create(tree, null);
predicate.apply(pe);
- verify(pe, never()).matches();
- verify(pe, times(1)).matches(tree.getPath());
- verify(pe, times(0)).matches(PathUtils.ROOT_PATH);
- verify(pe, never()).matches(Mockito.any(Tree.class), Mockito.any(PropertyState.class));
- verify(pe, never()).getPrivilegeBits();
+ verify(pe, times(1)).matches(tree.getPath(), false);
+ verifyNoMoreInteractions(pe);
}
@Test
@@ -90,11 +108,8 @@ public class EntryPredicateTest {
Predicate<PermissionEntry> predicate = EntryPredicate.create(tree, propertyState);
predicate.apply(pe);
- verify(pe, never()).matches();
- verify(pe, times(1)).matches(PROP_PATH);
- verify(pe, never()).matches(tree.getPath());
- verify(pe, never()).matches(Mockito.any(Tree.class), Mockito.any(PropertyState.class));
- verify(pe, never()).getPrivilegeBits();
+ verify(pe, times(1)).matches(PROP_PATH, true);
+ verifyNoMoreInteractions(pe);
}
@Test
@@ -104,10 +119,8 @@ public class EntryPredicateTest {
Predicate<PermissionEntry> predicate = EntryPredicate.create(tree, null);
predicate.apply(pe);
- verify(pe, never()).matches();
- verify(pe, never()).matches(TREE_PATH);
verify(pe, times(1)).matches(tree, null);
- verify(pe, never()).getPrivilegeBits();
+ verifyNoMoreInteractions(pe);
}
@Test
@@ -117,11 +130,8 @@ public class EntryPredicateTest {
Predicate<PermissionEntry> predicate = EntryPredicate.create(tree, propertyState);
predicate.apply(pe);
- verify(pe, never()).matches();
- verify(pe, never()).matches(PROP_PATH);
verify(pe, times(1)).matches(tree, propertyState);
- verify(pe, never()).matches(tree, PropertyStates.createProperty("another", "value"));
- verify(pe, never()).getPrivilegeBits();
+ verifyNoMoreInteractions(pe);
}
@Test
@@ -147,13 +157,10 @@ public class EntryPredicateTest {
Predicate<PermissionEntry> predicate = EntryPredicate.createParent(TREE_PATH, null, Permissions.ALL);
predicate.apply(pe);
-
- verify(pe, never()).matches();
- verify(pe, never()).matches(TREE_PATH);
+
verify(pe, times(1)).appliesTo(PARENT_PATH);
- verify(pe, times(1)).matches(PARENT_PATH);
- verify(pe, never()).matches(any(Tree.class), any(PropertyState.class));
- verify(pe, never()).getPrivilegeBits();
+ verify(pe, times(1)).matches(PARENT_PATH, false);
+ verifyNoMoreInteractions(pe);
}
@Test
@@ -164,12 +171,9 @@ public class EntryPredicateTest {
Predicate<PermissionEntry> predicate = EntryPredicate.createParent(TREE_PATH, parentTree, Permissions.ALL);
predicate.apply(pe);
- verify(pe, never()).matches();
- verify(pe, never()).matches(TREE_PATH);
verify(pe, times(1)).appliesTo(PARENT_PATH);
verify(pe, times(1)).matches(parentTree, null);
- verify(pe, never()).matches(PARENT_PATH);
- verify(pe, never()).getPrivilegeBits();
+ verifyNoMoreInteractions(pe);
}
@Test
@@ -179,12 +183,9 @@ public class EntryPredicateTest {
Predicate<PermissionEntry> predicate = EntryPredicate.createParent(TREE_PATH, mockTree(PARENT_PATH, false), Permissions.ALL);
predicate.apply(pe);
- verify(pe, never()).matches();
- verify(pe, never()).matches(TREE_PATH);
verify(pe, times(1)).appliesTo(PARENT_PATH);
- verify(pe, times(1)).matches(PARENT_PATH);
- verify(pe, never()).matches(any(Tree.class), any(PropertyState.class));
- verify(pe, never()).getPrivilegeBits();
+ verify(pe, times(1)).matches(PARENT_PATH, false);
+ verifyNoMoreInteractions(pe);
}
@Test
@@ -193,12 +194,8 @@ public class EntryPredicateTest {
predicate.apply(pe);
String parentPath = PathUtils.getParentPath(TREE_PATH);
- verify(pe, never()).matches();
- verify(pe, never()).matches(TREE_PATH);
verify(pe, times(1)).appliesTo(parentPath);
- verify(pe, never()).matches(parentPath);
- verify(pe, never()).matches(any(Tree.class), any(PropertyState.class));
- verify(pe, never()).getPrivilegeBits();
+ verifyNoMoreInteractions(pe);
}
@Test
@@ -209,13 +206,8 @@ public class EntryPredicateTest {
predicate.apply(pe);
String parentPath = PathUtils.getParentPath(TREE_PATH);
- verify(pe, never()).matches();
- verify(pe, never()).matches(TREE_PATH);
verify(pe, times(1)).appliesTo(parentPath);
- verify(pe, never()).matches(parentPath);
- verify(pe, never()).matches(any(Tree.class), any(PropertyState.class));
- verify(pe, never()).getPrivilegeBits();
- }
+ verifyNoMoreInteractions(pe); }
@Test
public void testCreateParentTreeReadPermission() {
@@ -230,13 +222,10 @@ public class EntryPredicateTest {
Predicate<PermissionEntry> predicate = EntryPredicate.createParent(tree, Permissions.ADD_NODE|Permissions.READ_NODE);
predicate.apply(pe);
-
- verify(pe, never()).matches();
- verify(pe, never()).matches(TREE_PATH);
+
verify(pe, times(1)).appliesTo(PARENT_PATH);
- verify(pe, times(1)).matches(PARENT_PATH);
- verify(pe, never()).matches(any(Tree.class), any(PropertyState.class));
- verify(pe, never()).getPrivilegeBits();
+ verify(pe, times(1)).matches(PARENT_PATH, false);
+ verifyNoMoreInteractions(pe);
}
@Test
@@ -244,12 +233,8 @@ public class EntryPredicateTest {
Predicate<PermissionEntry> predicate = EntryPredicate.createParent(tree, Permissions.ADD_NODE|Permissions.READ_NODE);
predicate.apply(pe);
- verify(pe, never()).matches();
- verify(pe, never()).matches(TREE_PATH);
verify(pe, times(1)).appliesTo(PARENT_PATH);
- verify(pe, never()).matches(PARENT_PATH);
- verify(pe, never()).matches(any(Tree.class), any(PropertyState.class));
- verify(pe, never()).getPrivilegeBits();
+ verifyNoMoreInteractions(pe);
}
@Test
@@ -270,13 +255,9 @@ public class EntryPredicateTest {
Predicate<PermissionEntry> predicate = EntryPredicate.createParent(tree, Permissions.REMOVE_NODE|Permissions.READ_PROPERTY|Permissions.LOCK_MANAGEMENT);
predicate.apply(pe);
- verify(pe, never()).matches();
- verify(pe, never()).matches(TREE_PATH);
- verify(pe, never()).matches(PARENT_PATH);
verify(pe, times(1)).appliesTo(PARENT_PATH);
verify(pe, times(1)).matches(parentTree, null);
- verify(pe, never()).matches(tree, null);
- verify(pe, never()).getPrivilegeBits();
+ verifyNoMoreInteractions(pe);
}
@Test
@@ -289,12 +270,7 @@ public class EntryPredicateTest {
Predicate<PermissionEntry> predicate = EntryPredicate.createParent(tree, Permissions.REMOVE_NODE|Permissions.READ_PROPERTY|Permissions.LOCK_MANAGEMENT);
predicate.apply(pe);
- verify(pe, never()).matches();
- verify(pe, never()).matches(TREE_PATH);
- verify(pe, never()).matches(PARENT_PATH);
verify(pe, times(1)).appliesTo(PARENT_PATH);
- verify(pe, never()).matches(parentTree, null);
- verify(pe, never()).matches(tree, null);
- verify(pe, never()).getPrivilegeBits();
+ verifyNoMoreInteractions(pe);
}
}
\ No newline at end of file
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/AllPermissions.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/AllPermissions.java
index f387e7a..2ecdc26 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/AllPermissions.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/AllPermissions.java
@@ -73,6 +73,11 @@ final class AllPermissions implements CompiledPermissions {
}
@Override
+ public boolean isGranted(@NotNull String path, boolean isProperty, long permissions) {
+ return true;
+ }
+
+ @Override
public boolean isGranted(@NotNull String path, long permissions) {
return true;
}
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java
index f3ea78c..57f9d5c 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java
@@ -271,7 +271,7 @@ final class CompiledPermissionImpl implements CompiledPermissions, PermissionCon
if (property != null) {
path = PathUtils.concat(path, property.getName());
}
- return isGranted(path, permissions);
+ return isGranted(path, property != null, permissions);
}
case INTERNAL:
return false;
@@ -281,6 +281,12 @@ final class CompiledPermissionImpl implements CompiledPermissions, PermissionCon
}
@Override
+ public boolean isGranted(@NotNull String path, boolean isProperty, long permissions) {
+ EntryPredicate predicate = EntryPredicate.create(path, isProperty, Permissions.respectParentPermissions(permissions));
+ return hasPermissions(getEntryIterator(predicate), predicate, permissions, path);
+ }
+
+ @Override
public boolean isGranted(@NotNull String path, long permissions) {
EntryPredicate predicate = EntryPredicate.create(path, Permissions.respectParentPermissions(permissions));
return hasPermissions(getEntryIterator(predicate), predicate, permissions, path);
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissions.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissions.java
index 649b351..954e633 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissions.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissions.java
@@ -97,6 +97,18 @@ interface CompiledPermissions {
* Returns {@code true} if the given {@code permissions} are granted on the
* tree identified by the specified {@code path}.
*
+ * @param path Path of an item
+ * @param isProperty If the path points to an property
+ * @param permissions The permissions to be tested.
+ * @return {@code true} if granted.
+ * @see {@link org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider#isGranted(String, String)}
+ */
+ boolean isGranted(@NotNull String path, boolean isProperty, long permissions);
+
+ /**
+ * Returns {@code true} if the given {@code permissions} are granted on the
+ * tree identified by the specified {@code path}.
+ *
* @param path Path of a tree
* @param permissions The permissions to be tested.
* @return {@code true} if granted.
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/EntryPredicate.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/EntryPredicate.java
index 5536ea5..88d028b 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/EntryPredicate.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/EntryPredicate.java
@@ -71,6 +71,24 @@ interface EntryPredicate extends Predicate<PermissionEntry> {
};
}
+ static EntryPredicate create(@NotNull String path, boolean isProperty, boolean respectParent) {
+ String parentPath = (!respectParent || PathUtils.ROOT_PATH.equals(path)) ? null : PathUtils.getParentPath(path);
+ boolean rp = respectParent && parentPath != null;
+ return new EntryPredicate() {
+ @NotNull
+ @Override
+ public String getPath() {
+ return path;
+ }
+
+ @Override
+ public boolean apply(@NotNull PermissionEntry entry, boolean respectParent) {
+ respectParent &= rp;
+ return entry.matches(path, isProperty) || (respectParent && entry.matches(parentPath, false));
+ }
+ };
+ }
+
static EntryPredicate create(@NotNull String path, boolean respectParent) {
String parentPath = (!respectParent || PathUtils.ROOT_PATH.equals(path)) ? null : PathUtils.getParentPath(path);
boolean rp = respectParent && parentPath != null;
@@ -84,7 +102,7 @@ interface EntryPredicate extends Predicate<PermissionEntry> {
@Override
public boolean apply(@NotNull PermissionEntry entry, boolean respectParent) {
respectParent &= rp;
- return entry.matches(path) || (respectParent && entry.matches(parentPath));
+ return entry.matches(path) || (respectParent && entry.matches(parentPath, false));
}
};
}
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/NoPermissions.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/NoPermissions.java
index fcce733..d1d95dd 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/NoPermissions.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/NoPermissions.java
@@ -70,6 +70,11 @@ final class NoPermissions implements CompiledPermissions {
}
@Override
+ public boolean isGranted(@NotNull String path, boolean isProperty, long permissions) {
+ return false;
+ }
+
+ @Override
public boolean isGranted(@NotNull String path, long permissions) {
return false;
}
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntry.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntry.java
index 291939a..d4bdd1e 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntry.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntry.java
@@ -72,12 +72,16 @@ final class PermissionEntry implements Comparable<PermissionEntry>, PermissionCo
return restriction == RestrictionPattern.EMPTY || restriction.matches(treePath);
}
+ boolean matches(@NotNull String path, boolean isProperty) {
+ return restriction == RestrictionPattern.EMPTY || restriction.matches(path, isProperty);
+ }
+
boolean matches() {
return restriction == RestrictionPattern.EMPTY || restriction.matches();
}
boolean matchesParent(@NotNull String parentPath) {
- return Text.isDescendantOrEqual(path, parentPath) && (restriction == RestrictionPattern.EMPTY || restriction.matches(parentPath));
+ return Text.isDescendantOrEqual(path, parentPath) && (restriction == RestrictionPattern.EMPTY || restriction.matches(parentPath, false));
}
@Override
diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/EntryPredicateTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/EntryPredicateTest.java
index eff053b..614b30c 100644
--- a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/EntryPredicateTest.java
+++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/EntryPredicateTest.java
@@ -23,25 +23,30 @@ import org.apache.jackrabbit.oak.spi.security.authorization.restriction.Restrict
import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBits;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import org.junit.After;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
public class EntryPredicateTest {
- private String path = "/some/path";
- private String parentPath = PathUtils.getParentPath(path);
+ private final String path = "/some/path";
+ private final String parentPath = PathUtils.getParentPath(path);
- private RestrictionPattern pattern = mock(RestrictionPattern.class);
- private PermissionEntry entry = new PermissionEntry(path, true, 1, PrivilegeBits.EMPTY, pattern);
+ private final RestrictionPattern pattern = mock(RestrictionPattern.class);
+ private final PermissionEntry entry = new PermissionEntry(path, true, 1, PrivilegeBits.EMPTY, pattern);
private Tree mockTree(@NotNull String path, @Nullable Tree parent) {
Tree t = mock(Tree.class);
@@ -51,6 +56,11 @@ public class EntryPredicateTest {
when(t.getPath()).thenReturn(path);
return t;
}
+
+ @After
+ public void after() {
+ reset(pattern);
+ }
@Test
public void testPredicateRepositoryLevel() {
@@ -66,6 +76,7 @@ public class EntryPredicateTest {
assertTrue(pred.apply(entry, false));
verify(pattern, times(3)).matches();
+ verifyNoMoreInteractions(pattern);
}
@Test
@@ -74,16 +85,26 @@ public class EntryPredicateTest {
assertEquals(path, pred.getPath());
// pattern neither matches path nor parent path
- when(pattern.matches(path)).thenReturn(false);
- when(pattern.matches(parentPath)).thenReturn(false);
+ when(pattern.matches(anyString())).thenReturn(false);
+ when(pattern.matches(anyString(), anyBoolean())).thenReturn(false);
+ assertFalse(pred.apply(null));
assertFalse(pred.apply(entry));
assertFalse(pred.apply(entry, true));
assertFalse(pred.apply(entry, false));
+ verify(pattern, times(3)).matches(path);
+ verify(pattern, times(2)).matches(parentPath, false);
+ verifyNoMoreInteractions(pattern);
+ }
+
+ @Test
+ public void testPredicatePathRespectParent2() {
+ EntryPredicate pred = EntryPredicate.create(path, true);
+
// pattern matches path and parent path
- when(pattern.matches(path)).thenReturn(true);
- when(pattern.matches(parentPath)).thenReturn(true);
+ when(pattern.matches(anyString())).thenReturn(true);
+ when(pattern.matches(anyString(), anyBoolean())).thenReturn(true);
assertFalse(pred.apply(null));
@@ -91,24 +112,43 @@ public class EntryPredicateTest {
assertTrue(pred.apply(entry, true));
assertTrue(pred.apply(entry, false));
+ verify(pattern, times(3)).matches(path);
+ verifyNoMoreInteractions(pattern);
+ }
+
+ @Test
+ public void testPredicatePathRespectParent3() {
+ EntryPredicate pred = EntryPredicate.create(path, true);
+
// pattern only matches path
when(pattern.matches(path)).thenReturn(true);
when(pattern.matches(parentPath)).thenReturn(false);
+ when(pattern.matches(parentPath, false)).thenReturn(false);
assertTrue(pred.apply(entry));
assertTrue(pred.apply(entry, true));
assertTrue(pred.apply(entry, false));
+ verify(pattern, times(3)).matches(path);
+ verifyNoMoreInteractions(pattern);
+ }
+
+ @Test
+ public void testPredicatePathRespectParent4() {
+ EntryPredicate pred = EntryPredicate.create(path, true);
+
// pattern only matches parent path
when(pattern.matches(path)).thenReturn(false);
when(pattern.matches(parentPath)).thenReturn(true);
+ when(pattern.matches(parentPath, false)).thenReturn(true);
assertTrue(pred.apply(entry));
assertTrue(pred.apply(entry, true));
assertFalse(pred.apply(entry, false));
- verify(pattern, times(12)).matches(path);
- verify(pattern, times(4)).matches(parentPath);
+ verify(pattern, times(3)).matches(path);
+ verify(pattern, times(2)).matches(parentPath, false);
+ verifyNoMoreInteractions(pattern);
}
@Test
@@ -117,16 +157,24 @@ public class EntryPredicateTest {
assertEquals(path, pred.getPath());
// pattern neither matches path nor parent path
- when(pattern.matches(path)).thenReturn(false);
- when(pattern.matches(parentPath)).thenReturn(false);
+ when(pattern.matches(anyString())).thenReturn(false);
+ when(pattern.matches(anyString(), anyBoolean())).thenReturn(false);
assertFalse(pred.apply(entry));
assertFalse(pred.apply(entry, true));
assertFalse(pred.apply(entry, false));
+ verify(pattern, times(3)).matches(path);
+ verifyNoMoreInteractions(pattern);
+ }
+
+ @Test
+ public void testPredicatePathDontRespectParent2() {
+ EntryPredicate pred = EntryPredicate.create(path, false);
+
// pattern matches path and parent path
- when(pattern.matches(path)).thenReturn(true);
- when(pattern.matches(parentPath)).thenReturn(true);
+ when(pattern.matches(anyString())).thenReturn(true);
+ when(pattern.matches(anyString(), anyBoolean())).thenReturn(true);
assertFalse(pred.apply(null));
@@ -134,6 +182,14 @@ public class EntryPredicateTest {
assertTrue(pred.apply(entry, true));
assertTrue(pred.apply(entry, false));
+ verify(pattern, times(3)).matches(path);
+ verifyNoMoreInteractions(pattern);
+ }
+
+ @Test
+ public void testPredicatePathDontRespectParent3() {
+ EntryPredicate pred = EntryPredicate.create(path, false);
+
// pattern only matches path
when(pattern.matches(path)).thenReturn(true);
when(pattern.matches(parentPath)).thenReturn(false);
@@ -142,16 +198,153 @@ public class EntryPredicateTest {
assertTrue(pred.apply(entry, true));
assertTrue(pred.apply(entry, false));
+ verify(pattern, times(3)).matches(path);
+ verifyNoMoreInteractions(pattern);
+ }
+
+ @Test
+ public void testPredicatePathDontRespectParent4() {
+ EntryPredicate pred = EntryPredicate.create(path, false);
+
// pattern only matches parent path
when(pattern.matches(path)).thenReturn(false);
when(pattern.matches(parentPath)).thenReturn(true);
+ when(pattern.matches(parentPath, false)).thenReturn(true);
+
+ assertFalse(pred.apply(entry));
+ assertFalse(pred.apply(entry, true));
+ assertFalse(pred.apply(entry, false));
+
+ verify(pattern, times(3)).matches(path);
+ verifyNoMoreInteractions(pattern);
+ }
+
+ @Test
+ public void testPredicatePathIsPropertyRespectParent() {
+ EntryPredicate pred = EntryPredicate.create(path, true, true);
+ assertEquals(path, pred.getPath());
+
+ // pattern neither matches path nor parent path
+ when(pattern.matches(anyString(), anyBoolean())).thenReturn(false);
+
+ assertFalse(pred.apply(entry));
+ assertFalse(pred.apply(entry, true));
+ assertFalse(pred.apply(entry, false));
+
+ verify(pattern, times(3)).matches(path, true);
+ verify(pattern, times(2)).matches(parentPath, false);
+ verifyNoMoreInteractions(pattern);
+ }
+
+ @Test
+ public void testPredicatePathIsPropertyRespectParent2() {
+ EntryPredicate pred = EntryPredicate.create(path, true, true);
+
+ // pattern matches path and parent path
+ when(pattern.matches(anyString(), anyBoolean())).thenReturn(true);
+
+ assertFalse(pred.apply(null));
+ assertTrue(pred.apply(entry));
+ assertTrue(pred.apply(entry, true));
+ assertTrue(pred.apply(entry, false));
+
+ verify(pattern, times(3)).matches(path, true);
+ verifyNoMoreInteractions(pattern);
+ }
+
+ @Test
+ public void testPredicatePathIsPropertyRespectParent3() {
+ EntryPredicate pred = EntryPredicate.create(path, true, true);
+
+ // pattern only matches path
+ when(pattern.matches(path, true)).thenReturn(true);
+
+ assertTrue(pred.apply(entry));
+ assertTrue(pred.apply(entry, true));
+ assertTrue(pred.apply(entry, false));
+
+ verify(pattern, times(3)).matches(path, true);
+ verifyNoMoreInteractions(pattern);
+ }
+
+ @Test
+ public void testPredicatePathIsPropertyRespectParent4() {
+ EntryPredicate pred = EntryPredicate.create(path, true, true);
+
+ // pattern only matches parent path
+ when(pattern.matches(parentPath, false)).thenReturn(true);
+
+ assertTrue(pred.apply(entry));
+ assertTrue(pred.apply(entry, true));
+ assertFalse(pred.apply(entry, false));
+
+ verify(pattern, times(3)).matches(path, true);
+ verify(pattern, times(2)).matches(parentPath, false);
+ verifyNoMoreInteractions(pattern);
+ }
+
+ @Test
+ public void testPredicatePathIsPropertyDontRespectParent() {
+ EntryPredicate pred = EntryPredicate.create(path, true, false);
+ assertEquals(path, pred.getPath());
+
+ // pattern neither matches path nor parent path
+ when(pattern.matches(anyString(), anyBoolean())).thenReturn(false);
+
+ assertFalse(pred.apply(entry));
+ assertFalse(pred.apply(entry, true));
+ assertFalse(pred.apply(entry, false));
+
+ verify(pattern, times(3)).matches(path, true);
+ verifyNoMoreInteractions(pattern);
+ }
+
+ @Test
+ public void testPredicatePathIsPropertyDontRespectParent2() {
+ EntryPredicate pred = EntryPredicate.create(path, true, false);
+
+ // pattern matches path and parent path
+ when(pattern.matches(anyString(), anyBoolean())).thenReturn(true);
+
+ assertFalse(pred.apply(null));
+
+ assertTrue(pred.apply(entry));
+ assertTrue(pred.apply(entry, true));
+ assertTrue(pred.apply(entry, false));
+
+ verify(pattern, times(3)).matches(path, true);
+ verifyNoMoreInteractions(pattern);
+ }
+
+ @Test
+ public void testPredicatePathIsPropertyDontRespectParent3() {
+ EntryPredicate pred = EntryPredicate.create(path, true, false);
+
+ // pattern only matches path
+ when(pattern.matches(path, true)).thenReturn(true);
+
+ assertTrue(pred.apply(entry));
+ assertTrue(pred.apply(entry, true));
+ assertTrue(pred.apply(entry, false));
+
+ verify(pattern, times(3)).matches(path, true);
+ verifyNoMoreInteractions(pattern);
+ }
+
+ @Test
+ public void testPredicatePathIsPropertyDontRespectParent4() {
+ EntryPredicate pred = EntryPredicate.create(path, true, false);
+
+ // pattern only matches parent path
+ when(pattern.matches(path, true)).thenReturn(false);
+ when(pattern.matches(parentPath, false)).thenReturn(true);
assertFalse(pred.apply(entry));
assertFalse(pred.apply(entry, true));
assertFalse(pred.apply(entry, false));
- verify(pattern, times(12)).matches(path);
- verify(pattern, never()).matches(parentPath);
+ verify(pattern, times(3)).matches(path, true);
+ verifyNoMoreInteractions(pattern);
}
@Test
diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryTest.java
index ccd9011..5448e94 100644
--- a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryTest.java
+++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryTest.java
@@ -29,7 +29,9 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -38,12 +40,12 @@ import static org.mockito.Mockito.when;
public class PermissionEntryTest {
- private String path = "/path";
- private int index = 15;
- private PermissionEntry entry = new PermissionEntry(path, true, index, PrivilegeBits.BUILT_IN.get(PrivilegeConstants.REP_READ_NODES), RestrictionPattern.EMPTY);
+ private final String path = "/path";
+ private final int index = 15;
+ private final PermissionEntry entry = new PermissionEntry(path, true, index, PrivilegeBits.BUILT_IN.get(PrivilegeConstants.REP_READ_NODES), RestrictionPattern.EMPTY);
- private RestrictionPattern pattern = mock(RestrictionPattern.class);
- private PermissionEntry entryWithNonEmptyPattern = new PermissionEntry(path, false, index, PrivilegeBits.BUILT_IN.get(PrivilegeConstants.REP_ADD_PROPERTIES), pattern);
+ private final RestrictionPattern pattern = mock(RestrictionPattern.class);
+ private final PermissionEntry entryWithNonEmptyPattern = new PermissionEntry(path, false, index, PrivilegeBits.BUILT_IN.get(PrivilegeConstants.REP_ADD_PROPERTIES), pattern);
@Test
public void testMatchesEmptyPattern() {
@@ -125,6 +127,7 @@ public class PermissionEntryTest {
// the entry matchesParent if the parent of the path to be evaluated is equal or a descendant of the entry-path
// and the pattern evaluates to true (which is always the case here)
when(pattern.matches(anyString())).thenReturn(true);
+ when(pattern.matches(anyString(), anyBoolean())).thenReturn(true);
assertTrue(entryWithNonEmptyPattern.matchesParent(path));
assertTrue(entryWithNonEmptyPattern.matchesParent(PathUtils.concat(path, "parent", "of", "target")));
assertFalse(entryWithNonEmptyPattern.matchesParent(PathUtils.getParentPath(path)));
@@ -132,11 +135,28 @@ public class PermissionEntryTest {
// pattern doesn't match => always false
when(pattern.matches(anyString())).thenReturn(false);
+ when(pattern.matches(anyString(), anyBoolean())).thenReturn(false);
assertFalse(entryWithNonEmptyPattern.matchesParent(path));
assertFalse(entryWithNonEmptyPattern.matchesParent(PathUtils.concat(path, "parent", "of", "target")));
assertFalse(entryWithNonEmptyPattern.matchesParent(PathUtils.getParentPath(path)));
assertFalse(entryWithNonEmptyPattern.matchesParent("/another/path"));
}
+
+ @Test
+ public void testMatchesIsProperty() {
+ // empty restriction pattern
+ assertTrue(entry.matches(path, true));
+ assertTrue(entry.matches(path, false));
+
+ // pattern always returns false
+ assertFalse(entryWithNonEmptyPattern.matches(path, true));
+ assertFalse(entryWithNonEmptyPattern.matches(path, false));
+
+ // mock pattern
+ when(pattern.matches(path, true)).thenReturn(true);
+ assertTrue(entryWithNonEmptyPattern.matches(path, true));
+ assertFalse(entryWithNonEmptyPattern.matches(path, false));
+ }
@Test
public void testCompareToEqualPath() {
diff --git a/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/restriction/CompositePattern.java b/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/restriction/CompositePattern.java
index c0a08f4..ce02687 100644
--- a/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/restriction/CompositePattern.java
+++ b/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/restriction/CompositePattern.java
@@ -65,6 +65,16 @@ public final class CompositePattern implements RestrictionPattern {
}
@Override
+ public boolean matches(@NotNull String path, boolean isProperty) {
+ for (RestrictionPattern pattern : patterns) {
+ if (!pattern.matches(path, isProperty)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
public boolean matches() {
for (RestrictionPattern pattern : patterns) {
if (!pattern.matches()) {
diff --git a/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/restriction/RestrictionPattern.java b/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/restriction/RestrictionPattern.java
index 6fd55f6..70551f3 100644
--- a/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/restriction/RestrictionPattern.java
+++ b/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/restriction/RestrictionPattern.java
@@ -40,8 +40,9 @@ public interface RestrictionPattern {
boolean matches(@NotNull Tree tree, @Nullable PropertyState property);
/**
- * Returns {@code true} if the underlying restriction matches the specified
- * path.
+ * Returns {@code true} if the underlying restriction matches the specified path.
+ * Note, that if the nature of the item at {@code path} is know {@link #matches(String, boolean)} should be called
+ * instead.
*
* @param path The path of the target item.
* @return {@code true} if the underlying restriction matches the specified
@@ -50,6 +51,23 @@ public interface RestrictionPattern {
boolean matches(@NotNull String path);
/**
+ * Returns {@code true} if the underlying restriction matches the specified path and item type.
+ * If the nature of the item at {@code path} is unknown {@link #matches(String)} should be called instead.
+ *
+ * Note, for backwards compatibility this method comes with a default implementation making it equivalent to {@link #matches(String)}.
+ * Implementations of the {@link RestrictionPattern} interface should overwrite the default if the underlying
+ * restriction applies different behavior for nodes and properties.
+ *
+ * @param path The path of the target item.
+ * @param isProperty If {@code true} the target item is known to be a property, otherwise it is known to be a node.
+ * @return {@code true} if the underlying restriction matches the specified path and item type; {@code false} otherwise.
+ * @since OAK 1.42.0
+ */
+ default boolean matches(@NotNull String path, boolean isProperty) {
+ return matches(path);
+ }
+
+ /**
* Returns {@code true} if the underlying restriction matches for repository
* level permissions.
*
@@ -75,6 +93,11 @@ public interface RestrictionPattern {
}
@Override
+ public boolean matches(@NotNull String path, boolean isProperty) {
+ return true;
+ }
+
+ @Override
public boolean matches() {
return true;
}
diff --git a/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/restriction/package-info.java b/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/restriction/package-info.java
index 2f7258c..ef9b81a 100644
--- a/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/restriction/package-info.java
+++ b/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/restriction/package-info.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@Version("1.1.2")
+@Version("1.2.0")
package org.apache.jackrabbit.oak.spi.security.authorization.restriction;
import org.osgi.annotation.versioning.Version;