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

Source Code of org.apache.jackrabbit.core.security.authorization.acl.EntryCollector

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

import org.apache.jackrabbit.core.NodeImpl;
import org.apache.jackrabbit.core.SessionImpl;
import org.apache.jackrabbit.core.id.NodeId;
import org.apache.jackrabbit.core.security.authorization.AccessControlConstants;
import org.apache.jackrabbit.core.security.authorization.AccessControlModifications;
import org.apache.jackrabbit.core.security.authorization.AccessControlObserver;
import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry;
import org.apache.jackrabbit.util.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.observation.Event;
import javax.jcr.observation.EventIterator;
import javax.jcr.observation.ObservationManager;
import javax.jcr.security.AccessControlEntry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
* <code>EntryCollector</code> collects ACEs defined and effective for a
* given <code>Node</code> and listens to access control modifications in order
* to inform listeners.
*/
public class EntryCollector extends AccessControlObserver implements AccessControlConstants {

    /**
     * logger instance
     */
    private static final Logger log = LoggerFactory.getLogger(EntryCollector.class);

    /**
     * The system session used to register an event listener and process the
     * events as well as collect AC entries.
     */
    protected final SessionImpl systemSession;

    /**
     * The root id.
     */
    protected final NodeId rootID;

    private final PrivilegeRegistry privilegeRegistry;
   
    /**
     * Standard JCR name form of the {@link #N_POLICY} constant.
     */
    private final String repPolicyName;

    /**
     *
     * @param systemSession
     * @param rootID
     * @throws RepositoryException
     */
    protected EntryCollector(SessionImpl systemSession, NodeId rootID) throws RepositoryException {
        this.systemSession = systemSession;
        this.rootID = rootID;

        privilegeRegistry = new PrivilegeRegistry(systemSession);
        repPolicyName = systemSession.getJCRName(N_POLICY);

        ObservationManager observationMgr = systemSession.getWorkspace().getObservationManager();
        /*
         Make sure the collector and all subscribed listeners are informed upon
         ACL modifications. Interesting events are:
         - new ACL (NODE_ADDED)
         - new ACE (NODE_ADDED)
         - changing ACE (PROPERTY_CHANGED)
         - removed ACL (NODE_REMOVED)
         - removed ACE (NODE_REMOVED)
        */
        int events = Event.PROPERTY_CHANGED | Event.NODE_ADDED | Event.NODE_REMOVED;
        String[] ntNames = new String[] {
                systemSession.getJCRName(NT_REP_ACCESS_CONTROLLABLE),
                systemSession.getJCRName(NT_REP_ACL),
                systemSession.getJCRName(NT_REP_ACE)
        };
        observationMgr.addEventListener(this, events, systemSession.getRootNode().getPath(), true, null, ntNames, true);       
    }

    /**
     * Release all resources contained by this instance. It will no longer be
     * used. This implementation only stops listening to ac modification events.
     */
    @Override
    protected void close() {
        super.close();
        try {
            systemSession.getWorkspace().getObservationManager().removeEventListener(this);
        } catch (RepositoryException e) {
            log.error("Unexpected error while closing CachingEntryCollector", e);
        }
    }

    /**
     * Collect the ACEs effective at the given node applying the specified
     * filter.
     *
     * @param node
     * @param filter
     * @return
     * @throws RepositoryException
     */
    protected List<AccessControlEntry> collectEntries(NodeImpl node, EntryFilter filter) throws RepositoryException {
        LinkedList<AccessControlEntry> userAces = new LinkedList<AccessControlEntry>();
        LinkedList<AccessControlEntry> groupAces = new LinkedList<AccessControlEntry>();

        NodeId next = node.getNodeId();
        while (next != null) {
            List<AccessControlEntry> entries = getEntries(next);
            if (!entries.isEmpty() && filter != null) {
                filter.filterEntries(entries, userAces, groupAces);
            }
            next = getParentId(next);
        }
       
        List<AccessControlEntry> entries = new ArrayList<AccessControlEntry>(userAces.size() + groupAces.size());
        entries.addAll(userAces);
        entries.addAll(groupAces);

        return entries;
    }

    /**
     * Retrieve the access control entries defined for the given node. If the
     * node is not access controlled or if the ACL is empty this method returns
     * an empty list.
     *
     * @param node
     * @return
     * @throws RepositoryException
     */
    protected List<AccessControlEntry> getEntries(NodeImpl node) throws RepositoryException {
        List<AccessControlEntry> entries;
        if (ACLProvider.isAccessControlled(node)) {
            // collect the aces of that node.
            NodeImpl aclNode = node.getNode(N_POLICY);
            entries = new ACLTemplate(aclNode, privilegeRegistry).getEntries();
        } else {
            // not access controlled
            entries = Collections.emptyList();
        }
        return entries;
    }

    /**
     *
     * @param nodeId
     * @return
     * @throws RepositoryException
     */
    protected List<AccessControlEntry> getEntries(NodeId nodeId) throws RepositoryException {
        NodeImpl node = getNodeById(nodeId);
        return getEntries(node);
    }

    /**
     * Returns the parentId of the given nodeId.
     *
     * @param nodeId
     * @return
     * @throws RepositoryException
     */
    protected NodeId getParentId(NodeId nodeId) throws RepositoryException {
        NodeId parentId;
        if (rootID.equals(nodeId)) {
            parentId = null; // root node reached.
        } else {
            parentId = getNodeById(nodeId).getParentId();
        }
        return parentId;
    }

    /**
     *
     * @param nodeId
     * @return
     * @throws javax.jcr.RepositoryException
     */
    NodeImpl getNodeById(NodeId nodeId) throws RepositoryException {
        return ((NodeImpl) systemSession.getItemManager().getItem(nodeId));
    }

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

    private static NodeId accessControlledIdFromAclNode(Node aclNode) throws RepositoryException {
        return ((NodeImpl) aclNode.getParent()).getNodeId();
    }

    private static NodeId accessControlledIdFromAceNode(Node aceNode) throws RepositoryException {
        return ((NodeImpl) aceNode.getParent().getParent()).getNodeId();
    }

    private static void addModification(NodeId accessControllNodeId, int modType,
                                        Map<NodeId,Integer> modMap) {
        if (modMap.containsKey(accessControllNodeId)) {
            // update modMap
            modMap.put(accessControllNodeId, modType | modMap.get(accessControllNodeId));
        } else {
            modMap.put(accessControllNodeId, modType);
        }
    }
   
    //------------------------------------------------------< EventListener >---
    /**
     * Collect is of access controlled nodes that are effected by access control
     * modification together with the corresponding modification type and
     * finally inform listeners about the modifications.
     *
     * @param events
     */
    public void onEvent(EventIterator events) {
        /* map of access-controlled nodeId to type of ac modification */
        Map<NodeId,Integer> modMap = new HashMap<NodeId,Integer>();

        // collect the ids of all access controlled nodes that have been affected
        // by the events and thus need their cache entries updated or cleared.
        while (events.hasNext()) {
            try {
                Event ev = events.nextEvent();
                String identifier = ev.getIdentifier();
                String path = ev.getPath();

                switch (ev.getType()) {
                    case Event.NODE_ADDED:
                        NodeImpl n = (NodeImpl) systemSession.getNodeByIdentifier(identifier);
                        if (n.isNodeType(NT_REP_ACL)) {
                            // a new ACL was added -> use the added node to update
                            // the cache.
                            addModification(accessControlledIdFromAclNode(n), POLICY_ADDED, modMap);
                        } else if (n.isNodeType(NT_REP_ACE)) {
                            // a new ACE was added -> use the parent node (acl)
                            // to update the cache.
                            addModification(accessControlledIdFromAceNode(n), POLICY_MODIFIED, modMap);
                        } /* else: some other node added below an access controlled
                             parent node -> not interested. */
                        break;
                    case Event.NODE_REMOVED:
                        String parentPath = Text.getRelativeParent(path, 1);
                        if (systemSession.nodeExists(parentPath)) {
                            NodeImpl parent = (NodeImpl) systemSession.getNode(parentPath);
                            if (repPolicyName.equals(Text.getName(path))){
                                // the complete acl was removed -> clear cache entry
                                addModification(parent.getNodeId(), POLICY_REMOVED, modMap);
                            } else if (parent.isNodeType(NT_REP_ACL)) {
                                // an ace was removed -> refresh cache for the
                                // containing access control list upon next access
                                addModification(accessControlledIdFromAclNode(parent), POLICY_MODIFIED, modMap);
                            } /* else:
                             a) some other child node of an access controlled
                                node -> not interested.
                             b) a child node of an ACE. not relevant for this
                                implementation -> ignore
                           */
                        } else {
                            log.debug("Cannot process NODE_REMOVED event. Parent " + parentPath + " doesn't exist (anymore).");
                        }
                        break;
                    case Event.PROPERTY_CHANGED:
                        // test if the changed prop belongs to an ACE
                        NodeImpl parent = (NodeImpl) systemSession.getNodeByIdentifier(identifier);
                        if (parent.isNodeType(NT_REP_ACE)) {
                            addModification(accessControlledIdFromAceNode(parent), POLICY_MODIFIED, modMap);
                        } /* some other property below an access controlled node
                             changed -> not interested. (NOTE: rep:ACL doesn't
                             define any properties. */
                        break;
                    default:
                        // illegal event-type: should never occur. ignore
                }
            } catch (RepositoryException e) {
                // should not get here
                log.error("Internal error: ", e);
            }
        }

        if (!modMap.isEmpty()) {
            // notify listeners and eventually clean up internal caches.
            notifyListeners(new AccessControlModifications<NodeId>(modMap));
        }
    }
}
TOP

Related Classes of org.apache.jackrabbit.core.security.authorization.acl.EntryCollector

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.