Package org.apache.jackrabbit.core.security.authorization.principalbased

Source Code of org.apache.jackrabbit.core.security.authorization.principalbased.ACLProvider$CompiledPermissionImpl

/*
* 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.authorization.principalbased;

import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.jcr.Item;
import javax.jcr.ItemNotFoundException;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.ValueFactory;
import javax.jcr.observation.Event;
import javax.jcr.observation.EventIterator;
import javax.jcr.security.AccessControlEntry;
import javax.jcr.security.AccessControlException;
import javax.jcr.security.AccessControlManager;
import javax.jcr.security.AccessControlPolicy;
import javax.jcr.security.Privilege;

import org.apache.jackrabbit.api.security.principal.PrincipalManager;
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.SecurityConstants;
import org.apache.jackrabbit.core.security.authorization.AbstractAccessControlProvider;
import org.apache.jackrabbit.core.security.authorization.AbstractCompiledPermissions;
import org.apache.jackrabbit.core.security.authorization.AccessControlConstants;
import org.apache.jackrabbit.core.security.authorization.AccessControlEditor;
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.spi.Path;
import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
import org.apache.jackrabbit.util.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* <code>CombinedProvider</code>...
*/
public class ACLProvider extends AbstractAccessControlProvider implements AccessControlConstants {

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

    // TODO: add means to show effective-policy to a user.
    private static final AccessControlPolicy effectivePolicy = EffectivePrincipalBasedPolicy.getInstance();

    private ACLEditor editor;

    private NodeImpl acRoot;

    //-------------------------------------------------< AccessControlUtils >---
    /**
     * @see org.apache.jackrabbit.core.security.authorization.AccessControlUtils#isAcItem(Path)
     */
    public boolean isAcItem(Path absPath) throws RepositoryException {
        Path.Element[] elems = absPath.getElements();
        for (Path.Element elem : elems) {
            if (N_POLICY.equals(elem.getName())) {
                return true;
            }
        }
        return false;
    }

    /**
     * @see org.apache.jackrabbit.core.security.authorization.AccessControlUtils#isAcItem(ItemImpl)
     */
    public boolean isAcItem(ItemImpl item) throws RepositoryException {
        NodeImpl n = ((item.isNode()) ? (NodeImpl) item : (NodeImpl) item.getParent());
        return n.isNodeType(NT_REP_ACL) || n.isNodeType(NT_REP_ACE);
    }

    //----------------------------------------------< AccessControlProvider >---
    /**
     * @see org.apache.jackrabbit.core.security.authorization.AccessControlProvider#init(javax.jcr.Session, java.util.Map)
     */
    @Override
    public void init(Session systemSession, Map configuration) throws RepositoryException {
        super.init(systemSession, configuration);

        NodeImpl root = (NodeImpl) session.getRootNode();
        if (root.hasNode(N_ACCESSCONTROL)) {
            acRoot = root.getNode(N_ACCESSCONTROL);
            if (!acRoot.isNodeType(NT_REP_ACCESS_CONTROL)) {
                throw new RepositoryException("Error while initializing Access Control Provider: Found ac-root to be wrong node type " + acRoot.getPrimaryNodeType().getName());
            }
        } else {
            acRoot = root.addNode(N_ACCESSCONTROL, NT_REP_ACCESS_CONTROL, null);
        }

        editor = new ACLEditor(session, resolver.getQPath(acRoot.getPath()));
        // TODO: replace by configurable default policy (see JCR-2331)
        if (!configuration.containsKey(PARAM_OMIT_DEFAULT_PERMISSIONS)) {
            try {
                log.debug("Install initial permissions: ...");

                ValueFactory vf = session.getValueFactory();
                Map<String, Value> restrictions = new HashMap<String, Value>();
                restrictions.put(session.getJCRName(ACLTemplate.P_NODE_PATH), vf.createValue(root.getPath(), PropertyType.PATH));
                restrictions.put(session.getJCRName(ACLTemplate.P_GLOB), vf.createValue(GlobPattern.WILDCARD_ALL));

                PrincipalManager pMgr = session.getPrincipalManager();
                AccessControlManager acMgr = session.getAccessControlManager();

                // initial default permissions for the administrators group               
                Principal administrators;
                String pName = SecurityConstants.ADMINISTRATORS_NAME;
                if (pMgr.hasPrincipal(pName)) {
                    administrators = pMgr.getPrincipal(pName);
                    installDefaultPermissions(administrators,
                        new Privilege[] {acMgr.privilegeFromName(Privilege.JCR_ALL)},
                        restrictions, editor);
                } else {
                    log.warn("Administrators principal group is missing -> Not adding default permissions.");
                }

                // initialize default permissions for the everyone group
                installDefaultPermissions(pMgr.getEveryone(),
                        new Privilege[] {acMgr.privilegeFromName(Privilege.JCR_READ)},
                        restrictions, editor);

                session.save();
            } catch (RepositoryException e) {
                log.error("Failed to set-up minimal access control for root node of workspace " + session.getWorkspace().getName());
                session.getRootNode().refresh(false);
            }
        }
    }

    private static void installDefaultPermissions(Principal principal,
                                                  Privilege[] privs,
                                                  Map<String, Value> restrictions,
                                                  AccessControlEditor editor)
            throws RepositoryException, AccessControlException {
        AccessControlPolicy[] acls = editor.editAccessControlPolicies(principal);
        if (acls.length > 0) {
            ACLTemplate acl = (ACLTemplate) acls[0];
            if (acl.isEmpty()) {
                acl.addEntry(principal, privs, true, restrictions);
                editor.setPolicy(acl.getPath(), acl);
            } else {
                log.debug("... policy for principal '"+principal.getName()+"' already present.");
            }
        } else {
            log.debug("... policy for principal  '"+principal.getName()+"'  already present.");
        }
    }

    /**
     * @see org.apache.jackrabbit.core.security.authorization.AccessControlProvider#getEffectivePolicies(Path)
     */
    public AccessControlPolicy[] getEffectivePolicies(Path absPath)
            throws ItemNotFoundException, RepositoryException {
        /*
           since the per-node effect of the policies is defined by the
           rep:nodePath restriction present with the individual access control
           entries, returning the principal-based policy at 'absPath' (which for
           most nodes in the repository isn't available anyway) doesn't
           provide the desired information.
           As tmp. solution some default policy is returned indicating.
           TODO: add proper evaluation and return a set of ACLs that take effect on the node at abs path
        */
        return new AccessControlPolicy[] {effectivePolicy};
    }

    /**
     * @see org.apache.jackrabbit.core.security.authorization.AccessControlProvider#getEditor(Session)
     */
    public AccessControlEditor getEditor(Session editingSession) {
        checkInitialized();
        if (editingSession instanceof SessionImpl) {
            try {
                return new ACLEditor((SessionImpl) editingSession, session.getQPath(acRoot.getPath()));
            } catch (RepositoryException e) {
                // should never get here
                log.error("Internal error: ", e.getMessage());
            }
        }

        log.debug("Unable to build access control editor " + ACLEditor.class.getName() + ".");
        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 if (isReadOnly(principals)) {
            return getReadOnlyPermissions();
        } else {
            return new ACLProvider.CompiledPermissionImpl(principals);
        }
    }

    /**
     * @see org.apache.jackrabbit.core.security.authorization.AccessControlProvider#canAccessRoot(Set)
     */
    public boolean canAccessRoot(Set<Principal> principals) throws RepositoryException {
        checkInitialized();
        if (isAdminOrSystem(principals)) {
            return true;
        } else {
            CompiledPermissions cp = new CompiledPermissionImpl(principals, false);
            return cp.grants(PathFactoryImpl.getInstance().getRootPath(), Permission.READ);
        }
    }

    //-----------------------------------------------------< CompiledPolicy >---
    /**
     *
     */
    private class CompiledPermissionImpl extends AbstractCompiledPermissions
            implements SynchronousEventListener {

        private final Set<Principal> principals;
        private final Set<String> acPaths;
        private List<AccessControlEntry> entries;

        /**
         * @param principals the underlying principals
         * @throws RepositoryException if an error occurs
         */
        private CompiledPermissionImpl(Set<Principal> principals) throws RepositoryException {
            this(principals, true);
        }

        /**
         * @param principals the underlying principals
         * @param listenToEvents if <code>true</code> listens to events
         * @throws RepositoryException if an error occurs
         */
        private CompiledPermissionImpl(Set<Principal> principals, boolean listenToEvents) throws RepositoryException {

            this.principals = principals;
            acPaths = new HashSet<String>(principals.size());
            entries = reload();

            // TODO: describe
            if (listenToEvents) {
                int events = Event.PROPERTY_CHANGED | Event.PROPERTY_ADDED |
                        Event.PROPERTY_REMOVED | Event.NODE_ADDED | Event.NODE_REMOVED;
                String[] ntNames = new String[] {
                        session.getJCRName(NT_REP_ACE)
                };
                observationMgr.addEventListener(this, events, acRoot.getPath(), true, null, ntNames, false);
            }
        }

        //------------------------------------< AbstractCompiledPermissions >---
        /**
         * @see AbstractCompiledPermissions#buildResult(Path)
         */
        @Override
        protected synchronized Result buildResult(Path absPath) throws RepositoryException {
            if (!absPath.isAbsolute()) {
                throw new RepositoryException("Absolute path expected.");
            }

            boolean isAcItem = isAcItem(absPath);
            String jcrPath = session.getJCRPath(absPath);

            // retrieve principal-based permissions and privileges
            Result result;
            if (session.itemExists(jcrPath)) {
                Item item = session.getItem(jcrPath);
                result = getResult(item, item.getPath(), isAcItem);
            } else {
                result = getResult(null, jcrPath, isAcItem);
            }
            return result;
        }


        /**
         * Loop over all entries and evaluate allows/denies for those matching
         * the given jcrPath.
         *
         * @param target Existing target item for which the permissions will be
         * evaluated or <code>null</code>.
         * @param targetPath Path used for the evaluation; pointing to an
         * existing or non-existing item.
         * @param isAcItem the item
         * @return the result
         * @throws RepositoryException if an error occurs
         */
        private Result getResult(Item target,
                                 String targetPath,
                                 boolean isAcItem) throws RepositoryException {
            int allows = Permission.NONE;
            int denies = Permission.NONE;
            int allowPrivileges = PrivilegeRegistry.NO_PRIVILEGE;
            int denyPrivileges = PrivilegeRegistry.NO_PRIVILEGE;
            int parentAllows = PrivilegeRegistry.NO_PRIVILEGE;
            int parentDenies = PrivilegeRegistry.NO_PRIVILEGE;

            String parentPath = Text.getRelativeParent(targetPath, 1);
            for (AccessControlEntry entry : entries) {
                if (!(entry instanceof ACLTemplate.Entry)) {
                    log.warn("Unexpected AccessControlEntry instance -> ignore");
                    continue;
                }
                ACLTemplate.Entry entr = (ACLTemplate.Entry) entry;
                int privs = entr.getPrivilegeBits();

                if (!"".equals(parentPath) && entr.matches(parentPath)) {
                    if (entr.isAllow()) {
                        parentAllows |= Permission.diff(privs, parentDenies);
                    } else {
                        parentDenies |= Permission.diff(privs, parentAllows);
                    }
                }

                boolean matches = (target != null) ? entr.matches(target) : entr.matches(targetPath);
                if (matches) {
                    if (entr.isAllow()) {
                        allowPrivileges |= Permission.diff(privs, denyPrivileges);
                        int permissions = PrivilegeRegistry.calculatePermissions(allowPrivileges, parentAllows, true, isAcItem);
                        allows |= Permission.diff(permissions, denies);
                    } else {
                        denyPrivileges |= Permission.diff(privs, allowPrivileges);
                        int permissions = PrivilegeRegistry.calculatePermissions(denyPrivileges, parentDenies, false, isAcItem);
                        denies |= Permission.diff(permissions, allows);
                    }
                }
            }
            return new Result(allows, denies, allowPrivileges, denyPrivileges);
        }

        //--------------------------------------------< CompiledPermissions >---
        /**
         * @see CompiledPermissions#close()
         */
        @Override
        public void close() {
            try {
                observationMgr.removeEventListener(this);
            } catch (RepositoryException e) {
                log.debug("Unable to unregister listener: ", e.getMessage());
            }
            super.close();
        }

        //--------------------------------------------------< EventListener >---
        /**
         * @see javax.jcr.observation.EventListener#onEvent(EventIterator)
         */
        public synchronized void onEvent(EventIterator events) {
            try {
                boolean reload = false;
                while (events.hasNext() && !reload) {
                    Event ev = events.nextEvent();
                    String path = ev.getPath();
                    // only invalidate cache if any of the events affects the
                    // nodes defining permissions for the principals.
                    switch (ev.getType()) {
                        case Event.NODE_ADDED:
                        case Event.NODE_REMOVED:
                        case Event.NODE_MOVED:
                            reload = acPaths.contains(Text.getRelativeParent(path, 2));
                            break;
                        case Event.PROPERTY_ADDED:
                        case Event.PROPERTY_CHANGED:
                        case Event.PROPERTY_REMOVED:
                            reload = acPaths.contains(Text.getRelativeParent(path, 3));
                            break;

                        default:
                            // illegal event-type: should never occur. ignore
                            break;
                    }
                }
                // eventually reload the ACL and clear the cache
                if (reload) {
                    clearCache();
                    // reload the acl
                    entries = reload();
                }
            } catch (RepositoryException e) {
                // should never get here
                log.warn("Internal error: ", e.getMessage());
            }
        }

        /**
         *
         * @return the aces
         * @throws RepositoryException if an error occurs
         */
        private List<AccessControlEntry> reload() throws RepositoryException {
            // reload the paths
            acPaths.clear();

            // acNodes must be ordered in the same order as the principals
            // in order to obtain proper acl-evaluation in case the given
            // principal-set is ordered.
            List<AccessControlEntry> allACEs = new ArrayList<AccessControlEntry>();
            // build acl-hierarchy assuming that principal-order determines the
            // acl-inheritance.
            for (Principal p : principals) {
                ACLTemplate acl = editor.getACL(p);
                if (acl == null || acl.isEmpty()) {
                    acPaths.add(editor.getPathToAcNode(p));
                } else {
                    // retrieve the ACEs from the node
                    AccessControlEntry[] aces = acl.getAccessControlEntries();
                    allACEs.addAll(Arrays.asList(aces));
                    acPaths.add(acl.getPath());
                }
            }

            return allACEs;
        }
    }

    //--------------------------------------------------------------------------
    /**
     * Dummy effective policy
     */
    private static final class EffectivePrincipalBasedPolicy implements AccessControlPolicy {

        private static EffectivePrincipalBasedPolicy INSTANCE = new EffectivePrincipalBasedPolicy();
        private EffectivePrincipalBasedPolicy() {
        }

        private static EffectivePrincipalBasedPolicy getInstance() {
            return INSTANCE;
        }
    }
}
TOP

Related Classes of org.apache.jackrabbit.core.security.authorization.principalbased.ACLProvider$CompiledPermissionImpl

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.