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 2012/08/16 18:43:24 UTC

svn commit: r1373912 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/identifier/ oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/ oa...

Author: angela
Date: Thu Aug 16 16:43:23 2012
New Revision: 1373912

URL: http://svn.apache.org/viewvc?rev=1373912&view=rev
Log:
OAK-50 : Implement User Management (WIP)

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/MembershipProvider.java
Removed:
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableTypePredicate.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/InheritingAuthorizableIterator.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/MembershipManager.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/identifier/IdentifierManager.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserProviderImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserValidator.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserValidatorProvider.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/UserConstants.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/UserProvider.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableIterator.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/ImpersonationImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerImpl.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/identifier/IdentifierManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/identifier/IdentifierManager.java?rev=1373912&r1=1373911&r2=1373912&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/identifier/IdentifierManager.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/identifier/IdentifierManager.java Thu Aug 16 16:43:23 2012
@@ -24,7 +24,7 @@ import java.util.Set;
 import java.util.UUID;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
-import javax.jcr.RepositoryException;
+import javax.jcr.PropertyType;
 import javax.jcr.query.Query;
 
 import org.apache.jackrabbit.JcrConstants;
@@ -60,12 +60,12 @@ public class IdentifierManager {
     }
 
     @Nonnull
-    public static String generateUUID(String hint) throws RepositoryException {
+    public static String generateUUID(String hint) {
         try {
             UUID uuid = UUID.nameUUIDFromBytes(hint.getBytes("UTF-8"));
             return uuid.toString();
         } catch (UnsupportedEncodingException e) {
-            throw new RepositoryException("Unexpected error while creating authorizable node", e);
+            throw new RuntimeException("Unexpected error while creating authorizable node", e);
         }
     }
 
@@ -109,7 +109,7 @@ public class IdentifierManager {
     @CheckForNull
     public Tree getTree(String identifier) {
         if (isValidUUID(identifier)) {
-            return findByJcrUuid(identifier);
+            return findTreeByJcrUuid(identifier);
         } else {
             // TODO as stated in NodeDelegate#getIdentifier() a non-uuid ID should
             // TODO consisting of closest referenceable parent and a relative path
@@ -119,6 +119,43 @@ public class IdentifierManager {
     }
 
     /**
+     * The path of the tree identified by the specified {@code identifier} or {@code null}.
+     *
+     * @param identifier The identifier of the Tree such as exposed by {@link javax.jcr.Node#getIdentifier()}
+     * @return The tree with the given {@code identifier} or {@code null} if no
+     * such tree exists or isn't accessible to the content session.
+     */
+    @CheckForNull
+    public String getPath(String identifier) {
+        if (isValidUUID(identifier)) {
+            return resolveUUID(identifier);
+        } else {
+            // TODO as stated in NodeDelegate#getIdentifier() a non-uuid ID should
+            // TODO consisting of closest referenceable parent and a relative path
+            // TODO irrespective of the accessibility of the parent node(s)
+            return root.getTree(identifier).getPath();
+        }
+    }
+
+    /**
+     * Returns the path of the tree references by the specified (weak)
+     * reference {@code CoreValue value}.
+     *
+     * @param referenceValue A (weak) reference value.
+     * @return The tree with the given {@code identifier} or {@code null} if no
+     * such tree exists or isn't accessible to the content session.
+     */
+    @CheckForNull
+    public String getPath(CoreValue referenceValue) {
+        int type = referenceValue.getType();
+        if (type == PropertyType.REFERENCE || type == PropertyType.WEAKREFERENCE) {
+            return resolveUUID(referenceValue);
+        } else {
+            throw new IllegalArgumentException("Invalid value type");
+        }
+    }
+
+    /**
      * Searches all reference properties to the specified {@code tree} that match
      * the given name and node type constraints.
      *
@@ -170,10 +207,13 @@ public class IdentifierManager {
     }
 
     @CheckForNull
-    private Tree findByJcrUuid(String id) {
-        try {
-            Map<String, CoreValue> bindings = Collections.singletonMap("id", contentSession.getCoreValueFactory().createValue(id));
+    private String resolveUUID(String uuid) {
+        return resolveUUID(contentSession.getCoreValueFactory().createValue(uuid));
+    }
 
+    private String resolveUUID(CoreValue uuid) {
+        try {
+            Map<String, CoreValue> bindings = Collections.singletonMap("id", uuid);
             Result result = contentSession.getQueryEngine().executeQuery("SELECT * FROM [nt:base] WHERE [jcr:uuid] = $id", Query.JCR_SQL2,
                     Long.MAX_VALUE, 0, bindings, new NamePathMapper.Default());
 
@@ -186,10 +226,16 @@ public class IdentifierManager {
                     path = rr.getPath();
                 }
             }
-            return path == null ? null : root.getTree(path);
+            return path == null ? null : path;
         } catch (ParseException ex) {
             log.error("query failed", ex);
             return null;
         }
     }
+
+    @CheckForNull
+    private Tree findTreeByJcrUuid(String uuid) {
+        String path = resolveUUID(uuid);
+        return (path == null) ? null : root.getTree(path);
+    }
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserProviderImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserProviderImpl.java?rev=1373912&r1=1373911&r2=1373912&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserProviderImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserProviderImpl.java Thu Aug 16 16:43:23 2012
@@ -16,15 +16,30 @@
  */
 package org.apache.jackrabbit.oak.security.user;
 
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Nullable;
+import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.nodetype.ConstraintViolationException;
 
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.commons.iterator.RangeIteratorAdapter;
 import org.apache.jackrabbit.oak.api.ContentSession;
+import org.apache.jackrabbit.oak.api.CoreValue;
+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.plugins.identifier.IdentifierManager;
+import org.apache.jackrabbit.oak.spi.security.user.MembershipProvider;
 import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
 import org.apache.jackrabbit.oak.spi.security.user.UserManagerConfig;
 import org.apache.jackrabbit.oak.spi.security.user.UserProvider;
@@ -34,7 +49,13 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * User provider implementation creating the JCR nodes corresponding the a given
+ * User provider implementation and manager for group memberships with the
+ * following characteristics:
+ *
+ * <h1>UserProvider</h1>
+ *
+ * <h2>Creation</h2>
+ * This implementation creates the JCR nodes corresponding the a given
  * authorizable ID with the following behavior:
  * <ul>
  * <li>Users are created below /rep:security/rep:authorizables/rep:users or
@@ -108,8 +129,15 @@ import org.slf4j.LoggerFactory;
  *     <li>autoExpandTree</li>
  *     <li>autoExpandSize</li>
  * </ul>
+ *
+ * <h2>Access by ID</h2>
+ * TODO
+ *
+ * <h1>MembershipProvider</h1>
+ *
+ * TODO
  */
-public class UserProviderImpl implements UserProvider {
+public class UserProviderImpl implements UserProvider, MembershipProvider, UserConstants {
 
     /**
      * logger instance
@@ -124,6 +152,7 @@ public class UserProviderImpl implements
     private final IdentifierManager identifierManager;
 
     private final int defaultDepth;
+    private final int splitSize;
 
     private final String groupPath;
     private final String userPath;
@@ -134,11 +163,18 @@ public class UserProviderImpl implements
         this.identifierManager = new IdentifierManager(contentSession, root);
 
         defaultDepth = config.getConfigValue(UserManagerConfig.PARAM_DEFAULT_DEPTH, DEFAULT_DEPTH);
+        int splitValue = config.getConfigValue(UserManagerConfig.PARAM_GROUP_MEMBERSHIP_SPLIT_SIZE, 0);
+        if (splitValue < 4) {
+            log.warn("Invalid value {} for {}. Expected integer >= 4", splitValue, UserManagerConfig.PARAM_GROUP_MEMBERSHIP_SPLIT_SIZE);
+            splitValue = 0;
+        }
+        this.splitSize = splitValue;
 
-        groupPath = config.getConfigValue(UserManagerConfig.PARAM_GROUP_PATH, UserConstants.DEFAULT_GROUP_PATH);
-        userPath = config.getConfigValue(UserManagerConfig.PARAM_USER_PATH, UserConstants.DEFAULT_USER_PATH);
+        groupPath = config.getConfigValue(UserManagerConfig.PARAM_GROUP_PATH, DEFAULT_GROUP_PATH);
+        userPath = config.getConfigValue(UserManagerConfig.PARAM_USER_PATH, DEFAULT_USER_PATH);
     }
 
+    //-------------------------------------------------------< UserProvider >---
     @Override
     public Tree createUser(String userID, String intermediateJcrPath) throws RepositoryException {
         return createAuthorizableNode(userID, false, intermediateJcrPath);
@@ -150,7 +186,7 @@ public class UserProviderImpl implements
     }
 
     @Override
-    public Tree getAuthorizable(String authorizableId) throws RepositoryException {
+    public Tree getAuthorizable(String authorizableId) {
         Tree tree = identifierManager.getTree(getContentID(authorizableId));
         if (isAuthorizableTree(tree, UserManager.SEARCH_TYPE_AUTHORIZABLE)) {
             return tree;
@@ -160,7 +196,7 @@ public class UserProviderImpl implements
     }
 
     @Override
-    public Tree getAuthorizable(String authorizableId, int authorizableType) throws RepositoryException {
+    public Tree getAuthorizable(String authorizableId, int authorizableType) {
         Tree tree = identifierManager.getTree(getContentID(authorizableId));
         if (isAuthorizableTree(tree, authorizableType)) {
             return tree;
@@ -179,24 +215,215 @@ public class UserProviderImpl implements
         }
     }
 
+    //--------------------------------------------------< MembershipProvider>---
+    @Override
+    public Iterator<String> getMembership(String authorizableId, boolean includeInherited) {
+        return getMembership(getAuthorizable(authorizableId), includeInherited);
+    }
+
+    @Override
+    public Iterator<String> getMembership(Tree authorizableTree, boolean includeInherited) {
+        Set<String> groupPaths = new HashSet<String>();
+        Set<String> refPaths = identifierManager.getWeakReferences(authorizableTree, null, NT_REP_GROUP, NT_REP_MEMBERS);
+        for (String propPath : refPaths) {
+            int index = propPath.indexOf('/'+REP_MEMBERS);
+            if (index > 0) {
+                groupPaths.add(propPath.substring(0, index));
+            } else {
+                log.debug("Not a membership reference property " + propPath);
+            }
+        }
+
+        Iterator<String> it = groupPaths.iterator();
+        if (includeInherited && it.hasNext()) {
+            return getAllMembership(groupPaths.iterator());
+        } else {
+            return new RangeIteratorAdapter(it, groupPaths.size());
+        }
+    }
+
+    @Override
+    public Iterator<String> getMembers(String groupId, int authorizableType, boolean includeInherited) {
+        return getMembers(getAuthorizable(groupId), UserManager.SEARCH_TYPE_AUTHORIZABLE, includeInherited);
+    }
+
+    @Override
+    public Iterator<String> getMembers(Tree groupTree, int authorizableType, boolean includeInherited) {
+        Iterable memberPaths = Collections.emptySet();
+        if (useMemberNode(groupTree)) {
+            Tree membersTree = groupTree.getChild(REP_MEMBERS);
+            if (membersTree != null) {
+                // FIXME: replace usage of PropertySequence (oak-api not possible there)
+//                PropertySequence propertySequence = getPropertySequence(membersTree);
+//                iterator = new AuthorizableIterator(propertySequence, authorizableType, userManager);
+            }
+        } else {
+            PropertyState property = groupTree.getProperty(REP_MEMBERS);
+            if (property != null) {
+                List<CoreValue> vs = property.getValues();
+                memberPaths = Iterables.transform(vs, new Function<CoreValue,String>() {
+                    @Override
+                    public String apply(@Nullable CoreValue value) {
+                        return identifierManager.getPath(value);
+                    }
+                });
+            }
+        }
+
+        Iterator it = memberPaths.iterator();
+        if (includeInherited && it.hasNext()) {
+            return getAllMembers(it, authorizableType);
+        } else {
+            return new RangeIteratorAdapter(it, Iterables.size(memberPaths));
+        }
+    }
+
+    @Override
+    public boolean isMember(Tree groupTree, Tree authorizableTree, boolean includeInherited) {
+        if (includeInherited) {
+            Iterator<String> groupPaths = getMembership(authorizableTree, true);
+            String path = groupTree.getPath();
+            while (groupPaths.hasNext()) {
+                if (path.equals(groupPaths.next())) {
+                    return true;
+                }
+            }
+        } else {
+            if (useMemberNode(groupTree)) {
+                Tree membersTree = groupTree.getChild(REP_MEMBERS);
+                if (membersTree != null) {
+                    // FIXME: fix.. testing for property name isn't correct.
+                    // FIXME: usage of PropertySequence isn't possible when operating on oak-API
+//                    PropertySequence propertySequence = getPropertySequence(membersTree);
+//                    return propertySequence.hasItem(authorizable.getID());
+                    return false;
+                }
+            } else {
+                PropertyState property = groupTree.getProperty(REP_MEMBERS);
+                if (property != null) {
+                    List<CoreValue> members = property.getValues();
+                    String authorizableUUID = getContentID(authorizableTree);
+                    for (CoreValue v : members) {
+                        if (authorizableUUID.equals(v.getString())) {
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
+        // no a member of the specified group
+        return false;
+    }
+
+    @Override
+    public boolean addMember(Tree groupTree, Tree newMemberTree) {
+        if (useMemberNode(groupTree)) {
+            NodeUtil groupNode = new NodeUtil(groupTree, contentSession);
+            NodeUtil membersNode = groupNode.getOrAddChild(REP_MEMBERS, NT_REP_MEMBERS);
+
+            //FIXME: replace usage of PropertySequence with oak-compatible utility
+//            PropertySequence properties = getPropertySequence(membersTree);
+//            String propName = Text.escapeIllegalJcrChars(authorizable.getID());
+//            if (properties.hasItem(propName)) {
+//                log.debug("Authorizable {} is already member of {}", authorizable, this);
+//                return false;
+//            } else {
+//                CoreValue newMember = createCoreValue(authorizable);
+//                properties.addProperty(propName, newMember);
+//            }
+        } else {
+            List<CoreValue> values;
+            CoreValue toAdd = createCoreValue(newMemberTree);
+            PropertyState property = groupTree.getProperty(REP_MEMBERS);
+            if (property != null) {
+                values = property.getValues();
+                if (values.contains(toAdd)) {
+                    return false;
+                } else {
+                    values.add(toAdd);
+                }
+            } else {
+                values = Collections.singletonList(toAdd);
+            }
+            groupTree.setProperty(REP_MEMBERS, values);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean removeMember(Tree groupTree, Tree memberTree) {
+        if (useMemberNode(groupTree)) {
+            Tree membersTree = groupTree.getChild(REP_MEMBERS);
+            if (membersTree != null) {
+                // FIXME: replace usage of PropertySequence with oak-compatible utility
+//                PropertySequence properties = getPropertySequence(membersTree);
+//                String propName = authorizable.getTree().getName();
+                // FIXME: fix.. testing for property name isn't correct.
+//                if (properties.hasItem(propName)) {
+//                    Property p = properties.getItem(propName);
+//                    userManager.removeInternalProperty(p.getParent(), propName);
+//                }
+//                return true;
+                return false;
+            }
+        } else {
+            PropertyState property = groupTree.getProperty(REP_MEMBERS);
+            if (property != null) {
+                CoreValue toRemove = createCoreValue(memberTree);
+                List<CoreValue> values = property.getValues();
+                if (values.remove(toRemove)) {
+                    if (values.isEmpty()) {
+                        groupTree.removeProperty(REP_MEMBERS);
+                    } else {
+                        groupTree.setProperty(REP_MEMBERS, values);
+                    }
+                    return true;
+                }
+            }
+        }
+
+        // nothing changed
+        log.debug("Authorizable {} was not member of {}", memberTree.getName(), groupTree.getName());
+        return false;
+    }
+
     //------------------------------------------------------------< private >---
-    private String getContentID(String authorizableId) throws RepositoryException {
+
+    private String getContentID(String authorizableId) {
         return IdentifierManager.generateUUID(authorizableId.toLowerCase());
     }
 
+    private String getContentID(Tree authorizableTree) {
+        return identifierManager.getIdentifier(authorizableTree);
+    }
+
     private boolean isAuthorizableTree(Tree tree, int type) {
-        // TODO: check for node type according to the specified type constraint
-        return true;
+        // FIXME: check for node type according to the specified type constraint
+        if (tree.hasProperty(JcrConstants.JCR_PRIMARYTYPE)) {
+            String ntName = tree.getProperty(JcrConstants.JCR_PRIMARYTYPE).getValue().getString();
+            switch (type) {
+                case UserManager.SEARCH_TYPE_GROUP:
+                    return NT_REP_GROUP.equals(ntName);
+                case UserManager.SEARCH_TYPE_USER:
+                    return NT_REP_USER.equals(ntName);
+                default:
+                    return NT_REP_USER.equals(ntName) || NT_REP_GROUP.equals(ntName);
+            }
+        }
+        return false;
     }
 
+    //-----------------------------------------------< private UserProvider >---
+
     private Tree createAuthorizableNode(String authorizableId, boolean isGroup, String intermediatePath) throws RepositoryException {
         String nodeName = Text.escapeIllegalJcrChars(authorizableId);
         NodeUtil folder = createFolderNodes(authorizableId, nodeName, isGroup, intermediatePath);
 
-        String ntName = (isGroup) ? UserConstants.NT_REP_GROUP : UserConstants.NT_REP_USER;
+        String ntName = (isGroup) ? NT_REP_GROUP : NT_REP_USER;
         NodeUtil authorizableNode = folder.addChild(nodeName, ntName);
 
         String nodeID = getContentID(authorizableId);
+        authorizableNode.setString(REP_AUTHORIZABLE_ID, authorizableId);
         authorizableNode.setString(JcrConstants.JCR_UUID, nodeID);
 
         return authorizableNode.getTree();
@@ -223,7 +450,7 @@ public class UserProviderImpl implements
         if (authTree == null) {
             folder = new NodeUtil(root.getTree(""), contentSession);
             for (String name : Text.explode(authRoot, '/', false)) {
-                folder = folder.getOrAddChild(name, UserConstants.NT_REP_AUTHORIZABLE_FOLDER);
+                folder = folder.getOrAddChild(name, NT_REP_AUTHORIZABLE_FOLDER);
             }
         }  else {
             folder = new NodeUtil(authTree, contentSession);
@@ -231,9 +458,9 @@ public class UserProviderImpl implements
         String folderPath = getFolderPath(authorizableId, intermediatePath);
         String[] segmts = Text.explode(folderPath, '/', false);
         for (String segment : segmts) {
-            folder = folder.getOrAddChild(segment, UserConstants.NT_REP_AUTHORIZABLE_FOLDER);
+            folder = folder.getOrAddChild(segment, NT_REP_AUTHORIZABLE_FOLDER);
             // TODO: remove check once UserValidator is active
-            if (!folder.hasPrimaryNodeTypeName(UserConstants.NT_REP_AUTHORIZABLE_FOLDER)) {
+            if (!folder.hasPrimaryNodeTypeName(NT_REP_AUTHORIZABLE_FOLDER)) {
                 String msg = "Cannot create user/group: Intermediate folders must be of type rep:AuthorizableFolder.";
                 throw new ConstraintViolationException(msg);
             }
@@ -243,7 +470,7 @@ public class UserProviderImpl implements
         while (folder.hasChild(nodeName)) {
             NodeUtil colliding = folder.getChild(nodeName);
             // TODO: remove check once UserValidator is active
-            if (colliding.hasPrimaryNodeTypeName(UserConstants.NT_REP_AUTHORIZABLE_FOLDER)) {
+            if (colliding.hasPrimaryNodeTypeName(NT_REP_AUTHORIZABLE_FOLDER)) {
                 log.debug("Existing folder node collides with user/group to be created. Expanding path by: " + colliding.getName());
                 folder = colliding;
             } else {
@@ -280,4 +507,93 @@ public class UserProviderImpl implements
         }
         return sb.toString();
     }
+
+    //-----------------------------------------< private MembershipProvider >---
+
+    private CoreValue createCoreValue(Tree authorizableTree) {
+        return contentSession.getCoreValueFactory().createValue(getContentID(authorizableTree), PropertyType.WEAKREFERENCE);
+    }
+
+    private boolean useMemberNode(Tree groupTree) {
+        return splitSize >= 4 && !groupTree.hasProperty(REP_MEMBERS);
+    }
+
+    /**
+     * Returns an iterator of authorizables which includes all indirect members
+     * of the given iterator of authorizables.
+     *
+     *
+     * @param declaredMembers
+     * @param authorizableType
+     * @return Iterator of Authorizable objects
+     */
+    private Iterator<String> getAllMembers(final Iterator<String> declaredMembers,
+                                           final int authorizableType) {
+        Iterator<Iterator<String>> inheritedMembers = new Iterator<Iterator<String>>() {
+            @Override
+            public boolean hasNext() {
+                return declaredMembers.hasNext();
+            }
+
+            @Override
+            public Iterator<String> next() {
+                String memberPath = declaredMembers.next();
+                return Iterators.concat(Iterators.singletonIterator(memberPath), inherited(memberPath));
+            }
+
+            @Override
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+
+            private Iterator<String> inherited(String authorizablePath) {
+                Tree group = getAuthorizableByPath(authorizablePath);
+                if (isAuthorizableTree(group, UserManager.SEARCH_TYPE_GROUP)) {
+                    return getMembers(group, authorizableType, true);
+                } else {
+                    return Iterators.emptyIterator();
+                }
+            }
+        };
+        return Iterators.filter(Iterators.concat(inheritedMembers), new ProcessedPathPredicate());
+    }
+
+    private Iterator<String> getAllMembership(final Iterator<String> groupPaths) {
+        Iterator<Iterator<String>> inheritedMembership = new Iterator<Iterator<String>>() {
+            @Override
+            public boolean hasNext() {
+                return groupPaths.hasNext();
+            }
+
+            @Override
+            public Iterator<String> next() {
+                String groupPath = groupPaths.next();
+                return Iterators.concat(Iterators.singletonIterator(groupPath), inherited(groupPath));
+            }
+
+            @Override
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+
+            private Iterator<String> inherited(String authorizablePath) {
+                Tree group = getAuthorizableByPath(authorizablePath);
+                if (isAuthorizableTree(group, UserManager.SEARCH_TYPE_GROUP)) {
+                    return getMembership(group, true);
+                } else {
+                    return Iterators.emptyIterator();
+                }
+            }
+        };
+
+        return Iterators.filter(Iterators.concat(inheritedMembership), new ProcessedPathPredicate());
+    }
+
+    private static final class ProcessedPathPredicate implements Predicate<String> {
+        private final Set<String> processed = new HashSet<String>();
+        @Override
+        public boolean apply(@Nullable String path) {
+            return processed.add(path);
+        }
+    }
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserValidator.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserValidator.java?rev=1373912&r1=1373911&r2=1373912&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserValidator.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserValidator.java Thu Aug 16 16:43:23 2012
@@ -16,11 +16,11 @@
  */
 package org.apache.jackrabbit.oak.security.user;
 
-import java.util.Collections;
 import java.util.Set;
 import javax.annotation.Nonnull;
 import javax.jcr.nodetype.ConstraintViolationException;
 
+import com.google.common.collect.ImmutableSet;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.spi.commit.UniquePropertyValidator;
@@ -36,6 +36,9 @@ import org.apache.jackrabbit.util.Text;
  */
 class UserValidator extends UniquePropertyValidator {
 
+    private final static Set<String> PROPERTY_NAMES = ImmutableSet.copyOf(new String[] {
+            UserConstants.REP_AUTHORIZABLE_ID, UserConstants.REP_PRINCIPAL_NAME});
+
     private final UserValidatorProvider provider;
 
     private final NodeUtil parentBefore;
@@ -52,7 +55,7 @@ class UserValidator extends UniqueProper
     @Override
     protected Set<String> getPropertyNames() {
         // TODO: make configurable
-        return Collections.singleton(UserConstants.REP_PRINCIPAL_NAME);
+        return PROPERTY_NAMES;
     }
 
     //----------------------------------------------------------< Validator >---

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserValidatorProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserValidatorProvider.java?rev=1373912&r1=1373911&r2=1373912&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserValidatorProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserValidatorProvider.java Thu Aug 16 16:43:23 2012
@@ -32,9 +32,16 @@ import org.apache.jackrabbit.oak.util.No
  */
 public class UserValidatorProvider implements ValidatorProvider {
 
-    private final UserManagerConfig config = null; // TODO
-    private final ContentSession contentSession = null; // TODO
+    private final ContentSession contentSession;
+    private final UserManagerConfig config;
 
+    public UserValidatorProvider(ContentSession contentSession, UserManagerConfig config) {
+        assert contentSession != null;
+        assert config != null;
+
+        this.contentSession = contentSession;
+        this.config = config;
+    }
     //--------------------------------------------------< ValidatorProvider >---
     @Nonnull
     @Override
@@ -47,12 +54,18 @@ public class UserValidatorProvider imple
     }
 
     //-----------------------------------------------------------< internal >---
-
+    @Nonnull
     UserManagerConfig getConfig() {
         return config;
     }
 
+    @Nonnull
     CoreValueFactory getValueFactory() {
         return contentSession.getCoreValueFactory();
     }
+
+    @Nonnull
+    ContentSession getContentSession() {
+        return contentSession;
+    }
 }
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/MembershipProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/MembershipProvider.java?rev=1373912&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/MembershipProvider.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/MembershipProvider.java Thu Aug 16 16:43:23 2012
@@ -0,0 +1,47 @@
+/*
+ * 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.user;
+
+import java.util.Iterator;
+
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.api.Tree;
+
+/**
+ * MembershipProvider... TODO
+ */
+public interface MembershipProvider {
+
+    @Nonnull
+    Iterator<String> getMembership(String authorizableId, boolean includeInherited);
+
+    @Nonnull
+    Iterator<String> getMembership(Tree authorizableTree, boolean includeInherited);
+
+    @Nonnull
+    Iterator<String> getMembers(String groupId, int authorizableType, boolean includeInherited);
+
+    @Nonnull
+    Iterator<String> getMembers(Tree groupTree, int authorizableType, boolean includeInherited);
+
+    boolean isMember(Tree groupTree, Tree authorizableTree, boolean includeInherited);
+
+    boolean addMember(Tree groupTree, Tree newMemberTree);
+
+    boolean removeMember(Tree groupTree, Tree memberTree);
+}

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/UserConstants.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/UserConstants.java?rev=1373912&r1=1373911&r2=1373912&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/UserConstants.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/UserConstants.java Thu Aug 16 16:43:23 2012
@@ -27,6 +27,7 @@ public interface UserConstants {
     String NT_REP_GROUP = "rep:Group";
     String NT_REP_MEMBERS = "rep:Members";
     String REP_PRINCIPAL_NAME = "rep:principalName";
+    String REP_AUTHORIZABLE_ID = "rep:authorizableId";
     String REP_PASSWORD = "rep:password";
     String REP_DISABLED = "rep:disabled";
     String REP_MEMBERS = "rep:members";

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/UserProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/UserProvider.java?rev=1373912&r1=1373911&r2=1373912&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/UserProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/UserProvider.java Thu Aug 16 16:43:23 2012
@@ -35,10 +35,10 @@ public interface UserProvider {
     Tree createGroup(String groupId, String intermediateJcrPath) throws RepositoryException;
 
     @CheckForNull
-    Tree getAuthorizable(String authorizableId) throws RepositoryException;
+    Tree getAuthorizable(String authorizableId);
 
     @CheckForNull
-    Tree getAuthorizable(String authorizableId, int authorizableType) throws RepositoryException;
+    Tree getAuthorizable(String authorizableId, int authorizableType);
 
     @CheckForNull
     Tree getAuthorizableByPath(String authorizableOakPath);

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableImpl.java?rev=1373912&r1=1373911&r2=1373912&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableImpl.java Thu Aug 16 16:43:23 2012
@@ -16,28 +16,32 @@
  */
 package org.apache.jackrabbit.oak.jcr.security.user;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.PropertyDefinition;
+
 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.commons.iterator.RangeIteratorAdapter;
+import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
+import org.apache.jackrabbit.oak.spi.security.user.MembershipProvider;
 import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
 import org.apache.jackrabbit.util.Text;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.PropertyIterator;
-import javax.jcr.RepositoryException;
-import javax.jcr.Value;
-import javax.jcr.nodetype.NodeType;
-import javax.jcr.nodetype.PropertyDefinition;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-
 /**
  * AuthorizableImpl... TODO
  *
@@ -76,8 +80,13 @@ abstract class AuthorizableImpl implemen
      * @see org.apache.jackrabbit.api.security.user.Authorizable#getID()
      */
     @Override
-    public String getID() throws RepositoryException {
-        return Text.unescapeIllegalJcrChars(getNode().getName());
+    public String getID() {
+        PropertyState idProp = tree.getProperty(UserConstants.REP_AUTHORIZABLE_ID);
+        if (idProp != null) {
+            return idProp.getValue().getString();
+        } else {
+            return Text.unescapeIllegalJcrChars(getTree().getName());
+        }
     }
 
     /**
@@ -107,7 +116,7 @@ abstract class AuthorizableImpl implemen
             throw new RepositoryException("The administrator cannot be removed.");
         }
         userManager.onRemove(this);
-        node.remove();
+        tree.remove();
     }
 
     /**
@@ -272,12 +281,8 @@ abstract class AuthorizableImpl implemen
      */
     @Override
     public String toString() {
-        try {
-            String typeStr = (isGroup()) ? "Group '" : "User '";
-            return new StringBuilder().append(typeStr).append(getID()).append('\'').toString();
-        } catch (RepositoryException e) {
-            return super.toString();
-        }
+        String typeStr = (isGroup()) ? "Group '" : "User '";
+        return new StringBuilder().append(typeStr).append(getID()).append('\'').toString();
     }
 
     //--------------------------------------------------------------------------
@@ -292,10 +297,6 @@ abstract class AuthorizableImpl implemen
         return tree;
     }
 
-    String getContentID() throws RepositoryException {
-        return getNode().getUUID();
-    }
-
     String getJcrName(String oakName) {
         return userManager.getJcrName(oakName);
     }
@@ -314,8 +315,8 @@ abstract class AuthorizableImpl implemen
     String getPrincipalName() throws RepositoryException {
         String principalName;
         String propName = getJcrName(REP_PRINCIPAL_NAME);
-        if (node.hasProperty(propName)) {
-            principalName = node.getProperty(propName).getString();
+        if (tree.hasProperty(propName)) {
+            principalName = tree.getProperty(propName).getValue().getString();
         } else {
             log.debug("Authorizable without principal name -> using ID as fallback.");
             principalName = getID();
@@ -421,7 +422,13 @@ abstract class AuthorizableImpl implemen
             return Collections.<Group>emptySet().iterator();
         }
 
-        MembershipManager membershipManager = userManager.getMembershipManager();
-        return membershipManager.getMembership(this, includeInherited);
+        MembershipProvider mMgr = userManager.getMembershipProvider();
+        Iterator<String> oakPaths = mMgr.getMembership(tree, includeInherited);
+        if (oakPaths.hasNext()) {
+            AuthorizableIterator groups = new AuthorizableIterator(oakPaths, userManager, UserManager.SEARCH_TYPE_AUTHORIZABLE);
+            return new RangeIteratorAdapter(groups, groups.getSize());
+        } else {
+            return RangeIteratorAdapter.EMPTY;
+        }
     }
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableIterator.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableIterator.java?rev=1373912&r1=1373911&r2=1373912&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableIterator.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableIterator.java Thu Aug 16 16:43:23 2012
@@ -16,18 +16,17 @@
  */
 package org.apache.jackrabbit.oak.jcr.security.user;
 
-import java.util.Arrays;
-import java.util.Collection;
 import java.util.Iterator;
-import java.util.List;
-import java.util.NoSuchElementException;
-import javax.jcr.Property;
+import javax.annotation.Nullable;
+import javax.jcr.RangeIterator;
 import javax.jcr.RepositoryException;
-import javax.jcr.Value;
 
+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.user.Authorizable;
-import org.apache.jackrabbit.commons.flat.PropertySequence;
-import org.apache.jackrabbit.oak.api.CoreValue;
+import org.apache.jackrabbit.api.security.user.UserManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -38,50 +37,35 @@ class AuthorizableIterator implements It
 
     private static final Logger log = LoggerFactory.getLogger(AuthorizableIterator.class);
 
-    private final Iterator<?> authorizableIds;
-    private final AuthorizableTypePredicate predicate;
-    private final UserManagerImpl userManager;
+    private final Iterator<Authorizable> authorizables;
     private final long size;
 
     private Authorizable next;
 
-    AuthorizableIterator(List<CoreValue> authorizableNodeIds, int authorizableType, UserManagerImpl userManager) {
-        this(Arrays.asList(authorizableNodeIds).iterator(), authorizableType, userManager, authorizableNodeIds.size());
+    AuthorizableIterator(Iterator<String> authorizableOakPath, UserManagerImpl userManager) {
+        this(authorizableOakPath, userManager, UserManager.SEARCH_TYPE_AUTHORIZABLE);
     }
 
-    AuthorizableIterator(PropertySequence authorizableNodeIds, int authorizableType, UserManagerImpl userManager) {
-        this(authorizableNodeIds.iterator(), authorizableType, userManager, -1);  // TODO calculate size here
-    }
-
-    AuthorizableIterator(Collection<String> authorizablePaths, int authorizableType, UserManagerImpl userManager) {
-        this(authorizablePaths.iterator(), authorizableType, userManager, authorizablePaths.size());
-    }
+    AuthorizableIterator(Iterator<String> authorizableOakPaths, UserManagerImpl userManager, int authorizableType) {
+        Iterator<Authorizable> it = Iterators.transform(authorizableOakPaths, new ToAuthorizable(userManager, authorizableType));
+        this.authorizables = Iterators.filter(it, Predicates.notNull());
 
-    private AuthorizableIterator(Iterator<?> authorizableIds, int authorizableType,
-                                 UserManagerImpl userManager, long size) {
-        this.authorizableIds = authorizableIds;
-        this.predicate = new AuthorizableTypePredicate(authorizableType);
-        this.userManager = userManager;
-        this.size = size;
-
-        next = fetchNext();
+        if (authorizableOakPaths instanceof RangeIterator) {
+            size = ((RangeIterator) authorizableOakPaths).getSize();
+        } else {
+            size = -1;
+        }
     }
 
     //-----------------------------------------------------------< Iterator >---
     @Override
     public boolean hasNext() {
-        return next != null;
+        return authorizables.hasNext();
     }
 
     @Override
     public Authorizable next() {
-        if (next == null) {
-            throw new NoSuchElementException();
-        }
-
-        Authorizable a = next;
-        next = fetchNext();
-        return a;
+        return authorizables.next();
     }
 
     @Override
@@ -94,36 +78,57 @@ class AuthorizableIterator implements It
         return size;
     }
 
-    private Authorizable fetchNext() {
-        while (authorizableIds.hasNext()) {
-            Object next = authorizableIds.next();
+    //--------------------------------------------------------------------------
+
+    private static class ToAuthorizable implements Function<String, Authorizable> {
+
+        private final UserManagerImpl userManager;
+        private final Predicate predicate;
+
+        public ToAuthorizable(UserManagerImpl userManager, int type) {
+            this.userManager = userManager;
+            this.predicate = new AuthorizableTypePredicate(type);
+        }
+
+        @Override
+        public Authorizable apply(@Nullable String oakPath) {
+            String jcrPath = userManager.getNamePathMapper().getJcrPath(oakPath);
             try {
-                Authorizable a;
-                if (next instanceof String) {
-                    a = userManager.getAuthorizableByPath(next.toString());
-                } else {
-                    String nid = getNodeId(next);
-                    a = userManager.getAuthorizableByNodeID(nid);
-                }
-                if (a != null && predicate.evaluate(a)) {
+                Authorizable a = userManager.getAuthorizableByPath(jcrPath);
+                if (predicate.apply(a)) {
                     return a;
                 }
             } catch (RepositoryException e) {
-                log.debug(e.getMessage());
+                log.debug("Failed to access authorizable " + jcrPath);
             }
+            return null;
         }
-        return null;
     }
 
-    private static String getNodeId(Object o) throws RepositoryException {
-        if (o instanceof CoreValue) {
-            return ((CoreValue) o).getString();
-        } else if (o instanceof Value) {
-            return ((Value) o).getString();
-        } else if (o instanceof Property) {
-            return ((Property) o).getParent().getUUID();
-        } else {
-            return o.toString();
+    private static class AuthorizableTypePredicate implements Predicate<Authorizable> {
+
+        private final int authorizableType;
+
+        AuthorizableTypePredicate(int authorizableType) {
+            this.authorizableType = authorizableType;
+        }
+
+        @Override
+        public boolean apply(Authorizable authorizable) {
+            if (authorizable == null) {
+                return false;
+            }
+            switch (authorizableType) {
+                case UserManager.SEARCH_TYPE_AUTHORIZABLE:
+                    return true;
+                case UserManager.SEARCH_TYPE_GROUP:
+                    return authorizable.isGroup();
+                case UserManager.SEARCH_TYPE_USER:
+                    return !authorizable.isGroup();
+                default:
+                    log.warn("Illegal authorizable type " + authorizableType);
+                    return false;
+            }
         }
     }
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImpl.java?rev=1373912&r1=1373911&r2=1373912&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImpl.java Thu Aug 16 16:43:23 2012
@@ -25,9 +25,11 @@ import javax.jcr.RepositoryException;
 import org.apache.jackrabbit.api.security.user.Authorizable;
 import org.apache.jackrabbit.api.security.user.Group;
 import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.commons.iterator.RangeIteratorAdapter;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.spi.security.principal.TreeBasedPrincipal;
 import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
+import org.apache.jackrabbit.oak.spi.security.user.MembershipProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -117,8 +119,9 @@ class GroupImpl extends AuthorizableImpl
             return false;
         }
 
+        String memberID = authorizable.getID();
         if (authorizableImpl.isGroup()) {
-            if (getID().equals(authorizableImpl.getID())) {
+            if (getID().equals(memberID)) {
                 String msg = "Attempt to add a group as member of itself (" + getID() + ").";
                 log.debug(msg);
                 return false;
@@ -130,11 +133,11 @@ class GroupImpl extends AuthorizableImpl
         }
 
         if (isDeclaredMember(authorizable)) {
-            log.debug("Authorizable {} is already declared member of {}", authorizable.getID(), getID());
+            log.debug("Authorizable {} is already declared member of {}", memberID, getID());
             return false;
         }
 
-        return getUserManager().getMembershipManager().addMember(this, authorizableImpl);
+        return getUserManager().getMembershipProvider().addMember(getTree(), authorizableImpl.getTree());
     }
 
     /**
@@ -149,7 +152,8 @@ class GroupImpl extends AuthorizableImpl
         if (isEveryone()) {
             return false;
         } else {
-            return getUserManager().getMembershipManager().removeMember(this, (AuthorizableImpl) authorizable);
+            MembershipProvider mMgr = getUserManager().getMembershipProvider();
+            return mMgr.removeMember(getTree(), ((AuthorizableImpl) authorizable).getTree());
         }
     }
 
@@ -163,13 +167,21 @@ class GroupImpl extends AuthorizableImpl
      * @throws RepositoryException If an error occurs.
      */
     private Iterator<Authorizable> getMembers(boolean includeInherited) throws RepositoryException {
+        UserManagerImpl uMgr = getUserManager();
         if (isEveryone()) {
             // TODO: improve using authorizable-query
             String propName = getJcrName(REP_PRINCIPAL_NAME);
-            return getUserManager().findAuthorizables(propName, null, UserManager.SEARCH_TYPE_AUTHORIZABLE);
+            return uMgr.findAuthorizables(propName, null, UserManager.SEARCH_TYPE_AUTHORIZABLE);
         } else {
-            MembershipManager mMgr = getUserManager().getMembershipManager();
-            return mMgr.getMembers(this, UserManager.SEARCH_TYPE_AUTHORIZABLE, includeInherited);
+            MembershipProvider mMgr = uMgr.getMembershipProvider();
+            Iterator oakPaths = mMgr.getMembers(getTree(), UserManager.SEARCH_TYPE_AUTHORIZABLE, includeInherited);
+            if (!oakPaths.hasNext()) {
+                AuthorizableIterator iterator = new AuthorizableIterator(oakPaths, uMgr);
+                return new RangeIteratorAdapter(iterator, iterator.getSize());
+            } else {
+                return RangeIteratorAdapter.EMPTY;
+
+            }
         }
     }
 
@@ -193,8 +205,9 @@ class GroupImpl extends AuthorizableImpl
         } else if (getID().equals(authorizable.getID())) {
             return false;
         } else {
-            AuthorizableImpl authorizableImpl = (AuthorizableImpl) authorizable;
-            return getUserManager().getMembershipManager().isMember(this, authorizableImpl, includeInherited);
+            Tree authorizableTree = ((AuthorizableImpl) authorizable).getTree();
+            MembershipProvider mgr = getUserManager().getMembershipProvider();
+            return mgr.isMember(this.getTree(), authorizableTree, includeInherited);
         }
     }
 
@@ -204,7 +217,7 @@ class GroupImpl extends AuthorizableImpl
     private class GroupPrincipal extends TreeBasedPrincipal implements java.security.acl.Group {
 
         GroupPrincipal(String principalName) {
-            super(principalName, getTree(), getUserManager().getSessionDelegate().getNamePathMapper());
+            super(principalName, getTree(), getUserManager().getNamePathMapper());
         }
 
         @Override

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/ImpersonationImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/ImpersonationImpl.java?rev=1373912&r1=1373911&r2=1373912&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/ImpersonationImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/ImpersonationImpl.java Thu Aug 16 16:43:23 2012
@@ -31,6 +31,7 @@ import org.apache.jackrabbit.api.securit
 import org.apache.jackrabbit.api.security.principal.PrincipalManager;
 import org.apache.jackrabbit.api.security.user.Authorizable;
 import org.apache.jackrabbit.api.security.user.Impersonation;
+import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Tree;
@@ -111,7 +112,7 @@ class ImpersonationImpl implements Imper
 
         // make sure the given principal doesn't refer to the admin user.
         Authorizable a = user.getUserManager().getAuthorizable(p);
-        if (a != null && user.getUserManager().isAdminId(a.getID())) {
+        if (a != null && !a.isGroup() && ((User)a).isAdmin()) {
             log.debug("Admin principal is already granted impersonation.");
             return false;
         }
@@ -192,8 +193,11 @@ class ImpersonationImpl implements Imper
 
     private void updateImpersonatorNames(Set<String> principalNames) throws RepositoryException {
         String[] pNames = principalNames.toArray(new String[principalNames.size()]);
+        Tree userTree = user.getTree();
         if (pNames.length == 0) {
-            user.getUserManager().removeInternalProperty(user.getTree(), REP_IMPERSONATORS);
+            if (userTree.hasProperty(REP_IMPERSONATORS)) {
+                userTree.removeProperty(REP_IMPERSONATORS);
+            } // nothing to do.
         } else {
             user.getUserManager().setInternalProperty(user.getTree(), REP_IMPERSONATORS, pNames, PropertyType.STRING);
         }
@@ -207,4 +211,4 @@ class ImpersonationImpl implements Imper
             throw new UnsupportedRepositoryOperationException("Principal management not supported.");
         }
     }
-}
\ No newline at end of file
+}

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserImpl.java?rev=1373912&r1=1373911&r2=1373912&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserImpl.java Thu Aug 16 16:43:23 2012
@@ -67,7 +67,7 @@ class UserImpl extends AuthorizableImpl 
     @Override
     public Principal getPrincipal() throws RepositoryException {
         String principalName = getPrincipalName();
-        return new TreeBasedPrincipal(principalName, getTree(), getUserManager().getSessionDelegate().getNamePathMapper());
+        return new TreeBasedPrincipal(principalName, getTree(), getUserManager().getNamePathMapper());
 
     }
 
@@ -77,13 +77,7 @@ class UserImpl extends AuthorizableImpl 
      */
     @Override
     public boolean isAdmin() {
-        try {
-            return getUserManager().isAdminId(getID());
-        } catch (RepositoryException e) {
-            // should never get here
-            log.error("Internal error while retrieving UserID.", e);
-            return false;
-        }
+        return getUserManager().isAdminId(getID());
     }
 
     /**
@@ -139,11 +133,12 @@ class UserImpl extends AuthorizableImpl 
         if (isAdmin()) {
             throw new RepositoryException("The administrator user cannot be disabled.");
         }
+        Tree userTree = getTree();
         if (reason == null) {
             if (isDisabled()) {
                 // enable the user again.
-                getUserManager().removeInternalProperty(getTree(), REP_DISABLED);
-            } // else: nothing to do.
+                userTree.removeProperty(REP_DISABLED);
+            }
         } else {
             getUserManager().setInternalProperty(getTree(), REP_DISABLED, reason, PropertyType.STRING);
         }

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerImpl.java?rev=1373912&r1=1373911&r2=1373912&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerImpl.java Thu Aug 16 16:43:23 2012
@@ -23,7 +23,6 @@ import java.util.Iterator;
 import java.util.List;
 import javax.annotation.CheckForNull;
 import javax.jcr.Node;
-import javax.jcr.PathNotFoundException;
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
@@ -37,19 +36,19 @@ import org.apache.jackrabbit.api.securit
 import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.api.security.user.UserManager;
 import org.apache.jackrabbit.oak.api.CoreValue;
-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.jcr.SessionDelegate;
 import org.apache.jackrabbit.oak.jcr.security.user.query.XPathQueryBuilder;
 import org.apache.jackrabbit.oak.jcr.security.user.query.XPathQueryEvaluator;
 import org.apache.jackrabbit.oak.jcr.value.ValueConverter;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.security.user.UserProviderImpl;
 import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
+import org.apache.jackrabbit.oak.spi.security.user.MembershipProvider;
 import org.apache.jackrabbit.oak.spi.security.user.PasswordUtility;
 import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
 import org.apache.jackrabbit.oak.spi.security.user.UserManagerConfig;
-import org.apache.jackrabbit.oak.spi.security.user.UserProvider;
 import org.apache.jackrabbit.oak.spi.security.user.action.AuthorizableAction;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -63,11 +62,9 @@ public class UserManagerImpl implements 
 
     private final SessionDelegate sessionDelegate;
     private final UserManagerConfig config;
-    private final UserProvider userProvider;
+    private final UserProviderImpl userProvider;
     private final NodeTreeUtil util;
 
-    private MembershipManager membershipManager;
-    
     public UserManagerImpl(SessionDelegate sessionDelegate, Root root, UserManagerConfig config) {
         this.sessionDelegate = sessionDelegate;
         this.config = (config == null) ? new UserManagerConfig("admin") : config;
@@ -352,37 +349,16 @@ public class UserManagerImpl implements 
         userNode.setProperty(oakName, cvs);
     }
 
-    void setInternalProperty(Tree userNode, String oakName, List<CoreValue> values) throws RepositoryException {
-        userNode.setProperty(oakName, values);
-    }
-
-    void removeInternalProperty(Tree userNode, String oakName) throws RepositoryException {
-        PropertyState pd = userNode.getProperty(oakName);
-        if (pd == null) {
-            throw new PathNotFoundException("Missing authorizable property " + oakName);
-        } else {
-            userNode.removeProperty(oakName);
-        }
-    }
-
     Session getSession() {
         return sessionDelegate.getSession();
     }
 
-    SessionDelegate getSessionDelegate() {
-        return sessionDelegate;
+    NamePathMapper getNamePathMapper() {
+        return sessionDelegate.getNamePathMapper();
     }
 
-    MembershipManager getMembershipManager() {
-        if (membershipManager == null) {
-            int splitSize = config.getConfigValue(UserManagerConfig.PARAM_GROUP_MEMBERSHIP_SPLIT_SIZE, 0);
-            if (splitSize < 4) {
-                log.warn("Invalid value {} for {}. Expected integer >= 4", splitSize, UserManagerConfig.PARAM_GROUP_MEMBERSHIP_SPLIT_SIZE);
-                splitSize = 0;
-            }
-            membershipManager = new MembershipManager(this, splitSize);
-        }
-        return membershipManager;
+    MembershipProvider getMembershipProvider() {
+        return userProvider;
     }
 
     @CheckForNull
@@ -400,11 +376,6 @@ public class UserManagerImpl implements 
         }
     }
 
-    @CheckForNull
-    Authorizable getAuthorizableByNodeID(String identifier) throws RepositoryException {
-        return getAuthorizable(sessionDelegate.getIdManager().getTree(identifier));
-    }
-
     String getJcrName(String oakName) {
         return sessionDelegate.getNamePathMapper().getJcrName(oakName);
     }