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 2018/05/03 16:51:10 UTC

svn commit: r1830843 [2/2] - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/ oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/ oak-security-spi/src/main/jav...

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryProviderImplTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryProviderImplTest.java?rev=1830843&r1=1830842&r2=1830843&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryProviderImplTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryProviderImplTest.java Thu May  3 16:51:10 2018
@@ -19,24 +19,19 @@ package org.apache.jackrabbit.oak.securi
 import java.lang.reflect.Field;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Map;
 import java.util.Set;
-
+import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
-
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
 import org.junit.Test;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 public class PermissionEntryProviderImplTest {
 
@@ -50,7 +45,6 @@ public class PermissionEntryProviderImpl
     @Test
     public void testInitLongOverflow() throws Exception {
         MockPermissionStore store = new MockPermissionStore();
-        PermissionEntryCache cache = new MockPermissionEntryCache();
         Set<String> principalNames = ImmutableSet.of(GROUP_LONG_MAX);
 
         /*
@@ -60,13 +54,11 @@ public class PermissionEntryProviderImpl
         return Long.MAX_VALUE the cache should not be filled (-> the mock-cache
         implementation will fail.
         */
-        PermissionEntryProviderImpl provider = new PermissionEntryProviderImpl(store, cache, principalNames, ConfigurationParameters.EMPTY);
-        Field existingNamesField = provider.getClass().getDeclaredField("existingNames");
-        existingNamesField.setAccessible(true);
-
-        // test that PermissionEntryProviderImpl.existingNames nevertheless is
-        // properly filled with all principal names for which permission entries exist
-        assertEquals(principalNames, existingNamesField.get(provider));
+        PermissionEntryProviderImpl provider = new PermissionEntryProviderImpl(store, principalNames, ConfigurationParameters.EMPTY);
+
+        // test that PermissionEntryProviderImpl.noExistingNames nevertheless is
+        // properly set
+        assertFalse(getNoExistingNames(provider));
         assertNotSame(Collections.emptyIterator(), provider.getEntryIterator(new EntryPredicate()));
     }
 
@@ -76,7 +68,6 @@ public class PermissionEntryProviderImpl
     @Test
     public void testInitLongOverflow2() throws Exception {
         MockPermissionStore store = new MockPermissionStore();
-        PermissionEntryCache cache = new MockPermissionEntryCache();
         Set<String> principalNames = ImmutableSet.of(GROUP_LONG_MAX_MINUS_10, GROUP_50);
 
         /*
@@ -86,10 +77,9 @@ public class PermissionEntryProviderImpl
         entries must deal with the fact that the counter may become bigger that
         Long.MAX_VALUE
         */
-        PermissionEntryProviderImpl provider = new PermissionEntryProviderImpl(store, cache, principalNames, ConfigurationParameters.EMPTY);
-        Set<String> existingNames = getExistingNames(provider);
+        PermissionEntryProviderImpl provider = new PermissionEntryProviderImpl(store, principalNames, ConfigurationParameters.EMPTY);
+        assertFalse(getNoExistingNames(provider));
 
-        assertEquals(principalNames, existingNames);
         assertNotSame(Collections.emptyIterator(), provider.getEntryIterator(new EntryPredicate()));
     }
 
@@ -99,37 +89,23 @@ public class PermissionEntryProviderImpl
     @Test
     public void testExistingNamesAndLongOverFlow() throws Exception {
         MockPermissionStore store = new MockPermissionStore();
-        PermissionEntryCache cache = new MockPermissionEntryCache();
         Set<String> principalNames = Sets.newHashSet(GROUP_LONG_MAX_MINUS_10, GROUP_50, "noEntries");
 
         /*
-        same as before but principal-set contains a name for which not entries
-        exist -> the 'existingNames' set must properly reflect that
+        same as before but principal-set contains a name for which not entries exist
         */
-        PermissionEntryProviderImpl provider = new PermissionEntryProviderImpl(store, cache, principalNames, ConfigurationParameters.EMPTY);
-        Set<String> existingNames = getExistingNames(provider);
-        
-        assertFalse(principalNames.equals(existingNames));
-        assertEquals(2, existingNames.size());
-
-        principalNames.remove("noEntries");
-        assertEquals(principalNames, existingNames);
+        PermissionEntryProviderImpl provider = new PermissionEntryProviderImpl(store, principalNames, ConfigurationParameters.EMPTY);
+        assertFalse(getNoExistingNames(provider));
     }
 
     @Test
     public void testNoExistingName() throws Exception {
         MockPermissionStore store = new MockPermissionStore();
-        PermissionEntryCache cache = new MockPermissionEntryCache();
         Set<String> principalNames = Sets.newHashSet("noEntries", "noEntries2", "noEntries3");
 
-        PermissionEntryProviderImpl provider = new PermissionEntryProviderImpl(store, cache, principalNames, ConfigurationParameters.EMPTY);
-        Set<String> existingNames = getExistingNames(provider);
-
-        assertTrue(existingNames.isEmpty());
+        PermissionEntryProviderImpl provider = new PermissionEntryProviderImpl(store, principalNames, ConfigurationParameters.EMPTY);
 
-        Field pathMapField = provider.getClass().getDeclaredField("pathEntryMap");
-        pathMapField.setAccessible(true);
-        assertNull(pathMapField.get(provider));
+        assertTrue(getNoExistingNames(provider));
     }
 
     /**
@@ -141,18 +117,19 @@ public class PermissionEntryProviderImpl
      * @return the existingNames set.
      * @throws Exception
      */
-    private static Set<String> getExistingNames(@Nonnull PermissionEntryProviderImpl provider) throws Exception {
-        Field existingNamesField = provider.getClass().getDeclaredField("existingNames");
-        existingNamesField.setAccessible(true);
-        return (Set<String>) existingNamesField.get(provider);
+    private static boolean getNoExistingNames(@Nonnull PermissionEntryProviderImpl provider) throws Exception {
+        Field noExistingNamesField = provider.getClass().getDeclaredField("noExistingNames");
+        noExistingNamesField.setAccessible(true);
+        return (boolean) noExistingNamesField.get(provider);
     }
 
     // Inner Classes
     private class MockPermissionStore implements PermissionStore {
 
+        @CheckForNull
         @Override
         public Collection<PermissionEntry> load(
-                Collection<PermissionEntry> entries, @Nonnull String principalName,
+                @Nonnull String principalName,
                 @Nonnull String path) {
             return null;
         }
@@ -163,30 +140,25 @@ public class PermissionEntryProviderImpl
             return new PrincipalPermissionEntries();
         }
 
+        @Nonnull
         @Override
-        public long getNumEntries(@Nonnull String principalName, long max) {
+        public NumEntries getNumEntries(@Nonnull String principalName, long max) {
             long cnt = 0;
-            if (GROUP_LONG_MAX_MINUS_10.equals(principalName)) {
-                cnt = Long.MAX_VALUE - 10;
-            } else if (GROUP_50.equals(principalName)) {
-                cnt = 50;
-            } else if (GROUP_LONG_MAX.equals(principalName)) {
-                cnt = Long.MAX_VALUE;
+            switch (principalName) {
+                case GROUP_LONG_MAX_MINUS_10:
+                    cnt = Long.MAX_VALUE - 10;
+                    break;
+                case GROUP_50:
+                    cnt = 50;
+                    break;
+                case GROUP_LONG_MAX:
+                    cnt = Long.MAX_VALUE;
+                    break;
             }
-            return cnt;
+            return NumEntries.valueOf(cnt, true);
         }
 
         public void flush(@Nonnull Root root) {
         }
-
-    }
-
-    private class MockPermissionEntryCache extends PermissionEntryCache {
-        @Override
-        public void load(@Nonnull PermissionStore store,
-                @Nonnull Map<String, Collection<PermissionEntry>> pathEntryMap,
-                @Nonnull String principalName) {
-            fail("The number of  entries exceeds the max cache size");
-        }
     }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionHookTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionHookTest.java?rev=1830843&r1=1830842&r2=1830843&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionHookTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionHookTest.java Thu May  3 16:51:10 2018
@@ -21,13 +21,13 @@ import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
-
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 import javax.jcr.RepositoryException;
 import javax.jcr.security.AccessControlEntry;
 import javax.jcr.security.AccessControlManager;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import org.apache.jackrabbit.JcrConstants;
@@ -72,7 +72,7 @@ public class PermissionHookTest extends
 
     protected Principal testPrincipal;
     protected PrivilegeBitsProvider bitsProvider;
-    protected List<Principal> principals = new ArrayList<Principal>();
+    protected List<Principal> principals = new ArrayList<>();
 
     @Override
     @Before
@@ -418,8 +418,55 @@ public class PermissionHookTest extends
     }
 
     @Test
+    public void testNumPermissionsProperty() throws Exception {
+        Tree everyoneRoot = getPrincipalRoot(EveryonePrincipal.getInstance());
+        Tree testRoot = getPrincipalRoot(testPrincipal);
+
+        // initial state after setup
+        assertNumPermissionsProperty(1, everyoneRoot);
+        assertNumPermissionsProperty(1, testRoot);
+
+        // add another acl with an entry for everyone
+        addACE(childPath, EveryonePrincipal.getInstance(), JCR_READ);
+        root.commit();
+
+        assertNumPermissionsProperty(2, everyoneRoot);
+        assertNumPermissionsProperty(1, testRoot);
+
+        // adding another ACE at an existing ACL must not change num-permissions
+        AccessControlManager acMgr = getAccessControlManager(root);
+        JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(acMgr, childPath);
+        acl = AccessControlUtils.getAccessControlList(acMgr, childPath);
+        acl.addEntry(EveryonePrincipal.getInstance(), privilegesFromNames(JCR_READ), false, ImmutableMap.of(REP_GLOB, getValueFactory(root).createValue("/*/jcr:content")));
+        acMgr.setPolicy(childPath, acl);
+        root.commit();
+
+        assertNumPermissionsProperty(2, everyoneRoot);
+        assertNumPermissionsProperty(1, testRoot);
+
+        // remove policy at 'testPath'
+        acMgr.removePolicy(testPath, AccessControlUtils.getAccessControlList(acMgr, testPath));
+        root.commit();
+
+        assertNumPermissionsProperty(1, everyoneRoot);
+        assertNumPermissionsProperty(0, testRoot);
+
+        // remove all ACEs on the childPath policy -> same effect as policy removal on permission store
+        acl = AccessControlUtils.getAccessControlList(acMgr, childPath);
+        for (AccessControlEntry entry : acl.getAccessControlEntries()) {
+            acl.removeAccessControlEntry(entry);
+        }
+        acMgr.setPolicy(childPath, acl);
+        root.commit();
+
+        assertNumPermissionsProperty(0, everyoneRoot);
+        assertNumPermissionsProperty(0, testRoot);
+    }
+
+    @Test
     public void testCollisions() throws Exception {
         Tree testRoot = getPrincipalRoot(testPrincipal);
+        assertNumPermissionsProperty(1, testRoot);
 
         String aaPath = testPath + "/Aa";
         String bbPath = testPath + "/BB";
@@ -435,6 +482,7 @@ public class PermissionHookTest extends
                 root.commit();
 
                 assertEquals(2, testRoot.getChildrenCount(Long.MAX_VALUE));
+                assertNumPermissionsProperty(3, testRoot);
 
                 Set<String> accessControlledPaths = Sets.newHashSet(testPath, aa.getPath(), bb.getPath());
                 assertEquals(accessControlledPaths, getAccessControlledPaths(testRoot));
@@ -443,12 +491,16 @@ public class PermissionHookTest extends
                 root.getTree(bbPath).remove();
                 root.commit();
             }
+        } else {
+            fail();
         }
+
     }
 
     @Test
     public void testCollisionRemoval() throws Exception {
         Tree testRoot = getPrincipalRoot(testPrincipal);
+        assertNumPermissionsProperty(1, testRoot);
 
         String aaPath = testPath + "/Aa";
         String bbPath = testPath + "/BB";
@@ -469,12 +521,14 @@ public class PermissionHookTest extends
             assertTrue(testRoot.hasChild(bbPath.hashCode() + ""));
 
             assertEquals(Sets.newHashSet(testPath, bb.getPath()), getAccessControlledPaths(testRoot));
+            assertNumPermissionsProperty(2, testRoot);
         }
     }
 
     @Test
     public void testCollisionRemoval2() throws Exception {
         Tree testRoot = getPrincipalRoot(testPrincipal);
+        assertNumPermissionsProperty(1, testRoot);
 
         String aaPath = testPath + "/Aa";
         String bbPath = testPath + "/BB";
@@ -495,12 +549,14 @@ public class PermissionHookTest extends
             assertTrue(testRoot.hasChild(aaPath.hashCode() + ""));
 
             assertEquals(Sets.newHashSet(testPath, aa.getPath()), getAccessControlledPaths(testRoot));
+            assertNumPermissionsProperty(2, testRoot);
         }
     }
 
     @Test
     public void testCollisionRemoval3() throws Exception {
         Tree testRoot = getPrincipalRoot(testPrincipal);
+        assertNumPermissionsProperty(1, testRoot);
 
         String aaPath = testPath + "/Aa";
         String bbPath = testPath + "/BB";
@@ -523,6 +579,7 @@ public class PermissionHookTest extends
             assertFalse(testRoot.hasChild(bbPath.hashCode() + ""));
 
             assertEquals(Sets.newHashSet(testPath), getAccessControlledPaths(testRoot));
+            assertNumPermissionsProperty(1, testRoot);
         }
     }
 
@@ -553,6 +610,7 @@ public class PermissionHookTest extends
 
             assertEquals(2, testRoot.getChildrenCount(Long.MAX_VALUE));
             assertEquals(paths, getAccessControlledPaths(testRoot));
+            assertNumPermissionsProperty(paths.size(), testRoot);
 
             String toRemove = null;
             for (String path : paths) {
@@ -573,6 +631,7 @@ public class PermissionHookTest extends
             assertNotEquals(toRemove, getAccessControlledPath(testRoot.getChild(name)));
 
             assertEquals(paths, getAccessControlledPaths(testRoot));
+            assertNumPermissionsProperty(paths.size(), testRoot);
         }
     }
 
@@ -655,4 +714,10 @@ public class PermissionHookTest extends
         return (pathProp == null) ? null : pathProp.getValue(Type.STRING);
 
     }
+
+    private static void assertNumPermissionsProperty(long expectedValue, @Nonnull Tree parent) {
+        PropertyState p = parent.getProperty(REP_NUM_PERMISSIONS);
+        assertNotNull(p);
+        assertEquals(expectedValue, p.getValue(Type.LONG).longValue());
+    }
 }
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionStoreImplTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionStoreImplTest.java?rev=1830843&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionStoreImplTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionStoreImplTest.java Thu May  3 16:51:10 2018
@@ -0,0 +1,193 @@
+/*
+ * 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.lang.reflect.Method;
+import java.security.Principal;
+import java.util.Collection;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.jcr.RepositoryException;
+import javax.jcr.security.AccessControlManager;
+
+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.AbstractSecurityTest;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
+import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration;
+import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionConstants;
+import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+import org.apache.jackrabbit.oak.util.NodeUtil;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class PermissionStoreImplTest extends AbstractSecurityTest implements PermissionConstants {
+
+    private PermissionStoreImpl permissionStore;
+
+    private Principal testPrincipal;
+
+    private String testPath = "/testPath";
+    private String childPath = "/testPath/childNode";
+
+    @Before
+    public void before() throws Exception {
+        super.before();
+        testPrincipal = getTestUser().getPrincipal();
+
+        NodeUtil rootNode = new NodeUtil(root.getTree("/"), namePathMapper);
+        NodeUtil testNode = rootNode.addChild("testPath", JcrConstants.NT_UNSTRUCTURED);
+        testNode.addChild("childNode", JcrConstants.NT_UNSTRUCTURED);
+
+        addAcl(testPath, EveryonePrincipal.getInstance());
+        addAcl(childPath, EveryonePrincipal.getInstance());
+        root.commit();
+
+        permissionStore = new PermissionStoreImpl(root, root.getContentSession().getWorkspaceName(), getConfig(AuthorizationConfiguration.class).getRestrictionProvider());
+    }
+
+    private void addAcl(@Nonnull String path, @Nonnull Principal principal) throws RepositoryException {
+        AccessControlManager acMgr = getAccessControlManager(root);
+        JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(acMgr, path);
+        acl.addAccessControlEntry(EveryonePrincipal.getInstance(), privilegesFromNames(PrivilegeConstants.JCR_READ));
+        acMgr.setPolicy(path, acl);
+    }
+
+    @After
+    public void after() throws Exception {
+        try {
+            AccessControlManager acMgr = getAccessControlManager(root);
+            JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(acMgr, testPath);
+            acMgr.removePolicy(testPath, acl);
+            root.commit();
+        } finally {
+            super.after();
+        }
+    }
+
+    @Test
+    public void testLoad() {
+        PrincipalPermissionEntries entries = permissionStore.load(EveryonePrincipal.NAME);
+        assertNotNull(entries);
+        assertTrue(entries.isFullyLoaded());
+        assertEquals(2, entries.getSize());
+    }
+
+    @Test
+    public void testLoadMissingPrincipalRoot() {
+        PrincipalPermissionEntries entries = permissionStore.load(testPrincipal.getName());
+        assertNotNull(entries);
+        assertTrue(entries.isFullyLoaded());
+        assertEquals(0, entries.getSize());
+    }
+
+    @Test
+    public void testLoadWithNesting() throws Exception {
+        try {
+            Tree everyoneTree = getPermissionRoot(EveryonePrincipal.NAME);
+            everyoneTree.removeProperty(REP_NUM_PERMISSIONS);
+            for (Tree child : everyoneTree.getChildren()) {
+                if (child.hasProperty(REP_ACCESS_CONTROLLED_PATH)) {
+                    String name = child.getName();
+                    Tree collision = TreeUtil.addChild(child, "c_"+child.getName(), NT_REP_PERMISSION_STORE);
+                    collision.setProperty(REP_ACCESS_CONTROLLED_PATH, "/another/path");
+                    Tree entry = TreeUtil.addChild(collision, "1", NT_REP_PERMISSIONS);
+                    entry.setProperty(REP_PRIVILEGE_BITS, PermissionStore.DYNAMIC_ALL_BITS);
+                    entry.setProperty(REP_IS_ALLOW, false);
+                    break;
+                }
+            }
+
+            PrincipalPermissionEntries entries = permissionStore.load(EveryonePrincipal.NAME);
+            assertNotNull(entries);
+            assertTrue(entries.isFullyLoaded());
+            assertEquals(3, entries.getSize());
+        } finally {
+            root.refresh();
+        }
+    }
+
+    @Test
+    public void testLoadByPath() {
+        Collection<PermissionEntry> entries = permissionStore.load(EveryonePrincipal.NAME, testPath);
+        assertNotNull(entries);
+        assertFalse(entries.isEmpty());
+    }
+
+    @Test
+    public void testLoadByPathWithoutEntries() {
+        assertNull(permissionStore.load(EveryonePrincipal.NAME, testPath + "/notAccessControlled"));
+    }
+
+    @Test
+    public void testLoadByPathMissingPrincipalRoot() {
+        assertNull(permissionStore.load(testPrincipal.getName(), testPath));
+    }
+
+    @Test
+    public void testGetNumEntries() {
+        assertEquals(NumEntries.valueOf(2, true), permissionStore.getNumEntries(EveryonePrincipal.NAME, Long.MAX_VALUE));
+    }
+
+    @Test
+    public void testGetNumEntriesMissingPrincipalRoot() {
+        assertEquals(NumEntries.valueOf(0, true), permissionStore.getNumEntries(testPrincipal.getName(), Long.MAX_VALUE));
+    }
+
+    @Test
+    public void testGetNumEntriesMissingProperty() throws Exception {
+        try {
+            Tree everyoneTree = getPermissionRoot(EveryonePrincipal.NAME);
+            everyoneTree.removeProperty(REP_NUM_PERMISSIONS);
+
+            assertEquals(NumEntries.valueOf(2, false), permissionStore.getNumEntries(EveryonePrincipal.NAME, Long.MAX_VALUE));
+        } finally {
+            root.refresh();
+        }
+    }
+
+    @Test
+    public void testGetNumEntriesMissingPropertyThreshold() throws Exception {
+        try {
+            Tree everyoneTree = getPermissionRoot(EveryonePrincipal.NAME);
+            everyoneTree.removeProperty(REP_NUM_PERMISSIONS);
+
+            long max = 1;
+            assertEquals(NumEntries.valueOf(everyoneTree.getChildrenCount(max), false), permissionStore.getNumEntries(EveryonePrincipal.NAME, max));
+        } finally {
+            root.refresh();
+        }
+    }
+
+    @CheckForNull
+    private Tree getPermissionRoot(@Nonnull String principalName) throws Exception {
+        Method m = PermissionStoreImpl.class.getDeclaredMethod("getPrincipalRoot", String.class);
+        m.setAccessible(true);
+
+        return (Tree) m.invoke(permissionStore, principalName);
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionStoreImplTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PrincipalPermissionEntriesTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PrincipalPermissionEntriesTest.java?rev=1830843&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PrincipalPermissionEntriesTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PrincipalPermissionEntriesTest.java Thu May  3 16:51:10 2018
@@ -0,0 +1,166 @@
+/*
+ * 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.lang.reflect.Field;
+import java.util.Collection;
+import java.util.Map;
+import javax.annotation.Nonnull;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionPattern;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBits;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class PrincipalPermissionEntriesTest {
+
+    private final PermissionEntry permissionEntry = new PermissionEntry("/path", true, 0, PrivilegeBits.BUILT_IN.get(PrivilegeBits.JCR_READ), RestrictionPattern.EMPTY);
+
+    @Test
+    public void testExpectedSize() throws Exception {
+        assertEquals(Long.MAX_VALUE, inspectExpectedSize(new PrincipalPermissionEntries()));
+        assertEquals(1, inspectExpectedSize(new PrincipalPermissionEntries(1)));
+    }
+
+    @Test
+    public void testGetEntriesUponCreation() {
+        assertTrue(new PrincipalPermissionEntries(1).getEntries().isEmpty());
+        assertTrue(new PrincipalPermissionEntries().getEntries().isEmpty());
+    }
+
+    @Test
+    public void testGetEntriesByPathUponCreation() {
+        assertNull(new PrincipalPermissionEntries(1).getEntriesByPath("/path"));
+        assertNull(new PrincipalPermissionEntries().getEntriesByPath("/path"));
+    }
+
+    @Test
+    public void testIsFullyLoadedUponCreation() {
+        assertFalse(new PrincipalPermissionEntries(0).isFullyLoaded());
+        assertFalse(new PrincipalPermissionEntries(1).isFullyLoaded());
+        assertFalse(new PrincipalPermissionEntries().isFullyLoaded());
+    }
+
+    @Test
+    public void testSetFullyLoaded() {
+        PrincipalPermissionEntries ppe = new PrincipalPermissionEntries(1);
+        ppe.setFullyLoaded(true);
+
+        assertTrue(ppe.isFullyLoaded());
+    }
+
+    @Test
+    public void testSetFullyLoadedNoExpectedSize() {
+        PrincipalPermissionEntries ppe = new PrincipalPermissionEntries();
+        ppe.setFullyLoaded(true);
+
+        assertTrue(ppe.isFullyLoaded());
+    }
+
+    @Test
+    public void testPutAllEntriesSetsFullyLoadedIgnoresExpectedSize() {
+        PrincipalPermissionEntries ppe = new PrincipalPermissionEntries(1);
+        ppe.putAllEntries(ImmutableMap.of());
+
+        assertTrue(ppe.isFullyLoaded());
+    }
+
+    @Test
+    public void testPutAllEntriesSetsFullyLoaded() {
+        PrincipalPermissionEntries ppe = new PrincipalPermissionEntries(1);
+        ppe.putAllEntries(ImmutableMap.of("/path", ImmutableSet.of(permissionEntry)));
+        assertTrue(ppe.isFullyLoaded());
+    }
+
+    @Test
+    public void testPutAllEntriesWithoutExpectedSizeSetsFullyLoaded() {
+        PrincipalPermissionEntries ppe = new PrincipalPermissionEntries();
+        ppe.putAllEntries(ImmutableMap.of("/path", ImmutableSet.of(permissionEntry)));
+        assertTrue(ppe.isFullyLoaded());
+    }
+
+    @Test
+    public void testPutAllEntries() {
+        PrincipalPermissionEntries ppe = new PrincipalPermissionEntries();
+
+        Map<String, Collection<PermissionEntry>> allEntries = ImmutableMap.of("/path", ImmutableSet.of(permissionEntry));
+        ppe.putAllEntries(allEntries);
+
+        assertEquals(allEntries, ppe.getEntries());
+    }
+
+    @Test
+    public void testPutEntriesByPathSetsFullyLoaded() {
+        PrincipalPermissionEntries ppe = new PrincipalPermissionEntries(1);
+        ppe.putEntriesByPath("/path", ImmutableSet.of(permissionEntry));
+
+        assertTrue(ppe.isFullyLoaded());
+    }
+
+    @Test
+    public void testPutEntriesByPathExceedingExpectedSizeSetsFullyLoaded() {
+        PrincipalPermissionEntries ppe = new PrincipalPermissionEntries(1);
+        Collection<PermissionEntry> collection = ImmutableSet.of(permissionEntry);
+        ppe.putEntriesByPath("/path", collection);
+        ppe.putEntriesByPath("/path2", collection);
+
+        assertTrue(ppe.isFullyLoaded());
+    }
+
+    @Test
+    public void testPutEntriesByPathNotReachingExpectedSize() {
+        PrincipalPermissionEntries ppe = new PrincipalPermissionEntries(2);
+        ppe.putEntriesByPath("/path", ImmutableSet.of(permissionEntry));
+
+        assertFalse(ppe.isFullyLoaded());
+    }
+
+    @Test
+    public void testPutEntriesByPath() {
+        PrincipalPermissionEntries ppe = new PrincipalPermissionEntries(2);
+        ppe.putEntriesByPath("/path", ImmutableSet.of(permissionEntry));
+
+        assertEquals(1, ppe.getEntries().size());
+    }
+
+    @Test
+    public void testGetEntriesByPath() {
+        PrincipalPermissionEntries ppe = new PrincipalPermissionEntries(2);
+        Collection<PermissionEntry> collection = ImmutableSet.of(permissionEntry);
+        Map<String, Collection<PermissionEntry>> allEntries =
+                        ImmutableMap.of("/path", collection, "/path2", collection);
+
+        ppe.putAllEntries(allEntries);
+
+        assertEquals(collection, ppe.getEntriesByPath("/path"));
+        assertEquals(collection, ppe.getEntriesByPath("/path2"));
+        assertNull(ppe.getEntriesByPath("/nonExisting"));
+    }
+
+    private static final long inspectExpectedSize(@Nonnull PrincipalPermissionEntries ppe) throws Exception {
+        Field f = PrincipalPermissionEntries.class.getDeclaredField("expectedSize");
+        f.setAccessible(true);
+
+        return (long) f.get(ppe);
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PrincipalPermissionEntriesTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/PermissionConstants.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/PermissionConstants.java?rev=1830843&r1=1830842&r2=1830843&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/PermissionConstants.java (original)
+++ jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/PermissionConstants.java Thu May  3 16:51:10 2018
@@ -37,8 +37,9 @@ public interface PermissionConstants {
     String PERMISSIONS_STORE_PATH = '/' + JcrConstants.JCR_SYSTEM + '/' + REP_PERMISSION_STORE;
 
     String REP_ACCESS_CONTROLLED_PATH = "rep:accessControlledPath";
-	String REP_IS_ALLOW = "rep:isAllow";
+    String REP_IS_ALLOW = "rep:isAllow";
     String REP_PRIVILEGE_BITS = "rep:privileges";
+    String REP_NUM_PERMISSIONS = "rep:numPermissions";
 
     Set<String> PERMISSION_NODETYPE_NAMES = ImmutableSet.of(NT_REP_PERMISSIONS, NT_REP_PERMISSION_STORE);
     Set<String> PERMISSION_NODE_NAMES = ImmutableSet.of(REP_PERMISSION_STORE);

Modified: jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/package-info.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/package-info.java?rev=1830843&r1=1830842&r2=1830843&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/package-info.java (original)
+++ jackrabbit/oak/trunk/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/package-info.java Thu May  3 16:51:10 2018
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("4.0.0")
+@Version("4.1.0")
 package org.apache.jackrabbit.oak.spi.security.authorization.permission;
 
 import org.osgi.annotation.versioning.Version;