org.alfresco.repo.security.permissions.impl.IntersectPermissionServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.repo.security.permissions.impl.IntersectPermissionServiceImpl.java

Source

/*
 * Copyright (C) 2005-2010 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 * 
 * modified by Takeshi Totani, https://github.com/to2y
 */
package org.alfresco.repo.security.permissions.impl;

import java.io.Serializable;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.GrantedAuthority;
import net.sf.acegisecurity.providers.dao.User;

import org.alfresco.model.ContentModel;
import org.alfresco.repo.avm.AVMNodeConverter;
import org.alfresco.repo.avm.AVMRepository;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.domain.permissions.AclDAO;
import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.authority.AuthorityServiceImpl;
import org.alfresco.repo.security.permissions.ACLType;
import org.alfresco.repo.security.permissions.AccessControlEntry;
import org.alfresco.repo.security.permissions.AccessControlList;
import org.alfresco.repo.security.permissions.AccessControlListProperties;
import org.alfresco.repo.security.permissions.DynamicAuthority;
import org.alfresco.repo.security.permissions.NodePermissionEntry;
import org.alfresco.repo.security.permissions.PermissionEntry;
import org.alfresco.repo.security.permissions.PermissionReference;
import org.alfresco.repo.security.permissions.PermissionServiceSPI;
import org.alfresco.repo.security.permissions.impl.ModelDAO;
import org.alfresco.repo.security.permissions.impl.PermissionsDaoComponent;
import org.alfresco.repo.security.permissions.impl.SimplePermissionReference;
import org.alfresco.repo.security.permissions.impl.RequiredPermission;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.version.Version2Model;
import org.alfresco.repo.version.VersionModel;
import org.alfresco.repo.version.common.VersionUtil;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.AccessPermission;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.OwnableService;
import org.alfresco.service.cmr.security.PermissionContext;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.EqualsHelper;
import org.alfresco.util.Pair;
import org.alfresco.util.PropertyCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.extensions.surf.util.AbstractLifecycleBean;

/**
 * The Alfresco implementation of a permissions service against our APIs for the permissions model and permissions
 * persistence.
 * 
 * @author andyh
 */
public class IntersectPermissionServiceImpl extends AbstractLifecycleBean implements PermissionServiceSPI {
    static SimplePermissionReference OLD_ALL_PERMISSIONS_REFERENCE = new SimplePermissionReference(
            QName.createQName("", PermissionService.ALL_PERMISSIONS), PermissionService.ALL_PERMISSIONS);

    private static Log log = LogFactory.getLog(IntersectPermissionServiceImpl.class);

    /** a transactionally-safe cache to be injected */
    protected SimpleCache<Serializable, AccessStatus> accessCache;

    protected SimpleCache<Serializable, Set<String>> readersCache;

    protected SimpleCache<Serializable, Set<String>> readersDeniedCache;

    /*
     * Access to the model
     */
    protected ModelDAO modelDAO;

    /*
     * Access to permissions
     */
    protected PermissionsDaoComponent permissionsDaoComponent;

    /*
     * Access to the node service
     */
    protected NodeService nodeService;

    /*
     * Access to the tenant service
     */
    protected TenantService tenantService;

    /*
     * Access to the data dictionary
     */
    protected DictionaryService dictionaryService;

    /*
     * Access to the ownable service
     */
    protected OwnableService ownableService;

    /*
     * Access to the authority component
     */
    protected AuthorityService authorityService;

    /*
     * Dynamic authorities providers
     */
    protected List<DynamicAuthority> dynamicAuthorities;

    protected PolicyComponent policyComponent;

    protected AclDAO aclDaoComponent;

    protected PermissionReference allPermissionReference;

    protected boolean anyDenyDenies = false;

    /**
     * Standard spring construction.
     */
    public IntersectPermissionServiceImpl() {
        super();
    }

    //
    // Inversion of control
    //

    /**
     * Set the dictionary service
     * @param dictionaryService 
     */
    public void setDictionaryService(DictionaryService dictionaryService) {
        this.dictionaryService = dictionaryService;
    }

    /**
     * @param anyDenyDenies the anyDenyDenies to set
     */
    public void setAnyDenyDenies(boolean anyDenyDenies) {
        this.anyDenyDenies = anyDenyDenies;
        accessCache.clear();
        readersCache.clear();
        readersDeniedCache.clear();
    }

    public boolean getAnyDenyDenies() {
        return anyDenyDenies;
    }

    /**
     * Set the permissions model dao
     * 
     * @param modelDAO
     */
    public void setModelDAO(ModelDAO modelDAO) {
        this.modelDAO = modelDAO;
    }

    /**
     * Set the node service.
     * 
     * @param nodeService
     */
    public void setNodeService(NodeService nodeService) {
        this.nodeService = nodeService;
    }

    /**
     * Set the ownable service.
     * 
     * @param ownableService
     */
    public void setOwnableService(OwnableService ownableService) {
        this.ownableService = ownableService;
    }

    /**
     * Set the tenant service.
     * @param tenantService
     */
    public void setTenantService(TenantService tenantService) {
        this.tenantService = tenantService;
    }

    /**
     * Set the permissions dao component
     * 
     * @param permissionsDaoComponent
     */
    public void setPermissionsDaoComponent(PermissionsDaoComponent permissionsDaoComponent) {
        this.permissionsDaoComponent = permissionsDaoComponent;
    }

    /**
     * Set the authority service.
     * 
     * @param authorityService
     */
    public void setAuthorityService(AuthorityService authorityService) {
        this.authorityService = authorityService;
    }

    /**
     * Set the dynamic authorities
     * 
     * @param dynamicAuthorities
     */
    public void setDynamicAuthorities(List<DynamicAuthority> dynamicAuthorities) {
        this.dynamicAuthorities = dynamicAuthorities;
    }

    /**
     * Set the ACL DAO component.
     * 
     * @param aclDaoComponent
     */
    public void setAclDAO(AclDAO aclDaoComponent) {
        this.aclDaoComponent = aclDaoComponent;
    }

    /**
     * Set the permissions access cache.
     * 
     * @param accessCache
     *            a transactionally safe cache
     */
    public void setAccessCache(SimpleCache<Serializable, AccessStatus> accessCache) {
        this.accessCache = accessCache;
    }

    /**
     * @param readersCache the readersCache to set
     */
    public void setReadersCache(SimpleCache<Serializable, Set<String>> readersCache) {
        this.readersCache = readersCache;
    }

    /**
     * @param readersDeniedCache the readersDeniedCache to set
     */
    public void setReadersDeniedCache(SimpleCache<Serializable, Set<String>> readersDeniedCache) {
        this.readersDeniedCache = readersDeniedCache;
    }

    /**
     * Set the policy component
     * 
     * @param policyComponent
     */
    public void setPolicyComponent(PolicyComponent policyComponent) {
        this.policyComponent = policyComponent;
    }

    /**
     * Cache clear on move node
     * 
     * @param oldChildAssocRef
     * @param newChildAssocRef
     */
    public void onMoveNode(ChildAssociationRef oldChildAssocRef, ChildAssociationRef newChildAssocRef) {
        accessCache.clear();
    }

    /**
     * Cache clear on create of a child association from an authority container.
     * 
     * @param childAssocRef
     */
    public void onCreateChildAssociation(ChildAssociationRef childAssocRef) {
        accessCache.clear();
    }

    /**
     * Cache clear on delete of a child association from an authority container.
     * 
     * @param childAssocRef
     */
    public void beforeDeleteChildAssociation(ChildAssociationRef childAssocRef) {
        accessCache.clear();
    }

    @Override
    protected void onBootstrap(ApplicationEvent event) {
        PropertyCheck.mandatory(this, "dictionaryService", dictionaryService);
        PropertyCheck.mandatory(this, "modelDAO", modelDAO);
        PropertyCheck.mandatory(this, "nodeService", nodeService);
        PropertyCheck.mandatory(this, "ownableService", ownableService);
        PropertyCheck.mandatory(this, "permissionsDaoComponent", permissionsDaoComponent);
        PropertyCheck.mandatory(this, "authorityService", authorityService);
        PropertyCheck.mandatory(this, "accessCache", accessCache);
        PropertyCheck.mandatory(this, "readersCache", readersCache);
        PropertyCheck.mandatory(this, "policyComponent", policyComponent);
        PropertyCheck.mandatory(this, "aclDaoComponent", aclDaoComponent);

        allPermissionReference = getPermissionReference(ALL_PERMISSIONS);
    }

    /**
     * No-op
     */
    @Override
    protected void onShutdown(ApplicationEvent event) {
    }

    public void init() {
        policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onMoveNode"),
                ContentModel.TYPE_BASE, new JavaBehaviour(this, "onMoveNode"));

        policyComponent.bindClassBehaviour(
                QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateChildAssociation"),
                ContentModel.TYPE_AUTHORITY_CONTAINER, new JavaBehaviour(this, "onCreateChildAssociation"));
        policyComponent.bindClassBehaviour(
                QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteChildAssociation"),
                ContentModel.TYPE_AUTHORITY_CONTAINER, new JavaBehaviour(this, "beforeDeleteChildAssociation"));
    }

    //
    // Permissions Service
    //

    public String getOwnerAuthority() {
        return OWNER_AUTHORITY;
    }

    public String getAllAuthorities() {
        return ALL_AUTHORITIES;
    }

    public String getAllPermission() {
        return ALL_PERMISSIONS;
    }

    public Set<AccessPermission> getPermissions(NodeRef nodeRef) {
        return getAllPermissionsImpl(nodeRef, true, true);
    }

    public Set<AccessPermission> getAllSetPermissions(NodeRef nodeRef) {
        HashSet<AccessPermission> accessPermissions = new HashSet<AccessPermission>();
        NodePermissionEntry nodePremissionEntry = getSetPermissions(nodeRef);
        for (PermissionEntry pe : nodePremissionEntry.getPermissionEntries()) {
            accessPermissions.add(new AccessPermissionImpl(getPermission(pe.getPermissionReference()),
                    pe.getAccessStatus(), pe.getAuthority(), pe.getPosition()));
        }
        return accessPermissions;
    }

    public Set<AccessPermission> getAllSetPermissions(StoreRef storeRef) {
        HashSet<AccessPermission> accessPermissions = new HashSet<AccessPermission>();
        NodePermissionEntry nodePremissionEntry = getSetPermissions(storeRef);
        for (PermissionEntry pe : nodePremissionEntry.getPermissionEntries()) {
            accessPermissions.add(new AccessPermissionImpl(getPermission(pe.getPermissionReference()),
                    pe.getAccessStatus(), pe.getAuthority(), pe.getPosition()));
        }
        return accessPermissions;
    }

    protected Set<AccessPermission> getAllPermissionsImpl(NodeRef nodeRef, boolean includeTrue,
            boolean includeFalse) {
        String userName = AuthenticationUtil.getRunAsUser();
        HashSet<AccessPermission> accessPermissions = new HashSet<AccessPermission>();
        for (PermissionReference pr : getSettablePermissionReferences(nodeRef)) {
            if (hasPermission(nodeRef, pr) == AccessStatus.ALLOWED) {
                accessPermissions
                        .add(new AccessPermissionImpl(getPermission(pr), AccessStatus.ALLOWED, userName, -1));
            } else {
                if (includeFalse) {
                    accessPermissions
                            .add(new AccessPermissionImpl(getPermission(pr), AccessStatus.DENIED, userName, -1));
                }
            }
        }
        return accessPermissions;
    }

    public Set<String> getSettablePermissions(NodeRef nodeRef) {
        Set<PermissionReference> settable = getSettablePermissionReferences(nodeRef);
        Set<String> strings = new HashSet<String>(settable.size());
        for (PermissionReference pr : settable) {
            strings.add(getPermission(pr));
        }
        return strings;
    }

    public Set<String> getSettablePermissions(QName type) {
        Set<PermissionReference> settable = getSettablePermissionReferences(type);
        Set<String> strings = new LinkedHashSet<String>(settable.size());
        for (PermissionReference pr : settable) {
            strings.add(getPermission(pr));
        }
        return strings;
    }

    public NodePermissionEntry getSetPermissions(NodeRef nodeRef) {
        return permissionsDaoComponent.getPermissions(tenantService.getName(nodeRef));
    }

    public NodePermissionEntry getSetPermissions(StoreRef storeRef) {
        return permissionsDaoComponent.getPermissions(storeRef);
    }

    public AccessStatus hasPermission(NodeRef passedNodeRef, final PermissionReference permIn) {

        // If the node ref is null there is no sensible test to do - and there
        // must be no permissions
        // - so we allow it
        if (passedNodeRef == null) {
            return AccessStatus.ALLOWED;
        }

        // If the permission is null we deny
        if (permIn == null) {
            return AccessStatus.DENIED;
        }

        // AVM nodes - test for existence underneath
        if (passedNodeRef.getStoreRef().getProtocol().equals(StoreRef.PROTOCOL_AVM)) {
            return doAvmCan(passedNodeRef, permIn);
        }

        // Note: if we're directly accessing a frozen state (version) node (ie. in the 'version' store) we need to check permissions for the versioned node (ie. in the 'live' store)
        if (isVersionNodeRef(passedNodeRef)) {
            passedNodeRef = convertVersionNodeRefToVersionedNodeRef(VersionUtil.convertNodeRef(passedNodeRef));
        }

        // Allow permissions for nodes that do not exist
        if (!nodeService.exists(passedNodeRef)) {
            return AccessStatus.ALLOWED;
        }

        final NodeRef nodeRef = tenantService.getName(passedNodeRef);

        final PermissionReference perm;
        if (permIn.equals(OLD_ALL_PERMISSIONS_REFERENCE)) {
            perm = getAllPermissionReference();
        } else {
            perm = permIn;
        }

        if (AuthenticationUtil.getRunAsUser() == null) {
            return AccessStatus.DENIED;
        }

        if (AuthenticationUtil.isRunAsUserTheSystemUser()) {
            return AccessStatus.ALLOWED;
        }

        // New ACLs

        AccessControlListProperties properties = permissionsDaoComponent.getAccessControlListProperties(nodeRef);
        if ((properties != null) && (properties.getAclType() != null) && (properties.getAclType() != ACLType.OLD)) {
            QName typeQname = nodeService.getType(nodeRef);
            Set<QName> aspectQNames = nodeService.getAspects(nodeRef);
            PermissionContext context = new PermissionContext(typeQname);
            context.getAspects().addAll(aspectQNames);
            Authentication auth = AuthenticationUtil.getRunAsAuthentication();
            if (auth != null) {
                String user = AuthenticationUtil.getRunAsUser();
                for (String dynamicAuthority : getDynamicAuthorities(auth, nodeRef, perm)) {
                    context.addDynamicAuthorityAssignment(user, dynamicAuthority);
                }
            }
            return hasPermission(properties.getId(), context, perm);
        }

        // Get the current authentications
        // Use the smart authentication cache to improve permissions performance
        Authentication auth = AuthenticationUtil.getRunAsAuthentication();
        final Set<String> authorisations = getAuthorisations(auth, nodeRef, perm);

        // If the node does not support the given permission there is no point
        // doing the test
        Set<PermissionReference> available = AuthenticationUtil.runAs(new RunAsWork<Set<PermissionReference>>() {
            public Set<PermissionReference> doWork() throws Exception {
                return modelDAO.getAllPermissions(nodeRef);
            }

        }, AuthenticationUtil.getSystemUserName());

        available.add(getAllPermissionReference());
        available.add(OLD_ALL_PERMISSIONS_REFERENCE);

        final Serializable key = generateKey(authorisations, nodeRef, perm, CacheType.HAS_PERMISSION);
        if (!(available.contains(perm))) {
            accessCache.put(key, AccessStatus.DENIED);
            return AccessStatus.DENIED;
        }

        if (AuthenticationUtil.isRunAsUserTheSystemUser()) {
            return AccessStatus.ALLOWED;
        }

        return AuthenticationUtil.runAs(new RunAsWork<AccessStatus>() {

            public AccessStatus doWork() throws Exception {

                AccessStatus status = accessCache.get(key);
                if (status != null) {
                    return status;
                }

                //
                // TODO: Dynamic permissions via evaluators
                //

                /*
                 * Does the current authentication have the supplied permission on the given node.
                 */

                QName typeQname = nodeService.getType(nodeRef);
                Set<QName> aspectQNames = nodeService.getAspects(nodeRef);

                NodeTest nt = new NodeTest(perm, typeQname, aspectQNames);
                boolean result = nt.evaluate(authorisations, nodeRef);
                if (log.isDebugEnabled()) {
                    log.debug("Permission <" + perm + "> is " + (result ? "allowed" : "denied") + " for "
                            + AuthenticationUtil.getRunAsUser() + " on node " + nodeService.getPath(nodeRef));
                }

                status = result ? AccessStatus.ALLOWED : AccessStatus.DENIED;
                accessCache.put(key, status);
                return status;
            }
        }, AuthenticationUtil.getSystemUserName());

    }

    private AccessStatus doAvmCan(NodeRef nodeRef, PermissionReference permission) {
        org.alfresco.util.Pair<Integer, String> avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef);
        int version = avmVersionPath.getFirst();
        String path = avmVersionPath.getSecond();
        boolean result = AVMRepository.GetInstance().can(nodeRef.getStoreRef().getIdentifier(), version, path,
                permission.getName());
        AccessStatus status = result ? AccessStatus.ALLOWED : AccessStatus.DENIED;
        return status;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.alfresco.service.cmr.security.PermissionService#hasPermission(java.lang.Long, java.lang.String,
     *      java.lang.String)
     */
    public AccessStatus hasPermission(Long aclID, PermissionContext context, String permission) {
        return hasPermission(aclID, context, getPermissionReference(permission));
    }

    protected AccessStatus hasPermission(Long aclId, PermissionContext context, PermissionReference permission) {
        if (aclId == null) {
            // Enforce store ACLs if set - the AVM default was to "allow" if there are no permissions set ...
            if (context.getStoreAcl() == null) {
                return AccessStatus.ALLOWED;
            } else {
                if (AuthenticationUtil.isRunAsUserTheSystemUser()) {
                    return AccessStatus.ALLOWED;
                }

                Authentication auth = AuthenticationUtil.getRunAsAuthentication();
                if (auth == null) {
                    throw new IllegalStateException("Unauthenticated");
                }
                Set<String> storeAuthorisations = getAuthorisations(auth, (PermissionContext) null);
                QName typeQname = context.getType();
                Set<QName> aspectQNames = context.getAspects();
                AclTest aclTest = new AclTest(permission, typeQname, aspectQNames);
                boolean result = aclTest.evaluate(storeAuthorisations, context.getStoreAcl(), context);
                AccessStatus status = result ? AccessStatus.ALLOWED : AccessStatus.DENIED;
                return status;
            }
        }

        if (permission == null) {
            return AccessStatus.DENIED;
        }

        if (AuthenticationUtil.getRunAsUser() == null) {
            return AccessStatus.DENIED;
        }

        if (AuthenticationUtil.getRunAsUser().equals(AuthenticationUtil.getSystemUserName())) {
            return AccessStatus.ALLOWED;
        }

        // Get the current authentications
        // Use the smart authentication cache to improve permissions performance
        Authentication auth = AuthenticationUtil.getRunAsAuthentication();
        if (auth == null) {
            throw new IllegalStateException("Unauthenticated");
        }

        Set<String> authorisations = getAuthorisations(auth, context);

        // If the node does not support the given permission there is no point
        // doing the test

        final QName typeQname = context.getType();
        final Set<QName> aspectQNames = context.getAspects();

        Set<PermissionReference> available = AuthenticationUtil.runAs(new RunAsWork<Set<PermissionReference>>() {
            public Set<PermissionReference> doWork() throws Exception {
                return modelDAO.getAllPermissions(typeQname, aspectQNames);
            }

        }, AuthenticationUtil.getSystemUserName());
        available.add(getAllPermissionReference());
        available.add(OLD_ALL_PERMISSIONS_REFERENCE);

        if (!(available.contains(permission))) {
            return AccessStatus.DENIED;
        }

        if (AuthenticationUtil.isRunAsUserTheSystemUser()) {
            return AccessStatus.ALLOWED;
        }

        if (permission.equals(OLD_ALL_PERMISSIONS_REFERENCE)) {
            permission = getAllPermissionReference();
        }

        boolean result;
        if (context.getStoreAcl() == null) {
            AclTest aclTest = new AclTest(permission, typeQname, aspectQNames);
            result = aclTest.evaluate(authorisations, aclId, context);
        } else {
            Set<String> storeAuthorisations = getAuthorisations(auth, (PermissionContext) null);
            AclTest aclTest = new AclTest(permission, typeQname, aspectQNames);
            result = aclTest.evaluate(authorisations, aclId, context)
                    && aclTest.evaluate(storeAuthorisations, context.getStoreAcl(), context);
        }
        AccessStatus status = result ? AccessStatus.ALLOWED : AccessStatus.DENIED;
        return status;

    }

    /**
     * Control permissions cache - only used when we do old style permission evaluations 
     * - which should only be in DM stores where no permissions have been set 
     * 
     * @author andyh
     *
     */
    enum CacheType {
        /**
         * cache full check
         */
        HAS_PERMISSION,
        /**
         * Cache single permission check
         */
        SINGLE_PERMISSION,
        /**
         * Cache single permission check for global permission checks
         */
        SINGLE_PERMISSION_GLOBAL;
    }

    /**
     * Key for a cache object is built from all the known Authorities (which can change dynamically so they must all be
     * used) the NodeRef ID and the permission reference itself. This gives a unique key for each permission test.
     */
    Serializable generateKey(Set<String> auths, NodeRef nodeRef, PermissionReference perm, CacheType type) {
        LinkedHashSet<Serializable> key = new LinkedHashSet<Serializable>();
        key.add(perm.toString());
        // We will just have to key our dynamic sets by username. We wrap it so as not to be confused with a static set
        if (auths instanceof AuthorityServiceImpl.UserAuthoritySet) {
            key.add((Serializable) Collections
                    .singleton(((AuthorityServiceImpl.UserAuthoritySet) auths).getUsername()));
        } else {
            key.addAll(auths);
        }
        key.add(nodeRef);
        // Ensure some concept of node version or transaction is included in the key so we can track without cache replication 
        NodeRef.Status nodeStatus = nodeService.getNodeStatus(nodeRef);
        key.add(nodeStatus == null ? "null" : nodeStatus.getChangeTxnId());
        key.add(type);
        return key;
    }

    /**
     * Get the core authorisations for this {@code auth}. If {@code null} this
     * will be an empty set. Otherwise it will be a Lazy loaded Set of authorities
     * from the authority node structure PLUS any granted authorities.
     */
    protected Set<String> getCoreAuthorisations(Authentication auth) {
        if (auth == null) {
            return Collections.<String>emptySet();
        }

        User user = (User) auth.getPrincipal();
        String username = user.getUsername();
        Set<String> auths = authorityService.getAuthoritiesForUser(username);

        auths.add(username);

        for (GrantedAuthority grantedAuthority : auth.getAuthorities()) {
            auths.add(grantedAuthority.getAuthority());
        }
        return auths;
    }

    /**
     * Get the authorisations for the currently authenticated user
     * 
     * @param auth
     * @return the set of authorisations
     */
    protected Set<String> getAuthorisations(Authentication auth, NodeRef nodeRef, PermissionReference required) {
        Set<String> auths = getCoreAuthorisations(auth);
        if (auth != null) {
            auths.addAll(getDynamicAuthorities(auth, nodeRef, required));
        }
        return auths;
    }

    protected Set<String> getDynamicAuthorities(Authentication auth, NodeRef nodeRef,
            PermissionReference required) {
        Set<String> dynAuths = new HashSet<String>(64);
        User user = (User) auth.getPrincipal();
        String username = user.getUsername();

        nodeRef = tenantService.getName(nodeRef);
        if (nodeRef != null) {
            if (dynamicAuthorities != null) {
                for (DynamicAuthority da : dynamicAuthorities) {
                    Set<PermissionReference> requiredFor = da.requiredFor();
                    if ((requiredFor == null) || (requiredFor.contains(required))) {
                        if (da.hasAuthority(nodeRef, username)) {
                            dynAuths.add(da.getAuthority());
                        }
                    }
                }
            }
        }
        return dynAuths;
    }

    protected Set<String> getAuthorisations(Authentication auth, PermissionContext context) {
        Set<String> auths = getCoreAuthorisations(auth);
        if (auth != null) {
            if (context != null) {
                auths.addAll(getDynamicAuthorities(auth, context, auths));
            }
        }
        return auths;
    }

    protected Set<String> getDynamicAuthorities(Authentication auth, PermissionContext context, Set<String> auths) {
        Set<String> dynAuths = new HashSet<String>();
        Map<String, Set<String>> dynamicAuthorityAssignments = context.getDynamicAuthorityAssignment();
        for (String dynKey : dynamicAuthorityAssignments.keySet()) {
            if (auths.contains(dynKey)) {
                Set<String> dynos = dynamicAuthorityAssignments.get(dynKey);
                if (dynos != null) {
                    dynAuths.addAll(dynos);
                }
            }
        }
        return dynAuths;
    }

    public NodePermissionEntry explainPermission(NodeRef nodeRef, PermissionReference perm) {
        // TODO Auto-generated method stub
        return null;
    }

    public void clearPermission(StoreRef storeRef, String authority) {
        permissionsDaoComponent.deletePermissions(storeRef, authority);
        accessCache.clear();
    }

    public void deletePermission(StoreRef storeRef, String authority, String perm) {
        deletePermission(storeRef, authority, getPermissionReference(perm));
    }

    protected void deletePermission(StoreRef storeRef, String authority, PermissionReference perm) {
        permissionsDaoComponent.deletePermission(storeRef, authority, perm);
        accessCache.clear();
    }

    public void deletePermissions(StoreRef storeRef) {
        permissionsDaoComponent.deletePermissions(storeRef);
        accessCache.clear();
    }

    public void setPermission(StoreRef storeRef, String authority, String perm, boolean allow) {
        setPermission(storeRef, authority, getPermissionReference(perm), allow);
    }

    protected void setPermission(StoreRef storeRef, String authority, PermissionReference permission,
            boolean allow) {
        permissionsDaoComponent.setPermission(storeRef, authority, permission, allow);
        accessCache.clear();
    }

    public void deletePermissions(NodeRef nodeRef) {
        permissionsDaoComponent.deletePermissions(tenantService.getName(nodeRef));
        accessCache.clear();
    }

    public void deletePermissions(NodePermissionEntry nodePermissionEntry) {
        permissionsDaoComponent.deletePermissions(tenantService.getName(nodePermissionEntry.getNodeRef()));
        accessCache.clear();
    }

    /**
     * @see #deletePermission(NodeRef, String, PermissionReference)
     */
    public void deletePermission(PermissionEntry permissionEntry) {
        NodeRef nodeRef = permissionEntry.getNodeRef();
        String authority = permissionEntry.getAuthority();
        PermissionReference permission = permissionEntry.getPermissionReference();
        deletePermission(nodeRef, authority, permission);
    }

    protected void deletePermission(NodeRef nodeRef, String authority, PermissionReference perm) {
        permissionsDaoComponent.deletePermission(tenantService.getName(nodeRef), authority, perm);
        accessCache.clear();
    }

    public void clearPermission(NodeRef nodeRef, String authority) {
        permissionsDaoComponent.deletePermissions(tenantService.getName(nodeRef), authority);
        accessCache.clear();
    }

    protected void setPermission(NodeRef nodeRef, String authority, PermissionReference perm, boolean allow) {
        permissionsDaoComponent.setPermission(tenantService.getName(nodeRef), authority, perm, allow);
        accessCache.clear();
    }

    public void setPermission(PermissionEntry permissionEntry) {
        // TODO - not MT-enabled nodeRef - currently only used by tests
        permissionsDaoComponent.setPermission(permissionEntry);
        accessCache.clear();
    }

    public void setPermission(NodePermissionEntry nodePermissionEntry) {
        // TODO - not MT-enabled nodeRef- currently only used by tests
        permissionsDaoComponent.setPermission(nodePermissionEntry);
        accessCache.clear();
    }

    public void setInheritParentPermissions(NodeRef nodeRef, boolean inheritParentPermissions) {
        NodeRef actualRef = tenantService.getName(nodeRef);
        permissionsDaoComponent.setInheritParentPermissions(actualRef, inheritParentPermissions);
        accessCache.clear();
    }

    /**
     * @see org.alfresco.service.cmr.security.PermissionService#getInheritParentPermissions(org.alfresco.service.cmr.repository.NodeRef)
     */
    public boolean getInheritParentPermissions(NodeRef nodeRef) {
        return permissionsDaoComponent.getInheritParentPermissions(tenantService.getName(nodeRef));
    }

    public PermissionReference getPermissionReference(QName qname, String permissionName) {
        return modelDAO.getPermissionReference(qname, permissionName);
    }

    public PermissionReference getAllPermissionReference() {
        return allPermissionReference;
    }

    public String getPermission(PermissionReference permissionReference) {
        if (modelDAO.isUnique(permissionReference)) {
            return permissionReference.getName();
        } else {
            return permissionReference.toString();
        }
    }

    public PermissionReference getPermissionReference(String permissionName) {
        return modelDAO.getPermissionReference(null, permissionName);
    }

    public Set<PermissionReference> getSettablePermissionReferences(QName type) {
        return modelDAO.getExposedPermissions(type);
    }

    public Set<PermissionReference> getSettablePermissionReferences(NodeRef nodeRef) {
        return modelDAO.getExposedPermissions(tenantService.getName(nodeRef));
    }

    public void deletePermission(NodeRef nodeRef, String authority, String perm) {
        deletePermission(nodeRef, authority, getPermissionReference(perm));
    }

    public AccessStatus hasPermission(NodeRef nodeRef, String perm) {
        return hasPermission(nodeRef, getPermissionReference(perm));
    }

    public void setPermission(NodeRef nodeRef, String authority, String perm, boolean allow) {
        setPermission(nodeRef, authority, getPermissionReference(perm), allow);
    }

    public void deletePermissions(String recipient) {
        permissionsDaoComponent.deletePermissions(recipient);
        accessCache.clear();
    }

    /**
     * Optimised read permission evaluation
     * caveats:
     * doesn't take into account dynamic authorities/groups
     * doesn't take into account node types/aspects for permissions
     *  
     */
    public AccessStatus hasReadPermission(NodeRef nodeRef) {
        AccessStatus status = AccessStatus.DENIED;

        // If the node ref is null there is no sensible test to do - and there
        // must be no permissions
        // - so we allow it
        if (nodeRef == null) {
            return AccessStatus.ALLOWED;
        }

        // Allow permissions for nodes that do not exist
        if (!nodeService.exists(nodeRef)) {
            return AccessStatus.ALLOWED;
        }

        String runAsUser = AuthenticationUtil.getRunAsUser();
        if (runAsUser == null) {
            return AccessStatus.DENIED;
        }

        if (AuthenticationUtil.isRunAsUserTheSystemUser()) {
            return AccessStatus.ALLOWED;
        }

        // any dynamic authorities other than those defined in the default permissions model with full
        // control or read permission force hasPermission check
        Boolean forceHasPermission = (Boolean) AlfrescoTransactionSupport.getResource("forceHasPermission");
        if (forceHasPermission == null) {
            for (DynamicAuthority dynamicAuthority : dynamicAuthorities) {
                String authority = dynamicAuthority.getAuthority();
                Set<PermissionReference> requiredFor = dynamicAuthority.requiredFor();
                if (authority != PermissionService.OWNER_AUTHORITY
                        && authority != PermissionService.ADMINISTRATOR_AUTHORITY
                        && authority != PermissionService.LOCK_OWNER_AUTHORITY
                        && (requiredFor == null
                                || requiredFor.contains(
                                        modelDAO.getPermissionReference(null, PermissionService.FULL_CONTROL))
                                || requiredFor
                                        .contains(modelDAO.getPermissionReference(null, PermissionService.READ)))) {
                    forceHasPermission = Boolean.TRUE;
                    break;
                }
            }
            AlfrescoTransactionSupport.bindResource("forceHasPermission", forceHasPermission);
        }

        if (forceHasPermission == Boolean.TRUE) {
            return hasPermission(nodeRef, PermissionService.READ);
        }

        Long aclID = nodeService.getNodeAclId(nodeRef);
        if (aclID == null) {
            // ACLID is null - need to call default permissions evaluation
            // This will end up calling the old-style ACL code that walks up the ACL tree
            status = hasPermission(nodeRef, getPermissionReference(null, PermissionService.READ));
        } else {
            status = (canRead(aclID) == AccessStatus.ALLOWED || adminRead() == AccessStatus.ALLOWED
                    || ownerRead(runAsUser, nodeRef) == AccessStatus.ALLOWED) ? AccessStatus.ALLOWED
                            : AccessStatus.DENIED;
        }

        return status;
    }

    protected AccessStatus adminRead() {
        AccessStatus result = AccessStatus.DENIED;

        Set<String> authorisations = getAuthorisations();
        if (authorisations.contains(AuthenticationUtil.getAdminRoleName())) {
            result = AccessStatus.ALLOWED;
        }

        // ROLE_ADMINISTRATOR authority has FULL_CONTROL in permissionDefinitions
        // so we don't need to check node requirements
        return result;
    }

    protected AccessStatus ownerRead(String username, NodeRef nodeRef) {
        // Reviewed the behaviour of deny and ownership with Mike F
        // ATM ownership takes precendence over READ deny
        // TODO: check that global owner rights are set

        AccessStatus result = AccessStatus.DENIED;

        String owner = ownableService.getOwner(nodeRef);
        if (owner == null) {
            // TODO node may not have auditable aspect and hence creator property
            result = AccessStatus.DENIED;
        }

        // is the user the owner of the node?
        if (EqualsHelper.nullSafeEquals(username, owner)) {
            // ROLE_OWNER authority has FULL_CONTROL in permissionDefinitions
            // so we don't need to check node requirements          
            return AccessStatus.ALLOWED;
        }

        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Set<String> getReaders(Long aclId) {
        AccessControlList acl = aclDaoComponent.getAccessControlList(aclId);
        if (acl == null) {
            return Collections.emptySet();
        }

        Set<String> aclReaders = readersCache.get((Serializable) acl.getProperties());
        if (aclReaders != null) {
            return aclReaders;
        }

        HashSet<String> assigned = new HashSet<String>();
        HashSet<String> readers = new HashSet<String>();

        for (AccessControlEntry ace : acl.getEntries()) {
            assigned.add(ace.getAuthority());
        }

        for (String authority : assigned) {
            UnconditionalAclTest test = new UnconditionalAclTest(getPermissionReference(PermissionService.READ));
            if (test.evaluate(authority, aclId)) {
                readers.add(authority);
            }
        }

        aclReaders = Collections.unmodifiableSet(readers);
        readersCache.put((Serializable) acl.getProperties(), aclReaders);
        return aclReaders;
    }

    /**
     * @param aclId
     * @return set of authorities with read permission on the ACL
     */
    private Set<String> getReadersDenied(Long aclId) {
        AccessControlList acl = aclDaoComponent.getAccessControlList(aclId);

        if (acl == null) {
            return Collections.emptySet();
        }
        Set<String> denied = readersDeniedCache.get(aclId);
        if (denied != null) {
            return denied;
        }
        denied = new HashSet<String>();
        Set<String> assigned = new HashSet<String>();

        for (AccessControlEntry ace : acl.getEntries()) {
            assigned.add(ace.getAuthority());
        }

        for (String authority : assigned) {
            UnconditionalDeniedAclTest test = new UnconditionalDeniedAclTest(
                    getPermissionReference(PermissionService.READ));
            if (test.evaluate(authority, aclId)) {
                denied.add(authority);
            }
        }

        readersDeniedCache.put((Serializable) acl.getProperties(), denied);

        return denied;
    }

    protected AccessStatus canRead(Long aclId) {
        Set<String> authorities = getAuthorisations();

        // test denied 

        if (anyDenyDenies) {

            Set<String> aclReadersDenied = getReadersDenied(aclId);

            for (String auth : aclReadersDenied) {
                if (authorities.contains(auth)) {
                    return AccessStatus.DENIED;
                }
            }

        }

        // test acl readers
        Set<String> aclReaders = getReaders(aclId);

        for (String auth : aclReaders) {
            if (authorities.contains(auth)) {
                return AccessStatus.ALLOWED;
            }
        }

        return AccessStatus.DENIED;
    }

    //
    // SUPPORT CLASSES
    //

    /**
     * Support class to test the permission on a node.
     * 
     * Not fixed up for deny as should not be used
     * 
     * @author Andy Hind
     */
    protected class NodeTest {
        /*
         * The required permission.
         */
        PermissionReference required;

        /*
         * Granters of the permission
         */
        Set<PermissionReference> granters;

        /*
         * The additional permissions required at the node level.
         */
        Set<PermissionReference> nodeRequirements = new HashSet<PermissionReference>();

        /*
         * The additional permissions required on the parent.
         */
        Set<PermissionReference> parentRequirements = new HashSet<PermissionReference>();

        /*
         * The permissions required on all children .
         */
        Set<PermissionReference> childrenRequirements = new HashSet<PermissionReference>();

        /*
         * The type name of the node.
         */
        QName typeQName;

        /*
         * The aspects set on the node.
         */
        Set<QName> aspectQNames;

        /*
         * Constructor just gets the additional requirements
         */
        NodeTest(PermissionReference required, QName typeQName, Set<QName> aspectQNames) {
            this.required = required;
            this.typeQName = typeQName;
            this.aspectQNames = aspectQNames;

            // Set the required node permissions
            if (required.equals(getPermissionReference(ALL_PERMISSIONS))) {
                nodeRequirements = modelDAO.getRequiredPermissions(
                        getPermissionReference(PermissionService.FULL_CONTROL), typeQName, aspectQNames,
                        RequiredPermission.On.NODE);
            } else {
                nodeRequirements = modelDAO.getRequiredPermissions(required, typeQName, aspectQNames,
                        RequiredPermission.On.NODE);
            }

            parentRequirements = modelDAO.getRequiredPermissions(required, typeQName, aspectQNames,
                    RequiredPermission.On.PARENT);

            childrenRequirements = modelDAO.getRequiredPermissions(required, typeQName, aspectQNames,
                    RequiredPermission.On.CHILDREN);

            // Find all the permissions that grant the allowed permission
            // All permissions are treated specially.
            granters = new LinkedHashSet<PermissionReference>(128, 1.0f);
            granters.addAll(modelDAO.getGrantingPermissions(required));
            granters.add(getAllPermissionReference());
            granters.add(OLD_ALL_PERMISSIONS_REFERENCE);
        }

        /**
         * External hook point
         * @return true if allowed
         */
        boolean evaluate(Set<String> authorisations, NodeRef nodeRef) {
            Set<Pair<String, PermissionReference>> denied = new HashSet<Pair<String, PermissionReference>>();
            return evaluate(authorisations, nodeRef, denied, null);
        }

        /**
         * Internal hook point for recursion
         * @return true if allowed
         */
        boolean evaluate(Set<String> authorisations, NodeRef nodeRef, Set<Pair<String, PermissionReference>> denied,
                MutableBoolean recursiveIn) {
            // Do we defer our required test to a parent (yes if not null)
            MutableBoolean recursiveOut = null;

            Set<Pair<String, PermissionReference>> locallyDenied = new HashSet<Pair<String, PermissionReference>>();
            locallyDenied.addAll(denied);
            locallyDenied.addAll(getDenied(nodeRef));

            // Start out true and "and" all other results
            boolean success = true;

            // Check the required permissions but not for sets they rely on
            // their underlying permissions
            if (modelDAO.checkPermission(required)) {
                if (parentRequirements.contains(required)) {
                    if (checkGlobalPermissions(authorisations)
                            || checkRequired(authorisations, nodeRef, locallyDenied)) {
                        // No need to do the recursive test as it has been found
                        if (recursiveIn != null) {
                            recursiveIn.setValue(true);
                        }
                    } else {
                        // Much cheaper to do this as we go then check all the
                        // stack values for each parent
                        recursiveOut = new MutableBoolean(false);
                    }
                } else {
                    // We have to do the test as no parent will help us out
                    success &= hasSinglePermission(authorisations, nodeRef);
                }
                if (!success) {
                    return false;
                }
            }

            // Check the other permissions required on the node
            for (PermissionReference pr : nodeRequirements) {
                // Build a new test
                NodeTest nt = new NodeTest(pr, typeQName, aspectQNames);
                success &= nt.evaluate(authorisations, nodeRef, locallyDenied, null);
                if (!success) {
                    return false;
                }
            }

            // Check the permission required of the parent

            if (success) {
                ChildAssociationRef car = nodeService.getPrimaryParent(nodeRef);
                if (car.getParentRef() != null) {

                    NodePermissionEntry nodePermissions = permissionsDaoComponent.getPermissions(car.getChildRef());
                    if ((nodePermissions == null) || (nodePermissions.inheritPermissions())) {

                        locallyDenied.addAll(getDenied(car.getParentRef()));
                        for (PermissionReference pr : parentRequirements) {
                            if (pr.equals(required)) {
                                // Recursive permission
                                success &= this.evaluate(authorisations, car.getParentRef(), locallyDenied,
                                        recursiveOut);
                                if ((recursiveOut != null) && recursiveOut.getValue()) {
                                    if (recursiveIn != null) {
                                        recursiveIn.setValue(true);
                                    }
                                }
                            } else {
                                NodeTest nt = new NodeTest(pr, typeQName, aspectQNames);
                                success &= nt.evaluate(authorisations, car.getParentRef(), locallyDenied, null);
                            }

                            if (!success) {
                                return false;
                            }
                        }
                    }
                }
            }

            if ((recursiveOut != null) && (!recursiveOut.getValue())) {
                // The required authentication was not resolved in recursion
                return false;
            }

            // Check permissions required of children
            if (childrenRequirements.size() > 0) {
                List<ChildAssociationRef> childAssocRefs = nodeService.getChildAssocs(nodeRef);
                for (PermissionReference pr : childrenRequirements) {
                    for (ChildAssociationRef child : childAssocRefs) {
                        success &= (hasPermission(child.getChildRef(), pr) == AccessStatus.ALLOWED);
                        if (!success) {
                            return false;
                        }
                    }
                }
            }

            return success;
        }

        boolean hasSinglePermission(Set<String> authorisations, NodeRef nodeRef) {
            nodeRef = tenantService.getName(nodeRef);

            Serializable key = generateKey(authorisations, nodeRef, this.required,
                    CacheType.SINGLE_PERMISSION_GLOBAL);

            AccessStatus status = accessCache.get(key);
            if (status != null) {
                return status == AccessStatus.ALLOWED;
            }

            // Check global permission

            if (checkGlobalPermissions(authorisations)) {
                accessCache.put(key, AccessStatus.ALLOWED);
                return true;
            }

            Set<Pair<String, PermissionReference>> denied = new HashSet<Pair<String, PermissionReference>>();

            return hasSinglePermission(authorisations, nodeRef, denied);

        }

        boolean hasSinglePermission(Set<String> authorisations, NodeRef nodeRef,
                Set<Pair<String, PermissionReference>> denied) {
            nodeRef = tenantService.getName(nodeRef);

            // Add any denied permission to the denied list - these can not
            // then
            // be used to given authentication.
            // A -> B -> C
            // If B denies all permissions to any - allowing all permissions
            // to
            // andy at node A has no effect

            denied.addAll(getDenied(nodeRef));

            // Cache non denied
            Serializable key = null;
            if (denied.size() == 0) {
                key = generateKey(authorisations, nodeRef, this.required, CacheType.SINGLE_PERMISSION);
            }
            if (key != null) {
                AccessStatus status = accessCache.get(key);
                if (status != null) {
                    return status == AccessStatus.ALLOWED;
                }
            }

            // If the current node allows the permission we are done
            // The test includes any parent or ancestor requirements
            if (checkRequired(authorisations, nodeRef, denied)) {
                if (key != null) {
                    accessCache.put(key, AccessStatus.ALLOWED);
                }
                return true;
            }

            // Permissions are only evaluated up the primary parent chain
            // TODO: Do not ignore non primary permissions
            ChildAssociationRef car = nodeService.getPrimaryParent(nodeRef);

            // Build the next element of the evaluation chain
            if (car.getParentRef() != null) {
                NodePermissionEntry nodePermissions = permissionsDaoComponent.getPermissions(nodeRef);
                if ((nodePermissions == null) || (nodePermissions.inheritPermissions())) {
                    if (hasSinglePermission(authorisations, car.getParentRef(), denied)) {
                        if (key != null) {
                            accessCache.put(key, AccessStatus.ALLOWED);
                        }
                        return true;
                    } else {
                        if (key != null) {
                            accessCache.put(key, AccessStatus.DENIED);
                        }
                        return false;
                    }
                } else {
                    if (key != null) {
                        accessCache.put(key, AccessStatus.DENIED);
                    }
                    return false;
                }
            } else {
                if (key != null) {
                    accessCache.put(key, AccessStatus.DENIED);
                }
                return false;
            }
        }

        /**
         * Check if we have a global permission
         * 
         * @return true if allowed
         */
        private boolean checkGlobalPermissions(Set<String> authorisations) {
            for (PermissionEntry pe : modelDAO.getGlobalPermissionEntries()) {
                if (isGranted(pe, authorisations, null)) {
                    return true;
                }
            }
            return false;
        }

        /**
         * Get the list of permissions denied for this node.
         * 
         * @return the list of denied permissions
         */
        Set<Pair<String, PermissionReference>> getDenied(NodeRef nodeRef) {
            Set<Pair<String, PermissionReference>> deniedSet = new HashSet<Pair<String, PermissionReference>>();

            // Loop over all denied permissions
            NodePermissionEntry nodeEntry = permissionsDaoComponent.getPermissions(nodeRef);
            if (nodeEntry != null) {
                for (PermissionEntry pe : nodeEntry.getPermissionEntries()) {
                    if (pe.isDenied()) {
                        // All the sets that grant this permission must be
                        // denied
                        // Note that granters includes the orginal permission
                        Set<PermissionReference> granters = modelDAO
                                .getGrantingPermissions(pe.getPermissionReference());
                        for (PermissionReference granter : granters) {
                            deniedSet.add(new Pair<String, PermissionReference>(pe.getAuthority(), granter));
                        }

                        // All the things granted by this permission must be
                        // denied
                        Set<PermissionReference> grantees = modelDAO
                                .getGranteePermissions(pe.getPermissionReference());
                        for (PermissionReference grantee : grantees) {
                            deniedSet.add(new Pair<String, PermissionReference>(pe.getAuthority(), grantee));
                        }

                        // All permission excludes all permissions available for
                        // the node.
                        if (pe.getPermissionReference().equals(getAllPermissionReference())
                                || pe.getPermissionReference().equals(OLD_ALL_PERMISSIONS_REFERENCE)) {
                            for (PermissionReference deny : modelDAO.getAllPermissions(nodeRef)) {
                                deniedSet.add(new Pair<String, PermissionReference>(pe.getAuthority(), deny));
                            }
                        }
                    }
                }
            }
            return deniedSet;
        }

        /**
         * Check that a given authentication is available on a node
         * 
         * @return true if the check is required
         */
        boolean checkRequired(Set<String> authorisations, NodeRef nodeRef,
                Set<Pair<String, PermissionReference>> denied) {
            NodePermissionEntry nodeEntry = permissionsDaoComponent.getPermissions(nodeRef);

            // No permissions set - short cut to deny
            if (nodeEntry == null) {
                return false;
            }

            // Check if each permission allows - the first wins.
            // We could have other voting style mechanisms here
            for (PermissionEntry pe : nodeEntry.getPermissionEntries()) {
                if (isGranted(pe, authorisations, denied)) {
                    return true;
                }
            }
            return false;
        }

        /**
         * Is a permission granted
         * 
         * @param pe -
         *            the permissions entry to consider
         * @param granters -
         *            the set of granters
         * @param authorisations -
         *            the set of authorities
         * @param denied -
         *            the set of denied permissions/authority pais
         * @return true if granted
         */
        private boolean isGranted(PermissionEntry pe, Set<String> authorisations,
                Set<Pair<String, PermissionReference>> denied) {

            // If the permission entry denies then we just deny
            if (pe.isDenied()) {
                return false;
            }

            // The permission is allowed but we deny it as it is in the denied
            // set

            if (denied != null) {
                Pair<String, PermissionReference> specific = new Pair<String, PermissionReference>(
                        pe.getAuthority(), required);
                if (denied.contains(specific)) {
                    return false;
                }
            }

            // any deny denies

            if (anyDenyDenies) {
                if (denied != null) {
                    for (String auth : authorisations) {
                        Pair<String, PermissionReference> specific = new Pair<String, PermissionReference>(auth,
                                required);
                        if (denied.contains(specific)) {
                            return false;
                        }
                        for (PermissionReference perm : granters) {
                            specific = new Pair<String, PermissionReference>(auth, perm);
                            if (denied.contains(specific)) {
                                return false;
                            }
                        }
                    }
                }
            }

            // If the permission has a match in both the authorities and
            // granters list it is allowed
            // It applies to the current user and it is granted
            if (authorisations.contains(pe.getAuthority()) && granters.contains(pe.getPermissionReference())) {
                {
                    return true;
                }
            }

            // Default deny
            return false;
        }

    }

    /**
     * Test a permission in the context of the new ACL implementation. All components of the ACL are in the object -
     * there is no need to walk up the parent chain. Parent conditions cna not be applied as there is no context to do
     * this. Child conditions can not be applied as there is no context to do this
     * 
     * @author andyh
     */

    protected class AclTest {
        /*
         * The required permission.
         */
        PermissionReference required;

        /*
         * Granters of the permission
         */
        Set<PermissionReference> granters;

        /*
         * The additional permissions required at the node level.
         */
        Set<PermissionReference> nodeRequirements = new HashSet<PermissionReference>();

        /*
         * The type name of the node.
         */
        QName typeQName;

        /*
         * The aspects set on the node.
         */
        Set<QName> aspectQNames;

        /*
         * Constructor just gets the additional requirements
         */
        AclTest(PermissionReference required, QName typeQName, Set<QName> aspectQNames) {
            this.required = required;
            this.typeQName = typeQName;
            this.aspectQNames = aspectQNames;

            // Set the required node permissions
            if (required.equals(getPermissionReference(ALL_PERMISSIONS))) {
                nodeRequirements = modelDAO.getRequiredPermissions(
                        getPermissionReference(PermissionService.FULL_CONTROL), typeQName, aspectQNames,
                        RequiredPermission.On.NODE);
            } else {
                nodeRequirements = modelDAO.getRequiredPermissions(required, typeQName, aspectQNames,
                        RequiredPermission.On.NODE);
            }

            if (modelDAO.getRequiredPermissions(required, typeQName, aspectQNames, RequiredPermission.On.PARENT)
                    .size() > 0) {
                throw new IllegalStateException("Parent permissions can not be checked for an acl");
            }

            if (modelDAO.getRequiredPermissions(required, typeQName, aspectQNames, RequiredPermission.On.CHILDREN)
                    .size() > 0) {
                throw new IllegalStateException("Child permissions can not be checked for an acl");
            }

            // Find all the permissions that grant the allowed permission
            // All permissions are treated specially.
            granters = new LinkedHashSet<PermissionReference>(128, 1.0f);
            granters.addAll(modelDAO.getGrantingPermissions(required));
            granters.add(getAllPermissionReference());
            granters.add(OLD_ALL_PERMISSIONS_REFERENCE);
        }

        /**
         * Internal hook point for recursion
         * 
         * @param authorisations
         * @param nodeRef
         * @param denied
         * @param recursiveIn
         * @return true if granted
         */
        boolean evaluate(Set<String> authorisations, Long aclId, PermissionContext context) {
            // Start out true and "and" all other results
            boolean success = true;

            // Check the required permissions but not for sets they rely on
            // their underlying permissions
            if (modelDAO.checkPermission(required)) {

                // We have to do the test as no parent will help us out
                success &= hasSinglePermission(authorisations, aclId, context);

                if (!success) {
                    return false;
                }
            }

            // Check the other permissions required on the node
            for (PermissionReference pr : nodeRequirements) {
                // Build a new test
                AclTest nt = new AclTest(pr, typeQName, aspectQNames);
                success &= nt.evaluate(authorisations, aclId, context);
                if (!success) {
                    return false;
                }
            }

            return success;
        }

        boolean hasSinglePermission(Set<String> authorisations, Long aclId, PermissionContext context) {
            // Check global permission

            if (checkGlobalPermissions(authorisations)) {
                return true;
            }

            return checkRequired(authorisations, aclId, context);

        }

        /**
         * Check if we have a global permission
         * 
         * @param authorisations
         * @return true if granted
         */
        private boolean checkGlobalPermissions(Set<String> authorisations) {
            for (PermissionEntry pe : modelDAO.getGlobalPermissionEntries()) {
                if (isGranted(pe, authorisations)) {
                    return true;
                }
            }
            return false;
        }

        /**
         * Check that a given authentication is available on a node
         * 
         * @param authorisations
         * @param nodeRef
         * @param denied
         * @return true if a check is required
         */
        boolean checkRequired(Set<String> authorisations, Long aclId, PermissionContext context) {
            AccessControlList acl = aclDaoComponent.getAccessControlList(aclId);

            if (acl == null) {
                return false;
            }

            if (anyDenyDenies) {
                Set<Pair<String, PermissionReference>> allowed = new HashSet<Pair<String, PermissionReference>>();

                // Check if each permission allows - the first wins.
                // We could have other voting style mechanisms here
                for (AccessControlEntry ace : acl.getEntries()) {
                    if (isDenied(ace, authorisations, allowed, context)) {
                        return false;
                    }
                }
            }

            Set<Pair<String, PermissionReference>> denied = new HashSet<Pair<String, PermissionReference>>();

            // Check if each permission allows - the first wins.
            // We could have other voting style mechanisms here
            for (AccessControlEntry ace : acl.getEntries()) {
                if (isGranted(ace, authorisations, denied, context)) {
                    return true;
                }
            }
            return false;
        }

        /**
         * Is a permission granted
         * 
         * @param pe -
         *            the permissions entry to consider
         * @param granters -
         *            the set of granters
         * @param authorisations -
         *            the set of authorities
         * @param denied -
         *            the set of denied permissions/authority pais
         * @return true if granted
         */
        private boolean isGranted(AccessControlEntry ace, Set<String> authorisations,
                Set<Pair<String, PermissionReference>> denied, PermissionContext context) {
            //System.out.println("isGranted called");
            // If the permission entry denies then we just deny
            if (ace.getAccessStatus() == AccessStatus.DENIED) {
                denied.add(new Pair<String, PermissionReference>(ace.getAuthority(), ace.getPermission()));

                Set<PermissionReference> granters = modelDAO.getGrantingPermissions(ace.getPermission());
                for (PermissionReference granter : granters) {
                    denied.add(new Pair<String, PermissionReference>(ace.getAuthority(), granter));
                }

                // All the things granted by this permission must be
                // denied
                Set<PermissionReference> grantees = modelDAO.getGranteePermissions(ace.getPermission());
                for (PermissionReference grantee : grantees) {
                    denied.add(new Pair<String, PermissionReference>(ace.getAuthority(), grantee));
                }

                // All permission excludes all permissions available for
                // the node.
                if (ace.getPermission().equals(getAllPermissionReference())
                        || ace.getPermission().equals(OLD_ALL_PERMISSIONS_REFERENCE)) {
                    for (PermissionReference deny : modelDAO.getAllPermissions(context.getType(),
                            context.getAspects())) {
                        denied.add(new Pair<String, PermissionReference>(ace.getAuthority(), deny));
                    }
                }

                return false;
            }

            // The permission is allowed but we deny it as it is in the denied
            // set

            if (denied != null) {
                Pair<String, PermissionReference> specific = new Pair<String, PermissionReference>(
                        ace.getAuthority(), required);
                if (denied.contains(specific)) {
                    return false;
                }
            }

            // If the permission has a match in both the authorities and
            // granters list it is allowed
            // It applies to the current user and it is granted
            if (authorisations.contains(ace.getAuthority()) && granters.contains(ace.getPermission())) {
                {
                    return true;
                }
            }

            // Intersection customize
            if (ace.getAuthority().startsWith("AND(")) {
                //System.out.println("## ace.getAuthority: " + ace.getAuthority());
                Pattern p = Pattern.compile("AND\\((.*), (.*)\\)");
                Matcher matcher = p.matcher(ace.getAuthority());
                matcher.find();
                String auth1 = matcher.group(1);
                String auth2 = matcher.group(2);

                //System.out.println("## auth1: " + auth1);
                //System.out.println("## auth2: " + auth2);

                if (authorisations.contains(auth1) && authorisations.contains(auth2)
                        && granters.contains(ace.getPermission())) {
                    return true;
                }
            }

            // Default deny
            return false;
        }

        /**
         * Is a permission granted
         * 
         * @param pe -
         *            the permissions entry to consider
         * @param granters -
         *            the set of granters
         * @param authorisations -
         *            the set of authorities
         * @param denied -
         *            the set of denied permissions/authority pais
         * @return true if granted
         */
        private boolean isDenied(AccessControlEntry ace, Set<String> authorisations,
                Set<Pair<String, PermissionReference>> allowed, PermissionContext context) {
            // If the permission entry denies then we just deny
            if (ace.getAccessStatus() == AccessStatus.ALLOWED) {
                allowed.add(new Pair<String, PermissionReference>(ace.getAuthority(), ace.getPermission()));

                Set<PermissionReference> granters = modelDAO.getGrantingPermissions(ace.getPermission());
                for (PermissionReference granter : granters) {
                    allowed.add(new Pair<String, PermissionReference>(ace.getAuthority(), granter));
                }

                // All the things granted by this permission must be
                // denied
                Set<PermissionReference> grantees = modelDAO.getGranteePermissions(ace.getPermission());
                for (PermissionReference grantee : grantees) {
                    allowed.add(new Pair<String, PermissionReference>(ace.getAuthority(), grantee));
                }

                // All permission excludes all permissions available for
                // the node.
                if (ace.getPermission().equals(getAllPermissionReference())
                        || ace.getPermission().equals(OLD_ALL_PERMISSIONS_REFERENCE)) {
                    for (PermissionReference deny : modelDAO.getAllPermissions(context.getType(),
                            context.getAspects())) {
                        allowed.add(new Pair<String, PermissionReference>(ace.getAuthority(), deny));
                    }
                }

                return false;
            }

            // The permission is denied but we allow it as it is in the allowed
            // set

            if (allowed != null) {
                Pair<String, PermissionReference> specific = new Pair<String, PermissionReference>(
                        ace.getAuthority(), required);
                if (allowed.contains(specific)) {
                    return false;
                }
            }

            // If the permission has a match in both the authorities and
            // granters list it is allowed
            // It applies to the current user and it is granted
            if (authorisations.contains(ace.getAuthority()) && granters.contains(ace.getPermission())) {
                {
                    return true;
                }
            }

            // Default allow
            return false;
        }

        private boolean isGranted(PermissionEntry pe, Set<String> authorisations) {
            // If the permission entry denies then we just deny
            if (pe.isDenied()) {
                return false;
            }

            // If the permission has a match in both the authorities and
            // granters list it is allowed
            // It applies to the current user and it is granted
            if (granters.contains(pe.getPermissionReference()) && authorisations.contains(pe.getAuthority())) {
                {
                    return true;
                }
            }

            // Default deny
            return false;
        }

    }

    /**
     * Ignores type and aspect requirements on the node
     *
     */
    protected class UnconditionalAclTest {
        /*
         * The required permission.
         */
        PermissionReference required;

        /*
         * Granters of the permission
         */
        Set<PermissionReference> granters;

        /*
         * The additional permissions required at the node level.
         */
        Set<PermissionReference> nodeRequirements = new HashSet<PermissionReference>();

        /*
         * Constructor just gets the additional requirements
         */
        UnconditionalAclTest(PermissionReference required) {
            this.required = required;

            // Set the required node permissions
            if (required.equals(getPermissionReference(ALL_PERMISSIONS))) {
                nodeRequirements = modelDAO.getUnconditionalRequiredPermissions(
                        getPermissionReference(PermissionService.FULL_CONTROL), RequiredPermission.On.NODE);
            } else {
                nodeRequirements = modelDAO.getUnconditionalRequiredPermissions(required,
                        RequiredPermission.On.NODE);
            }

            if (modelDAO.getUnconditionalRequiredPermissions(required, RequiredPermission.On.PARENT).size() > 0) {
                throw new IllegalStateException("Parent permissions can not be checked for an acl");
            }

            if (modelDAO.getUnconditionalRequiredPermissions(required, RequiredPermission.On.CHILDREN).size() > 0) {
                throw new IllegalStateException("Child permissions can not be checked for an acl");
            }

            // Find all the permissions that grant the allowed permission
            // All permissions are treated specially.
            granters = new LinkedHashSet<PermissionReference>(128, 1.0f);
            granters.addAll(modelDAO.getGrantingPermissions(required));
            granters.add(getAllPermissionReference());
            granters.add(OLD_ALL_PERMISSIONS_REFERENCE);
        }

        /**
         * Internal hook point for recursion
         * 
         * @param authorisations
         * @param nodeRef
         * @param denied
         * @param recursiveIn
         * @return true if granted
         */
        boolean evaluate(String authority, Long aclId) {
            // Start out true and "and" all other results
            boolean success = true;

            // Check the required permissions but not for sets they rely on
            // their underlying permissions
            //if (modelDAO.checkPermission(required))
            //{

            // We have to do the test as no parent will help us out
            success &= hasSinglePermission(authority, aclId);

            if (!success) {
                return false;
            }
            //}

            // Check the other permissions required on the node
            for (PermissionReference pr : nodeRequirements) {
                // Build a new test
                UnconditionalAclTest nt = new UnconditionalAclTest(pr);
                success &= nt.evaluate(authority, aclId);
                if (!success) {
                    return false;
                }
            }

            return success;
        }

        boolean hasSinglePermission(String authority, Long aclId) {
            // Check global permission

            if (checkGlobalPermissions(authority)) {
                return true;
            }

            if (aclId == null) {
                return false;
            } else {
                return checkRequired(authority, aclId);
            }

        }

        /**
         * Check if we have a global permission
         * 
         * @param authorisations
         * @return true if granted
         */
        private boolean checkGlobalPermissions(String authority) {
            for (PermissionEntry pe : modelDAO.getGlobalPermissionEntries()) {
                if (isGranted(pe, authority)) {
                    return true;
                }
            }
            return false;
        }

        /**
         * Check that a given authentication is available on a node
         * 
         * @param authorisations
         * @param nodeRef
         * @param denied
         * @return true if a check is required
         */
        boolean checkRequired(String authority, Long aclId) {
            AccessControlList acl = aclDaoComponent.getAccessControlList(aclId);

            if (acl == null) {
                return false;
            }

            Set<Pair<String, PermissionReference>> denied = new HashSet<Pair<String, PermissionReference>>();

            // Check if each permission allows - the first wins.
            // We could have other voting style mechanisms here
            for (AccessControlEntry ace : acl.getEntries()) {
                if (isGranted(ace, authority, denied)) {
                    return true;
                }
            }
            return false;
        }

        /**
         * Is a permission granted
         * 
         * @param pe -
         *            the permissions entry to consider
         * @param granters -
         *            the set of granters
         * @param authorisations -
         *            the set of authorities
         * @param denied -
         *            the set of denied permissions/authority pais
         * @return true if granted
         */
        private boolean isGranted(AccessControlEntry ace, String authority,
                Set<Pair<String, PermissionReference>> denied) {
            // If the permission entry denies then we just deny
            if (ace.getAccessStatus() == AccessStatus.DENIED) {
                denied.add(new Pair<String, PermissionReference>(ace.getAuthority(), ace.getPermission()));

                Set<PermissionReference> granters = modelDAO.getGrantingPermissions(ace.getPermission());
                for (PermissionReference granter : granters) {
                    denied.add(new Pair<String, PermissionReference>(ace.getAuthority(), granter));
                }

                // All the things granted by this permission must be
                // denied
                Set<PermissionReference> grantees = modelDAO.getGranteePermissions(ace.getPermission());
                for (PermissionReference grantee : grantees) {
                    denied.add(new Pair<String, PermissionReference>(ace.getAuthority(), grantee));
                }

                // All permission excludes all permissions available for
                // the node.
                if (ace.getPermission().equals(getAllPermissionReference())
                        || ace.getPermission().equals(OLD_ALL_PERMISSIONS_REFERENCE)) {
                    for (PermissionReference deny : modelDAO.getAllPermissions()) {
                        denied.add(new Pair<String, PermissionReference>(ace.getAuthority(), deny));
                    }
                }

                return false;
            }

            // The permission is allowed but we deny it as it is in the denied
            // set

            if (denied != null) {
                Pair<String, PermissionReference> specific = new Pair<String, PermissionReference>(
                        ace.getAuthority(), required);
                if (denied.contains(specific)) {
                    return false;
                }
            }

            // If the permission has a match in both the authorities and
            // granters list it is allowed
            // It applies to the current user and it is granted
            if (authority.equals(ace.getAuthority()) && granters.contains(ace.getPermission())) {
                {
                    return true;
                }
            }

            // Default deny
            return false;
        }

        private boolean isGranted(PermissionEntry pe, String authority) {
            // If the permission entry denies then we just deny
            if (pe.isDenied()) {
                return false;
            }

            // If the permission has a match in both the authorities and
            // granters list it is allowed
            // It applies to the current user and it is granted
            if (granters.contains(pe.getPermissionReference()) && authority.equals(pe.getAuthority())) {
                {
                    return true;
                }
            }

            // Default deny
            return false;
        }
    }

    /**
     * Ignores type and aspect requirements on the node
     *
     */
    protected class UnconditionalDeniedAclTest {
        /*
         * The required permission.
         */
        PermissionReference required;

        /*
         * Granters of the permission
         */
        Set<PermissionReference> granters;

        /*
         * The additional permissions required at the node level.
         */
        Set<PermissionReference> nodeRequirements = new HashSet<PermissionReference>();

        /*
         * Constructor just gets the additional requirements
         */
        UnconditionalDeniedAclTest(PermissionReference required) {
            this.required = required;

            // Set the required node permissions
            if (required.equals(getPermissionReference(ALL_PERMISSIONS))) {
                nodeRequirements = modelDAO.getUnconditionalRequiredPermissions(
                        getPermissionReference(PermissionService.FULL_CONTROL), RequiredPermission.On.NODE);
            } else {
                nodeRequirements = modelDAO.getUnconditionalRequiredPermissions(required,
                        RequiredPermission.On.NODE);
            }

            if (modelDAO.getUnconditionalRequiredPermissions(required, RequiredPermission.On.PARENT).size() > 0) {
                throw new IllegalStateException("Parent permissions can not be checked for an acl");
            }

            if (modelDAO.getUnconditionalRequiredPermissions(required, RequiredPermission.On.CHILDREN).size() > 0) {
                throw new IllegalStateException("Child permissions can not be checked for an acl");
            }

            // Find all the permissions that grant the allowed permission
            // All permissions are treated specially.
            granters = new LinkedHashSet<PermissionReference>(128, 1.0f);
            granters.addAll(modelDAO.getGrantingPermissions(required));
            granters.add(getAllPermissionReference());
            granters.add(OLD_ALL_PERMISSIONS_REFERENCE);
        }

        /**
         * Internal hook point for recursion
         * 
         * @param authorisations
         * @param nodeRef
         * @param denied
         * @param recursiveIn
         * @return true if granted
         */
        boolean evaluate(String authority, Long aclId) {
            // Start out true and "and" all other results
            boolean success = true;

            // Check the required permissions but not for sets they rely on
            // their underlying permissions
            //if (modelDAO.checkPermission(required))
            //{

            // We have to do the test as no parent will help us out
            success &= hasSinglePermission(authority, aclId);

            if (!success) {
                return false;
            }
            //}

            // Check the other permissions required on the node
            for (PermissionReference pr : nodeRequirements) {
                // Build a new test
                UnconditionalDeniedAclTest nt = new UnconditionalDeniedAclTest(pr);
                success &= nt.evaluate(authority, aclId);
                if (!success) {
                    return false;
                }
            }

            return success;
        }

        boolean hasSinglePermission(String authority, Long aclId) {
            // Check global permission

            if (checkGlobalPermissions(authority)) {
                return true;
            }

            if (aclId == null) {
                return false;
            } else {
                return checkRequired(authority, aclId);
            }

        }

        /**
         * Check if we have a global permission
         * 
         * @param authorisations
         * @return true if granted
         */
        private boolean checkGlobalPermissions(String authority) {
            for (PermissionEntry pe : modelDAO.getGlobalPermissionEntries()) {
                if (isDenied(pe, authority)) {
                    return true;
                }
            }
            return false;
        }

        /**
         * Check that a given authentication is available on a node
         * 
         * @param authorisations
         * @param nodeRef
         * @param denied
         * @return true if a check is required
         */
        boolean checkRequired(String authority, Long aclId) {
            AccessControlList acl = aclDaoComponent.getAccessControlList(aclId);

            if (acl == null) {
                return false;
            }

            Set<Pair<String, PermissionReference>> allowed = new HashSet<Pair<String, PermissionReference>>();

            // Check if each permission allows - the first wins.
            // We could have other voting style mechanisms here
            for (AccessControlEntry ace : acl.getEntries()) {
                if (isDenied(ace, authority, allowed)) {
                    return true;
                }
            }
            return false;
        }

        /**
         * Is a permission granted
         * 
         * @param pe -
         *            the permissions entry to consider
         * @param granters -
         *            the set of granters
         * @param authorisations -
         *            the set of authorities
         * @param denied -
         *            the set of denied permissions/authority pais
         * @return true if granted
         */
        private boolean isDenied(AccessControlEntry ace, String authority,
                Set<Pair<String, PermissionReference>> allowed) {
            // If the permission entry denies then we just deny
            if (ace.getAccessStatus() == AccessStatus.ALLOWED) {
                allowed.add(new Pair<String, PermissionReference>(ace.getAuthority(), ace.getPermission()));

                Set<PermissionReference> granters = modelDAO.getGrantingPermissions(ace.getPermission());
                for (PermissionReference granter : granters) {
                    allowed.add(new Pair<String, PermissionReference>(ace.getAuthority(), granter));
                }

                // All the things granted by this permission must be
                // denied
                Set<PermissionReference> grantees = modelDAO.getGranteePermissions(ace.getPermission());
                for (PermissionReference grantee : grantees) {
                    allowed.add(new Pair<String, PermissionReference>(ace.getAuthority(), grantee));
                }

                // All permission excludes all permissions available for
                // the node.
                if (ace.getPermission().equals(getAllPermissionReference())
                        || ace.getPermission().equals(OLD_ALL_PERMISSIONS_REFERENCE)) {
                    for (PermissionReference deny : modelDAO.getAllPermissions()) {
                        allowed.add(new Pair<String, PermissionReference>(ace.getAuthority(), deny));
                    }
                }

                return false;
            }

            // The permission is allowed but we deny it as it is in the denied
            // set

            if (allowed != null) {
                Pair<String, PermissionReference> specific = new Pair<String, PermissionReference>(
                        ace.getAuthority(), required);
                if (allowed.contains(specific)) {
                    return false;
                }
            }

            // If the permission has a match in both the authorities and
            // granters list it is allowed
            // It applies to the current user and it is granted
            if (authority.equals(ace.getAuthority()) && granters.contains(ace.getPermission())) {
                {
                    return true;
                }
            }

            // Default deny
            return false;
        }

        private boolean isDenied(PermissionEntry pe, String authority) {
            // If the permission entry denies then we just deny
            if (pe.isAllowed()) {
                return false;
            }

            // If the permission has a match in both the authorities and
            // granters list it is allowed
            // It applies to the current user and it is granted
            if (granters.contains(pe.getPermissionReference()) && authority.equals(pe.getAuthority())) {
                {
                    return true;
                }
            }

            // Default deny
            return false;
        }
    }

    protected static class MutableBoolean {
        private boolean value;

        MutableBoolean(boolean value) {
            this.value = value;
        }

        void setValue(boolean value) {
            this.value = value;
        }

        boolean getValue() {
            return value;
        }
    }

    /**
     * This methods checks whether the specified nodeRef instance is a version nodeRef (ie. in the 'version' store)
     * 
     * @param nodeRef - version nodeRef
     * @return <b>true</b> if version nodeRef <b>false</b> otherwise
     */
    protected boolean isVersionNodeRef(NodeRef nodeRef) {
        return nodeRef.getStoreRef().getProtocol().equals(VersionModel.STORE_PROTOCOL);
    }

    /**
     * Converts specified version nodeRef (eg. versionStore://...) to versioned nodeRef (eg. workspace://SpacesStore/...)
     * 
     * @param nodeRef - <b>always</b> version nodeRef (ie. in the 'version' store)
     * @return versioned nodeRef (ie.in the 'live' store)
     */
    @SuppressWarnings("deprecation")
    protected NodeRef convertVersionNodeRefToVersionedNodeRef(NodeRef versionNodeRef) {
        Map<QName, Serializable> properties = nodeService.getProperties(versionNodeRef);

        NodeRef nodeRef = null;

        // Switch VersionStore depending on configured impl
        if (versionNodeRef.getStoreRef().getIdentifier().equals(Version2Model.STORE_ID)) {
            // V2 version store (eg. workspace://version2Store)
            nodeRef = (NodeRef) properties.get(Version2Model.PROP_QNAME_FROZEN_NODE_REF);
        } else if (versionNodeRef.getStoreRef().getIdentifier().equals(VersionModel.STORE_ID)) {
            // Deprecated V1 version store (eg. workspace://lightWeightVersionStore)
            nodeRef = new NodeRef((String) properties.get(VersionModel.PROP_QNAME_FROZEN_NODE_STORE_PROTOCOL),
                    (String) properties.get(VersionModel.PROP_QNAME_FROZEN_NODE_STORE_ID),
                    (String) properties.get(VersionModel.PROP_QNAME_FROZEN_NODE_ID));
        }

        return nodeRef;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Set<String> getAuthorisations() {
        // Use TX cache 
        @SuppressWarnings("unchecked")
        Set<String> auths = (Set<String>) AlfrescoTransactionSupport.getResource("MyAuthCache");
        Authentication auth = AuthenticationUtil.getRunAsAuthentication();
        if (auths != null) {
            if (auth == null || !auths.contains(((User) auth.getPrincipal()).getUsername())) {
                auths = null;
            }
        }
        if (auths == null) {
            auths = getCoreAuthorisations(auth);
            AlfrescoTransactionSupport.bindResource("MyAuthCache", auths);
        }
        return Collections.unmodifiableSet(auths);
    }
}