Index: oak-core/src/main/java/org/apache/jackrabbit/oak/core/LazyValue.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/core/LazyValue.java (revision 1851744)
+++ oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/LazyValue.java (date 1548156633000)
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.jackrabbit.oak.core;
+package org.apache.jackrabbit.oak.commons;
/**
* An instances of this class represents a lazy value of type {@code T}.
@@ -24,7 +24,7 @@
*
* {@code LazyValue} instances are thread safe.
*/
-abstract class LazyValue {
+public abstract class LazyValue {
private volatile T value;
/**
Index: oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/package-info.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/package-info.java (revision 1851744)
+++ oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/package-info.java (date 1548157576000)
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@Version("1.2.1")
+@Version("1.3.0")
package org.apache.jackrabbit.oak.commons;
import org.osgi.annotation.versioning.Version;
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/core/MutableRoot.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/core/MutableRoot.java (revision 1851744)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/core/MutableRoot.java (date 1548156850000)
@@ -39,6 +39,7 @@
import org.apache.jackrabbit.oak.api.ContentSession;
import org.apache.jackrabbit.oak.api.QueryEngine;
import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.commons.LazyValue;
import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.plugins.index.diffindex.UUIDDiffIndexProviderWrapper;
import org.apache.jackrabbit.oak.query.ExecutionContext;
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/core/SecureNodeBuilder.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/core/SecureNodeBuilder.java (revision 1851744)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/core/SecureNodeBuilder.java (date 1548156796000)
@@ -23,6 +23,7 @@
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.LazyValue;
import org.apache.jackrabbit.oak.plugins.tree.factories.TreeFactory;
import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider;
import org.apache.jackrabbit.oak.spi.security.authorization.permission.TreePermission;
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java (revision 1851744)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java (date 1548181373000)
@@ -20,11 +20,7 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Lists.newArrayList;
-import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
-import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
import static org.apache.jackrabbit.JcrConstants.NT_BASE;
-import static org.apache.jackrabbit.oak.api.Type.NAME;
-import static org.apache.jackrabbit.oak.api.Type.NAMES;
import java.util.ArrayList;
import java.util.List;
@@ -35,8 +31,11 @@
import org.apache.jackrabbit.oak.api.Result.SizePrecision;
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.LazyValue;
import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.core.ImmutableRoot;
import org.apache.jackrabbit.oak.plugins.memory.PropertyBuilder;
+import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
import org.apache.jackrabbit.oak.query.QueryImpl;
import org.apache.jackrabbit.oak.query.QueryOptions;
import org.apache.jackrabbit.oak.spi.query.fulltext.FullTextExpression;
@@ -158,6 +157,8 @@
private Tree lastTree;
private String lastPath;
+ private LazyValue lastReadOnlyTree;
+
public SelectorImpl(NodeTypeInfo nodeTypeInfo, String selectorName) {
this.nodeTypeInfo = checkNotNull(nodeTypeInfo);
this.selectorName = checkNotNull(selectorName);
@@ -496,24 +497,19 @@
}
private boolean evaluateTypeMatch() {
- Tree tree = getTree(currentRow.getPath());
+ String path = currentRow.getPath();
+ Tree tree = getTree(path);
if (tree == null || !tree.exists()) {
return false;
}
- PropertyState primary = tree.getProperty(JCR_PRIMARYTYPE);
- if (primary != null && primary.getType() == NAME) {
- String name = primary.getValue(NAME);
- if (primaryTypes.contains(name)) {
- return true;
- }
+ String primaryTypeName = TreeUtil.getPrimaryTypeName(tree, getReadOnlyTree(path));
+ if (primaryTypeName != null && primaryTypes.contains(primaryTypeName)) {
+ return true;
}
- PropertyState mixins = tree.getProperty(JCR_MIXINTYPES);
- if (mixins != null && mixins.getType() == NAMES) {
- for (String name : mixins.getValue(NAMES)) {
- if (mixinTypes.contains(name)) {
- return true;
- }
+ for (String mixinName : TreeUtil.getMixinTypeNames(tree, getReadOnlyTree(path))) {
+ if (mixinTypes.contains(mixinName)) {
+ return true;
}
}
// no matches found
@@ -552,10 +548,23 @@
if (lastPath == null || !path.equals(lastPath)) {
lastTree = query.getTree(path);
lastPath = path;
+ lastReadOnlyTree = null;
}
return lastTree;
}
+ private LazyValue getReadOnlyTree(@NotNull String path) {
+ if (lastReadOnlyTree == null) {
+ lastReadOnlyTree = new LazyValue() {
+ @Override
+ protected Tree createValue() {
+ return new ImmutableRoot(query.getExecutionContext().getBaseState()).getTree(path);
+ }
+ };
+ }
+ return lastReadOnlyTree;
+ }
+
/**
* The value for the given selector for the current node.
*
Index: oak-core/src/test/java/org/apache/jackrabbit/oak/core/SecureNodeBuilderTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/test/java/org/apache/jackrabbit/oak/core/SecureNodeBuilderTest.java (revision 1851744)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/core/SecureNodeBuilderTest.java (date 1548158005000)
@@ -19,6 +19,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.LazyValue;
import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
Index: oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/tree/impl/TreeUtilTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/tree/impl/TreeUtilTest.java (revision 1851744)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/tree/impl/TreeUtilTest.java (date 1548179661000)
@@ -16,17 +16,28 @@
*/
package org.apache.jackrabbit.oak.plugins.tree.impl;
+import com.google.common.collect.Iterables;
import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.oak.AbstractSecurityTest;
+import org.apache.jackrabbit.oak.api.ContentSession;
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.AbstractSecurityTest;
import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.LazyValue;
import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants;
import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+
+import javax.jcr.GuestCredentials;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
public class TreeUtilTest extends AbstractSecurityTest {
@@ -65,4 +76,133 @@
assertNotNull(ps);
assertEquals("", ps.getValue(Type.STRING));
}
+
+ @Test
+ public void testGetPrimaryTypeNameAccessible() throws Exception {
+ Tree tree = root.getTree("/");
+ String expected = TreeUtil.getPrimaryTypeName(tree);
+ assertEquals(expected, TreeUtil.getPrimaryTypeName(tree, new LazyValue() {
+ @Override
+ protected Tree createValue() {
+ return tree;
+ }
+ }));
+ }
+
+ @Test
+ public void testGetPrimaryTypeNameNotAccessible() throws Exception {
+ Tree tree = root.getTree("/");
+ String expected = TreeUtil.getPrimaryTypeName(tree);
+ try (ContentSession cs = login(new GuestCredentials())) {
+ Root r = cs.getLatestRoot();
+ assertNull(TreeUtil.getPrimaryTypeName(r.getTree("/")));
+ assertEquals(expected, TreeUtil.getPrimaryTypeName(r.getTree("/"), new LazyValue() {
+ @Override
+ protected Tree createValue() {
+ return tree;
+ }
+ }));
+ }
+ }
+
+ @Test
+ public void testGetPrimaryTypeNameNotAccessibleNew() throws Exception {
+ Tree testTree = TreeUtil.addChild(root.getTree("/"), "test", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+
+ Tree t = Mockito.mock(Tree.class);
+ when(t.hasProperty(JcrConstants.JCR_PRIMARYTYPE)).thenReturn(false);
+ when(t.getStatus()).thenReturn(Tree.Status.NEW);
+
+ assertNull(TreeUtil.getPrimaryTypeName(t, new LazyValue() {
+ @Override
+ protected Tree createValue() {
+ return testTree;
+ }
+ }));
+ }
+
+ @Test
+ public void testGetMixinTypeNamesMissingAccessible() throws Exception {
+ assertTrue(Iterables.isEmpty(TreeUtil.getMixinTypeNames(root.getTree("/"))));
+ assertTrue(Iterables.isEmpty(TreeUtil.getMixinTypeNames(root.getTree("/"), new LazyValue() {
+ @Override
+ protected Tree createValue() {
+ return root.getTree("/");
+ }
+ })));
+ }
+
+ @Test
+ public void testGetMixinTypeNamesMissingNotAccessible() throws Exception {
+ Tree tree = root.getTree("/");
+ assertTrue(Iterables.isEmpty(TreeUtil.getMixinTypeNames(tree)));
+
+ try (ContentSession cs = login(new GuestCredentials())) {
+ Root guestRoot = cs.getLatestRoot();
+ assertTrue(Iterables.isEmpty(TreeUtil.getMixinTypeNames(guestRoot.getTree("/"))));
+ assertTrue(Iterables.isEmpty(TreeUtil.getMixinTypeNames(guestRoot.getTree("/"), new LazyValue() {
+ @Override
+ protected Tree createValue() {
+ return tree;
+ }
+ })));
+ }
+ }
+
+ @Test
+ public void testGetMixinTypeNamesPresentAccessible() throws Exception {
+ Tree testTree = TreeUtil.addChild(root.getTree("/"), "test", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+ TreeUtil.addMixin(testTree, "mix:title", root.getTree(NodeTypeConstants.NODE_TYPES_PATH), "uid");
+
+ String path = testTree.getPath();
+ Iterable expected = TreeUtil.getMixinTypeNames(root.getTree(path));
+ assertTrue(Iterables.contains(expected, "mix:title"));
+
+ assertTrue(Iterables.elementsEqual(expected, TreeUtil.getMixinTypeNames(testTree, new LazyValue() {
+ @Override
+ protected Tree createValue() {
+ return testTree;
+ }
+ })));
+ }
+
+ @Test
+ public void testGetMixinTypeNamesPresentNotAccessible() throws Exception {
+ Tree testTree = TreeUtil.addChild(root.getTree("/"), "test", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+ TreeUtil.addMixin(testTree, "mix:title", root.getTree(NodeTypeConstants.NODE_TYPES_PATH), "uid");
+ root.commit();
+
+ String path = testTree.getPath();
+ Iterable expected = TreeUtil.getMixinTypeNames(root.getTree(path));
+ assertTrue(Iterables.contains(expected, "mix:title"));
+
+ try (ContentSession cs = login(new GuestCredentials())) {
+ Root guestRoot = cs.getLatestRoot();
+ assertTrue(Iterables.isEmpty(TreeUtil.getMixinTypeNames(guestRoot.getTree(path))));
+ assertTrue(Iterables.elementsEqual(expected, TreeUtil.getMixinTypeNames(guestRoot.getTree(path), new LazyValue() {
+ @Override
+ protected Tree createValue() {
+ return testTree;
+ }
+ })));
+ }
+ }
+
+ @Test
+ public void testGetMixinTypeNamesPresentNotAccessibleNew() throws Exception {
+ Tree testTree = TreeUtil.addChild(root.getTree("/"), "test", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+ TreeUtil.addMixin(testTree, "mix:title", root.getTree(NodeTypeConstants.NODE_TYPES_PATH), "uid");
+ root.commit();
+
+ Tree t = Mockito.mock(Tree.class);
+ when(t.hasProperty(JcrConstants.JCR_MIXINTYPES)).thenReturn(false);
+ when(t.getStatus()).thenReturn(Tree.Status.NEW);
+
+ assertTrue(Iterables.isEmpty(TreeUtil.getMixinTypeNames(t, new LazyValue() {
+ @Override
+ protected Tree createValue() {
+ return testTree;
+ }
+ })));
+ }
}
\ No newline at end of file
Index: oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/AbstractOakCoreTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/AbstractOakCoreTest.java (revision 1851744)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/AbstractOakCoreTest.java (date 1548164656000)
@@ -25,6 +25,8 @@
import org.apache.jackrabbit.oak.AbstractSecurityTest;
import org.apache.jackrabbit.oak.api.ContentSession;
import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
import org.apache.jackrabbit.oak.util.NodeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -50,18 +52,17 @@
testPrincipal = getTestUser().getPrincipal();
- NodeUtil rootNode = new NodeUtil(root.getTree("/"));
- NodeUtil a = rootNode.addChild("a", NT_UNSTRUCTURED);
- a.setString("aProp", "aValue");
+ Tree a = TreeUtil.addChild(root.getTree("/"), "a", NT_UNSTRUCTURED);
+ a.setProperty("aProp", "aValue");
- NodeUtil b = a.addChild("b", NT_UNSTRUCTURED);
- b.setString("bProp", "bValue");
+ Tree b = TreeUtil.addChild(a, "b", NT_UNSTRUCTURED);
+ b.setProperty("bProp", "bValue");
// sibling
- NodeUtil bb = a.addChild("bb", NT_UNSTRUCTURED);
- bb.setString("bbProp", "bbValue");
+ Tree bb = TreeUtil.addChild(a, "bb", NT_UNSTRUCTURED);
+ bb.setProperty("bbProp", "bbValue");
- NodeUtil c = b.addChild("c", NT_UNSTRUCTURED);
- c.setString("cProp", "cValue");
+ Tree c = TreeUtil.addChild(b, "c", NT_UNSTRUCTURED);
+ c.setProperty("cProp", "cValue");
root.commit();
}
Index: oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/AbstractQueryTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/AbstractQueryTest.java (date 1548175352000)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/AbstractQueryTest.java (date 1548175352000)
@@ -0,0 +1,164 @@
+/*
+ * 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.security.authorization.evaluation;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
+import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
+import org.apache.jackrabbit.oak.api.Result;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
+import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants;
+import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration;
+import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants;
+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.PrivilegeConstants;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.query.Query;
+import javax.jcr.security.AccessControlManager;
+import java.util.Collections;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public abstract class AbstractQueryTest extends AbstractOakCoreTest {
+
+ Tree node;
+ Tree subnode;
+
+ @Before
+ public void before() throws Exception {
+ super.before();
+
+ createIndexDefinition();
+
+ node = TreeUtil.addChild(root.getTree("/"), "node", JcrConstants.NT_UNSTRUCTURED);
+ subnode = TreeUtil.addChild(node, "subnode", JcrConstants.NT_UNSTRUCTURED);
+ root.commit();
+ }
+
+ void grantPropertyReadAccess(@NotNull String propertyName) throws Exception {
+ AccessControlManager acMgr = getAccessControlManager(root);
+ JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(acMgr, "/");
+ if (acl != null) {
+ Map restrictions = ImmutableMap.of(AccessControlConstants.REP_ITEM_NAMES, new Value[] {getValueFactory(root).createValue(propertyName, PropertyType.NAME)});
+ acl.addEntry(testPrincipal, AccessControlUtils.privilegesFromNames(acMgr, PrivilegeConstants.REP_READ_PROPERTIES), true, null, restrictions);
+ acMgr.setPolicy(acl.getPath(), acl);
+ }
+ }
+
+ void createIndexDefinition() throws RepositoryException {};
+ abstract String getStatement();
+
+ @Test
+ public void testQueryWithEmptyGlobRestriction() throws Exception {
+ // setup permissions for testuser using rep:glob restriction such that
+ // - access to /node is granted
+ // - access to /node/subnode is denied
+ AccessControlManager acm = getAccessControlManager(root);
+ JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(acm, node.getPath());
+ if (acl != null) {
+ Map restrictions = ImmutableMap.of(AccessControlConstants.REP_GLOB, getValueFactory(root).createValue(""));
+ acl.addEntry(testPrincipal, AccessControlUtils.privilegesFromNames(acm, PrivilegeConstants.JCR_ALL), true, restrictions);
+ acm.setPolicy(acl.getPath(), acl);
+ root.commit();
+ }
+
+ assertAccess(node.getPath(), subnode.getPath(), false);
+
+ // test that query result corresponds to the direct access (node readable, subnode not readable)
+ Result result = getTestRoot().getQueryEngine().executeQuery(getStatement(), Query.JCR_SQL2, Collections.emptyMap(), Collections.emptyMap());
+
+ Iterable expected = ImmutableSet.of(node.getPath());
+ assertTrue(Iterables.elementsEqual(expected, Iterables.transform(result.getRows(), row -> row.getPath())));
+ }
+
+ @Test
+ public void testQueryWithEmptyGlobRestrictionAndPropertyRead() throws Exception {
+ // setup permissions for testuser using rep:glob restriction such that
+ // - access to /node is granted
+ // - access to /node/subnode is denied
+ AccessControlManager acm = getAccessControlManager(root);
+ JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(acm, node.getPath());
+ if (acl != null) {
+ Map restrictions = ImmutableMap.of(AccessControlConstants.REP_GLOB, getValueFactory(root).createValue(""));
+ acl.addEntry(testPrincipal, privilegesFromNames(PrivilegeConstants.JCR_ALL), true, restrictions);
+
+ restrictions = ImmutableMap.of(AccessControlConstants.REP_GLOB, getValueFactory(root).createValue("/"+NodeTypeConstants.JCR_PRIMARYTYPE));
+ acl.addEntry(testPrincipal, privilegesFromNames(PrivilegeConstants.REP_READ_PROPERTIES), true, restrictions);
+
+ acm.setPolicy(acl.getPath(), acl);
+ root.commit();
+ }
+
+ assertAccess(node.getPath(), subnode.getPath(), true);
+
+ // test that query result corresponds to the direct access (node readable, subnode not readable)
+ Result result = getTestRoot().getQueryEngine().executeQuery(getStatement(), Query.JCR_SQL2, Collections.emptyMap(), Collections.emptyMap());
+
+ Iterable expected = ImmutableSet.of(node.getPath());
+ assertTrue(Iterables.elementsEqual(expected, Iterables.transform(result.getRows(), row -> row.getPath())));
+ }
+
+ @Test
+ public void testQueryWithAllowNodeAndDenySubNode() throws Exception {
+ // setup permissions for testuser using 2 aces such that
+ // - access to /node is granted
+ // - access to /node/subnode is denied
+ setupPermission(node.getPath(), testPrincipal, true, PrivilegeConstants.JCR_ALL);
+ setupPermission(subnode.getPath(), testPrincipal, false, PrivilegeConstants.JCR_ALL);
+
+ assertAccess(node.getPath(), subnode.getPath(), true);
+
+ // test that query result corresponds to the direct access (node readable, subnode not readable)
+ Result result = getTestRoot().getQueryEngine().executeQuery(getStatement(), Query.JCR_SQL2, Collections.emptyMap(), Collections.emptyMap());
+
+ Iterable expected = ImmutableSet.of(node.getPath());
+ assertTrue(Iterables.elementsEqual(expected, Iterables.transform(result.getRows(), row -> row.getPath())));
+ }
+
+ private void assertAccess(@NotNull String nodePath, @NotNull String subnodePath, boolean canReadPrimaryType) throws Exception {
+ // verify access control setup
+ assertTrue(getTestRoot().getTree(nodePath).exists());
+ assertFalse(getTestRoot().getTree(subnodePath).exists());
+
+ // verify PermissionProvider.isGranted(String, String) as it is used inside
+ // the query code base (FilterImpl.isAccessible)
+ PermissionProvider pp = getConfig(AuthorizationConfiguration.class).getPermissionProvider(getTestRoot(), getTestSession().getWorkspaceName(), getTestSession().getAuthInfo().getPrincipals());
+ assertTrue(pp.isGranted(nodePath, Session.ACTION_READ));
+
+ assertEquals(canReadPrimaryType, pp.isGranted(nodePath + '/' + JcrConstants.JCR_PRIMARYTYPE, Session.ACTION_READ));
+ assertEquals(canReadPrimaryType, pp.isGranted(nodePath + '/' + JcrConstants.JCR_PRIMARYTYPE, Permissions.getString(Permissions.READ_PROPERTY)));
+
+ assertFalse(pp.isGranted(subnodePath, Session.ACTION_READ));
+ assertFalse(pp.isGranted(subnodePath + '/' + JcrConstants.JCR_PRIMARYTYPE, Session.ACTION_READ));
+ assertFalse(pp.isGranted(subnodePath + '/' + JcrConstants.JCR_PRIMARYTYPE, Permissions.getString(Permissions.READ_PROPERTY)));
+ }
+}
\ No newline at end of file
Index: oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/IndexedQueryMixinTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/IndexedQueryMixinTest.java (date 1548175366000)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/IndexedQueryMixinTest.java (date 1548175366000)
@@ -0,0 +1,55 @@
+/*
+ * 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.security.authorization.evaluation;
+
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
+import org.apache.jackrabbit.oak.plugins.index.IndexUtils;
+import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
+import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants;
+
+import javax.jcr.RepositoryException;
+
+import static org.junit.Assert.assertTrue;
+
+public class IndexedQueryMixinTest extends AbstractQueryTest {
+
+ @Override
+ public void before() throws Exception {
+ super.before();
+
+ TreeUtil.addMixin(node, "mix:title", root.getTree(NodeTypeConstants.NODE_TYPES_PATH), "userId");
+ node.setProperty("jcr:title", "title");
+ TreeUtil.addMixin(subnode, "mix:title", root.getTree(NodeTypeConstants.NODE_TYPES_PATH), "userId");
+ subnode.setProperty("jcr:title", "title");
+
+ grantPropertyReadAccess("jcr:title");
+
+ root.commit();
+ }
+
+ @Override
+ void createIndexDefinition() throws RepositoryException {
+ Tree oakIndex = root.getTree("/"+IndexConstants.INDEX_DEFINITIONS_NAME);
+ assertTrue(oakIndex.exists());
+ IndexUtils.createIndexDefinition(oakIndex, "test-index", false, new String[] {"jcr:title"}, "mix:title");
+ }
+
+ String getStatement() {
+ return "SELECT * FROM [mix:title] WHERE [jcr:title] is not null";
+ }
+}
\ No newline at end of file
Index: oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/IndexedQueryTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/IndexedQueryTest.java (date 1548175348000)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/IndexedQueryTest.java (date 1548175348000)
@@ -0,0 +1,51 @@
+/*
+ * 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.security.authorization.evaluation;
+
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
+import org.apache.jackrabbit.oak.plugins.index.IndexUtils;
+
+import javax.jcr.RepositoryException;
+
+import static org.junit.Assert.assertTrue;
+
+public class IndexedQueryTest extends AbstractQueryTest {
+
+ @Override
+ public void before() throws Exception {
+ super.before();
+
+ node.setProperty("title", "a");
+ subnode.setProperty("title", "b");
+
+ grantPropertyReadAccess("title");
+
+ root.commit();
+ }
+
+ @Override
+ void createIndexDefinition() throws RepositoryException {
+ Tree oakIndex = root.getTree("/"+IndexConstants.INDEX_DEFINITIONS_NAME);
+ assertTrue(oakIndex.exists());
+ IndexUtils.createIndexDefinition(oakIndex, "test-index", false, new String[] {"title"}, "nt:unstructured");
+ }
+
+ String getStatement() {
+ return "SELECT * FROM [nt:unstructured] WHERE [title] is not null";
+ }
+}
\ No newline at end of file
Index: oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/TraversingQueryMixinTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/TraversingQueryMixinTest.java (date 1548168288000)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/TraversingQueryMixinTest.java (date 1548168288000)
@@ -0,0 +1,38 @@
+/*
+ * 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.security.authorization.evaluation;
+
+import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
+import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants;
+
+import javax.jcr.RepositoryException;
+
+public class TraversingQueryMixinTest extends AbstractQueryTest {
+
+ @Override
+ public void before() throws Exception {
+ super.before();
+
+ TreeUtil.addMixin(node, "mix:title", root.getTree(NodeTypeConstants.NODE_TYPES_PATH), "userId");
+ TreeUtil.addMixin(subnode, "mix:title", root.getTree(NodeTypeConstants.NODE_TYPES_PATH), "userId");
+ root.commit();
+ }
+
+ String getStatement() {
+ return "SELECT * FROM [mix:title] option (traversal ok)";
+ }
+}
\ No newline at end of file
Index: oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/TraversingQueryTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/TraversingQueryTest.java (date 1548164807000)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/TraversingQueryTest.java (date 1548164807000)
@@ -0,0 +1,55 @@
+/*
+ * 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.security.authorization.evaluation;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
+import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
+import org.apache.jackrabbit.oak.api.QueryEngine;
+import org.apache.jackrabbit.oak.api.Result;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
+import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants;
+import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration;
+import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants;
+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.PrivilegeConstants;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.query.Query;
+import javax.jcr.security.AccessControlManager;
+import java.util.Collections;
+import java.util.Map;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class TraversingQueryTest extends AbstractQueryTest {
+
+ String getStatement() {
+ return "SELECT * FROM [nt:unstructured] option (traversal ok)";
+ }
+}
\ No newline at end of file
Index: oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/NodeImpl.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/NodeImpl.java (revision 1851744)
+++ oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/NodeImpl.java (date 1548157422000)
@@ -79,6 +79,7 @@
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.api.Tree.Status;
import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.LazyValue;
import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.jcr.delegate.NodeDelegate;
import org.apache.jackrabbit.oak.jcr.delegate.PropertyDelegate;
@@ -1281,31 +1282,27 @@
//------------------------------------------------------------< internal >---
@Nullable
private String getPrimaryTypeName(@NotNull Tree tree) {
- String primaryTypeName = null;
- if (tree.hasProperty(JcrConstants.JCR_PRIMARYTYPE)) {
- primaryTypeName = TreeUtil.getPrimaryTypeName(tree);
- } else if (tree.getStatus() != Status.NEW) {
- // OAK-2441: for backwards compatibility with Jackrabbit 2.x try to
- // read the primary type from the underlying node state.
- primaryTypeName = TreeUtil.getPrimaryTypeName(RootFactory.createReadOnlyRoot(sessionDelegate.getRoot()).getTree(tree.getPath()));
- }
- return primaryTypeName;
+ return TreeUtil.getPrimaryTypeName(tree, getReadOnlyTree(tree));
}
@NotNull
private Iterator getMixinTypeNames(@NotNull Tree tree) throws RepositoryException {
- Iterator mixinNames = Collections.emptyIterator();
if (tree.hasProperty(JcrConstants.JCR_MIXINTYPES) || canReadMixinTypes(tree)) {
- mixinNames = TreeUtil.getNames(tree, JcrConstants.JCR_MIXINTYPES).iterator();
- } else if (tree.getStatus() != Status.NEW) {
- // OAK-2441: for backwards compatibility with Jackrabbit 2.x try to
- // read the primary type from the underlying node state.
- mixinNames = TreeUtil.getNames(
- RootFactory.createReadOnlyRoot(sessionDelegate.getRoot()).getTree(tree.getPath()),
- JcrConstants.JCR_MIXINTYPES).iterator();
+ return TreeUtil.getMixinTypeNames(tree).iterator();
+ } else {
+ return TreeUtil.getMixinTypeNames(tree, getReadOnlyTree(tree)).iterator();
}
- return mixinNames;
}
+
+ @NotNull
+ private LazyValue getReadOnlyTree(@NotNull Tree tree) {
+ return new LazyValue() {
+ @Override
+ protected Tree createValue() {
+ return RootFactory.createReadOnlyRoot(sessionDelegate.getRoot()).getTree(tree.getPath());
+ }
+ };
+ }
private boolean canReadMixinTypes(@NotNull Tree tree) throws RepositoryException {
// OAK-7652: use an zero length MVP to check read permission on jcr:mixinTypes
Index: oak-security-spi/src/main/java/org/apache/jackrabbit/oak/plugins/tree/package-info.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-security-spi/src/main/java/org/apache/jackrabbit/oak/plugins/tree/package-info.java (revision 1851744)
+++ oak-security-spi/src/main/java/org/apache/jackrabbit/oak/plugins/tree/package-info.java (date 1548157809000)
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@Version("3.1.0")
+@Version("3.2.0")
package org.apache.jackrabbit.oak.plugins.tree;
import org.osgi.annotation.versioning.Version;
Index: oak-security-spi/src/main/java/org/apache/jackrabbit/oak/plugins/tree/TreeUtil.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-security-spi/src/main/java/org/apache/jackrabbit/oak/plugins/tree/TreeUtil.java (revision 1851744)
+++ oak-security-spi/src/main/java/org/apache/jackrabbit/oak/plugins/tree/TreeUtil.java (date 1548157445000)
@@ -34,6 +34,7 @@
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.LazyValue;
import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.commons.UUIDUtils;
import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
@@ -87,6 +88,37 @@
return getStringInternal(tree, JcrConstants.JCR_PRIMARYTYPE, Type.NAME);
}
+ @Nullable
+ public static String getPrimaryTypeName(@NotNull Tree tree, @NotNull LazyValue readOnlyTree) {
+ String primaryTypeName = null;
+ if (tree.hasProperty(JcrConstants.JCR_PRIMARYTYPE)) {
+ primaryTypeName = TreeUtil.getPrimaryTypeName(tree);
+ } else if (tree.getStatus() != Tree.Status.NEW) {
+ // OAK-2441: for backwards compatibility with Jackrabbit 2.x try to
+ // read the primary type from the underlying node state.
+ primaryTypeName = TreeUtil.getPrimaryTypeName(readOnlyTree.get());
+ }
+ return primaryTypeName;
+ }
+
+ @NotNull
+ public static Iterable getMixinTypeNames(@NotNull Tree tree) {
+ return TreeUtil.getNames(tree, JcrConstants.JCR_MIXINTYPES);
+ }
+
+ @NotNull
+ public static Iterable getMixinTypeNames(@NotNull Tree tree, @NotNull LazyValue readOnlyTree) {
+ Iterable mixinNames = emptyList();
+ if (tree.hasProperty(JcrConstants.JCR_MIXINTYPES)) {
+ mixinNames = getMixinTypeNames(tree);
+ } else if (tree.getStatus() != Tree.Status.NEW) {
+ // OAK-2441: for backwards compatibility with Jackrabbit 2.x try to
+ // read the primary type from the underlying node state.
+ mixinNames = TreeUtil.getNames(readOnlyTree.get(), JcrConstants.JCR_MIXINTYPES);
+ }
+ return mixinNames;
+ }
+
@Nullable
public static Iterable getStrings(@NotNull Tree tree, @NotNull String propertyName) {
PropertyState property = tree.getProperty(propertyName);