Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserConfigurationImpl.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserConfigurationImpl.java	(revision 1669376)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserConfigurationImpl.java	(revision )
@@ -23,6 +23,7 @@
 import java.util.Set;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -42,6 +43,7 @@
 import org.apache.jackrabbit.oak.spi.security.Context;
 import org.apache.jackrabbit.oak.spi.security.SecurityConfiguration;
 import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalProvider;
 import org.apache.jackrabbit.oak.spi.security.user.UserAuthenticationFactory;
 import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
 import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
@@ -181,5 +183,11 @@
         } else {
             return umgr;
         }
+    }
+
+    @Nullable
+    @Override
+    public PrincipalProvider getUserPrincipalProvider(@Nonnull Root root, @Nonnull NamePathMapper namePathMapper) {
+        return new UserPrincipalProvider(root, this, namePathMapper);
     }
 }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/AbstractGroupPrincipal.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/AbstractGroupPrincipal.java	(revision )
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/AbstractGroupPrincipal.java	(revision )
@@ -0,0 +1,43 @@
+/*
+ * 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.user;
+
+import java.security.Principal;
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+
+/**
+ * Base class for {@code Group} principals.
+ */
+abstract class AbstractGroupPrincipal extends TreeBasedPrincipal implements java.security.acl.Group {
+
+    AbstractGroupPrincipal(@Nonnull String principalName, @Nonnull Tree groupTree, @Nonnull NamePathMapper namePathMapper) {
+        super(principalName, groupTree, namePathMapper);
+    }
+
+    @Override
+    public boolean addMember(Principal principal) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean removeMember(Principal principal) {
+        throw new UnsupportedOperationException();
+    }
+}
\ No newline at end of file
Index: oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserPrincipalProviderTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserPrincipalProviderTest.java	(revision )
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserPrincipalProviderTest.java	(revision )
@@ -0,0 +1,195 @@
+/*
+ * 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.user;
+
+import java.security.Principal;
+import java.util.Set;
+import java.util.UUID;
+
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.spi.security.principal.AbstractPrincipalProviderTest;
+import org.apache.jackrabbit.oak.spi.security.principal.AdminPrincipal;
+import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalProvider;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+public class UserPrincipalProviderTest extends AbstractPrincipalProviderTest {
+
+    @Override
+    protected PrincipalProvider createPrincipalProvider() {
+        return new UserPrincipalProvider(root, getUserConfiguration(), namePathMapper);
+    }
+
+    @Test
+    public void testTreeBasedUserPrincipal() throws Exception {
+        User user = getTestUser();
+        Principal principal = principalProvider.getPrincipal(user.getPrincipal().getName());
+        assertTrue(principal instanceof TreeBasedPrincipal);
+    }
+
+    @Test
+    public void testTreeBasedSystemUserPrincipal() throws Exception {
+        User systemUser = getUserManager(root).createSystemUser("systemUser" + UUID.randomUUID(), null);
+        root.commit();
+
+        try {
+            Principal principal = principalProvider.getPrincipal(systemUser.getPrincipal().getName());
+            assertTrue(principal instanceof SystemUserPrincipalImpl);
+        } finally {
+            systemUser.remove();
+            root.commit();
+        }
+    }
+
+    @Test
+    public void testTreeBasedGroupPrincipal() throws Exception {
+        Group group = getUserManager(root).createGroup("testGroup" + UUID.randomUUID());
+        root.commit();
+
+        try {
+            Principal principal = principalProvider.getPrincipal(group.getPrincipal().getName());
+            assertTrue(principal instanceof AbstractGroupPrincipal);
+        } finally {
+            group.remove();
+            root.commit();
+        }
+    }
+
+    @Test
+    public void testGetPrincipalsForUser() throws Exception {
+        Group group = getUserManager(root).createGroup("testGroup" + UUID.randomUUID());
+        group.addMember(getTestUser());
+        root.commit();
+
+        try {
+            Set<? extends Principal> principals = principalProvider.getPrincipals(getTestUser().getID());
+            for (Principal p : principals) {
+                String name = p.getName();
+                if (name.equals(getTestUser().getPrincipal().getName())) {
+                    assertTrue(p instanceof TreeBasedPrincipal);
+                } else if (!EveryonePrincipal.NAME.equals(name)) {
+                    assertTrue(p instanceof AbstractGroupPrincipal);
+                }
+            }
+        } finally {
+            group.remove();
+            root.commit();
+        }
+    }
+
+    @Test
+    public void testGetPrincipalsForSystemUser() throws Exception {
+        User systemUser = getUserManager(root).createSystemUser("systemUser" + UUID.randomUUID(), null);
+        Group group = getUserManager(root).createGroup("testGroup" + UUID.randomUUID());
+        group.addMember(systemUser);
+        root.commit();
+
+        try {
+            Set<? extends Principal> principals = principalProvider.getPrincipals(systemUser.getID());
+            for (Principal p : principals) {
+                String name = p.getName();
+                if (name.equals(systemUser.getPrincipal().getName())) {
+                    assertTrue(p instanceof SystemUserPrincipalImpl);
+                } else if (!EveryonePrincipal.NAME.equals(name)) {
+                    assertTrue(p instanceof AbstractGroupPrincipal);
+                }
+            }
+        } finally {
+            systemUser.remove();
+            group.remove();
+            root.commit();
+        }
+    }
+
+    @Test
+    public void testGetPrincipalsForAdminUser() throws Exception {
+        Authorizable adminUser = getUserManager(root).getAuthorizable(adminSession.getAuthInfo().getUserID());
+        if (adminUser != null && adminUser.getPrincipal() instanceof AdminPrincipal) {
+            Set<? extends Principal> principals = principalProvider.getPrincipals(adminUser.getID());
+            for (Principal p : principals) {
+                String name = p.getName();
+                if (name.equals(adminUser.getPrincipal().getName())) {
+                    assertTrue(p instanceof AdminPrincipalImpl);
+                } else if (!EveryonePrincipal.NAME.equals(name)) {
+                    assertTrue(p instanceof AbstractGroupPrincipal);
+                }
+            }
+        }
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testEveryoneMembers() throws Exception {
+        Principal everyone = principalProvider.getPrincipal(EveryonePrincipal.NAME);
+        assertTrue(everyone instanceof EveryonePrincipal);
+
+        Group everyoneGroup = null;
+        try {
+            UserManager userMgr = getUserManager(root);
+            everyoneGroup = userMgr.createGroup(EveryonePrincipal.NAME);
+            root.commit();
+
+            Principal ep = principalProvider.getPrincipal(EveryonePrincipal.NAME);
+
+            assertTrue(ep instanceof java.security.acl.Group);
+            ((java.security.acl.Group) ep).members();
+            ((java.security.acl.Group) ep).isMember(getTestUser().getPrincipal());
+
+        } finally {
+            if (everyoneGroup != null) {
+                everyoneGroup.remove();
+                root.commit();
+            }
+        }
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testGroupMembers() throws Exception {
+        Group group = getUserManager(root).createGroup("testGroup" + UUID.randomUUID());
+        root.commit();
+
+        try {
+            Principal principal = principalProvider.getPrincipal(group.getPrincipal().getName());
+
+            assertTrue(principal instanceof java.security.acl.Group);
+            ((java.security.acl.Group) principal).members();
+        } finally {
+            group.remove();
+            root.commit();
+        }
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testGroupIsMember() throws Exception {
+        Group group = getUserManager(root).createGroup("testGroup" + UUID.randomUUID());
+        root.commit();
+
+        try {
+            Principal principal = principalProvider.getPrincipal(group.getPrincipal().getName());
+
+            assertTrue(principal instanceof java.security.acl.Group);
+            ((java.security.acl.Group) principal).isMember(getTestUser().getPrincipal());
+        } finally {
+            group.remove();
+            root.commit();
+        }
+    }
+}
\ No newline at end of file
Index: oak-core/src/test/java/org/apache/jackrabbit/oak/security/principal/PrincipalProviderImplTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/test/java/org/apache/jackrabbit/oak/security/principal/PrincipalProviderImplTest.java	(revision 1669376)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/principal/PrincipalProviderImplTest.java	(revision )
@@ -17,86 +17,29 @@
 package org.apache.jackrabbit.oak.security.principal;
 
 import java.security.Principal;
-import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 import com.google.common.collect.ImmutableSet;
 import org.apache.jackrabbit.api.security.principal.PrincipalManager;
 import org.apache.jackrabbit.api.security.user.Group;
-import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.api.security.user.UserManager;
-import org.apache.jackrabbit.oak.AbstractSecurityTest;
-import org.apache.jackrabbit.oak.spi.security.principal.AdminPrincipal;
+import org.apache.jackrabbit.oak.spi.security.principal.AbstractPrincipalProviderTest;
 import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
 import org.apache.jackrabbit.oak.spi.security.principal.PrincipalProvider;
 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.assertTrue;
 
-/**
- * PrincipalProviderImplTest...
- */
-public class PrincipalProviderImplTest extends AbstractSecurityTest {
+public class PrincipalProviderImplTest extends AbstractPrincipalProviderTest {
 
-    private PrincipalProvider principalProvider;
-
-    @Override
-    public void before() throws Exception {
-        super.before();
-
-        principalProvider = new PrincipalProviderImpl(root, getUserConfiguration(), namePathMapper);
+    protected PrincipalProvider createPrincipalProvider() {
+        return new PrincipalProviderImpl(root, getUserConfiguration(), namePathMapper);
     }
 
     @Test
-    public void testGetPrincipals() throws Exception {
-        String adminId = adminSession.getAuthInfo().getUserID();
-        Set<? extends Principal> principals = principalProvider.getPrincipals(adminId);
-
-        assertNotNull(principals);
-        assertFalse(principals.isEmpty());
-        assertTrue(principals.contains(EveryonePrincipal.getInstance()));
-
-        boolean containsAdminPrincipal = false;
-        for (Principal principal : principals) {
-            assertNotNull(principalProvider.getPrincipal(principal.getName()));
-            if (principal instanceof AdminPrincipal) {
-                containsAdminPrincipal = true;
-            }
-        }
-        assertTrue(containsAdminPrincipal);
-    }
-
-    @Test
-    public void testEveryone() throws Exception {
-        Principal everyone = principalProvider.getPrincipal(EveryonePrincipal.NAME);
-        assertTrue(everyone instanceof EveryonePrincipal);
-
-        Group everyoneGroup = null;
-        try {
-            UserManager userMgr = getUserManager(root);
-            everyoneGroup = userMgr.createGroup(EveryonePrincipal.NAME);
-            root.commit();
-
-            Principal ep = principalProvider.getPrincipal(EveryonePrincipal.NAME);
-            assertFalse(ep instanceof EveryonePrincipal);
-        } finally {
-            if (everyoneGroup != null) {
-                everyoneGroup.remove();
-                root.commit();
-            }
-        }
-    }
-
-    @Test
     public void testEveryoneMembers() throws Exception {
         Principal everyone = principalProvider.getPrincipal(EveryonePrincipal.NAME);
         assertTrue(everyone instanceof EveryonePrincipal);
@@ -124,153 +67,6 @@
             if (everyoneGroup != null) {
                 everyoneGroup.remove();
                 root.commit();
-            }
-        }
-    }
-
-    @Test
-    public void testFindUserPrincipal() throws Exception {
-        User testUser = null;
-        try {
-            UserManager userMgr = getUserManager(root);
-            testUser = userMgr.createUser("TestUser", "pw");
-            root.commit();
-
-            String principalName = testUser.getPrincipal().getName();
-            assertNotNull(principalProvider.getPrincipal(principalName));
-
-            List<String> nameHints = new ArrayList<String>();
-            nameHints.add("TestUser");
-            nameHints.add("Test");
-            nameHints.add("User");
-            nameHints.add("stUs");
-
-            assertResult(principalProvider, nameHints, principalName, PrincipalManager.SEARCH_TYPE_NOT_GROUP, true);
-            assertResult(principalProvider, nameHints, principalName, PrincipalManager.SEARCH_TYPE_ALL, true);
-            assertResult(principalProvider, nameHints, principalName, PrincipalManager.SEARCH_TYPE_GROUP, false);
-        } finally {
-            if (testUser != null) {
-                testUser.remove();
-                root.commit();
-            }
-        }
-    }
-
-    @Test
-    public void testFindGroupPrincipal() throws Exception {
-        Group testGroup = null;
-        try {
-            UserManager userMgr = getUserManager(root);
-            testGroup = userMgr.createGroup("TestGroup");
-            root.commit();
-
-            String principalName = testGroup.getPrincipal().getName();
-            assertNotNull(principalProvider.getPrincipal(principalName));
-
-            List<String> nameHints = new ArrayList<String>();
-            nameHints.add("TestGroup");
-            nameHints.add("Test");
-            nameHints.add("Group");
-            nameHints.add("stGr");
-
-            assertResult(principalProvider, nameHints, principalName, PrincipalManager.SEARCH_TYPE_GROUP, true);
-            assertResult(principalProvider, nameHints, principalName, PrincipalManager.SEARCH_TYPE_ALL, true);
-            assertResult(principalProvider, nameHints, principalName, PrincipalManager.SEARCH_TYPE_NOT_GROUP, false);
-        } finally {
-            if (testGroup != null) {
-                testGroup.remove();
-                root.commit();
-            }
-        }
-    }
-
-    @Test
-    public void testFindEveryone() {
-        assertNotNull(principalProvider.getPrincipal(EveryonePrincipal.NAME));
-
-        Map<Integer, Boolean> tests = new HashMap<Integer, Boolean>();
-        tests.put(PrincipalManager.SEARCH_TYPE_ALL, Boolean.TRUE);
-        tests.put(PrincipalManager.SEARCH_TYPE_GROUP, Boolean.TRUE);
-        tests.put(PrincipalManager.SEARCH_TYPE_NOT_GROUP, Boolean.FALSE);
-
-        for (Integer searchType : tests.keySet()) {
-            boolean found = false;
-            Iterator<? extends Principal> it = principalProvider.findPrincipals(EveryonePrincipal.NAME, searchType);
-            while (it.hasNext()) {
-                Principal p = it.next();
-                if (p.getName().equals(EveryonePrincipal.NAME)) {
-                    found = true;
-                }
-            }
-            Boolean expected = tests.get(searchType);
-            assertEquals(expected.booleanValue(), found);
-
-        }
-    }
-
-    @Test
-    public void testFindEveryoneHint() {
-        assertNotNull(principalProvider.getPrincipal(EveryonePrincipal.NAME));
-
-        List<String> nameHints = new ArrayList<String>();
-        nameHints.add("everyone");
-        nameHints.add("every");
-        nameHints.add("one");
-        nameHints.add("very");
-
-        assertResult(principalProvider, nameHints, EveryonePrincipal.NAME, PrincipalManager.SEARCH_TYPE_ALL, true);
-        assertResult(principalProvider, nameHints, EveryonePrincipal.NAME, PrincipalManager.SEARCH_TYPE_GROUP, true);
-        assertResult(principalProvider, nameHints, EveryonePrincipal.NAME, PrincipalManager.SEARCH_TYPE_NOT_GROUP, false);
-    }
-
-    @Test
-    public void testFindWithoutHint() throws Exception {
-        User testUser = null;
-        Group testGroup = null;
-        try {
-            UserManager userMgr = getUserManager(root);
-            testUser = userMgr.createUser("TestUser", "pw");
-            testGroup = userMgr.createGroup("TestGroup");
-
-            root.commit();
-
-            Set<String> resultNames = new HashSet<String>();
-            Iterator<? extends Principal> principals = principalProvider.findPrincipals(PrincipalManager.SEARCH_TYPE_ALL);
-            while (principals.hasNext()) {
-                resultNames.add(principals.next().getName());
-            }
-
-            assertTrue(resultNames.contains(EveryonePrincipal.NAME));
-            assertTrue(resultNames.contains("TestUser"));
-            assertTrue(resultNames.contains("TestGroup"));
-
-        } finally {
-            if (testUser != null) {
-                testUser.remove();
-            }
-            if (testGroup != null) {
-                testGroup.remove();
-            }
-            root.commit();
-        }
-    }
-
-    private static void assertResult(PrincipalProvider principalProvider,
-                                     List<String> nameHints, String expectedName,
-                                     int searchType, boolean toBeFound) {
-        for (String nameHint : nameHints) {
-            Iterator<? extends Principal> result = principalProvider.findPrincipals(nameHint, searchType);
-            boolean found = false;
-            while (result.hasNext()) {
-                Principal p = result.next();
-                if (p.getName().equals(expectedName)) {
-                    found = true;
-                }
-            }
-            if (toBeFound) {
-                assertTrue("Expected principal to be found by name hint " + expectedName, found);
-            } else {
-                assertFalse("Expected principal NOT to be found by name hint " + expectedName, found);
             }
         }
     }
\ No newline at end of file
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/GroupImpl.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/GroupImpl.java	(revision 1669376)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/GroupImpl.java	(revision )
@@ -230,20 +230,10 @@
     /**
      * Principal representation of this group instance.
      */
-    private class GroupPrincipal extends TreeBasedPrincipal implements java.security.acl.Group {
+    private class GroupPrincipal extends AbstractGroupPrincipal {
 
-        GroupPrincipal(String principalName, Tree groupTree) {
+        private GroupPrincipal(String principalName, Tree groupTree) {
             super(principalName, groupTree, getUserManager().getNamePathMapper());
-        }
-
-        @Override
-        public boolean addMember(Principal principal) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public boolean removeMember(Principal principal) {
-            throw new UnsupportedOperationException();
         }
 
         @Override
\ No newline at end of file
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/UserConfiguration.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/UserConfiguration.java	(revision 1669396)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/UserConfiguration.java	(revision )
@@ -17,11 +17,13 @@
 package org.apache.jackrabbit.oak.spi.security.user;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 import org.apache.jackrabbit.api.security.user.UserManager;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.spi.security.SecurityConfiguration;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalProvider;
 
 /**
  * Configuration interface for user management.
@@ -39,4 +41,27 @@
      */
     @Nonnull
     UserManager getUserManager(Root root, NamePathMapper namePathMapper);
+
+    /**
+     * Optional method that allows a given user management implementation to
+     * provide a specific and optimized implementation of the {@link PrincipalProvider}
+     * interface for the principals represented by the user/groups known to
+     * this implementation.
+     *
+     * If this method returns {@code null} the security setup will by default
+     * use a basic {@code PrincipalProvider} implementation based on public
+     * user management API or a combination of other {@link PrincipalProvider}s
+     * as configured with the repository setup.
+     *
+     * @param root The root used to read the principal information from.
+     * @param namePathMapper The {@code NamePathMapper} to convert oak paths to JCR paths.
+     * @return An implementation of {@code PrincipalProvider} or {@code null} if
+     * principal discovery is provided by other means of if the default principal
+     * provider implementation should be used that acts on public user management
+     * API.
+     *
+     * @see {@link org.apache.jackrabbit.oak.spi.security.principal.PrincipalConfiguration}
+     */
+    @Nullable
+    PrincipalProvider getUserPrincipalProvider(@Nonnull Root root, @Nonnull NamePathMapper namePathMapper);
 }
Index: oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/principal/AbstractPrincipalProviderTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/principal/AbstractPrincipalProviderTest.java	(revision )
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/principal/AbstractPrincipalProviderTest.java	(revision )
@@ -0,0 +1,299 @@
+/*
+ * 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.spi.security.principal;
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import org.apache.jackrabbit.api.security.principal.PrincipalManager;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.AbstractSecurityTest;
+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.assertTrue;
+
+public abstract class AbstractPrincipalProviderTest extends AbstractSecurityTest {
+
+    protected PrincipalProvider principalProvider;
+
+    @Override
+    public void before() throws Exception {
+        super.before();
+
+        principalProvider = createPrincipalProvider();
+    }
+
+    protected abstract PrincipalProvider createPrincipalProvider();
+
+    @Test
+    public void testGetPrincipals() throws Exception {
+        String adminId = adminSession.getAuthInfo().getUserID();
+        Set<? extends Principal> principals = principalProvider.getPrincipals(adminId);
+
+        assertNotNull(principals);
+        assertFalse(principals.isEmpty());
+        assertTrue(principals.contains(EveryonePrincipal.getInstance()));
+
+        for (Principal principal : principals) {
+            assertNotNull(principalProvider.getPrincipal(principal.getName()));
+        }
+    }
+
+    @Test
+    public void testUserPrincipal() throws Exception {
+        User user = getTestUser();
+
+        Principal principal = principalProvider.getPrincipal(user.getPrincipal().getName());
+
+        assertNotNull(principal);
+    }
+
+    @Test
+    public void testAdminPrincipal() throws Exception {
+        String userId = adminSession.getAuthInfo().getUserID();
+        Authorizable admin = getUserManager(root).getAuthorizable(userId);
+        if (admin != null && admin.getPrincipal() instanceof AdminPrincipal) {
+
+            Principal principal = principalProvider.getPrincipal(admin.getPrincipal().getName());
+            assertTrue(principal instanceof AdminPrincipal);
+
+            Set<? extends Principal> principals = principalProvider.getPrincipals(userId);
+            boolean containsAdminPrincipal = false;
+            for (Principal p : principals) {
+                if (p instanceof AdminPrincipal) {
+                    containsAdminPrincipal = true;
+                    break;
+                }
+            }
+            assertTrue(containsAdminPrincipal);
+        }
+    }
+
+    @Test
+    public void testSystemUserPrincipal() throws Exception {
+        User user = getUserManager(root).createSystemUser("testSystemUser" + UUID.randomUUID(), null);
+        root.commit();
+
+        try {
+            Principal principal = principalProvider.getPrincipal(user.getPrincipal().getName());
+
+            assertNotNull(principal);
+            assertTrue(principal instanceof SystemUserPrincipal);
+
+        } finally {
+            user.remove();
+            root.commit();
+        }
+    }
+
+    @Test
+    public void testGroupPrincipal() throws Exception {
+        Group group = getUserManager(root).createGroup("testGroup" + UUID.randomUUID());
+        root.commit();
+
+        try {
+            Principal principal = principalProvider.getPrincipal(group.getPrincipal().getName());
+
+            assertNotNull(principal);
+            assertTrue(principal instanceof java.security.acl.Group);
+
+        } finally {
+            group.remove();
+            root.commit();
+        }
+    }
+
+    @Test
+    public void testEveryone() throws Exception {
+        Principal everyone = principalProvider.getPrincipal(EveryonePrincipal.NAME);
+        assertTrue(everyone instanceof EveryonePrincipal);
+
+        Group everyoneGroup = null;
+        try {
+            UserManager userMgr = getUserManager(root);
+            everyoneGroup = userMgr.createGroup(EveryonePrincipal.NAME);
+            root.commit();
+
+            Principal ep = principalProvider.getPrincipal(EveryonePrincipal.NAME);
+            assertFalse(ep instanceof EveryonePrincipal);
+        } finally {
+            if (everyoneGroup != null) {
+                everyoneGroup.remove();
+                root.commit();
+            }
+        }
+    }
+
+    @Test
+    public void testFindUserPrincipal() throws Exception {
+        User testUser = null;
+        try {
+            UserManager userMgr = getUserManager(root);
+            testUser = userMgr.createUser("TestUser", "pw");
+            root.commit();
+
+            String principalName = testUser.getPrincipal().getName();
+            assertNotNull(principalProvider.getPrincipal(principalName));
+
+            List<String> nameHints = new ArrayList<String>();
+            nameHints.add("TestUser");
+            nameHints.add("Test");
+            nameHints.add("User");
+            nameHints.add("stUs");
+
+            assertResult(principalProvider, nameHints, principalName, PrincipalManager.SEARCH_TYPE_NOT_GROUP, true);
+            assertResult(principalProvider, nameHints, principalName, PrincipalManager.SEARCH_TYPE_ALL, true);
+            assertResult(principalProvider, nameHints, principalName, PrincipalManager.SEARCH_TYPE_GROUP, false);
+        } finally {
+            if (testUser != null) {
+                testUser.remove();
+                root.commit();
+            }
+        }
+    }
+
+    @Test
+    public void testFindGroupPrincipal() throws Exception {
+        Group testGroup = null;
+        try {
+            UserManager userMgr = getUserManager(root);
+            testGroup = userMgr.createGroup("TestGroup");
+            root.commit();
+
+            String principalName = testGroup.getPrincipal().getName();
+            assertNotNull(principalProvider.getPrincipal(principalName));
+
+            List<String> nameHints = new ArrayList<String>();
+            nameHints.add("TestGroup");
+            nameHints.add("Test");
+            nameHints.add("Group");
+            nameHints.add("stGr");
+
+            assertResult(principalProvider, nameHints, principalName, PrincipalManager.SEARCH_TYPE_GROUP, true);
+            assertResult(principalProvider, nameHints, principalName, PrincipalManager.SEARCH_TYPE_ALL, true);
+            assertResult(principalProvider, nameHints, principalName, PrincipalManager.SEARCH_TYPE_NOT_GROUP, false);
+        } finally {
+            if (testGroup != null) {
+                testGroup.remove();
+                root.commit();
+            }
+        }
+    }
+
+    @Test
+    public void testFindEveryone() {
+        assertNotNull(principalProvider.getPrincipal(EveryonePrincipal.NAME));
+
+        Map<Integer, Boolean> tests = new HashMap<Integer, Boolean>();
+        tests.put(PrincipalManager.SEARCH_TYPE_ALL, Boolean.TRUE);
+        tests.put(PrincipalManager.SEARCH_TYPE_GROUP, Boolean.TRUE);
+        tests.put(PrincipalManager.SEARCH_TYPE_NOT_GROUP, Boolean.FALSE);
+
+        for (Integer searchType : tests.keySet()) {
+            boolean found = false;
+            Iterator<? extends Principal> it = principalProvider.findPrincipals(EveryonePrincipal.NAME, searchType);
+            while (it.hasNext()) {
+                Principal p = it.next();
+                if (p.getName().equals(EveryonePrincipal.NAME)) {
+                    found = true;
+                }
+            }
+            Boolean expected = tests.get(searchType);
+            assertEquals(expected, found);
+        }
+    }
+
+    @Test
+    public void testFindEveryoneHint() {
+        assertNotNull(principalProvider.getPrincipal(EveryonePrincipal.NAME));
+
+        List<String> nameHints = new ArrayList<String>();
+        nameHints.add("everyone");
+        nameHints.add("every");
+        nameHints.add("one");
+        nameHints.add("very");
+
+        assertResult(principalProvider, nameHints, EveryonePrincipal.NAME, PrincipalManager.SEARCH_TYPE_ALL, true);
+        assertResult(principalProvider, nameHints, EveryonePrincipal.NAME, PrincipalManager.SEARCH_TYPE_GROUP, true);
+        assertResult(principalProvider, nameHints, EveryonePrincipal.NAME, PrincipalManager.SEARCH_TYPE_NOT_GROUP, false);
+    }
+
+    @Test
+    public void testFindWithoutHint() throws Exception {
+        User testUser = null;
+        Group testGroup = null;
+        try {
+            UserManager userMgr = getUserManager(root);
+            testUser = userMgr.createUser("TestUser", "pw");
+            testGroup = userMgr.createGroup("TestGroup");
+
+            root.commit();
+
+            Set<String> resultNames = new HashSet<String>();
+            Iterator<? extends Principal> principals = principalProvider.findPrincipals(PrincipalManager.SEARCH_TYPE_ALL);
+            while (principals.hasNext()) {
+                resultNames.add(principals.next().getName());
+            }
+
+            assertTrue(resultNames.contains(EveryonePrincipal.NAME));
+            assertTrue(resultNames.contains("TestUser"));
+            assertTrue(resultNames.contains("TestGroup"));
+
+        } finally {
+            if (testUser != null) {
+                testUser.remove();
+            }
+            if (testGroup != null) {
+                testGroup.remove();
+            }
+            root.commit();
+        }
+    }
+
+    private static void assertResult(PrincipalProvider principalProvider,
+                                     List<String> nameHints, String expectedName,
+                                     int searchType, boolean toBeFound) {
+        for (String nameHint : nameHints) {
+            Iterator<? extends Principal> result = principalProvider.findPrincipals(nameHint, searchType);
+            boolean found = false;
+            while (result.hasNext()) {
+                Principal p = result.next();
+                if (p.getName().equals(expectedName)) {
+                    found = true;
+                }
+            }
+            if (toBeFound) {
+                assertTrue("Expected principal to be found by name hint " + expectedName, found);
+            } else {
+                assertFalse("Expected principal NOT to be found by name hint " + expectedName, found);
+            }
+        }
+    }
+}
\ No newline at end of file
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/AuthorizableType.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/AuthorizableType.java	(revision 1669396)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/AuthorizableType.java	(revision )
@@ -21,6 +21,8 @@
 import javax.annotation.Nonnull;
 
 import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.api.security.user.UserManager;
 
 /**
@@ -64,6 +66,18 @@
             default:
                 // TYPE_AUTHORIZABLE:
                 return true;
+        }
+    }
+
+    public Class<? extends Authorizable> getAuthorizableClass() {
+        switch (userType) {
+            case UserManager.SEARCH_TYPE_GROUP:
+                return Group.class;
+            case UserManager.SEARCH_TYPE_USER:
+                return User.class;
+            default:
+                // TYPE_AUTHORIZABLE:
+                return Authorizable.class;
         }
     }
 }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/principal/PrincipalConfigurationImpl.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/security/principal/PrincipalConfigurationImpl.java	(revision 1669376)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/principal/PrincipalConfigurationImpl.java	(revision )
@@ -42,6 +42,7 @@
 @Service({PrincipalConfiguration.class, SecurityConfiguration.class})
 public class PrincipalConfigurationImpl extends ConfigurationBase implements PrincipalConfiguration {
 
+    @SuppressWarnings("UnusedDeclaration")
     public PrincipalConfigurationImpl() {
         super();
     }
@@ -69,7 +70,14 @@
     @Override
     public PrincipalProvider getPrincipalProvider(Root root, NamePathMapper namePathMapper) {
         UserConfiguration uc = getSecurityProvider().getConfiguration(UserConfiguration.class);
+        PrincipalProvider principalProvider = uc.getUserPrincipalProvider(root, namePathMapper);
+        if (principalProvider != null) {
+            // use user-implementation specific principal provider implementation
+            return principalProvider;
+        } else {
+            // use default implementation acting on user management API
-        return new PrincipalProviderImpl(root, uc, namePathMapper);
+            return new PrincipalProviderImpl(root, uc, namePathMapper);
+        }
     }
 
     //----------------------------------------------< SecurityConfiguration >---
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/principal/PrincipalProviderImpl.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/security/principal/PrincipalProviderImpl.java	(revision 1669376)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/principal/PrincipalProviderImpl.java	(revision )
@@ -34,13 +34,13 @@
 import org.apache.jackrabbit.api.security.user.Authorizable;
 import org.apache.jackrabbit.api.security.user.Query;
 import org.apache.jackrabbit.api.security.user.QueryBuilder;
-import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.api.security.user.UserManager;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
 import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
 import org.apache.jackrabbit.oak.spi.security.principal.PrincipalProvider;
+import org.apache.jackrabbit.oak.spi.security.user.AuthorizableType;
 import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
 import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
 import org.slf4j.Logger;
@@ -164,25 +164,10 @@
             @Override
             public <T> void build(QueryBuilder<T> builder) {
                 builder.setCondition(builder.like('@' +UserConstants.REP_PRINCIPAL_NAME, buildSearchPattern(nameHint)));
-                builder.setSelector(getAuthorizableClass(searchType));
+                builder.setSelector(AuthorizableType.getType(searchType).getAuthorizableClass());
             }
         };
         return userManager.findAuthorizables(userQuery);
-    }
-
-
-    private static Class<? extends Authorizable> getAuthorizableClass(int searchType) {
-        switch (searchType) {
-            case PrincipalManager.SEARCH_TYPE_GROUP:
-                return org.apache.jackrabbit.api.security.user.Group.class;
-            case PrincipalManager.SEARCH_TYPE_NOT_GROUP:
-                return User.class;
-            case PrincipalManager.SEARCH_TYPE_ALL:
-                return Authorizable.class;
-            default:
-                throw new IllegalArgumentException("Invalid search type " + searchType);
-
-        }
     }
 
     private static String buildSearchPattern(String nameHint) {
\ No newline at end of file
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserPrincipalProvider.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserPrincipalProvider.java	(revision )
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserPrincipalProvider.java	(revision )
@@ -0,0 +1,298 @@
+/*
+ * 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.user;
+
+import java.security.Principal;
+import java.security.acl.Group;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.jcr.RepositoryException;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterators;
+import org.apache.jackrabbit.api.security.principal.PrincipalManager;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Query;
+import org.apache.jackrabbit.api.security.user.QueryBuilder;
+import org.apache.jackrabbit.api.security.user.UserManager;
+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.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalProvider;
+import org.apache.jackrabbit.oak.spi.security.user.AuthorizableType;
+import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
+import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.apache.jackrabbit.oak.spi.security.user.util.UserUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+
+/**
+ * The {@code PrincipalProviderImpl} is a principal provider implementation
+ * that operates on principal information read from user information exposed by
+ * the configured {@link org.apache.jackrabbit.api.security.user.UserManager}.
+ */
+class UserPrincipalProvider implements PrincipalProvider {
+
+    private static final Logger log = LoggerFactory.getLogger(UserPrincipalProvider.class);
+
+    private final ConfigurationParameters config;
+    private final NamePathMapper namePathMapper;
+
+    private final UserManager userManager;
+    private final UserProvider userProvider;
+    private final MembershipProvider membershipProvider;
+
+    UserPrincipalProvider(@Nonnull Root root,
+                          @Nonnull UserConfiguration userConfiguration,
+                          @Nonnull NamePathMapper namePathMapper) {
+        this.config = userConfiguration.getParameters();
+        this.namePathMapper = namePathMapper;
+
+        this.userManager = userConfiguration.getUserManager(root, namePathMapper);
+        this.userProvider = new UserProvider(root, config);
+        this.membershipProvider = new MembershipProvider(root, config);
+    }
+
+    //--------------------------------------------------< PrincipalProvider >---
+    @Override
+    public Principal getPrincipal(String principalName) {
+        Tree authorizableTree = userProvider.getAuthorizableByPrincipal(new PrincipalImpl(principalName));
+        Principal principal = null;
+        if (authorizableTree != null) {
+            if (UserUtil.isType(authorizableTree, AuthorizableType.GROUP)) {
+                principal = createGroupPrincipal(authorizableTree);
+            } else {
+                principal = createUserPrincipal(UserUtil.getAuthorizableId(authorizableTree), authorizableTree);
+            }
+        }
+
+        if (principal == null) {
+            // no such principal or error while accessing principal from user/group
+            return (EveryonePrincipal.NAME.equals(principalName)) ? EveryonePrincipal.getInstance() : null;
+        } else {
+            return principal;
+        }
+    }
+
+    @Override
+    public Set<Group> getGroupMembership(Principal principal) {
+        Tree tree = getAuthorizableTree(principal);
+        if (tree == null) {
+            return Collections.emptySet();
+        } else {
+            return getGroupMembership(tree);
+        }
+    }
+
+    @Override
+    public Set<? extends Principal> getPrincipals(String userID) {
+        Set<Principal> principals = new HashSet<Principal>();
+        Tree tree = userProvider.getAuthorizable(userID);
+        if (tree != null && UserUtil.isType(tree, AuthorizableType.USER)) {
+            Principal userPrincipal = createUserPrincipal(userID, tree);
+            if (userPrincipal != null) {
+                principals.add(userPrincipal);
+                principals.addAll(getGroupMembership(tree));
+            }
+        }
+        return principals;
+    }
+
+    @Override
+    public Iterator<? extends Principal> findPrincipals(final String nameHint,
+                                                        final int searchType) {
+        // TODO: omit usage of user mgt API
+        try {
+            Iterator<Authorizable> authorizables = findAuthorizables(nameHint, searchType);
+            Iterator<Principal> principals = Iterators.transform(
+                    Iterators.filter(authorizables, Predicates.notNull()),
+                    new AuthorizableToPrincipal());
+
+            if (matchesEveryone(nameHint, searchType)) {
+                principals = Iterators.concat(principals, Iterators.singletonIterator(EveryonePrincipal.getInstance()));
+                return Iterators.filter(principals, new EveryonePredicate());
+            } else {
+                return principals;
+            }
+        } catch (RepositoryException e) {
+            log.debug(e.getMessage());
+            return Iterators.emptyIterator();
+        }
+    }
+
+    @Nonnull
+    @Override
+    public Iterator<? extends Principal> findPrincipals(int searchType) {
+        return findPrincipals(null, searchType);
+    }
+
+    //------------------------------------------------------------< private >---
+    @CheckForNull
+    private Tree getAuthorizableTree(@Nonnull Principal principal) {
+        return userProvider.getAuthorizableByPrincipal(principal);
+    }
+
+    @CheckForNull
+    private Principal createUserPrincipal(@Nonnull String id, @Nonnull Tree userTree) {
+        String principalName = getPrincipalName(userTree);
+        if (principalName == null) {
+            return null;
+        }
+        if (UserUtil.isSystemUser(userTree)) {
+            return new SystemUserPrincipalImpl(principalName, userTree, namePathMapper);
+        } else if (UserUtil.isAdmin(config, id)) {
+            return new AdminPrincipalImpl(principalName, userTree, namePathMapper);
+        } else {
+            return new TreeBasedPrincipal(principalName, userTree, namePathMapper);
+        }
+    }
+
+    @CheckForNull
+    private Group createGroupPrincipal(@Nonnull Tree groupTree) {
+        String principalName = getPrincipalName(groupTree);
+        if (principalName == null) {
+            return null;
+        }
+        return new AbstractGroupPrincipal(principalName, groupTree, namePathMapper) {
+            @Override
+            public boolean isMember(Principal principal) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public Enumeration<? extends Principal> members() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    @CheckForNull
+    private String getPrincipalName(@Nonnull Tree tree) {
+        PropertyState principalName = tree.getProperty(UserConstants.REP_PRINCIPAL_NAME);
+        if (principalName != null) {
+            return principalName.getValue(STRING);
+        } else {
+            String msg = "Authorizable without principal name " + UserUtil.getAuthorizableId(tree);
+            log.warn(msg);
+            return null;
+        }
+    }
+
+    @Nonnull
+    private Set<Group> getGroupMembership(@Nonnull Tree authorizableTree) {
+        Set<Group> groupPrincipals = new HashSet<Group>();
+        Iterator<String> groupPaths = membershipProvider.getMembership(authorizableTree, true);
+        while (groupPaths.hasNext()) {
+            Tree groupTree = userProvider.getAuthorizableByPath(groupPaths.next());
+            if (groupTree != null && UserUtil.isType(groupTree, AuthorizableType.GROUP)) {
+                Group gr = createGroupPrincipal(groupTree);
+                if (gr != null) {
+                    groupPrincipals.add(createGroupPrincipal(groupTree));
+                }
+            }
+        }
+        // add the dynamic everyone principal group which is not included in
+        // the 'getMembership' call.
+        groupPrincipals.add(EveryonePrincipal.getInstance());
+        return groupPrincipals;
+    }
+
+    private Iterator<Authorizable> findAuthorizables(@Nullable final String nameHint,
+                                                     final int searchType) throws RepositoryException {
+        Query userQuery = new Query() {
+            @Override
+            public <T> void build(QueryBuilder<T> builder) {
+                builder.setCondition(builder.like('@' +UserConstants.REP_PRINCIPAL_NAME, buildSearchPattern(nameHint)));
+                builder.setSelector(AuthorizableType.getType(searchType).getAuthorizableClass());
+            }
+        };
+        return userManager.findAuthorizables(userQuery);
+    }
+
+    private static String buildSearchPattern(String nameHint) {
+        if (nameHint == null) {
+            return "%";
+        } else {
+            StringBuilder sb = new StringBuilder();
+            sb.append('%');
+            sb.append(nameHint.replace("%", "\\%").replace("_", "\\_"));
+            sb.append('%');
+            return sb.toString();
+        }
+
+    }
+
+    private static boolean matchesEveryone(String nameHint, int searchType) {
+        return searchType != PrincipalManager.SEARCH_TYPE_NOT_GROUP &&
+                (nameHint == null || EveryonePrincipal.NAME.contains(nameHint));
+    }
+
+    //--------------------------------------------------------------------------
+    /**
+     * Function to covert an authorizable tree to a principal.
+     */
+    private static final class AuthorizableToPrincipal implements Function<Authorizable, Principal> {
+        @Override
+        public Principal apply(@Nullable Authorizable authorizable) {
+            if (authorizable != null) {
+                try {
+                    return authorizable.getPrincipal();
+                } catch (RepositoryException e) {
+                    log.debug(e.getMessage());
+                }
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Predicate to make sure the everyone principal is only included once in
+     * the result set.
+     */
+    private static final class EveryonePredicate implements Predicate<Principal> {
+        private boolean servedEveryone = false;
+        @Override
+        public boolean apply(@Nullable Principal principal) {
+            String pName = (principal == null) ? null : principal.getName();
+            if (EveryonePrincipal.NAME.equals(pName)) {
+                if (servedEveryone) {
+                    return false;
+                } else {
+                    servedEveryone = true;
+                    return true;
+                }
+            } else {
+                // not everyone
+                return true;
+            }
+        }
+    }
+}
\ No newline at end of file
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/SystemUserPrincipalImpl.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/SystemUserPrincipalImpl.java	(revision )
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/SystemUserPrincipalImpl.java	(revision )
@@ -0,0 +1,34 @@
+/*
+ * 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.user;
+
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.namepath.PathMapper;
+import org.apache.jackrabbit.oak.spi.security.principal.SystemUserPrincipal;
+
+/**
+ * Implementation of the {@code SystemUserPrincipal} for this user management
+ * implementation.
+ */
+final class SystemUserPrincipalImpl extends TreeBasedPrincipal implements SystemUserPrincipal {
+
+    SystemUserPrincipalImpl(@Nonnull String principalName, @Nonnull Tree tree, @Nonnull PathMapper pathMapper) {
+        super(principalName, tree, pathMapper);
+    }
+}
\ No newline at end of file
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/SystemUserImpl.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/SystemUserImpl.java	(revision 1669376)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/SystemUserImpl.java	(revision )
@@ -22,7 +22,6 @@
 import javax.jcr.UnsupportedRepositoryOperationException;
 
 import org.apache.jackrabbit.oak.api.Tree;
-import org.apache.jackrabbit.oak.spi.security.principal.SystemUserPrincipal;
 import org.apache.jackrabbit.oak.spi.security.user.util.UserUtil;
 
 /**
@@ -47,7 +46,7 @@
         if (isAdmin()) {
             return new AdminPrincipalImpl(getPrincipalName(), getTree(), getUserManager().getNamePathMapper());
         } else {
-            return new SystemUserPrincipalImpl(getTree());
+            return new SystemUserPrincipalImpl(getPrincipalName(), getTree(), getUserManager().getNamePathMapper());
         }
     }
 
@@ -65,13 +64,5 @@
     @Override
     public void changePassword(String password, String oldPassword) throws RepositoryException {
         throw new UnsupportedRepositoryOperationException("system user");
-    }
-
-    //--------------------------------------------------------------------------
-    private final class SystemUserPrincipalImpl extends TreeBasedPrincipal implements SystemUserPrincipal {
-
-        private SystemUserPrincipalImpl(Tree tree) throws RepositoryException {
-            super(getPrincipalName(), tree, getUserManager().getNamePathMapper());
-        }
     }
 }
\ No newline at end of file
Index: oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/PasswordValidationActionTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/PasswordValidationActionTest.java	(revision 1669376)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/PasswordValidationActionTest.java	(revision )
@@ -19,6 +19,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import javax.jcr.RepositoryException;
 import javax.jcr.nodetype.ConstraintViolationException;
 
@@ -32,6 +33,7 @@
 import org.apache.jackrabbit.oak.security.user.UserConfigurationImpl;
 import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
 import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalProvider;
 import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
 import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
 import org.apache.jackrabbit.oak.spi.security.user.util.PasswordUtil;
@@ -196,6 +198,12 @@
                     public ConfigurationParameters getParameters() {
                         return ConfigurationParameters.of(super.getParameters(),
                                 ConfigurationParameters.of(UserConstants.PARAM_AUTHORIZABLE_ACTION_PROVIDER, actionProvider));
+                    }
+
+                    @Nullable
+                    @Override
+                    public PrincipalProvider getUserPrincipalProvider(@Nonnull Root root, @Nonnull NamePathMapper namePathMapper) {
+                        return null;
                     }
                 };
             } else {
