Package org.apache.jackrabbit.core.security.user

Source Code of org.apache.jackrabbit.core.security.user.UserManagerImpl$AuthorizableIterator

/*
* 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.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.ItemImpl;
import org.apache.jackrabbit.core.NodeImpl;
import org.apache.jackrabbit.core.ProtectedItemModifier;
import org.apache.jackrabbit.core.SessionImpl;
import org.apache.jackrabbit.core.SessionListener;
import org.apache.jackrabbit.core.security.principal.ItemBasedPrincipal;
import org.apache.jackrabbit.core.security.principal.PrincipalImpl;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.commons.name.NameConstants;
import org.apache.jackrabbit.util.Text;
import org.apache.commons.collections.map.LRUMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.AccessDeniedException;
import javax.jcr.Item;
import javax.jcr.ItemExistsException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.version.VersionException;
import java.security.Principal;
import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Map;

/**
* UserManagerImpl
*/
public class UserManagerImpl extends ProtectedItemModifier implements UserManager, UserConstants, SessionListener {

    private static final Logger log = LoggerFactory.getLogger(UserManagerImpl.class);

    private final SessionImpl session;
    private final String adminId;
    private final NodeResolver authResolver;

    /**
     * Simple unmanaged map from authorizableID to nodePath (node representing
     * the authorizable) used limit the number of calls to the
     * <code>NodeResolver</code> in order to find authorizable nodes by the
     * authorizable id.
     */
    private final Map idPathMap = new LRUMap(1000);

    public UserManagerImpl(SessionImpl session, String adminId) throws RepositoryException {
        super();
        this.session = session;
        this.adminId = adminId;

        NodeResolver nr;
        try {
            nr = new IndexNodeResolver(session, session);
        } catch (RepositoryException e) {
            log.debug("UserManger: no QueryManager available for workspace '" + session.getWorkspace().getName() + "' -> Use traversing node resolver.");
            nr = new TraversingNodeResolver(session, 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 = getUserNode(id);
        if (n != null) {
            authorz = createUser(n);
        } else {
            n = getGroupNode(id);
            if (n != null) {
                authorz = createGroup(n);
            }
        }
        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;
                }
            }
        } else {
            // another Principal -> search
            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 createUser(n);
            } else if (n.isNodeType(NT_REP_GROUP)) {
               return createGroup(n);
            } else {
                log.warn("Unexpected user nodetype " + n.getPrimaryNodeType().getName());
            }
        }
        return null;
    }

    /**
     * @see UserManager#findAuthorizables(String,String)
     */
    public Iterator findAuthorizables(String propertyName, String value) throws RepositoryException {
        return findAuthorizables(propertyName, value, SEARCH_TYPE_AUTHORIZABLE);
    }

    /**
     * @see UserManager#findAuthorizables(String,String, int)
     */
    public Iterator findAuthorizables(String propertyName, String value, int searchType)
            throws RepositoryException {
        Name name = session.getQName(propertyName);
        Name ntName;
        switch (searchType) {
            case SEARCH_TYPE_AUTHORIZABLE:
                ntName = NT_REP_AUTHORIZABLE;
                break;
            case SEARCH_TYPE_GROUP:
                ntName = NT_REP_GROUP;
                break;
            case SEARCH_TYPE_USER:
                ntName = NT_REP_USER;
                break;
            default: throw new IllegalArgumentException("Invalid search type " + searchType);
        }
        NodeIterator nodes = authResolver.findNodes(name, value, ntName, true);
        return new AuthorizableIterator(nodes);
    }

    /**
     * Creates a new Node on the repository with the specified
     * <code>userName</code>.<br>
     * 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 password
     * @see UserManager#createUser(String,String)
     * @inheritDoc
     */
    public User createUser(String userID, String password) throws RepositoryException {
        return createUser(userID, password, new PrincipalImpl(userID), null);
    }

    /**
     *
     * @param userID
     * @param password
     * @param principal
     * @param intermediatePath
     * @return
     * @throws AuthorizableExistsException
     * @throws RepositoryException
     */
    public User createUser(String userID, String password,
                           Principal principal, String intermediatePath)
            throws AuthorizableExistsException, RepositoryException {
        if (userID == null || userID.length() == 0) {
            throw new IllegalArgumentException("Cannot create user: UserID can neither be null nor empty String.");
        }
        if (password == null) {
            throw new IllegalArgumentException("Cannot create user: null password.");
        }
        if (!isValidPrincipal(principal)) {
            throw new IllegalArgumentException("Cannot create user: Principal may not be null and must have a valid name.");
        }
        if (getAuthorizable(userID) != null) {
            throw new AuthorizableExistsException("User for '" + userID + "' already exists");
        }
        if (hasAuthorizableOrReferee(principal)) {
            throw new AuthorizableExistsException("Authorizable for '" + principal.getName() + "' already exists");
        }

        NodeImpl parent = null;
        try {
            String parentPath = getParentPath(intermediatePath, getCurrentUserPath());
            parent = createParentNode(parentPath);

            Name nodeName = session.getQName(Text.escapeIllegalJcrChars(userID));
            NodeImpl userNode = addNode(parent, nodeName, NT_REP_USER);

            setProperty(userNode, P_USERID, getValue(userID), true);
            setProperty(userNode, P_PASSWORD, getValue(UserImpl.buildPasswordValue(password)), true);
            setProperty(userNode, P_PRINCIPAL_NAME, getValue(principal.getName()), true);
            parent.save();

            log.debug("User created: " + userID + "; " + userNode.getPath());
            return createUser(userNode);
        } 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;
        }
    }

    /**
     * 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 (!isValidPrincipal(principal)) {
            throw new IllegalArgumentException("Cannot create Group: Principal may not be null and must have a valid name.");
        }
        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 = addNode(parent, groupID, NT_REP_GROUP);
            setProperty(groupNode, P_PRINCIPAL_NAME, getValue(principal.getName()));
            parent.save();

            log.debug("Group created: " + groupID + "; " + groupNode.getPath());

            return createGroup(groupNode);
        } catch (RepositoryException e) {
            if (parent != null) {
                parent.refresh(false);
                log.debug("newInstance new Group failed, revert changes on parent");
            }
            throw e;
        }
    }

    //--------------------------------------------------------------------------
    /**
     *
     * @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 value) throws RepositoryException, LockException, ConstraintViolationException, ItemExistsException, VersionException {
        setProperty(node, propName, value);
        node.save();
    }

    void setProtectedProperty(NodeImpl node, Name propName, Value[] values) throws RepositoryException, LockException, ConstraintViolationException, ItemExistsException, VersionException {
        setProperty(node, propName, values);
        node.save();
    }

    void removeProtectedItem(ItemImpl item, Node parent) throws RepositoryException, AccessDeniedException, VersionException {
        removeItem(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) {
            groupID = escHint + "_" + i;
            i++;
        }
        return session.getQName(groupID);
    }

    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 == null) ? false : adminId.equals(userID);
    }

    /**
     * Build the User object from the given user node.
     *
     * @param userNode
     * @return
     * @throws RepositoryException
     */
    User createUser(NodeImpl userNode) throws RepositoryException {
        if (userNode == null || !userNode.isNodeType(NT_REP_USER)) {
            throw new IllegalArgumentException();
        }
        if (!Text.isDescendant(USERS_PATH, userNode.getPath())) {
            throw new IllegalArgumentException("User has to be within the User Path");
        }
        User user = doCreateUser(userNode);
        idPathMap.put(user.getID(), userNode.getPath());
        return user;
    }

    /**
     * Build the user object from the given user node. May be overridden to
     * return a custom implementation.
     *
     * @param node user node
     * @return user object
     * @throws RepositoryException if an error occurs
     */
    protected User doCreateUser(NodeImpl node) throws RepositoryException {
        return new UserImpl(node, this);
    }


    /**
     * Build the Group object from the given group node.
     *
     * @param groupNode
     * @return
     * @throws RepositoryException
     */
    Group createGroup(NodeImpl groupNode) throws RepositoryException {
        Group group = GroupImpl.create(groupNode, this);
        idPathMap.put(group.getID(), groupNode.getPath());
        return group;
    }

    /**
     * @param userID
     * @return the node associated with the given userID or <code>null</code>.
     */
    private NodeImpl getUserNode(String userID) throws RepositoryException {
        NodeImpl n = null;
        if (idPathMap.containsKey(userID)) {
            String path = idPathMap.get(userID).toString();
            if (session.itemExists(path)) {
                Item itm = session.getItem(path);
                // make sure the item really represents the node associated with
                // the given userID. if not the search below is execute.
                if (itm.isNode()) {
                    NodeImpl tmp = (NodeImpl) itm;
                    if (tmp.isNodeType(NT_REP_USER) && userID.equals(((NodeImpl) itm).getProperty(P_USERID).getString())) {
                        n = (NodeImpl) itm;
                    }
                }
            }
        }

        if (n == null) {
            // clear eventual previous entry
            idPathMap.remove(userID);
            // search for it the node belonging to that userID
            n = (NodeImpl) authResolver.findNode(P_USERID, userID, NT_REP_USER);
        }
        return n;
    }

    private NodeImpl getGroupNode(String groupID) throws RepositoryException {
        NodeImpl n = null;
        if (idPathMap.containsKey(groupID)) {
            String path = idPathMap.get(groupID).toString();
            if (session.itemExists(path)) {
                Item itm = session.getItem(path);
                // make sure the item really represents the node associated with
                // the given userID. if not the search below is execute.
                if (itm.isNode()) {
                    NodeImpl tmp = (NodeImpl) itm;
                    if (tmp.isNodeType(NT_REP_GROUP) && groupID.equals(tmp.getName())) {
                        n = (NodeImpl) itm;
                    }
                }
            }
        }
        if (n == null) {
            // clear eventual previous entry
            idPathMap.remove(groupID);
            // search for it the node belonging to that groupID
            Name nodeName = session.getQName(groupID);
            n = (NodeImpl) authResolver.findNode(nodeName, NT_REP_GROUP);
        }
        return n;
    }

    /**
     * @return the path refering to the node associated with the user this
     * <code>UserManager</code> has been built for.
     */
    private String getCurrentUserPath() {
        // fallback: default user-path
        String currentUserPath = USERS_PATH;
        String userId = session.getUserID();

        if (idPathMap.containsKey(userId)) {
            currentUserPath = idPathMap.get(userId).toString();
        } else {
            try {
                Node n = getUserNode(userId);
                if (n != null) {
                    currentUserPath = n.getPath();
                }
            } catch (RepositoryException e) {
                // should never get here
                log.error("Internal error: unable to build current user path.", e.getMessage());
            }
        }
        return currentUserPath;
    }

    private static boolean isValidPrincipal(Principal principal) {
        return principal != null && principal.getName() != null && principal.getName().length() > 0;
    }

    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 = addNode(parent, nName, ntName);
                parent.save();
                parent = added;
            } else {
                parent = parent.getNode(nName);
            }
        }
        return parent;
    }

    //----------------------------------------------------< SessionListener >---
    /**
     * @see SessionListener#loggingOut(org.apache.jackrabbit.core.SessionImpl)
     */
    public void loggingOut(SessionImpl session) {
        // nothing to do.
    }

    /**
     * @see SessionListener#loggedOut(org.apache.jackrabbit.core.SessionImpl)
     */
    public void loggedOut(SessionImpl session) {
        // clear the map
        idPathMap.clear();
        // and logout the session unless it is the loggedout session itself.
        if (session != this.session) {
            this.session.logout();
        }
    }

    //--------------------------------------------------------------------------
    /**
     * 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 = createUser(node);
                        } else if (node.isNodeType(NT_REP_GROUP)) {
                            authr = createGroup(node);
                        } 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;
        }
    }
}
TOP

Related Classes of org.apache.jackrabbit.core.security.user.UserManagerImpl$AuthorizableIterator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.