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 2019/02/01 16:33:35 UTC

svn commit: r1852758 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/tree/impl/ oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/eva...

Author: angela
Date: Fri Feb  1 16:33:35 2019
New Revision: 1852758

URL: http://svn.apache.org/viewvc?rev=1852758&view=rev
Log:
OAK-7997 : Adding restrictions to ACLs yields empty results for queries in Jackrabbit Oak

Added:
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/AbstractQueryTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/IndexedQueryMixinTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/IndexedQueryTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/TraversingQueryMixinTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/TraversingQueryTest.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/tree/impl/TreeUtilTest.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/NodeImpl.java
    jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/plugins/tree/TreeUtil.java
    jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/plugins/tree/package-info.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java?rev=1852758&r1=1852757&r2=1852758&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java Fri Feb  1 16:33:35 2019
@@ -20,11 +20,7 @@ package org.apache.jackrabbit.oak.query.
 
 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;
@@ -36,8 +32,11 @@ import org.apache.jackrabbit.oak.api.Pro
 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;
@@ -171,6 +170,8 @@ public class SelectorImpl extends Source
     private String planIndexName;
     private TimerStats timerDuration;
 
+    private LazyValue<Tree> lastReadOnlyTree;
+
     public SelectorImpl(NodeTypeInfo nodeTypeInfo, String selectorName) {
         this.nodeTypeInfo = checkNotNull(nodeTypeInfo);
         this.selectorName = checkNotNull(selectorName);
@@ -566,24 +567,20 @@ public class SelectorImpl extends Source
     }
 
     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;
-            }
+        LazyValue<Tree> readOnly = getReadOnlyTree(path);
+        String primaryTypeName = TreeUtil.getPrimaryTypeName(tree, readOnly);
+        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, readOnly)) {
+            if (mixinTypes.contains(mixinName)) {
+                return true;
             }
         }
         // no matches found
@@ -622,10 +619,23 @@ public class SelectorImpl extends Source
         if (lastPath == null || !path.equals(lastPath)) {
             lastTree = query.getTree(path);
             lastPath = path;
+            lastReadOnlyTree = null;
         }
         return lastTree;
     }
 
+    private LazyValue<Tree> getReadOnlyTree(@NotNull String path) {
+        if (lastReadOnlyTree == null) {
+            lastReadOnlyTree = new LazyValue<Tree>() {
+                @Override
+                protected Tree createValue() {
+                    return new ImmutableRoot(query.getExecutionContext().getBaseState()).getTree(path);
+                }
+            };
+        }
+        return lastReadOnlyTree;
+    }
+
     /**
      * The value for the given selector for the current node.
      * 

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/tree/impl/TreeUtilTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/tree/impl/TreeUtilTest.java?rev=1852758&r1=1852757&r2=1852758&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/tree/impl/TreeUtilTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/tree/impl/TreeUtilTest.java Fri Feb  1 16:33:35 2019
@@ -16,17 +16,27 @@
  */
 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.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 +75,133 @@ public class TreeUtilTest extends Abstra
         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<Tree>() {
+            @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<Tree>() {
+                @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<Tree>() {
+            @Override
+            protected Tree createValue() {
+                return testTree;
+            }
+        }));
+    }
+
+    @Test
+    public void testGetMixinTypeNamesMissingAccessible() {
+        assertTrue(Iterables.isEmpty(TreeUtil.getMixinTypeNames(root.getTree("/"))));
+        assertTrue(Iterables.isEmpty(TreeUtil.getMixinTypeNames(root.getTree("/"), new LazyValue<Tree>() {
+            @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<Tree>() {
+                @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<String> expected = TreeUtil.getMixinTypeNames(root.getTree(path));
+        assertTrue(Iterables.contains(expected, "mix:title"));
+
+        assertTrue(Iterables.elementsEqual(expected, TreeUtil.getMixinTypeNames(testTree, new LazyValue<Tree>() {
+            @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<String> 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<Tree>() {
+                @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<Tree>() {
+            @Override
+            protected Tree createValue() {
+                return testTree;
+            }
+        })));
+    }
 }
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/AbstractQueryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/AbstractQueryTest.java?rev=1852758&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/AbstractQueryTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/AbstractQueryTest.java Fri Feb  1 16:33:35 2019
@@ -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<String, Value[]> 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<String, Value> 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<String> 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<String, Value> 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<String> 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<String> 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

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/IndexedQueryMixinTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/IndexedQueryMixinTest.java?rev=1852758&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/IndexedQueryMixinTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/IndexedQueryMixinTest.java Fri Feb  1 16:33:35 2019
@@ -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

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/IndexedQueryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/IndexedQueryTest.java?rev=1852758&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/IndexedQueryTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/IndexedQueryTest.java Fri Feb  1 16:33:35 2019
@@ -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

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/TraversingQueryMixinTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/TraversingQueryMixinTest.java?rev=1852758&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/TraversingQueryMixinTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/TraversingQueryMixinTest.java Fri Feb  1 16:33:35 2019
@@ -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

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/TraversingQueryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/TraversingQueryTest.java?rev=1852758&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/TraversingQueryTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/TraversingQueryTest.java Fri Feb  1 16:33:35 2019
@@ -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

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/NodeImpl.java?rev=1852758&r1=1852757&r2=1852758&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/NodeImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/NodeImpl.java Fri Feb  1 16:33:35 2019
@@ -79,6 +79,7 @@ import org.apache.jackrabbit.oak.api.Pro
 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,30 +1282,26 @@ public class NodeImpl<T extends NodeDele
     //------------------------------------------------------------< 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<String> getMixinTypeNames(@NotNull Tree tree) throws RepositoryException {
-        Iterator<String> 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<Tree> getReadOnlyTree(@NotNull Tree tree) {
+        return new LazyValue<Tree>() {
+            @Override
+            protected Tree createValue() {
+                return RootFactory.createReadOnlyRoot(sessionDelegate.getRoot()).getTree(tree.getPath());
+            }
+        };
     }
 
     private boolean canReadMixinTypes(@NotNull Tree tree) throws RepositoryException {

Modified: jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/plugins/tree/TreeUtil.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/plugins/tree/TreeUtil.java?rev=1852758&r1=1852757&r2=1852758&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/plugins/tree/TreeUtil.java (original)
+++ jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/plugins/tree/TreeUtil.java Fri Feb  1 16:33:35 2019
@@ -34,6 +34,7 @@ import org.apache.jackrabbit.JcrConstant
 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;
@@ -88,6 +89,37 @@ public final class TreeUtil {
     }
 
     @Nullable
+    public static String getPrimaryTypeName(@NotNull Tree tree, @NotNull LazyValue<Tree> 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<String> getMixinTypeNames(@NotNull Tree tree) {
+        return TreeUtil.getNames(tree, JcrConstants.JCR_MIXINTYPES);
+    }
+
+    @NotNull
+    public static Iterable<String> getMixinTypeNames(@NotNull Tree tree, @NotNull LazyValue<Tree> readOnlyTree) {
+        Iterable<String> 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<String> getStrings(@NotNull Tree tree, @NotNull String propertyName) {
         PropertyState property = tree.getProperty(propertyName);
         if (property == null) {

Modified: jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/plugins/tree/package-info.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/plugins/tree/package-info.java?rev=1852758&r1=1852757&r2=1852758&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/plugins/tree/package-info.java (original)
+++ jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/plugins/tree/package-info.java Fri Feb  1 16:33:35 2019
@@ -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;