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

Source Code of org.apache.jackrabbit.core.security.user.UserAccessControlProvider$CompiledPermissionsImpl

/*
* 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 javax.jcr.security.AccessControlPolicy;
import javax.jcr.security.Privilege;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.apache.jackrabbit.api.security.principal.ItemBasedPrincipal;
import org.apache.jackrabbit.core.ItemImpl;
import org.apache.jackrabbit.core.NodeImpl;
import org.apache.jackrabbit.core.SessionImpl;
import org.apache.jackrabbit.core.observation.SynchronousEventListener;
import org.apache.jackrabbit.core.security.authorization.AbstractAccessControlProvider;
import org.apache.jackrabbit.core.security.authorization.AbstractCompiledPermissions;
import org.apache.jackrabbit.core.security.authorization.AccessControlEditor;
import org.apache.jackrabbit.core.security.authorization.CompiledPermissions;
import org.apache.jackrabbit.core.security.authorization.NamedAccessControlPolicyImpl;
import org.apache.jackrabbit.core.security.authorization.Permission;
import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry;
import org.apache.jackrabbit.core.security.principal.PrincipalImpl;
import org.apache.jackrabbit.core.security.SecurityConstants;
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.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.observation.Event;
import javax.jcr.observation.EventIterator;
import java.security.Principal;
import java.security.acl.Group;
import java.util.Iterator;
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.
* <p/>
* This provider creates upon initialization the following 2 groups:
* <ul>
* <li>User administrator</li>
* <li>Group administrator</li>
* </ul>
*
* The default access control policy defined by this provider has the following
* characteristics:
* <ul>
* <li>everybody has READ permission to all items,</li>
*
* <li>every known user is allowed to modify it's own properties except for
* her/his group membership,</li>
*
* <li>members of the 'User administrator' group are allowed to create, modify
* and remove users,</li>
*
* <li>members of the 'Group administrator' group are allowed to create, modify
* and remove groups,</li>
*
* <li>group membership can only be edited by members of the 'Group administrator'
* and the 'User administrator' group.</li>
* </ul>
*/
public class UserAccessControlProvider extends AbstractAccessControlProvider
        implements UserConstants {

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

    private final AccessControlPolicy policy;

    private String groupsPath;
    private String usersPath;

    private Principal userAdminGroup;
    private Principal groupAdminGroup;

    private String userAdminGroupPath;
    private String groupAdminGroupPath;
    private String administratorsGroupPath;

    /**
     *
     */
    public UserAccessControlProvider() {
        policy = new NamedAccessControlPolicyImpl("userPolicy");
    }

    //-------------------------------------------------< AccessControlUtils >---
    /**
     * Always returns false, since this ac provider does not use content stored
     * in items to evaluate AC information.
     *
     * @see org.apache.jackrabbit.core.security.authorization.AccessControlUtils#isAcItem(Path)
     */
    public boolean isAcItem(Path absPath) throws RepositoryException {
        return false;
    }

    /**
     * Always returns false, since this ac provider does not use content stored
     * in items to evaluate AC information.
     *
     * @see org.apache.jackrabbit.core.security.authorization.AccessControlUtils#isAcItem(ItemImpl)
     */
    public boolean isAcItem(ItemImpl item) throws RepositoryException {
        return false;
    }

    //----------------------------------------------< AccessControlProvider >---
    /**
     * @see org.apache.jackrabbit.core.security.authorization.AccessControlProvider#init(Session, Map)
     */
    public void init(Session systemSession, Map configuration) throws RepositoryException {
        super.init(systemSession, configuration);
        if (systemSession instanceof SessionImpl) {
            SessionImpl sImpl = (SessionImpl) systemSession;
            String userAdminName = (configuration.containsKey(USER_ADMIN_GROUP_NAME)) ? configuration.get(USER_ADMIN_GROUP_NAME).toString() : USER_ADMIN_GROUP_NAME;
            String groupAdminName = (configuration.containsKey(GROUP_ADMIN_GROUP_NAME)) ? configuration.get(GROUP_ADMIN_GROUP_NAME).toString() : GROUP_ADMIN_GROUP_NAME;

            // make sure the groups exist (and possibly create them).
            UserManager uMgr = sImpl.getUserManager();
            userAdminGroup = initGroup(uMgr, userAdminName);
            if (userAdminGroup != null && userAdminGroup instanceof ItemBasedPrincipal) {
                userAdminGroupPath = ((ItemBasedPrincipal) userAdminGroup).getPath();
            }
            groupAdminGroup = initGroup(uMgr, groupAdminName);
            if (groupAdminGroup != null && groupAdminGroup instanceof ItemBasedPrincipal) {
                groupAdminGroupPath = ((ItemBasedPrincipal) groupAdminGroup).getPath();
            }

            Principal administrators = initGroup(uMgr, SecurityConstants.ADMINISTRATORS_NAME);
            if (administrators != null && administrators instanceof ItemBasedPrincipal) {
                administratorsGroupPath = ((ItemBasedPrincipal) administrators).getPath();
            }
            usersPath = (uMgr instanceof UserManagerImpl) ? ((UserManagerImpl) uMgr).getUsersPath() : UserConstants.USERS_PATH;
            groupsPath = (uMgr instanceof UserManagerImpl) ? ((UserManagerImpl) uMgr).getGroupsPath() : UserConstants.GROUPS_PATH;
        } else {
            throw new RepositoryException("SessionImpl (system session) expected.");
        }
    }

    /**
     * @see org.apache.jackrabbit.core.security.authorization.AccessControlProvider#getEffectivePolicies(Path)
     */
    public AccessControlPolicy[] getEffectivePolicies(Path absPath) throws ItemNotFoundException, RepositoryException {
        checkInitialized();
        return new AccessControlPolicy[] {policy};
    }

    /**
     * Always returns <code>null</code>.
     *
     * @see org.apache.jackrabbit.core.security.authorization.AccessControlProvider#getEditor(Session)
     */
    public AccessControlEditor getEditor(Session session) {
        checkInitialized();
        // not editable at all: policy is always the default and cannot be
        // changed using the JCR API.
        return null;
    }

    /**
     * @see org.apache.jackrabbit.core.security.authorization.AccessControlProvider#compilePermissions(Set)
     */
    public CompiledPermissions compilePermissions(Set<Principal> principals) throws 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.getPath());
            }
        }
    }

    /**
     * @see org.apache.jackrabbit.core.security.authorization.AccessControlProvider#canAccessRoot(Set)
     */
    public boolean canAccessRoot(Set<Principal> principals) throws RepositoryException {
        checkInitialized();
        return true;
    }

    //------------------------------------------------------------< private >---

    private ItemBasedPrincipal getUserPrincipal(Set<Principal> principals) {
        try {
            UserManager uMgr = session.getUserManager();
            for (Principal p : principals) {
                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) session.getNode(path);
            } catch (RepositoryException e) {
                log.warn("Error while retrieving user node.", e.getMessage());
            }
        }
        return userNode;
    }

    private Node getExistingNode(Path path) throws RepositoryException {
        String absPath = resolver.getJCRPath(path.getNormalizedPath());
        if (session.nodeExists(absPath)) {
            return session.getNode(absPath);
        } else if (session.propertyExists(absPath)) {
            return session.getProperty(absPath).getParent();
        } else {
            String pPath = Text.getRelativeParent(absPath, 1);
            while (!"/".equals(pPath)) {
                if (session.nodeExists(pPath)) {
                    return session.getNode(pPath);
                } else {
                    pPath = Text.getRelativeParent(pPath, 1);
                }
            }        
            throw new ItemNotFoundException("Unable to determine permissions: No item and no existing parent for target path " + absPath);
        }
    }

    private int getPrivilegeBits(String privName) throws RepositoryException {
        Privilege[] privs = new Privilege[] {session.getAccessControlManager().privilegeFromName(privName)};
        return PrivilegeRegistry.getBits(privs);
    }

    private static boolean containsGroup(Set<Principal> principals, Principal group) {
        for (Iterator<Principal> it = principals.iterator(); it.hasNext() && group != null;) {
            Principal p = it.next();
            if (p.getName().equals(group.getName())) {
                return true;
            }
        }
        return false;
    }

    private static Principal initGroup(UserManager uMgr, String principalName) {
        Principal prnc = new PrincipalImpl(principalName);
        try {
            Authorizable auth = uMgr.getAuthorizable(prnc);
            if (auth == null) {
                auth = uMgr.createGroup(prnc);
            } else {
                if (!auth.isGroup()) {
                    log.warn("Cannot create group '" + principalName + "'; User with that principal already exists.");
                    auth = null;
                }
            }
            if (auth != null) {
                return auth.getPrincipal();
            }
        } catch (RepositoryException e) {
            // should never get here
            log.error("Error while initializing user/group administrators", e.getMessage());
        }
        return null;
    }

    //--------------------------------------------------------< inner class >---
    /**
     *
     */
    private class CompiledPermissionsImpl extends AbstractCompiledPermissions
            implements SynchronousEventListener {

        private final String userNodePath;

        private boolean isUserAdmin;
        private boolean isGroupAdmin;

        protected CompiledPermissionsImpl(Set<Principal> principals, String userNodePath) throws RepositoryException {
            this.userNodePath = userNodePath;
            isUserAdmin = containsGroup(principals, userAdminGroup);
            isGroupAdmin = containsGroup(principals, groupAdminGroup);

            int events = Event.PROPERTY_CHANGED | Event.PROPERTY_ADDED | Event.PROPERTY_REMOVED;
            observationMgr.addEventListener(this, events, groupsPath, true, null, null, false);
        }

        //------------------------------------< AbstractCompiledPermissions >---
        /**
         * @see AbstractCompiledPermissions#buildResult(Path)
         */
        protected Result buildResult(Path path) throws RepositoryException {
            NodeImpl userNode = null;
            try {
                if (session.nodeExists(userNodePath)) {
                    userNode = (NodeImpl) session.getNode(userNodePath);
                }
            } catch (RepositoryException e) {
                // ignore
            }

            if (userNode == null) {
                // no Node corresponding to user for which the permissions are
                // calculated -> no permissions/privileges.
                log.debug("No node at " + userNodePath);
                return new Result(Permission.NONE, Permission.NONE, PrivilegeRegistry.NO_PRIVILEGE, PrivilegeRegistry.NO_PRIVILEGE);
            }

            // no explicit denied permissions:
            int denies = Permission.NONE;
            // default allow permission and default privileges
            int allows = Permission.READ;
            int privs;
            // Determine if for path, the set of privileges must be calculated:
            // Generally, privileges can only be determined for existing nodes.
            String jcrPath = resolver.getJCRPath(path.getNormalizedPath());
            boolean calcPrivs = session.nodeExists(jcrPath);
            if (calcPrivs) {
                privs = getPrivilegeBits(Privilege.JCR_READ);
            } else {
                privs = PrivilegeRegistry.NO_PRIVILEGE;
            }

            if (Text.isDescendant(usersPath, jcrPath)) {
                /*
                 below the user-tree
                 - determine position of target relative to the editing user
                 - target may not be below an existing user but only below an
                   authorizable folder.
                 - determine if the editing user is user-admin
                 */
                NodeImpl node = (NodeImpl) getExistingNode(path);
                if (node.isNodeType(NT_REP_AUTHORIZABLE_FOLDER)) {
                    // an authorizable folder -> must be user admin in order
                    // to have permission to write.
                    if (isUserAdmin) {
                        allows |= (Permission.ADD_NODE | Permission.REMOVE_NODE | Permission.SET_PROPERTY | Permission.REMOVE_PROPERTY | Permission.NODE_TYPE_MNGMT);
                        if (calcPrivs) {
                            // grant WRITE privilege
                            // note: ac-read/modification is not included
                            privs |= getPrivilegeBits(PrivilegeRegistry.REP_WRITE);
                        }
                    }
                } else {
                    // rep:User node or some other custom node below an existing user.
                    // as the auth-folder doesn't allow other residual child nodes.
                    boolean editingOwnUser = node.isSame(userNode);
                    if (editingOwnUser) {
                        // user can only read && write his own props
                        allows |= (Permission.SET_PROPERTY | Permission.REMOVE_PROPERTY);
                        if (calcPrivs) {
                            privs |= getPrivilegeBits(Privilege.JCR_MODIFY_PROPERTIES);
                        }
                    } else if (isUserAdmin) {
                        allows |= (Permission.ADD_NODE | Permission.REMOVE_NODE | Permission.SET_PROPERTY | Permission.REMOVE_PROPERTY | Permission.NODE_TYPE_MNGMT);
                        if (calcPrivs) {
                            // grant WRITE privilege
                            // note: ac-read/modification is not included
                            privs |= getPrivilegeBits(PrivilegeRegistry.REP_WRITE);
                        }
                    } // else: normal user that isn't allowed to modify another user.
                }
            } else if (Text.isDescendant(groupsPath, jcrPath)) {
                /*
                below group-tree:
                - test if the user is group-administrator.
                - make sure group-admin cannot modify user-admin or administrators
                - ... and cannot remove itself.
                */
                if (isGroupAdmin) {
                    if (!jcrPath.startsWith(administratorsGroupPath) &&
                            !jcrPath.startsWith(userAdminGroupPath)) {
                        if (jcrPath.equals(groupAdminGroupPath)) {
                            // no remove perm on group-admin node
                            allows |= (Permission.ADD_NODE | Permission.SET_PROPERTY | Permission.REMOVE_PROPERTY | Permission.NODE_TYPE_MNGMT);
                            if (calcPrivs) {
                                privs |= getPrivilegeBits(PrivilegeRegistry.REP_WRITE);
                                privs ^= getPrivilegeBits(Privilege.JCR_REMOVE_NODE);
                            }
                        } else {
                            // complete write
                            allows |= (Permission.ADD_NODE | Permission.REMOVE_NODE | Permission.SET_PROPERTY | Permission.REMOVE_PROPERTY | Permission.NODE_TYPE_MNGMT);
                            if (calcPrivs) {
                                privs |= getPrivilegeBits(PrivilegeRegistry.REP_WRITE);
                            }
                        }
                    }
                }
            } // else outside of user/group tree -> read only.
            return new Result(allows, denies, privs, PrivilegeRegistry.NO_PRIVILEGE);
        }

        //--------------------------------------------< CompiledPermissions >---
        /**
         * @see CompiledPermissions#close()
         */
        public void close() {
            try {
                observationMgr.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;
            }
            // otherwise: retrieve from cache (or build)
            return super.grants(absPath, permissions);
        }

        /**
         * @see CompiledPermissions#canReadAll()
         */
        public boolean canReadAll() throws RepositoryException {
            return true;
        }

        //--------------------------------------------------< EventListener >---
        /**
         * Event listener is only interested in changes of group-membership
         * that effect the permission-evaluation.
         *
         * @see javax.jcr.observation.EventListener#onEvent(EventIterator)
         */
        public void onEvent(EventIterator events) {
            while (events.hasNext()) {
                Event ev = events.nextEvent();
                try {
                    String evPath = ev.getPath();
                    String repMembers = session.getJCRName(UserConstants.P_MEMBERS);
                    if (repMembers.equals(Text.getName(evPath))) {
                        // recalculate the is...Admin flags
                        Node userNode = session.getNode(userNodePath);
                        String nodePath = Text.getRelativeParent(evPath, 1);
                        if (userAdminGroupPath.equals(nodePath)) {
                            isUserAdmin = false;
                            if (ev.getType() != Event.PROPERTY_REMOVED) {
                                Value[] vs = session.getProperty(evPath).getValues();
                                for (int i = 0; i < vs.length && !isUserAdmin; i++) {
                                    isUserAdmin = userNode.getIdentifier().equals(vs[i].getString());
                                }
                            }
                        } else if (groupAdminGroupPath.equals(nodePath)) {
                            isGroupAdmin = false;
                            if (ev.getType() != Event.PROPERTY_REMOVED) {
                                Value[] vs = session.getProperty(evPath).getValues();
                                for (int i = 0; i < vs.length && !isGroupAdmin; i++) {
                                    isGroupAdmin = userNode.getIdentifier().equals(vs[i].getString());
                                }
                            }
                        }
                        // invalidate the cached results
                        clearCache();
                        // only need to clear the cache once. stop processing
                        break;
                    }
                } catch (RepositoryException e) {
                    // should never get here
                    log.error("Internal error ", e.getMessage());
                }
            }
        }
    }
}
TOP

Related Classes of org.apache.jackrabbit.core.security.user.UserAccessControlProvider$CompiledPermissionsImpl

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.