You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by an...@apache.org on 2008/03/19 14:57:11 UTC

svn commit: r638834 [10/14] - in /jackrabbit/trunk: jackrabbit-api/src/main/java/org/apache/jackrabbit/api/ jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/ jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/principal/ jack...

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserAccessControlProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserAccessControlProvider.java?rev=638834&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserAccessControlProvider.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserAccessControlProvider.java Wed Mar 19 06:56:13 2008
@@ -0,0 +1,429 @@
+/*
+ * 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.core.security.user;
+
+import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.core.security.authorization.AbstractAccessControlProvider;
+import org.apache.jackrabbit.core.security.authorization.AbstractCompiledPermissions;
+import org.apache.jackrabbit.core.security.authorization.AccessControlProvider;
+import org.apache.jackrabbit.core.security.authorization.CompiledPermissions;
+import org.apache.jackrabbit.core.security.authorization.Permission;
+import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry;
+import org.apache.jackrabbit.core.security.principal.ItemBasedPrincipal;
+import org.apache.jackrabbit.core.security.principal.PrincipalImpl;
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.util.Text;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.ObservationManager;
+import javax.jcr.observation.EventListener;
+import javax.jcr.observation.EventIterator;
+import java.security.Principal;
+import java.security.acl.Group;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Implementation of the <code>AccessControlProvider</code> interface that
+ * is used to protected the 'security workspace' containing the user and
+ * group data. It applies special care to make sure that modifying user data
+ * (e.g. password), group membership and impersonation is properly controlled.
+ * The access control policy defined by this provider has the following
+ * characteristics:
+ * TODO describe policy.
+ */
+public class UserAccessControlProvider extends AbstractAccessControlProvider
+        implements UserConstants {
+
+    private static Logger log = LoggerFactory.getLogger(UserAccessControlProvider.class);
+
+    private Path groupsPath;
+    private Path usersPath;
+
+    private SessionImpl systemSession;
+    private ObservationManager obsMgr;
+
+    private String userAdminGroup;
+    private String groupAdminGroup;
+
+    /**
+     *
+     */
+    public UserAccessControlProvider() {
+        super("Access control policy for the 'security' workspace.",
+              "Policy that defines the general access control rules for the security workspace.");
+    }
+
+    //----------------------------------------------< AccessControlProvider >---
+     /**
+     * @see AccessControlProvider#init(Session, Map)
+     */
+    public void init(Session systemSession, Map options) throws RepositoryException {
+        if (initialized) {
+            throw new IllegalStateException("already initialized");
+        }
+        if (systemSession instanceof SessionImpl) {
+            this.systemSession = (SessionImpl) systemSession;
+            obsMgr = systemSession.getWorkspace().getObservationManager();
+
+            userAdminGroup = (options.containsKey(USER_ADMIN_GROUP_NAME)) ? options.get(USER_ADMIN_GROUP_NAME).toString() : USER_ADMIN_GROUP_NAME;
+            groupAdminGroup = (options.containsKey(GROUP_ADMIN_GROUP_NAME)) ? options.get(GROUP_ADMIN_GROUP_NAME).toString() : GROUP_ADMIN_GROUP_NAME;
+
+            // make sure the groups exist (and ev. create them).
+            // TODO: review again.
+            UserManager uMgr = this.systemSession.getUserManager();
+            if (!initGroup(uMgr, userAdminGroup)) {
+                log.warn("Unable to initialize User admininistrator group -> no user admins.");
+                userAdminGroup = null;
+            }
+            if (!initGroup(uMgr, groupAdminGroup)) {
+                log.warn("Unable to initialize Group admininistrator group -> no group admins.");
+                groupAdminGroup = null;
+            }
+
+            usersPath = this.systemSession.getQPath(USERS_PATH);
+            groupsPath = this.systemSession.getQPath(GROUPS_PATH);
+
+        } else {
+            throw new RepositoryException("SessionImpl (system session) expected.");
+        }
+        initialized = true;
+    }
+
+    public CompiledPermissions compilePermissions(Set principals) throws ItemNotFoundException, RepositoryException {
+        checkInitialized();
+        if (isAdminOrSystem(principals)) {
+            return getAdminPermissions();
+        } else {
+            // determined the 'user' present in the given set of principals.
+            ItemBasedPrincipal userPrincipal = getUserPrincipal(principals);
+            NodeImpl userNode = getUserNode(userPrincipal);
+            if (userNode == null) {
+                // no 'user' within set of principals -> READ-only
+                return getReadOnlyPermissions();
+            } else {
+                return new CompiledPermissionsImpl(principals, userNode);
+            }
+        }
+    }
+
+    //------------------------------------------------------------< private >---
+
+    private ItemBasedPrincipal getUserPrincipal(Set principals) {
+        try {
+            UserManager uMgr = systemSession.getUserManager();
+            for (Iterator it = principals.iterator(); it.hasNext();) {
+                Principal p = (Principal) it.next();
+                if (!(p instanceof Group) && p instanceof ItemBasedPrincipal
+                        && uMgr.getAuthorizable(p) != null) {
+                    return (ItemBasedPrincipal) p;
+                }
+            }
+        } catch (RepositoryException e) {
+            // should never get here
+            log.error("Internal error while retrieving user principal", e.getMessage());
+        }
+        // none of the principals in the set is assigned to a User.
+        return null;
+    }
+
+    private NodeImpl getUserNode(ItemBasedPrincipal principal) {
+        NodeImpl userNode = null;
+        if (principal != null) {
+            try {
+                String path = principal.getPath();
+                userNode = (NodeImpl) systemSession.getNode(path);
+            } catch (RepositoryException e) {
+                log.warn("Error while retrieving user node.", e.getMessage());
+            }
+        }
+        return userNode;
+    }
+
+    private boolean isMember(Node userNode, Path memberPath) throws RepositoryException, PathNotFoundException {
+        // precondition: memberPath points to a rep:members property
+        String propPath = systemSession.getJCRPath(memberPath);
+        if (systemSession.propertyExists(propPath)) {
+            // check if any of the ref-values equals to the value created from
+            // the user-Node (which must be present if the user is member of the group)
+            Property membersProp = systemSession.getProperty(propPath);
+            List values = Arrays.asList(membersProp.getValues());
+            return values.contains(systemSession.getValueFactory().createValue(userNode));
+        } else {
+            return false;
+        }
+    }
+
+    private Node getExistingNode(Path path) throws RepositoryException {
+        String absPath = systemSession.getJCRPath(path.getNormalizedPath());
+        if (systemSession.nodeExists(absPath)) {
+            return systemSession.getNode(absPath);
+        } else if (systemSession.propertyExists(absPath)) {
+            return systemSession.getProperty(absPath).getParent();
+        } else {
+            String pPath = Text.getRelativeParent(absPath, 1);
+            if (systemSession.nodeExists(pPath)) {
+                return systemSession.getNode(pPath);
+            } else {
+                throw new ItemNotFoundException("Unable to determine permissions: No item and no existing parent for target path " + absPath);
+            }
+        }
+    }
+
+    /**
+     * Determine if for the given <code>path</code>, the set of privileges
+     * must be calculated.
+     *
+     * @param path
+     * @return true if <code>path</code> denotes an existing <code>Node</code>,
+     * false otherwise.
+     * @throws RepositoryException
+     */
+    private boolean doCalculatePrivileges(Path path) throws RepositoryException {
+        String absPath = systemSession.getJCRPath(path.getNormalizedPath());
+        // privileges can only be determined for existing nodes.
+        // not for properties and neither for non-existing nodes.
+        return systemSession.nodeExists(absPath);
+    }
+
+    private static boolean containsGroup(Set principals, String groupName) {
+        for (Iterator it = principals.iterator(); it.hasNext() && groupName != null;) {
+            Principal p = (Principal) it.next();
+            if (p.getName().equals(groupName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean initGroup(UserManager uMgr, String principalName) {
+        boolean success;
+        Principal prnc = new PrincipalImpl(principalName);
+        try {
+            Authorizable auth = uMgr.getAuthorizable(prnc);
+            if (auth == null) {
+                success = (uMgr.createGroup(prnc) != null);
+            } else {
+                success = auth.isGroup();
+                if (!success) {
+                    log.warn("Cannot create group '" + principalName + "'; User with that principal already exists.");
+                }
+            }
+        } catch (RepositoryException e) {
+            // should never get here
+            log.error("Error while initializing user/group administrators", e.getMessage());
+            success = false;
+        }
+        return success;
+    }
+
+    //--------------------------------------------------------< inner class >---
+    /**
+     *
+     */
+    private class CompiledPermissionsImpl extends AbstractCompiledPermissions
+            implements EventListener {
+
+        private final NodeImpl userNode;
+        private final boolean isUserAdmin;
+        private final boolean isGroupAdmin;
+
+        protected CompiledPermissionsImpl(Set principals, NodeImpl userNode) throws RepositoryException {
+            this.userNode = userNode;
+
+            isUserAdmin = containsGroup(principals, userAdminGroup);
+            isGroupAdmin = containsGroup(principals, groupAdminGroup);
+
+            int events = Event.PROPERTY_CHANGED | Event.PROPERTY_ADDED | Event.PROPERTY_REMOVED;
+            obsMgr.addEventListener(this, events, GROUPS_PATH, true, null, null, false);
+        }
+
+        //------------------------------------< AbstractCompiledPermissions >---
+        /**
+         * @see AbstractCompiledPermissions#buildResult(Path)
+         */
+        protected Result buildResult(Path path) throws RepositoryException {
+            // default permission and default privileges
+            int perms = Permission.READ;
+            int privs;
+            boolean calcPrivs = doCalculatePrivileges(path);
+            if (calcPrivs) {
+                privs = PrivilegeRegistry.READ;
+            } else {
+                privs = 0;
+            }
+
+            Path abs2Path = path.subPath(0, 4);
+            if (usersPath.equals(abs2Path)) {
+                NodeImpl node = (NodeImpl) getExistingNode(path);
+                NodeImpl authN = null;
+                // seek next rep:authorizable parent
+                if (node.isNodeType(NT_REP_AUTHORIZABLE)) {
+                    authN = node;
+                } else if (node.isNodeType(NT_REP_AUTHORIZABLE_FOLDER)) {
+                    NodeImpl parent = node;
+                    while (authN == null && parent.getDepth() > 0) {
+                        parent = (NodeImpl) parent.getParent();
+                        if (parent.isNodeType(NT_REP_AUTHORIZABLE)) {
+                            authN = parent;
+                        } else if (!parent.isNodeType(NT_REP_AUTHORIZABLE_FOLDER)) {
+                            // outside of user/group-tree
+                            break;
+                        }
+                    }
+                } // else: outside of user tree -> authN = null
+
+                if (authN != null && authN.isNodeType(NT_REP_USER)) {
+                    int relDepth = systemSession.getHierarchyManager().getRelativeDepth(userNode.getNodeId(), authN.getNodeId());
+                    switch (relDepth) {
+                        case -1:
+                            // authN is not below the userNode -> can't write anyway.
+                            break;
+                        case 0:
+                            /*
+                            authN is same node as userNode. 2 cases to distinguish
+                            1) user is User-Admin -> R, W
+                            2) user is NOT U-admin but nodeID is its own node.
+                            */
+                            if (isUserAdmin) {
+                                // principals contain 'user-admin' -> user can modify
+                                // any item below the user-node.
+                                perms = Permission.ALL;
+                                if (calcPrivs) {
+                                    privs |= PrivilegeRegistry.WRITE;
+                                }
+                            } else if (userNode.isSame(node)) {
+                                // user can only read && write his own props
+                                perms |= (Permission.SET_PROPERTY | Permission.REMOVE_PROPERTY);
+                                if (calcPrivs) {
+                                    privs |= PrivilegeRegistry.MODIFY_PROPERTIES;
+                                }
+                            } // else some other node below but not U-admin -> read-only.
+                            break;
+                        default:
+                            /*
+                            authN is somewhere below the userNode, i.e.
+                            1) nodeId points to an authorizable below userNode
+                            2) nodeId points to an auth-folder below some authorizable below userNode.
+
+                            In either case user-admin group is required to have write
+                            permission.
+                            */
+                            if (isUserAdmin) {
+                                perms = Permission.ALL;
+                                if (calcPrivs) {
+                                    privs |= PrivilegeRegistry.WRITE;
+                                }
+                            }
+                    }
+                } // no rep:User parent node found.
+            } else if (groupsPath.equals(abs2Path)) {
+                /*
+                below group-tree:
+                - test if the user is group-administrator.
+                - in addition the following special condition must be checked:
+
+                if the target id is 'rep:members' the user MUST be member of that
+                the containing group in order to have WRITE privilege.
+                this required in order to make sure the group-admin cannot
+                modify the members of some other groups e.g. administrators.
+                */
+                if (isGroupAdmin) {
+                    if (P_MEMBERS.equals(path.getNameElement().getName())) {
+                        if (isMember(userNode, path)) {
+                            perms |= (Permission.SET_PROPERTY | Permission.REMOVE_PROPERTY);
+                        }
+                    } else {
+                        perms = Permission.ALL;
+                        if (calcPrivs) {
+                            privs |= PrivilegeRegistry.WRITE;
+                        }
+                    }
+                }
+
+            } // else outside of user/group tree -> read only.
+
+            return new Result(perms, privs);
+        }
+
+        //--------------------------------------------< CompiledPermissions >---
+        /**
+         * @see CompiledPermissions#close()
+         */
+        public void close() {
+            try {
+                obsMgr.removeEventListener(this);
+            } catch (RepositoryException e) {
+                log.error("Internal error: ", e.getMessage());
+            }
+            super.close();
+        }
+
+        /**
+         * @see CompiledPermissions#grants(Path, int)
+         */
+        public boolean grants(Path absPath, int permissions) throws RepositoryException {
+            if (permissions == Permission.READ) {
+                // read is always granted
+                return true;
+            }
+            // TODO: additional simple checks.... (last accessed... etc)
+
+            // finally retrieve from cache (or build)
+            return super.grants(absPath, permissions);
+        }
+
+        //--------------------------------------------------< EventListener >---
+        /**
+         * Event listener is only interested in changes of group-membership
+         * that effect the permission-evaluation.
+         *
+         * @param events
+         */
+        public void onEvent(EventIterator events) {
+            while (events.hasNext()) {
+                Event ev = events.nextEvent();
+                try {
+                    String evPath = ev.getPath();
+                    if ("rep:members".equals(Text.getName(evPath))) {
+                        // TODO: add better evaluation.
+                        clearCache();
+                        // only need to clear the cache once. stop processing
+                        break;
+                    }
+                } catch (RepositoryException e) {
+                    // should never get here
+                    log.error("Internal error ", e.getMessage());
+                }
+            }
+        }
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserAccessControlProvider.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserAccessControlProvider.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserConstants.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserConstants.java?rev=638834&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserConstants.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserConstants.java Wed Mar 19 06:56:13 2008
@@ -0,0 +1,68 @@
+/*
+ * 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.core.security.user;
+
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.NameFactory;
+import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
+
+/**
+ * <code>UserConstants</code>...
+ */
+interface UserConstants {
+
+    NameFactory NF = NameFactoryImpl.getInstance();
+    
+    /**
+     * root-path to security related content e.g. principals
+     */
+    String SECURITY_ROOT_PATH = "/rep:security";
+    String AUTHORIZABLES_PATH = SECURITY_ROOT_PATH + "/rep:authorizables";
+    String USERS_PATH = AUTHORIZABLES_PATH + "/rep:users";
+    String GROUPS_PATH = AUTHORIZABLES_PATH + "/rep:groups";
+
+    /**
+     * Configuration key and default value for the the name of the
+     * 'UserAdmin' group-principal.
+     */
+    String USER_ADMIN_GROUP_NAME = "UserAdmin";
+    /**
+     * Configuration key and default value for the the name of the
+     * 'GroupAdmin' group-principal
+     */
+    String GROUP_ADMIN_GROUP_NAME = "GroupAdmin";
+
+    Name P_REFEREES = NF.create(Name.NS_REP_URI, "referees");
+    Name P_PRINCIPAL_NAME = NF.create(Name.NS_REP_URI, "principalName");
+
+    Name P_USERID = NF.create(Name.NS_REP_URI, "userId");
+    Name P_PASSWORD = NF.create(Name.NS_REP_URI, "password");
+
+    Name P_MEMBERS = NF.create(Name.NS_REP_URI, "members");
+
+    /**
+     * Name of the user property containing the principal names of those allowed
+     * to impersonate.
+     */
+    Name P_IMPERSONATORS = NF.create(Name.NS_REP_URI, "impersonators");
+
+    Name NT_REP_AUTHORIZABLE = NF.create(Name.NS_REP_URI, "Authorizable");
+    Name NT_REP_AUTHORIZABLE_FOLDER = NF.create(Name.NS_REP_URI, "AuthorizableFolder");
+    Name NT_REP_USER = NF.create(Name.NS_REP_URI, "User");
+    Name NT_REP_GROUP = NF.create(Name.NS_REP_URI, "Group");
+
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserConstants.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserConstants.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImpl.java?rev=638834&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImpl.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImpl.java Wed Mar 19 06:56:13 2008
@@ -0,0 +1,128 @@
+/*
+ * 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.core.security.user;
+
+import org.apache.jackrabbit.core.NodeImpl;
+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.core.security.authentication.CryptedSimpleCredentials;
+import org.apache.jackrabbit.core.security.principal.AdminPrincipal;
+import org.apache.jackrabbit.util.Text;
+
+import javax.jcr.Credentials;
+import javax.jcr.RepositoryException;
+import java.io.UnsupportedEncodingException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.Iterator;
+
+/**
+ * UserImpl
+ */
+class UserImpl extends AuthorizableImpl implements User {
+
+    private final String id;
+
+    private Principal principal = null;
+    private Impersonation impersonation = null;
+
+    private UserImpl(NodeImpl node, UserManagerImpl userManager) throws RepositoryException {
+        super(node, userManager);
+
+        id = node.getProperty(P_USERID).getString();
+    }
+
+    //--------------------------------------------------------------------------
+    /**
+     * @param node
+     * @param userManager
+     * @return
+     * @throws RepositoryException
+     */
+    static User create(NodeImpl node, UserManagerImpl userManager) throws RepositoryException {
+        if (node == null || !node.isNodeType(NT_REP_USER)) {
+            throw new IllegalArgumentException();
+        }
+        if(!Text.isDescendant(USERS_PATH, node.getPath())) {
+            throw new IllegalArgumentException("User has to be within the User Path");
+        }
+        return new UserImpl(node, userManager);
+    }
+    
+    //-------------------------------------------------------< Authorizable >---
+    /**
+     * @see Authorizable#getID()
+     */
+    public String getID() throws RepositoryException {
+        return id;
+    }
+
+    //------------------------------------------------< User >------------------
+    /**
+     * @see User#isAdmin()
+     */
+    public boolean isAdmin() {
+        return userManager.isAdminId(id);
+    }
+
+    /**
+     * @see User#getCredentials()
+     */
+    public Iterator getCredentials() throws RepositoryException {
+        try {
+            String password = getNode().getProperty(P_PASSWORD).getString();
+            Credentials creds = new CryptedSimpleCredentials(getID(), password);
+            return Collections.singletonList(creds).iterator();
+        } catch (NoSuchAlgorithmException e) {
+            throw new RepositoryException(e);
+        } catch (UnsupportedEncodingException e) {
+            throw new RepositoryException(e);
+        }
+    }
+    /**
+     * @see User#isGroup()
+     */
+    public boolean isGroup() {
+        return false;
+    }
+
+    /**
+     * @see User#getPrincipal()
+     */
+    public Principal getPrincipal() throws RepositoryException {
+        if (principal == null) {
+            if (isAdmin()) {
+                principal = new AdminPrincipal(getPrincipalName());
+            } else {
+                principal = new NodeBasedPrincipal(getPrincipalName());
+            }
+        }
+        return principal;
+    }
+
+    /**
+     * @see User#getImpersonation()
+     */
+    public Impersonation getImpersonation() throws RepositoryException {
+        if (impersonation == null) {
+            impersonation = new ImpersonationImpl(this, userManager);
+        }
+        return impersonation;
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImpl.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java?rev=638834&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java Wed Mar 19 06:56:13 2008
@@ -0,0 +1,535 @@
+/*
+ * 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.core.security.user;
+
+import org.apache.jackrabbit.spi.commons.name.NameConstants;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.AuthorizableExistsException;
+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.core.security.authentication.CryptedSimpleCredentials;
+import org.apache.jackrabbit.core.security.principal.ItemBasedPrincipal;
+import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.SecurityItemModifier;
+import org.apache.jackrabbit.core.ItemImpl;
+import org.apache.jackrabbit.util.Text;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.Credentials;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.Value;
+import javax.jcr.AccessDeniedException;
+import javax.jcr.ItemExistsException;
+import javax.jcr.Node;
+import javax.jcr.Item;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.version.VersionException;
+import javax.jcr.lock.LockException;
+import java.io.UnsupportedEncodingException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+/**
+ * UserManagerImpl
+ */
+public class UserManagerImpl extends SecurityItemModifier implements UserManager, UserConstants {
+
+    private static final Logger log = LoggerFactory.getLogger(UserManagerImpl.class);
+
+    private final SessionImpl session;
+    private final String adminId;
+    private final NodeResolver authResolver;
+
+    private String currentUserPath;
+
+    public UserManagerImpl(SessionImpl session, String adminId) throws RepositoryException {
+        this.session = session;
+        this.adminId = adminId;
+
+        NodeResolver nr;
+        try {
+            session.getWorkspace().getQueryManager();
+            nr = new IndexNodeResolver(session);
+        } catch (RepositoryException e) {
+            log.debug("UserManger: no QueryManager available for workspace '" + session.getWorkspace().getName() + "' -> Use traversing node resolver.");
+            nr = new TraversingNodeResolver(session);
+        }
+        authResolver = nr;
+    }
+
+    //--------------------------------------------------------< UserManager >---
+    /**
+     * @see UserManager#getAuthorizable(String)
+     */
+    public Authorizable getAuthorizable(String id) throws RepositoryException {
+        if (id == null || id.length() == 0) {
+            throw new IllegalArgumentException("Invalid authorizable name '" + id + "'");
+        }
+        Authorizable authorz = null;
+        NodeImpl n = (NodeImpl) authResolver.findNode(P_USERID, id, NT_REP_USER);
+        if (n != null) {
+            authorz = UserImpl.create(n, this);
+        } else {
+            // TODO: TOBEFIXED. should rather search for node with escaped(id) == Node.getName
+            String principalName = getGroupPrincipalName(id);
+            n = (NodeImpl) authResolver.findNode(P_PRINCIPAL_NAME, principalName, NT_REP_GROUP);
+            if (n != null) {
+                authorz = GroupImpl.create(n, this);
+            }
+        }
+        return authorz;
+    }
+
+    /**
+     * @see UserManager#getAuthorizable(Principal)
+     */
+    public Authorizable getAuthorizable(Principal principal) throws RepositoryException {
+        NodeImpl n = null;
+        // shortcut that avoids executing a query.
+        if (principal instanceof ItemBasedPrincipal) {
+            String authPath = ((ItemBasedPrincipal) principal).getPath();
+            if (session.itemExists(authPath)) {
+                Item authItem = session.getItem(authPath);
+                if (authItem.isNode()) {
+                    n = (NodeImpl) authItem;
+                }
+            }
+        }
+        // another Principal -> search
+        if (n == null) {
+            String name = principal.getName();
+            n = (NodeImpl) authResolver.findNode(P_PRINCIPAL_NAME, name, NT_REP_AUTHORIZABLE);
+        }
+        // build the corresponding authorizable object
+        if (n != null) {
+            if (n.isNodeType(NT_REP_USER)) {
+               return UserImpl.create(n, this);
+            } else if (n.isNodeType(NT_REP_GROUP)) {
+               return GroupImpl.create(n, this);
+            } else {
+                log.warn("Unexpected user nodetype " + n.getPrimaryNodeType().getName());
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @see UserManager#findAuthorizable(String,String)
+     */
+    public Iterator findAuthorizable(String propertyName, String value) throws RepositoryException {
+        Name name = session.getQName(propertyName);
+        NodeIterator auths  = authResolver.findNodes(name, value, NT_REP_AUTHORIZABLE, true);
+        return new AuthorizableIterator(auths);
+    }
+
+    /**
+     * Creates a new Node on the repository with the specified
+     * <code>userName</code>.<br>
+     * The <code>userID</code> is expected to be a valid JCR-<code>Name</code>.
+     * The User will be created relative to path of the User who represents the
+     * Session this UserManager has been created for.<br>
+     * If the {@link javax.jcr.Credentials Credentials} are of type
+     * {@link javax.jcr.SimpleCredentials SimpleCredentials} they will be
+     * crypted.
+     *
+     * @param userID
+     * @param credentials
+     * @see UserManager#createUser(String, Credentials, Principal principal)
+     * @inheritDoc
+     */
+    public User createUser(String userID, Credentials credentials,
+                           Principal principal) throws RepositoryException {
+        return createUser(userID, credentials, principal, null);
+    }
+
+    /**
+     *
+     * @param userID
+     * @param credentials
+     * @param principal
+     * @param intermediatePath
+     * @return
+     * @throws AuthorizableExistsException
+     * @throws RepositoryException
+     */
+    public User createUser(String userID, Credentials credentials,
+                           Principal principal, String intermediatePath)
+            throws AuthorizableExistsException, RepositoryException {
+        if (userID == null || credentials == null || principal == null) {
+            throw new IllegalArgumentException("Not possible to create user with null parameters");
+        }
+        if (getAuthorizable(userID) != null) {
+            throw new AuthorizableExistsException("User for '" + userID + "' already exists");
+        }
+        if (hasAuthorizableOrReferee(principal)) {
+            throw new AuthorizableExistsException("Authorizable for '" + principal.getName() + "' already exists");
+        }
+        if (!(credentials instanceof SimpleCredentials)) {
+            throw new RepositoryException("SimpleCredentials required. Found " + credentials.getClass());
+        } else if (!userID.equals(((SimpleCredentials) credentials).getUserID())) {
+            throw new RepositoryException("UserID mismatch: " + userID + " <-> " + ((SimpleCredentials) credentials).getUserID());
+        }
+
+        NodeImpl parent = null;
+        try {
+            String parentPath = getParentPath(intermediatePath, getCurrentUserPath());
+            parent = createParentNode(parentPath);
+
+            Name nodeName = session.getQName(Text.escapeIllegalJcrChars(userID));
+            NodeImpl userNode = addSecurityNode(parent, nodeName, NT_REP_USER);
+
+            setSecurityProperty(userNode, P_USERID, getValue(userID));
+            CryptedSimpleCredentials creds = new CryptedSimpleCredentials((SimpleCredentials) credentials);
+            setSecurityProperty(userNode, P_PASSWORD, getValue(creds.getPassword()));
+            setSecurityProperty(userNode, P_PRINCIPAL_NAME, getValue(principal.getName()));
+            parent.save();
+
+            log.info("User created: " + userID + "; " + userNode.getPath());
+            return UserImpl.create(userNode, this);
+        } catch (RepositoryException e) {
+            // something went wrong -> revert changes and rethrow
+            if (parent != null) {
+                parent.refresh(false);
+                log.debug("Failed to create new User, reverting changes.");
+            }
+            throw e;
+        } catch (NoSuchAlgorithmException e) {
+            throw new RepositoryException(e);
+        } catch (UnsupportedEncodingException e) {
+            throw new RepositoryException(e);
+        }
+    }
+
+    /**
+     * Create a new <code>Group</code> with the given <code>groupName</code>.
+     * It will be created below the this UserManager's root Path.<br>
+     * If non-existant elements of the Path will be created as <code>Nodes</code>
+     * of type {@link #NT_REP_AUTHORIZABLE_FOLDER rep:AuthorizableFolder}
+     *
+     * @param principal
+     * @see UserManager#createGroup(Principal);
+     * @inheritDoc
+     */
+    public Group createGroup(Principal principal) throws RepositoryException {
+        return createGroup(principal, null);
+    }
+
+    /**
+     *
+     * @param principal
+     * @param intermediatePath
+     * @return
+     * @throws AuthorizableExistsException
+     * @throws RepositoryException
+     */
+    public Group createGroup(Principal principal, String intermediatePath) throws AuthorizableExistsException, RepositoryException {
+        if (principal == null) {
+            throw new IllegalArgumentException("Principal might not be null.");
+        }
+        if (hasAuthorizableOrReferee(principal)) {
+            throw new AuthorizableExistsException("Authorizable for '" + principal.getName() + "' already exists: ");
+        }
+
+        NodeImpl parent = null;
+        try {
+            String parentPath = getParentPath(intermediatePath, GROUPS_PATH);
+            parent = createParentNode(parentPath);
+            Name groupID = getGroupId(principal.getName());
+
+            NodeImpl groupNode = addSecurityNode(parent, groupID, NT_REP_GROUP);
+            setSecurityProperty(groupNode, P_PRINCIPAL_NAME, getValue(principal.getName()));
+            parent.save();
+
+            log.info("Group created: " + groupID + "; " + groupNode.getPath());
+
+            return GroupImpl.create(groupNode, this);
+        } catch (RepositoryException e) {
+            if (parent != null) {
+                parent.refresh(false);
+                log.debug("newInstance new Group failed, revert changes on parent");
+            }
+            throw e;
+        }
+    }
+
+    //--------------------------------------------------------------------------
+    /**
+     * Simple search for <code>User</code>s<br>
+     * The argument is a substring which must match the UserId or main
+     * Principal's name.
+     *
+     * @param simpleFilter substring to match against. The empty String matches
+     * all users.
+     * @return Iterator containing Authorizable-objects
+     * @throws RepositoryException
+     */
+    public Iterator findUsers(String simpleFilter) throws RepositoryException {
+        Set s = new HashSet(2);
+        s.add(P_USERID);
+        s.add(P_PRINCIPAL_NAME);
+        NodeIterator nodes = authResolver.findNodes(s, simpleFilter, NT_REP_USER, false, Long.MAX_VALUE);
+        return new AuthorizableIterator(nodes);
+    }
+
+    /**
+     * Simple search for a <code>Group</code>s<br>
+     * The argument is a substring which must match the Group's Principal name.
+     *
+     * @param simpleFilter substring to match against. The empty String matches
+     * all groups.
+     * @return Iterator containing Authorizable-objects
+     * @throws RepositoryException
+     */
+    public Iterator findGroups(String simpleFilter) throws RepositoryException {
+        NodeIterator nodes = authResolver.findNodes(P_PRINCIPAL_NAME, simpleFilter, NT_REP_GROUP, false);
+        return new AuthorizableIterator(nodes);
+    }
+
+    /**
+     *
+     * @param principal
+     * @return
+     * @throws RepositoryException
+     */
+    boolean hasAuthorizableOrReferee(Principal principal) throws RepositoryException {
+        Set s = new HashSet(2);
+        s.add(P_PRINCIPAL_NAME);
+        s.add(P_REFEREES);
+        NodeIterator res = authResolver.findNodes(s, principal.getName(), NT_REP_AUTHORIZABLE, true, 1);
+        return res.hasNext();
+    }
+
+    void setProtectedProperty(NodeImpl node, Name propName, Value[] values) throws RepositoryException, LockException, ConstraintViolationException, ItemExistsException, VersionException {
+        setSecurityProperty(node, propName, values);
+        node.save();
+    }
+
+    void removeProtectedItem(ItemImpl item, Node parent) throws RepositoryException, AccessDeniedException, VersionException {
+        removeSecurityItem(item);
+        parent.save();
+    }
+
+    /**
+     * Escape illegal JCR characters and test if a user exists that has the
+     * principals name as userId, which might happen if userID != principal-name.
+     * In this case: generate another ID for the group to be created.
+     *
+     * @param principalName to be used as hint for the groupid.
+     * @return a group id.
+     * @throws RepositoryException
+     */
+    private Name getGroupId(String principalName) throws RepositoryException {
+        String escHint = Text.escapeIllegalJcrChars(principalName);
+        String groupID = escHint;
+        int i = 0;
+        while (getAuthorizable(groupID) != null) {
+            // TODO: replace separator by "_" (see above)
+            groupID = escHint + "_GR_" + i;
+            i++;
+        }
+        return session.getQName(groupID);
+    }
+
+    /**
+     * TODO: Remove method as soon as searching group-by-id is fixed (see above).
+     */
+    private static String getGroupPrincipalName(String groupId) {
+        int pos = groupId.lastIndexOf("_GR_");
+        String pName = (pos > -1) ? groupId.substring(0, pos) : groupId;
+        return Text.unescapeIllegalJcrChars(pName);
+    }
+
+    private Value getValue(String strValue) throws RepositoryException {
+        return session.getValueFactory().createValue(strValue);
+    }
+
+    /**
+     * @return true if the given userID belongs to the administrator user.
+     */
+    boolean isAdminId(String userID) {
+        return adminId.equals(userID);
+    }
+
+    UserImpl getCurrentUser() {
+        try {
+            String uid = session.getUserID();
+            if (uid != null) {
+                AuthorizableImpl auth = (AuthorizableImpl) getAuthorizable(session.getUserID());
+                if (auth != null && !auth.isGroup()) {
+                    return (UserImpl) auth;
+                }
+            }
+        } catch (RepositoryException e) {
+            // should never get here
+            log.error("Internal error: unable to build current user path.", e.getMessage());
+        }
+        return null;
+    }
+
+    private String getCurrentUserPath() {
+        if (currentUserPath == null) {
+            StringBuffer b = new StringBuffer();
+            try {
+                String uid = session.getUserID();
+                if (uid != null) {
+                    UserImpl user = getCurrentUser();
+                    if (user != null) {
+                        b.append(user.getNode().getPath());
+                    }
+                }
+            } catch (RepositoryException e) {
+                // should never get here
+                log.error("Internal error: unable to build current user path.", e.getMessage());
+            }
+            if (b.length() == 0) {
+                // fallback: default user-path
+                b.append(USERS_PATH);
+            }
+            currentUserPath = b.toString();
+        }
+        return currentUserPath;
+    }
+
+    private static String getParentPath(String hint, String root) {
+        StringBuffer b = new StringBuffer();
+        if (hint == null || !hint.startsWith(root)) {
+            b.append(root);
+        }
+        if (hint != null && hint.length() > 1) {
+            if (!hint.startsWith("/")) {
+                b.append("/");
+            }
+            b.append(hint);
+        }
+        return b.toString();
+    }
+
+    /**
+     * @param path to the authorizable node to be created
+     * @return
+     * @throws RepositoryException
+     */
+    private NodeImpl createParentNode(String path) throws RepositoryException {
+        NodeImpl parent = (NodeImpl) session.getRootNode();
+        String[] elem = path.split("/");
+        for (int i = 0; i < elem.length; i++) {
+            String name = elem[i];
+            if (name.length() < 1) {
+                continue;
+            }
+            Name nName = session.getQName(name);
+            if (!parent.hasNode(nName)) {
+                Name ntName;
+                if (i == 0) {
+                    // rep:security node
+                    ntName = NameConstants.NT_UNSTRUCTURED;
+                } else {
+                    ntName = NT_REP_AUTHORIZABLE_FOLDER;
+                }
+                NodeImpl added = addSecurityNode(parent, nName, ntName);
+                parent.save();
+                parent = added;
+            } else {
+                parent = parent.getNode(nName);
+            }
+        }
+        return parent;
+    }
+
+    //--------------------------------------------------------------------------
+    /**
+     * Inner class
+     */
+    private final class AuthorizableIterator implements Iterator {
+
+        private final Set served = new HashSet();
+
+        private Authorizable next;
+        private NodeIterator authNodeIter;
+
+        private AuthorizableIterator(NodeIterator authNodeIter) {
+            this.authNodeIter = authNodeIter;
+            next = seekNext();
+        }
+
+        //-------------------------------------------------------< Iterator >---
+        /**
+         * @see Iterator#hasNext()
+         */
+        public boolean hasNext() {
+            return next != null;
+        }
+
+        /**
+         * @see Iterator#next()
+         */
+        public Object next() {
+            Authorizable authr = next;
+            if (authr == null) {
+                throw new NoSuchElementException();
+            }
+            next = seekNext();
+            return authr;
+        }
+
+        /**
+         * @see Iterator#remove()
+         */
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        //----------------------------------------------------------------------
+        private Authorizable seekNext() {
+            while (authNodeIter.hasNext()) {
+                NodeImpl node = (NodeImpl) authNodeIter.nextNode();
+                try {
+                    if (!served.contains(node.getUUID())) {
+                        Authorizable authr;
+                        if (node.isNodeType(NT_REP_USER)) {
+                            authr = UserImpl.create(node, UserManagerImpl.this);
+                        } else if (node.isNodeType(NT_REP_GROUP)) {
+                            authr = GroupImpl.create(node, UserManagerImpl.this);
+                        } else {
+                            log.warn("Ignoring unexpected nodetype: " + node.getPrimaryNodeType().getName());
+                            continue;
+                        }
+                        served.add(node.getUUID());
+                        return authr;
+                    }
+                } catch (RepositoryException e) {
+                    log.debug(e.getMessage());
+                    // continue seeking next authorizable
+                }
+            }
+
+            // no next authorizable -> iteration is completed.
+            return null;
+        }
+    }
+
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Modified: jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd?rev=638834&r1=638833&r2=638834&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd Wed Mar 19 06:56:13 2008
@@ -172,3 +172,54 @@
 [rep:versionStorage]
   + * (nt:versionHistory) = nt:versionHistory protected multiple abort
   + * (rep:versionStorage) = rep:versionStorage protected multiple abort
+
+// -----------------------------------------------------------------------------
+// J A C K R A B B I T  S E C U R I T Y
+// -----------------------------------------------------------------------------
+
+[rep:AccessControllable]
+  mixin
+  + rep:policy (rep:Policy) protected ignore
+
+[rep:Policy] abstract
+
+[rep:ACL] > rep:Policy
+  + * (rep:ACE) = rep:GrantACE protected ignore
+
+[rep:ACE]
+  - rep:principalName (string) protected mandatory
+  - rep:privileges (string) protected mandatory multiple
+  - rep:nodePath (path) protected
+  - rep:glob (string) protected
+  - * (undefined) protected
+
+[rep:GrantACE] > rep:ACE
+
+[rep:DenyACE] > rep:ACE
+
+[rep:AccessControl] > nt:base, rep:AccessControllable
+  + * (rep:AccessControl) protected ignore
+  
+[rep:Authorizable] > nt:base, mix:referenceable abstract
+  + * (rep:Authorizable) = rep:Authorizable protected version
+  + * (rep:AuthorizableFolder) = rep:AuthorizableFolder protected version
+  - rep:principalName (string) protected mandatory
+  - rep:referees (string) protected multiple
+  - * (undefined)
+  - * (undefined) multiple
+
+[rep:Impersonatable]
+  mixin
+  - rep:impersonators (string) protected multiple
+
+[rep:User] > rep:Authorizable, rep:Impersonatable
+  - rep:userId (string) protected mandatory
+  - rep:password (string) mandatory
+
+[rep:Group] > rep:Authorizable
+  - rep:members (reference) protected multiple
+    < 'rep:Authorizable'
+
+[rep:AuthorizableFolder] > nt:base, mix:referenceable
+  + * (rep:Authorizable) = rep:User protected version
+  + * (rep:AuthorizableFolder) = rep:AuthorizableFolder protected version
\ No newline at end of file

Modified: jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.xml
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.xml?rev=638834&r1=638833&r2=638834&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.xml (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.xml Wed Mar 19 06:56:13 2008
@@ -398,4 +398,126 @@
         </childNodeDefinition>
     </nodeType>
 
+    <!-- internal security node types -->
+    <nodeType name="rep:AccessControllable" isMixin="true" hasOrderableChildNodes="false" primaryItemName="">
+        <childNodeDefinition name="rep:policy" autoCreated="false" mandatory="false" onParentVersion="IGNORE" protected="true" sameNameSiblings="false">
+            <requiredPrimaryTypes>
+                <requiredPrimaryType>rep:Policy</requiredPrimaryType>
+            </requiredPrimaryTypes>
+        </childNodeDefinition>
+    </nodeType>
+
+    <nodeType name="rep:Policy" isMixin="false" hasOrderableChildNodes="false" primaryItemName="" abstract="true">
+        <supertypes>
+            <supertype>nt:base</supertype>
+        </supertypes>
+    </nodeType>
+
+    <nodeType name="rep:ACL" isMixin="false" hasOrderableChildNodes="false" primaryItemName="">
+        <supertypes>
+            <supertype>rep:Policy</supertype>
+        </supertypes>
+        <childNodeDefinition name="*" defaultPrimaryType="rep:GrantACE" autoCreated="false" mandatory="false" onParentVersion="IGNORE" protected="true" sameNameSiblings="false">
+            <requiredPrimaryTypes>
+                <requiredPrimaryType>rep:ACE</requiredPrimaryType>
+            </requiredPrimaryTypes>
+        </childNodeDefinition>
+    </nodeType>
+
+    <nodeType name="rep:ACE" isMixin="false" hasOrderableChildNodes="false" primaryItemName="">
+        <supertypes>
+            <supertype>nt:base</supertype>
+        </supertypes>
+        <propertyDefinition name="rep:principalName" requiredType="String" autoCreated="false" mandatory="true" onParentVersion="COPY" protected="true" multiple="false" />
+        <propertyDefinition name="rep:privileges" requiredType="String" autoCreated="false" mandatory="true" onParentVersion="COPY" protected="true" multiple="true" />
+        <propertyDefinition name="rep:nodePath" requiredType="Path" autoCreated="false" mandatory="false" onParentVersion="COPY" protected="true" multiple="false" />
+        <propertyDefinition name="rep:glob" requiredType="String" autoCreated="false" mandatory="false" onParentVersion="COPY" protected="true" multiple="false" />
+        <propertyDefinition name="*" requiredType="undefined" autoCreated="false" mandatory="false" onParentVersion="COPY" protected="true" multiple="false" />
+    </nodeType>
+
+    <nodeType name="rep:GrantACE" isMixin="false" hasOrderableChildNodes="false" primaryItemName="">
+        <supertypes>
+            <supertype>rep:ACE</supertype>
+        </supertypes>
+    </nodeType>
+
+    <nodeType name="rep:DenyACE" isMixin="false" hasOrderableChildNodes="false" primaryItemName="">
+        <supertypes>
+            <supertype>rep:ACE</supertype>
+        </supertypes>
+    </nodeType>
+
+    <nodeType name="rep:AccessControl" isMixin="false" hasOrderableChildNodes="false" primaryItemName="">
+        <supertypes>
+            <supertype>nt:base</supertype>
+            <supertype>rep:AccessControllable</supertype>
+        </supertypes>
+        <childNodeDefinition name="*" autoCreated="false" mandatory="false" onParentVersion="IGNORE" protected="true" sameNameSiblings="false">
+            <requiredPrimaryTypes>
+                <requiredPrimaryType>rep:AccessControl</requiredPrimaryType>
+            </requiredPrimaryTypes>
+        </childNodeDefinition>
+    </nodeType>
+
+    <nodeType name="rep:Authorizable" isMixin="false" hasOrderableChildNodes="false" primaryItemName="" abstract="true">
+        <supertypes>
+            <supertype>nt:base</supertype>
+            <supertype>mix:referenceable</supertype>
+        </supertypes>
+        <childNodeDefinition name="*" defaultPrimaryType="rep:AuthorizableFolder" autoCreated="false" mandatory="false" onParentVersion="VERSION" protected="true" sameNameSiblings="false">
+            <requiredPrimaryTypes>
+                <requiredPrimaryType>rep:AuthorizableFolder</requiredPrimaryType>
+            </requiredPrimaryTypes>
+        </childNodeDefinition>
+        <childNodeDefinition name="*" defaultPrimaryType="rep:Authorizable" autoCreated="false" mandatory="false" onParentVersion="VERSION" protected="true" sameNameSiblings="false">
+            <requiredPrimaryTypes>
+                <requiredPrimaryType>rep:Authorizable</requiredPrimaryType>
+            </requiredPrimaryTypes>
+        </childNodeDefinition>
+        <propertyDefinition name="rep:principalName" requiredType="String" autoCreated="false" mandatory="true" onParentVersion="COPY" protected="true" multiple="false" />
+        <propertyDefinition name="rep:referees" requiredType="String" autoCreated="false" mandatory="false" onParentVersion="COPY" protected="true" multiple="true" />
+        <propertyDefinition name="*" requiredType="undefined" autoCreated="false" mandatory="false" onParentVersion="COPY" protected="false" multiple="false" />
+        <propertyDefinition name="*" requiredType="undefined" autoCreated="false" mandatory="false" onParentVersion="COPY" protected="false" multiple="true" />
+    </nodeType>
+
+    <nodeType name="rep:Impersonatable" isMixin="true" hasOrderableChildNodes="false" primaryItemName="">
+        <propertyDefinition name="rep:impersonators" requiredType="String" autoCreated="false" mandatory="false" onParentVersion="COPY" protected="true" multiple="true" />
+    </nodeType>
+
+    <nodeType name="rep:User" isMixin="false" hasOrderableChildNodes="false" primaryItemName="">
+        <supertypes>
+            <supertype>rep:Authorizable</supertype>
+            <supertype>rep:Impersonatable</supertype>
+        </supertypes>
+        <propertyDefinition name="rep:userId" requiredType="String" autoCreated="false" mandatory="true" onParentVersion="COPY" protected="true" multiple="false" />
+        <propertyDefinition name="rep:password" requiredType="String" autoCreated="false" mandatory="true" onParentVersion="COPY" protected="false" multiple="false" />
+    </nodeType>
+
+    <nodeType name="rep:Group" isMixin="false" hasOrderableChildNodes="false" primaryItemName="">
+        <supertypes>
+            <supertype>rep:Authorizable</supertype>
+        </supertypes>
+        <propertyDefinition name="rep:members" requiredType="Reference" autoCreated="false" onParentVersion="COPY" protected="true" multiple="true">
+            <valueConstraints>
+                <valueConstraint>rep:Authorizable</valueConstraint>
+            </valueConstraints>
+        </propertyDefinition>
+    </nodeType>
+
+    <nodeType name="rep:AuthorizableFolder" isMixin="false" hasOrderableChildNodes="false" primaryItemName="">
+        <supertypes>
+            <supertype>nt:base</supertype>
+            <supertype>mix:referenceable</supertype>
+        </supertypes>
+        <childNodeDefinition name="*" defaultPrimaryType="rep:User" autoCreated="false" mandatory="false" onParentVersion="VERSION" protected="true" sameNameSiblings="false">
+            <requiredPrimaryTypes>
+                <requiredPrimaryType>rep:Authorizable</requiredPrimaryType>
+            </requiredPrimaryTypes>
+        </childNodeDefinition>
+        <childNodeDefinition name="*" defaultPrimaryType="rep:AuthorizableFolder" autoCreated="false" mandatory="false" onParentVersion="VERSION" protected="true" sameNameSiblings="false">
+            <requiredPrimaryTypes>
+                <requiredPrimaryType>rep:AuthorizableFolder</requiredPrimaryType>
+            </requiredPrimaryTypes>
+        </childNodeDefinition>
+    </nodeType>
 </nodeTypes>

Modified: jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/repository.xml
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/repository.xml?rev=638834&r1=638833&r2=638834&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/repository.xml (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/repository.xml Wed Mar 19 06:56:13 2008
@@ -15,9 +15,13 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 -->
-<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.4//EN"
-                            "http://jackrabbit.apache.org/dtd/repository-1.4.dtd">
-<!-- Example Repository Configuration File -->
+<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.5//EN"
+                            "http://svn.apache.org/repos/asf/jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/config/repository-1.5.dtd">
+<!-- Example Repository Configuration File
+     Used by
+     - org.apache.jackrabbit.core.config.RepositoryConfigTest.java
+     -
+-->
 <Repository>
     <!--
         virtual file system where the repository stores global state
@@ -32,16 +36,31 @@
     -->
     <Security appName="Jackrabbit">
         <!--
+            security manager:
+            class: FQN of class implementing the JackrabbitSecurityManager interface
+        -->
+        <SecurityManager class="org.apache.jackrabbit.core.security.simple.SimpleSecurityManager" workspaceName="security">
+            <!--
+            workspace access:
+            class: FQN of class implementing the WorkspaceAccessManager interface
+            -->
+            <!-- <WorkspaceAccessManager class="..."/> -->
+            <!-- <param name="config" value="${rep.home}/security.xml"/> -->
+        </SecurityManager>
+
+        <!--
             access manager:
             class: FQN of class implementing the AccessManager interface
         -->
-        <AccessManager class="org.apache.jackrabbit.core.security.SimpleAccessManager">
+        <AccessManager class="org.apache.jackrabbit.core.security.simple.SimpleAccessManager">
             <!-- <param name="config" value="${rep.home}/access.xml"/> -->
         </AccessManager>
 
-        <LoginModule class="org.apache.jackrabbit.core.security.SimpleLoginModule">
-           <!-- anonymous user name ('anonymous' is the default value) -->
+        <LoginModule class="org.apache.jackrabbit.core.security.simple.SimpleLoginModule">
+           <!-- anonymous user id -->
            <param name="anonymousId" value="anonymous"/>
+           <!-- administrator user id (default value if param is missing is 'admin') -->
+           <param name="adminId" value="admin"/>
            <!--
               default user name to be used instead of the anonymous user
               when no login credentials are provided (unset by default)

Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/principal/PrincipalManagerTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/principal/PrincipalManagerTest.java?rev=638834&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/principal/PrincipalManagerTest.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/principal/PrincipalManagerTest.java Wed Mar 19 06:56:13 2008
@@ -0,0 +1,314 @@
+/*
+ * 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.api.security.principal;
+
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.test.AbstractJCRTest;
+import org.apache.jackrabbit.test.NotExecutableException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import java.security.Principal;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * <code>PrincipalManagerTest</code>...
+ */
+public class PrincipalManagerTest extends AbstractJCRTest {
+
+    private static Logger log = LoggerFactory.getLogger(PrincipalManagerTest.class);
+
+    private PrincipalManager principalMgr;
+    private Principal everyone;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        if (!(superuser instanceof JackrabbitSession)) {
+            throw new NotExecutableException();
+        }
+        principalMgr = ((JackrabbitSession) superuser).getPrincipalManager();
+        everyone = principalMgr.getEveryone();
+    }
+
+    private static Principal[] getPrincipals(Session session) {
+        // TODO: get rid of SessionImpl dependency
+        Set princ = ((SessionImpl) session).getSubject().getPrincipals();
+        return (Principal[]) princ.toArray(new Principal[princ.size()]);
+    }
+
+    private static boolean isGroup(Principal p) {
+        return p instanceof java.security.acl.Group;
+    }
+
+    public void testGetEveryone() {
+        Principal principal = principalMgr.getEveryone();
+        assertTrue(principal != null);
+        assertTrue(isGroup(principal));
+    }
+
+    public void testSuperUserIsEveryOne() {
+        java.security.acl.Group everyone = (java.security.acl.Group) principalMgr.getEveryone();
+        Principal[] pcpls = getPrincipals(superuser);
+        for (int i = 0; i < pcpls.length; i++) {
+            if (!(pcpls[i].equals(everyone))) {
+                assertTrue(everyone.isMember(pcpls[i]));
+            }
+        }
+    }
+
+    public void testReadOnlyIsEveryOne() throws RepositoryException {
+        java.security.acl.Group everyone = (java.security.acl.Group) principalMgr.getEveryone();
+        Principal[] pcpls = getPrincipals(helper.getReadOnlySession());
+        for (int i = 0; i < pcpls.length; i++) {
+            if (!(pcpls[i].equals(everyone))) {
+                assertTrue(everyone.isMember(pcpls[i]));
+            }
+        }
+    }
+
+    public void testHasPrincipal() {
+        assertTrue(principalMgr.hasPrincipal(everyone.getName()));
+
+        Principal[] pcpls = getPrincipals(superuser);
+        for (int i = 0; i < pcpls.length; i++) {
+            assertTrue(principalMgr.hasPrincipal(pcpls[i].getName()));
+        }
+    }
+
+    public void testGetPrincipal() throws NoSuchPrincipalException {
+        Principal p = principalMgr.getPrincipal(everyone.getName());
+        assertEquals(everyone, p);
+
+        Principal[] pcpls = getPrincipals(superuser);
+        for (int i = 0; i < pcpls.length; i++) {
+            Principal pp = principalMgr.getPrincipal(pcpls[i].getName());
+            assertEquals("PrincipalManager.getPrincipal returned Principal with different Name", pcpls[i].getName(), pp.getName());
+        }
+    }
+
+    public void testGetPrincipalGetName() throws NoSuchPrincipalException {
+        Principal[] pcpls = getPrincipals(superuser);
+        for (int i = 0; i < pcpls.length; i++) {
+            Principal pp = principalMgr.getPrincipal(pcpls[i].getName());
+            assertEquals("PrincipalManager.getPrincipal returned Principal with different Name", pcpls[i].getName(), pp.getName());
+        }
+    }
+
+    public void testGetPrincipals() throws NoSuchPrincipalException {
+        PrincipalIterator it = principalMgr.getPrincipals(PrincipalManager.SEARCH_TYPE_NOT_GROUP);
+        while (it.hasNext()) {
+            Principal p = it.nextPrincipal();
+            assertFalse(isGroup(p));
+        }
+    }
+
+    public void testGetGroupPrincipals() throws NoSuchPrincipalException {
+        PrincipalIterator it = principalMgr.getPrincipals(PrincipalManager.SEARCH_TYPE_GROUP);
+        while (it.hasNext()) {
+            Principal p = it.nextPrincipal();
+            assertTrue(isGroup(p));
+        }
+    }
+
+    public void testGetAllPrincipals() throws NoSuchPrincipalException {
+        PrincipalIterator it = principalMgr.getPrincipals(PrincipalManager.SEARCH_TYPE_ALL);
+        while (it.hasNext()) {
+            Principal p = it.nextPrincipal();
+            assertTrue(principalMgr.hasPrincipal(p.getName()));
+            assertEquals(principalMgr.getPrincipal(p.getName()), p);
+        }
+    }
+
+    public void testGroupMembers() {
+        PrincipalIterator it = principalMgr.getPrincipals(PrincipalManager.SEARCH_TYPE_ALL);
+        while (it.hasNext()) {
+            Principal p = it.nextPrincipal();
+            if (isGroup(p) && !p.equals(principalMgr.getEveryone())) {
+                Enumeration en = ((java.security.acl.Group) p).members();
+                while (en.hasMoreElements()) {
+                    Principal memb = (Principal) en.nextElement();
+                    assertTrue(principalMgr.hasPrincipal(memb.getName()));
+                }
+            }
+        }
+    }
+
+    public void testGroupMembership() throws NoSuchPrincipalException {
+        testMembership(PrincipalManager.SEARCH_TYPE_NOT_GROUP);
+        testMembership(PrincipalManager.SEARCH_TYPE_GROUP);
+        testMembership(PrincipalManager.SEARCH_TYPE_ALL);
+    }
+
+    private void testMembership(int searchType) {
+        PrincipalIterator it = principalMgr.getPrincipals(searchType);
+        while (it.hasNext()) {
+            Principal p = it.nextPrincipal();
+            if (p.equals(everyone)) {
+                for (PrincipalIterator membership = principalMgr.getGroupMembership(p); membership.hasNext();) {
+                    Principal gr = membership.nextPrincipal();
+                    assertTrue(isGroup(gr));
+                    if (gr.equals(everyone)) {
+                        fail("Everyone must never be a member of the EveryOne group.");
+                    }
+                }
+            } else {
+                boolean atleastEveryone = false;
+                for (PrincipalIterator membership = principalMgr.getGroupMembership(p); membership.hasNext();) {
+                    Principal gr = membership.nextPrincipal();
+                    assertTrue(isGroup(gr));
+                    if (gr.equals(everyone)) {
+                        atleastEveryone = true;
+                    }
+                }
+                assertTrue("All principals (except everyone) must be member of the everyone group.", atleastEveryone);
+
+            }
+        }
+    }
+
+    public void testGetMembersConsistentWithMembership() {
+        Principal everyone = principalMgr.getEveryone();
+        PrincipalIterator it = principalMgr.getPrincipals(PrincipalManager.SEARCH_TYPE_GROUP);
+        while (it.hasNext()) {
+            Principal p = it.nextPrincipal();
+            if (p.equals(everyone)) {
+                continue;
+            }
+
+            assertTrue(isGroup(p));
+
+            Enumeration members = ((java.security.acl.Group) p).members();
+            while (members.hasMoreElements()) {
+                Principal memb = (Principal) members.nextElement();
+
+                Principal group = null;
+                PrincipalIterator mship = principalMgr.getGroupMembership(memb);
+                while (mship.hasNext() && group == null) {
+                    Principal gr = mship.nextPrincipal();
+                    if (p.equals(gr)) {
+                        group = gr;
+                    }
+                }
+                assertNotNull("Group member " + memb.getName() + "does not reveal group upon getGroupMembership", p.getName());
+            }
+        }
+    }
+
+    public void testFindPrincipal() {
+        Principal[] pcpls = getPrincipals(superuser);
+        for (int i = 0; i < pcpls.length; i++) {
+            if (pcpls[i].equals(everyone)) {
+                continue;
+            }
+            Iterator it = principalMgr.findPrincipals(pcpls[i].getName());
+            // search must find at least a single principal
+            assertTrue("findPrincipals does not find principal with filter "+pcpls[i].getName(), it.hasNext());
+        }
+    }
+
+    public void testFindPrincipalByType() {
+        Principal[] pcpls = getPrincipals(superuser);
+        for (int i = 0; i < pcpls.length; i++) {
+            if (pcpls[i].equals(everyone)) {
+                // special case covered by another test
+                continue;
+            }
+
+            if (isGroup(pcpls[i])) {
+                Iterator it = principalMgr.findPrincipals(pcpls[i].getName(),
+                        PrincipalManager.SEARCH_TYPE_GROUP);
+                // search must find at least a single matching group principal
+                assertTrue("findPrincipals does not find principal with filter "+pcpls[i].getName(), it.hasNext());
+            } else {
+                Iterator it = principalMgr.findPrincipals(pcpls[i].getName(),
+                        PrincipalManager.SEARCH_TYPE_NOT_GROUP);
+                // search must find at least a single matching non-group principal
+                assertTrue("findPrincipals does not find principal with filter "+pcpls[i].getName(), it.hasNext());
+            }
+        }
+    }
+
+    public void testFindPrincipalByTypeAll() {
+        Principal[] pcpls = getPrincipals(superuser);
+        for (int i = 0; i < pcpls.length; i++) {
+            if (pcpls[i].equals(everyone)) {
+                // special case covered by another test
+                continue;
+            }
+
+            PrincipalIterator it = principalMgr.findPrincipals(pcpls[i].getName(), PrincipalManager.SEARCH_TYPE_ALL);
+            PrincipalIterator it2 = principalMgr.findPrincipals(pcpls[i].getName());
+
+            // both search must reveal the same result and size
+            assertTrue(it.getSize() == it2.getSize());
+
+            Set s1 = new HashSet();
+            Set s2 = new HashSet();
+            while (it.hasNext() && it2.hasNext()) {
+                s1.add(it.nextPrincipal());
+                s2.add(it2.nextPrincipal());
+            }
+
+            assertEquals(s1, s2);
+            assertFalse(it.hasNext() && it2.hasNext());
+        }
+    }
+
+    public void testFindEveryone() {
+        Principal everyone = principalMgr.getEveryone();
+
+        boolean containedInResult = false;
+
+        // untyped search -> everyone must be part of the result set
+        PrincipalIterator it = principalMgr.findPrincipals(everyone.getName());
+        while (it.hasNext()) {
+            Principal p = it.nextPrincipal();
+            if (p.getName().equals(everyone.getName())) {
+                containedInResult = true;
+            }
+        }
+        assertTrue(containedInResult);
+
+        // search group only -> everyone must be part of the result set
+        containedInResult = false;
+        it = principalMgr.findPrincipals(everyone.getName(), PrincipalManager.SEARCH_TYPE_GROUP);
+        while (it.hasNext()) {
+            Principal p = it.nextPrincipal();
+            if (p.getName().equals(everyone.getName())) {
+                containedInResult = true;
+            }
+        }
+        assertTrue(containedInResult);
+
+        // search non-group only -> everyone should not be part of the result set
+        containedInResult = false;
+        it = principalMgr.findPrincipals(everyone.getName(), PrincipalManager.SEARCH_TYPE_NOT_GROUP);
+        while (it.hasNext()) {
+            Principal p = it.nextPrincipal();
+            if (p.getName().equals(everyone.getName())) {
+                containedInResult = true;
+            }
+        }
+        assertFalse(containedInResult);
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/principal/PrincipalManagerTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/principal/PrincipalManagerTest.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/principal/TestAll.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/principal/TestAll.java?rev=638834&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/principal/TestAll.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/principal/TestAll.java Wed Mar 19 06:56:13 2008
@@ -0,0 +1,39 @@
+/*
+ * 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.api.security.principal;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class TestAll extends TestCase {
+
+    /**
+     * Returns a <code>Test</code> suite that executes all tests inside this
+     * package.
+     *
+     * @return a <code>Test</code> suite that executes all tests inside this
+     *         package.
+     */
+    public static Test suite() {
+        TestSuite suite = new TestSuite("api.security.principal tests");
+
+        suite.addTestSuite(PrincipalManagerTest.class);
+
+        return suite;
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/principal/TestAll.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/principal/TestAll.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/AbstractUserTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/AbstractUserTest.java?rev=638834&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/AbstractUserTest.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/AbstractUserTest.java Wed Mar 19 06:56:13 2008
@@ -0,0 +1,126 @@
+/*
+ * 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.api.security.user;
+
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.security.TestPrincipal;
+import org.apache.jackrabbit.test.AbstractJCRTest;
+import org.apache.jackrabbit.test.NotExecutableException;
+import org.apache.jackrabbit.uuid.UUID;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.Credentials;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.security.auth.Subject;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * <code>AbstractUserTest</code>...
+ */
+public abstract class AbstractUserTest extends AbstractJCRTest {
+
+    private static Logger log = LoggerFactory.getLogger(AbstractUserTest.class);
+
+    protected UserManager userMgr;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        userMgr = getUserManager(superuser);
+    }
+
+    protected static UserManager getUserManager(Session session) throws RepositoryException, NotExecutableException {
+        if (!(session instanceof JackrabbitSession)) {
+            throw new NotExecutableException();
+        }
+        try {
+            return ((JackrabbitSession) session).getUserManager();
+        } catch (UnsupportedRepositoryOperationException e) {
+            throw new NotExecutableException(e.getMessage());
+        } catch (UnsupportedOperationException e) {
+            throw new NotExecutableException(e.getMessage());
+        }
+    }
+
+    protected static Subject buildSubject(Principal p) {
+        return new Subject(true, Collections.singleton(p), Collections.EMPTY_SET, Collections.EMPTY_SET);
+    }
+
+    protected Principal getTestPrincipal() throws RepositoryException {
+        String pn = "any_principal" + UUID.randomUUID();
+        Principal p = new TestPrincipal(pn);
+        return p;
+    }
+
+    protected Credentials buildCredentials(String uid, String pw) {
+        // todo: retrieve creds impl from config
+        return new SimpleCredentials(uid, pw.toCharArray());
+    }
+
+    protected Credentials buildCredentials(Principal p) {
+        return buildCredentials(p.getName(), p.getName());
+    }
+
+    protected static Set getPrincipalSetFromSession(Session session) throws NotExecutableException {
+        if (session instanceof SessionImpl) {
+            return ((SessionImpl) session).getSubject().getPrincipals();
+        } else {
+            // TODO add fallback
+            throw new NotExecutableException();
+        }
+    }
+
+    protected User getTestUser(Session session) throws NotExecutableException, RepositoryException {
+        Set principals = getPrincipalSetFromSession(session);
+        for (Iterator it = principals.iterator(); it.hasNext();) {
+            try {
+                Authorizable auth = userMgr.getAuthorizable((Principal) it.next());
+                if (auth != null && !auth.isGroup()) {
+                    return (User) auth;
+                }
+            } catch (RepositoryException e) {
+                // ignore
+            }
+        }
+        // should never happen. An Session should always have a corresponding User.
+        throw new RepositoryException("Unable to retrieve a User.");
+    }
+
+    protected Group getTestGroup(Session session) throws NotExecutableException, RepositoryException {
+        Set principals = getPrincipalSetFromSession(session);
+        for (Iterator it = principals.iterator(); it.hasNext();) {
+            try {
+                Authorizable auth = userMgr.getAuthorizable((Principal) it.next());
+                if (auth != null && auth.isGroup()) {
+                    return (Group) auth;
+                }
+            } catch (RepositoryException e) {
+                // ignore
+            }
+        }
+        // may happen -> don't throw RepositoryException
+        throw new NotExecutableException("No Group found.");
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/AbstractUserTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/AbstractUserTest.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url