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 2013/03/28 11:07:56 UTC

svn commit: r1462015 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/security/authorization/permission/ test/java/org/apache/jackrabbit/oak/security/authorization/permission/

Author: angela
Date: Thu Mar 28 10:07:56 2013
New Revision: 1462015

URL: http://svn.apache.org/r1462015
Log:
OAK-527: permissions (wip)

Added:
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImplTest.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImpl.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java?rev=1462015&r1=1462014&r2=1462015&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java Thu Mar 28 10:07:56 2013
@@ -59,8 +59,7 @@ class CompiledPermissionImpl implements 
 
     private PrivilegeBitsProvider bitsProvider;
     private Map<Key, PermissionEntry> repoEntries;
-    private Map<Key, PermissionEntry> userEntries;
-    private Map<Key, PermissionEntry> groupEntries;
+    private Map<Key, PermissionEntry> entries;
 
     CompiledPermissionImpl(@Nonnull Set<Principal> principals,
                            @Nonnull ImmutableTree permissionsTree,
@@ -107,11 +106,12 @@ class CompiledPermissionImpl implements 
     //------------------------------------------------< CompiledPermissions >---
     @Override
     public ReadStatus getReadStatus(@Nonnull Tree tree, @Nullable PropertyState property) {
-        // FIXME
         long permission = (property == null) ? Permissions.READ_NODE : Permissions.READ_PROPERTY;
         for (PermissionEntry entry : filterEntries(tree, property)) {
-            if (entry.privilegeBits.includesRead(permission)) {
-                return ReadStatus.ALLOW_THIS;
+            if (entry.readStatus != null) {
+                return entry.readStatus;
+            } else if (entry.privilegeBits.includesRead(permission)) {
+                return (entry.isAllow) ? ReadStatus.ALLOW_THIS : ReadStatus.DENY_THIS;
             }
         }
         return ReadStatus.DENY_THIS;
@@ -152,8 +152,7 @@ class CompiledPermissionImpl implements 
     private void buildEntries(@Nullable ImmutableTree permissionsTree) {
         if (permissionsTree == null) {
             repoEntries = Collections.emptyMap();
-            userEntries = Collections.emptyMap();
-            groupEntries = Collections.emptyMap();
+            entries = Collections.emptyMap();
         } else {
             EntriesBuilder builder = new EntriesBuilder();
             for (Principal principal : principals) {
@@ -164,15 +163,18 @@ class CompiledPermissionImpl implements 
                 }
             }
             repoEntries = builder.repoEntries.build();
-            userEntries = builder.userEntries.build();
-            groupEntries = builder.groupEntries.build();
+            entries = builder.entries.build();
+            buildReadStatus();
         }
     }
 
+    private void buildReadStatus() {
+        // TODO
+    }
+
     private Iterable<PermissionEntry> filterEntries(final @Nonnull Tree tree,
                                                     final @Nullable PropertyState property) {
-        return Iterables.filter(
-                Iterables.concat(userEntries.values(), groupEntries.values()),
+        return Iterables.filter(entries.values(),
                 new Predicate<PermissionEntry>() {
                     @Override
                     public boolean apply(@Nullable PermissionEntry entry) {
@@ -199,36 +201,41 @@ class CompiledPermissionImpl implements 
         private final String path;
         private final int depth;
         private final long index;
+        private boolean isGroupEntry;
 
-        private Key(Tree tree) {
+        private Key(Tree tree, boolean isGroupEntry) {
             path = Strings.emptyToNull(TreeUtil.getString(tree, REP_ACCESS_CONTROLLED_PATH));
             depth = (path == null) ? 0 : PathUtils.getDepth(path);
             index = checkNotNull(tree.getProperty(REP_INDEX).getValue(Type.LONG)).longValue();
+            this.isGroupEntry = isGroupEntry;
         }
 
         @Override
         public int compareTo(Key key) {
             checkNotNull(key);
+            if (isGroupEntry != key.isGroupEntry) {
+                return (isGroupEntry) ? 1 : -1;
+            }
             if (Objects.equal(path, key.path)) {
                 if (index == key.index) {
                     return 0;
-                } else if (index < key.index) {
-                    return -1;
-                } else {
+                } else if (index <                                                                                                                                                                                 key.index) {
                     return 1;
+                } else {
+                    return -1;
                 }
             } else {
                 if (depth == key.depth) {
                     return path.compareTo(key.path);
                 } else {
-                    return (depth < key.depth) ? -1 : 1;
+                    return (depth < key.depth) ? 1 : -1;
                 }
             }
         }
 
         @Override
         public int hashCode() {
-            return Objects.hashCode(path, index);
+            return Objects.hashCode(path, index, isGroupEntry);
         }
 
         @Override
@@ -238,7 +245,9 @@ class CompiledPermissionImpl implements 
             }
             if (o instanceof Key) {
                 Key other = (Key) o;
-                return index == other.index && Objects.equal(path, other.path);
+                return index == other.index
+                        &&  isGroupEntry == other.isGroupEntry
+                        && Objects.equal(path, other.path);
             }
             return false;
         }
@@ -251,10 +260,12 @@ class CompiledPermissionImpl implements 
         private final String path;
         private final RestrictionPattern restriction;
 
+        private ReadStatus readStatus;
+
         private PermissionEntry(String accessControlledPath, Tree entryTree, RestrictionProvider restrictionsProvider) {
             isAllow = (PREFIX_ALLOW == entryTree.getName().charAt(0));
             privilegeBits = PrivilegeBits.getInstance(entryTree.getProperty(REP_PRIVILEGE_BITS));
-            this.path = accessControlledPath;
+            path = accessControlledPath;
             restriction = restrictionsProvider.getPattern(accessControlledPath, entryTree);
         }
 
@@ -275,24 +286,20 @@ class CompiledPermissionImpl implements 
     private static final class EntriesBuilder {
 
         private ImmutableSortedMap.Builder<Key, PermissionEntry> repoEntries = ImmutableSortedMap.naturalOrder();
-        private ImmutableSortedMap.Builder<Key, PermissionEntry> userEntries = ImmutableSortedMap.naturalOrder();
-        private ImmutableSortedMap.Builder<Key, PermissionEntry> groupEntries = ImmutableSortedMap.naturalOrder();
+        private ImmutableSortedMap.Builder<Key, PermissionEntry> entries = ImmutableSortedMap.naturalOrder();
 
         private void addEntries(@Nonnull Principal principal,
                               @Nonnull Tree principalRoot,
                               @Nonnull RestrictionProvider restrictionProvider) {
+            boolean isGroupEntry = (principal instanceof Group);
             for (Tree entryTree : principalRoot.getChildren()) {
-                Key key = new Key(entryTree);
+                Key key = new Key(entryTree, isGroupEntry);
                 PermissionEntry entry = new PermissionEntry(key.path, entryTree, restrictionProvider);
                 if (!entry.privilegeBits.isEmpty()) {
                     if (key.path == null) {
                         repoEntries.put(key, entry);
                     } else {
-                        if (principal instanceof Group) {
-                            groupEntries.put(key, entry);
-                        } else {
-                            userEntries.put(key, entry);
-                        }
+                        entries.put(key, entry);
                     }
                 }
             }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImpl.java?rev=1462015&r1=1462014&r2=1462015&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImpl.java Thu Mar 28 10:07:56 2013
@@ -22,7 +22,6 @@ import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
-import com.google.common.base.Strings;
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Root;
@@ -110,8 +109,8 @@ public class PermissionProviderImpl impl
         } else if (isAccessControlContent(tree) && canReadAccessControlContent(tree, property)) {
             // TODO: review if read-ac permission is never fine-granular
             return ReadStatus.ALLOW_ALL;
-        } else if (isVersionContent(tree) && canReadVersionContent(tree, property)) {
-            return ReadStatus.ALLOW_THIS;
+        } else if (isVersionContent(tree)) {
+            return getVersionContentReadStatus(tree, property);
         } else {
             return compiledPermissions.getReadStatus(tree, property);
         }
@@ -125,8 +124,16 @@ public class PermissionProviderImpl impl
     @Override
     public boolean isGranted(@Nonnull Tree tree, @Nullable PropertyState property, long permissions) {
         if (isVersionContent(tree)) {
-            String path = getVersionablePath(tree, property);
-            return path != null && compiledPermissions.isGranted(path, permissions);
+            TreeLocation location = getVersionableLocation(tree, property);
+            if (location == null) {
+                return false;
+            }
+            Tree versionableTree = (property == null) ? location.getTree() : location.getParent().getTree();
+            if (versionableTree != null) {
+                return compiledPermissions.isGranted(versionableTree, property, permissions);
+            } else {
+                return compiledPermissions.isGranted(location.getPath(), permissions);
+            }
         } else {
             return compiledPermissions.isGranted(tree, property, permissions);
         }
@@ -199,18 +206,29 @@ public class PermissionProviderImpl impl
         return ImmutableTree.TypeProvider.TYPE_VERSION == ImmutableTree.getType(tree);
     }
 
-    private boolean canReadVersionContent(@Nonnull Tree versionStoreTree, @Nullable PropertyState property) {
-        String versionablePath = getVersionablePath(versionStoreTree, property);
-        if (versionablePath != null) {
-            long permission = (property == null) ? Permissions.READ_NODE : Permissions.READ_PROPERTY;
-            return compiledPermissions.isGranted(versionablePath, permission);
+    private ReadStatus getVersionContentReadStatus(@Nonnull Tree versionStoreTree, @Nullable PropertyState property) {
+        TreeLocation location = getVersionableLocation(versionStoreTree, property);
+        ReadStatus status;
+        if (location != null) {
+            Tree tree = (property == null) ? location.getTree() : location.getParent().getTree();
+            if (tree == null) {
+                long permission = (property == null) ? Permissions.READ_NODE : Permissions.READ_PROPERTY;
+                if (compiledPermissions.isGranted(location.getPath(), permission)) {
+                    status = ReadStatus.ALLOW_THIS;
+                } else {
+                    status = ReadStatus.DENY_THIS;
+                }
+            } else {
+                status = compiledPermissions.getReadStatus(tree, property);
+            }
         } else {
-            return false;
+            status = ReadStatus.DENY_THIS;
         }
+        return status;
     }
 
     @CheckForNull
-    private String getVersionablePath(@Nonnull Tree versionStoreTree, @Nullable PropertyState property) {
+    private TreeLocation getVersionableLocation(@Nonnull Tree versionStoreTree, @Nullable PropertyState property) {
         String relPath = "";
         String propName = (property == null) ? "" : property.getName();
         String versionablePath = null;
@@ -232,7 +250,9 @@ public class PermissionProviderImpl impl
 
         if (versionablePath == null || versionablePath.length() == 0) {
             log.warn("Unable to determine path of the version controlled node.");
+            return null;
+        } else {
+            return getImmutableRoot().getLocation(versionablePath);
         }
-        return Strings.emptyToNull(versionablePath);
     }
 }

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImplTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImplTest.java?rev=1462015&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImplTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImplTest.java Thu Mar 28 10:07:56 2013
@@ -0,0 +1,350 @@
+/*
+ * 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.permission;
+
+import java.security.Principal;
+import java.security.acl.Group;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Nonnull;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import org.apache.jackrabbit.oak.AbstractSecurityTest;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.core.ImmutableRoot;
+import org.apache.jackrabbit.oak.core.ImmutableTree;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.security.SecurityProviderImpl;
+import org.apache.jackrabbit.oak.security.authorization.restriction.RestrictionProviderImpl;
+import org.apache.jackrabbit.oak.security.principal.PrincipalImpl;
+import org.apache.jackrabbit.oak.security.privilege.PrivilegeBits;
+import org.apache.jackrabbit.oak.security.privilege.PrivilegeBitsProvider;
+import org.apache.jackrabbit.oak.security.privilege.PrivilegeConstants;
+import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
+import org.apache.jackrabbit.oak.spi.security.authorization.AccessControlConfiguration;
+import org.apache.jackrabbit.oak.spi.security.authorization.OpenAccessControlConfiguration;
+import org.apache.jackrabbit.oak.spi.security.authorization.permission.ReadStatus;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.Restriction;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider;
+import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
+import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.apache.jackrabbit.oak.util.NodeUtil;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED;
+import static org.junit.Assert.assertSame;
+
+/**
+ * CompiledPermissionImplTest... TODO
+ */
+@Ignore("work in progress")
+public class CompiledPermissionImplTest extends AbstractSecurityTest implements PermissionConstants {
+
+    private Principal userPrincipal;
+    private Principal group1;
+    private Principal group2;
+    private Principal group3;
+
+    private PrivilegeBitsProvider pbp;
+    private RestrictionProvider rp;
+
+    private String node1Path = "/nodeName1";
+    private String node2Path = node1Path + "/nodeName2";
+
+    private List<String> allPaths;
+    private List<String> rootAndUsers;
+    private List<String> nodePaths;
+
+    @Before
+    @Override
+    public void before() throws Exception {
+        super.before();
+
+        userPrincipal = new PrincipalImpl("test");
+        group1 = EveryonePrincipal.getInstance();
+        group2 = new GroupImpl("group2");
+        group3 = new GroupImpl("group3");
+
+        pbp = new PrivilegeBitsProvider(root);
+        rp = new RestrictionProviderImpl(NamePathMapper.DEFAULT);
+
+        NodeUtil rootNode = new NodeUtil(root.getTree("/"));
+        NodeUtil system = rootNode.getChild("jcr:system");
+        NodeUtil perms = system.addChild(REP_PERMISSION_STORE, NT_REP_PERMISSION_STORE);
+        perms.addChild(userPrincipal.getName(), NT_REP_PERMISSION_STORE);
+        perms.addChild(group1.getName(), NT_REP_PERMISSION_STORE);
+        perms.addChild(group2.getName(), NT_REP_PERMISSION_STORE);
+        perms.addChild(group3.getName(), NT_REP_PERMISSION_STORE);
+        NodeUtil testNode = rootNode.addChild("nodeName1", NT_UNSTRUCTURED);
+        testNode.setString("propName1", "strValue");
+        NodeUtil testNode2 = testNode.addChild("nodeName2", NT_UNSTRUCTURED);
+        testNode2.setString("propName2", "strValue");
+        root.commit();
+
+        allPaths = ImmutableList.of("/").of(UserConstants.DEFAULT_USER_PATH).of(node1Path).of(node2Path);
+        rootAndUsers = ImmutableList.of("/").of(UserConstants.DEFAULT_USER_PATH);
+        nodePaths = ImmutableList.of(node1Path).of(node2Path);
+    }
+
+    @Override
+    public void after() throws Exception {
+        root.getTree(PERMISSIONS_STORE_PATH).remove();
+        root.commit();
+
+        super.after();
+    }
+
+    @Override
+    protected SecurityProvider getSecurityProvider() {
+        return new SecurityProviderImpl() {
+            @Nonnull
+            @Override
+            public AccessControlConfiguration getAccessControlConfiguration() {
+                return new OpenAccessControlConfiguration();
+            }
+        };
+    }
+
+    @Test
+    public void testGetReadStatus() throws Exception {
+        setupPermission(userPrincipal, "/", true, 0, pbp.getBits(PrivilegeConstants.JCR_READ), Collections.<Restriction>emptySet());
+
+        CompiledPermissionImpl cp = createPermissions(ImmutableSet.of(userPrincipal));
+        assertReadStatus(ReadStatus.ALLOW_ALL, cp, allPaths);
+    }
+
+    @Test
+    public void testGetReadStatus1() throws Exception {
+        setupPermission(group1, node2Path, true, 0, pbp.getBits(PrivilegeConstants.JCR_READ), Collections.<Restriction>emptySet());
+
+        CompiledPermissionImpl cp = createPermissions(ImmutableSet.of(userPrincipal));
+
+        assertReadStatus(ReadStatus.DENY_THIS, cp, Collections.singletonList("/"));
+
+
+        List<String> treePaths = ImmutableList.of(node1Path).of(node1Path).of(UserConstants.DEFAULT_USER_PATH);
+        assertReadStatus(ReadStatus.ALLOW_ALL, cp, treePaths);
+    }
+
+    @Test
+    public void testGetReadStatus2() throws Exception {
+        setupPermission(userPrincipal, "/", true, 0, pbp.getBits(PrivilegeConstants.JCR_READ), Collections.<Restriction>emptySet());
+        setupPermission(group1, "/", false, 0, pbp.getBits(PrivilegeConstants.JCR_READ), Collections.<Restriction>emptySet());
+
+        CompiledPermissionImpl cp = createPermissions(ImmutableSet.of(userPrincipal));
+        assertReadStatus(ReadStatus.ALLOW_ALL, cp, allPaths);
+    }
+
+    @Test
+    public void testGetReadStatus3() throws Exception {
+        setupPermission(group1, "/", true, 0, pbp.getBits(PrivilegeConstants.JCR_READ), Collections.<Restriction>emptySet());
+        setupPermission(group2, "/", false, 1, pbp.getBits(PrivilegeConstants.JCR_READ), Collections.<Restriction>emptySet());
+
+        CompiledPermissionImpl cp = createPermissions(ImmutableSet.of(group1, group2));
+        assertReadStatus(ReadStatus.DENY_ALL, cp, allPaths);
+    }
+
+    @Test
+    public void testGetReadStatus4() throws Exception {
+        setupPermission(group1, "/", true, 0, pbp.getBits(PrivilegeConstants.JCR_READ), Collections.<Restriction>emptySet());
+        setupPermission(group2, node2Path, true, 1, pbp.getBits(PrivilegeConstants.JCR_READ), Collections.<Restriction>emptySet());
+
+        CompiledPermissionImpl cp = createPermissions(ImmutableSet.of(group1, group2));
+        assertReadStatus(ReadStatus.ALLOW_ALL, cp, allPaths);
+    }
+
+    @Test
+    public void testGetReadStatus5() throws Exception {
+        setupPermission(userPrincipal, "/", true, 0, pbp.getBits(PrivilegeConstants.JCR_READ), Collections.<Restriction>emptySet());
+        setupPermission(group2, node1Path, false, 1, pbp.getBits(PrivilegeConstants.JCR_READ), Collections.<Restriction>emptySet());
+
+        CompiledPermissionImpl cp = createPermissions(ImmutableSet.of(userPrincipal, group2));
+        assertReadStatus(ReadStatus.ALLOW_ALL, cp, allPaths);
+    }
+
+    @Test
+    public void testGetReadStatus6() throws Exception {
+        setupPermission(group2, "/", true, 0, pbp.getBits(PrivilegeConstants.JCR_READ), Collections.<Restriction>emptySet());
+        setupPermission(userPrincipal, node1Path, false, 0, pbp.getBits(PrivilegeConstants.JCR_READ), Collections.<Restriction>emptySet());
+
+        CompiledPermissionImpl cp = createPermissions(ImmutableSet.of(userPrincipal, group2));
+
+        assertReadStatus(ReadStatus.ALLOW_THIS, cp, rootAndUsers);
+        assertReadStatus(ReadStatus.DENY_ALL, cp, nodePaths);
+    }
+
+    @Test
+    public void testGetReadStatus7() throws Exception {
+        setupPermission(group2, "/", true, 0, pbp.getBits(PrivilegeConstants.REP_READ_PROPERTIES), Collections.<Restriction>emptySet());
+        setupPermission(userPrincipal, node1Path, true, 0, pbp.getBits(PrivilegeConstants.REP_READ_NODES), Collections.<Restriction>emptySet());
+
+        CompiledPermissionImpl cp = createPermissions(ImmutableSet.of(userPrincipal, group2));
+
+        assertReadStatus(ReadStatus.ALLOW_PROPERTIES, cp, rootAndUsers);
+        assertReadStatus(ReadStatus.ALLOW_ALL, cp, nodePaths);
+    }
+
+    @Test
+    public void testGetReadStatus8() throws Exception {
+        setupPermission(userPrincipal, "/", true, 0, pbp.getBits(PrivilegeConstants.REP_READ_PROPERTIES), Collections.<Restriction>emptySet());
+        setupPermission(group2, node1Path, true, 0, pbp.getBits(PrivilegeConstants.REP_READ_NODES), Collections.<Restriction>emptySet());
+
+        CompiledPermissionImpl cp = createPermissions(ImmutableSet.of(userPrincipal, group2));
+
+        assertReadStatus(ReadStatus.DENY_THIS, ReadStatus.ALLOW_PROPERTIES, cp, rootAndUsers);  // TODO
+        assertReadStatus(ReadStatus.ALLOW_ALL, cp, nodePaths); // TODO
+    }
+
+    @Test
+    public void testGetReadStatus9() throws Exception {
+        setupPermission(group2, "/", true, 0, pbp.getBits(PrivilegeConstants.REP_READ_PROPERTIES), Collections.<Restriction>emptySet());
+        setupPermission(group1, node1Path, true, 0, pbp.getBits(PrivilegeConstants.REP_READ_NODES), Collections.<Restriction>emptySet());
+
+        CompiledPermissionImpl cp = createPermissions(ImmutableSet.of(group1, group2));
+
+        assertReadStatus(ReadStatus.ALLOW_PROPERTIES, cp, rootAndUsers);
+        assertReadStatus(ReadStatus.ALLOW_ALL, cp, nodePaths);
+    }
+
+    @Test
+    public void testGetReadStatus10() throws Exception {
+        setupPermission(group2, "/", false, 0, pbp.getBits(PrivilegeConstants.JCR_READ), Collections.<Restriction>emptySet());
+        setupPermission(group1, node1Path, true, 0, pbp.getBits(PrivilegeConstants.REP_READ_NODES), Collections.<Restriction>emptySet());
+
+        CompiledPermissionImpl cp = createPermissions(ImmutableSet.of(group1, group2));
+
+        assertReadStatus(ReadStatus.DENY_THIS, cp, rootAndUsers);
+        assertReadStatus(ReadStatus.ALLOW_NODES, cp, nodePaths);
+    }
+
+    @Test
+    public void testGetReadStatus11() throws Exception {
+        setupPermission(group2, "/", false, 0, pbp.getBits(PrivilegeConstants.JCR_READ), Collections.<Restriction>emptySet());
+        setupPermission(group2, node1Path, false, 0, pbp.getBits(PrivilegeConstants.JCR_READ), Collections.<Restriction>emptySet());
+        setupPermission(group1, node2Path, true, 0, pbp.getBits(PrivilegeConstants.REP_READ_NODES), Collections.<Restriction>emptySet());
+
+        CompiledPermissionImpl cp = createPermissions(ImmutableSet.of(group1, group2));
+
+        List<String> treePaths = ImmutableList.of("/").of(UserConstants.DEFAULT_USER_PATH).of(node1Path);
+        assertReadStatus(ReadStatus.DENY_THIS, cp, treePaths);
+        assertReadStatus(ReadStatus.ALLOW_NODES, cp, Collections.singletonList(node2Path));
+    }
+
+    @Test
+    public void testGetReadStatus12() throws Exception {
+        setupPermission(group1, "/", true, 0, pbp.getBits(PrivilegeConstants.JCR_READ), Collections.<Restriction>emptySet());
+        setupPermission(group1, node1Path, false, 0, pbp.getBits(PrivilegeConstants.REP_READ_PROPERTIES), Collections.<Restriction>emptySet());
+        setupPermission(group1, node2Path, true, 0, pbp.getBits(PrivilegeConstants.REP_READ_NODES), Collections.<Restriction>emptySet());
+
+        CompiledPermissionImpl cp = createPermissions(ImmutableSet.of(group1));
+
+        assertReadStatus(ReadStatus.ALLOW_THIS, cp, rootAndUsers);
+        assertReadStatus(ReadStatus.ALLOW_NODES, cp, nodePaths);
+    }
+
+    @Test
+    public void testGetReadStatus13() throws Exception {
+        setupPermission(group1, "/", true, 0, pbp.getBits(PrivilegeConstants.JCR_READ), Collections.<Restriction>emptySet());
+        setupPermission(group1, node1Path, false, 0, pbp.getBits(PrivilegeConstants.REP_READ_PROPERTIES), Collections.<Restriction>emptySet());
+        setupPermission(group1, node2Path, true, 0, pbp.getBits(PrivilegeConstants.JCR_READ), Collections.<Restriction>emptySet());
+
+        CompiledPermissionImpl cp = createPermissions(ImmutableSet.of(group1));
+
+        assertReadStatus(ReadStatus.ALLOW_THIS, cp, rootAndUsers);
+        assertReadStatus(ReadStatus.ALLOW_NODES, cp, Collections.singletonList(node1Path));
+        assertReadStatus(ReadStatus.ALLOW_ALL, cp, nodePaths);
+    }
+
+    private CompiledPermissionImpl createPermissions(Set<Principal> principals) {
+        ImmutableTree permissionsTree = new ImmutableRoot(root, ImmutableTree.TypeProvider.EMPTY).getTree(PERMISSIONS_STORE_PATH);
+        return new CompiledPermissionImpl(principals, permissionsTree, pbp, rp);
+    }
+
+    private void setupPermission(Principal principal, String path, boolean isAllow,
+                                 int index, PrivilegeBits pb, Set<Restriction> restrictions) throws CommitFailedException {
+        String name = ((isAllow) ? PREFIX_ALLOW : PREFIX_DENY) + "-" + Objects.hashCode(path, principal, index, pb, isAllow, restrictions);
+        Tree principalRoot = root.getTree(PERMISSIONS_STORE_PATH + '/' + principal.getName());
+        Tree entry = principalRoot.addChild(name);
+        entry.setProperty(JCR_PRIMARYTYPE, NT_REP_PERMISSIONS);
+        entry.setProperty(REP_ACCESS_CONTROLLED_PATH, path);
+        entry.setProperty(REP_INDEX, index);
+        entry.setProperty(pb.asPropertyState(REP_PRIVILEGE_BITS));
+        for (Restriction restriction : restrictions) {
+            entry.setProperty(restriction.getProperty());
+        }
+        root.commit();
+    }
+
+    private void assertReadStatus(ReadStatus expectedTrees,
+                                  CompiledPermissions cp,
+                                  List<String> treePaths) {
+        assertReadStatus(expectedTrees, expectedTrees, cp, treePaths);
+    }
+
+    private void assertReadStatus(ReadStatus expectedTrees,
+                                  ReadStatus expectedProperties,
+                                  CompiledPermissions cp,
+                                  List<String> treePaths) {
+        for (String path : treePaths) {
+            Tree node = root.getTree(path);
+            assertSame(expectedTrees, cp.getReadStatus(node, null));
+            assertSame(expectedProperties, cp.getReadStatus(node, node.getProperty(JCR_PRIMARYTYPE)));
+        }
+    }
+
+    private class GroupImpl implements Group {
+
+        private final String name;
+
+        private GroupImpl(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public boolean addMember(Principal principal) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean removeMember(Principal principal) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean isMember(Principal principal) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Enumeration<? extends Principal> members() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String getName() {
+            return name;
+        }
+    }
+}
\ No newline at end of file