Java tutorial
/* * #%L * Alfresco Repository * %% * Copyright (C) 2005 - 2016 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of * the paid license agreement will prevail. Otherwise, the software is * provided under the following open source license terms: * * 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/>. * #L% */ package org.alfresco.repo.security.permissions.impl.model; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Collection; import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.alfresco.repo.security.permissions.PermissionEntry; import org.alfresco.repo.security.permissions.PermissionReference; import org.alfresco.repo.security.permissions.impl.ModelDAO; import org.alfresco.repo.security.permissions.impl.RequiredPermission; import org.alfresco.repo.security.permissions.impl.RequiredPermission.On; import org.alfresco.repo.security.permissions.impl.SimplePermissionReference; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.ClassDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.DynamicNamespacePrefixResolver; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.util.Pair; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentType; import org.dom4j.Element; import org.dom4j.io.OutputFormat; import org.dom4j.io.SAXReader; import org.dom4j.io.XMLWriter; import org.dom4j.tree.DefaultDocumentType; /** * The implementation of the model DAO Reads and stores the top level model information Encapsulates access to this * information * * @author andyh */ public class PermissionModel implements ModelDAO { // IOC private NodeService nodeService; private DictionaryService dictionaryService; // XML Constants private static final String NAMESPACES = "namespaces"; private static final String NAMESPACE = "namespace"; private static final String NAMESPACE_URI = "uri"; private static final String NAMESPACE_PREFIX = "prefix"; private static final String PERMISSION_SET = "permissionSet"; private static final String GLOBAL_PERMISSION = "globalPermission"; private static final String DENY = "deny"; private static final String ALLOW = "allow"; private static final String DEFAULT_PERMISSION = "defaultPermission"; // Instance variables private String model; private String dtdSchema; private boolean validate = true; /* * (non-Javadoc) * @seeorg.alfresco.repo.security.permissions.impl.ModelDAO#hasFull(org.alfresco.repo.security.permissions. * PermissionReference) */ private static PermissionReference ALL = SimplePermissionReference.getPermissionReference( QName.createQName(NamespaceService.SECURITY_MODEL_1_0_URI, PermissionService.ALL_PERMISSIONS), PermissionService.ALL_PERMISSIONS); private static class MutableState { private final DictionaryService dictionaryService; // Aprrox 6 - default size OK private Map<QName, PermissionSet> permissionSets = new HashMap<QName, PermissionSet>(128); // Global permissions - default size OK private Set<GlobalPermissionEntry> globalPermissions = new HashSet<GlobalPermissionEntry>(); private AccessStatus defaultPermission; private Collection<QName> allAspects; private Map<String, PermissionReference> uniqueMap; private Map<PermissionReference, Permission> permissionMap; private Map<PermissionReference, PermissionGroup> permissionGroupMap; private Map<String, PermissionReference> permissionReferenceMap; private Map<PermissionReference, Set<PermissionReference>> grantingPermissions = new HashMap<PermissionReference, Set<PermissionReference>>( 256); // Cache grantees private Map<PermissionReference, Set<PermissionReference>> granteePermissions = new HashMap<PermissionReference, Set<PermissionReference>>( 256); // Cache the mapping of extended groups to the base private Map<PermissionGroup, PermissionGroup> groupsToBaseGroup = new HashMap<PermissionGroup, PermissionGroup>( 256); private Map<RequiredKey, Set<PermissionReference>> requiredPermissionsCache = new HashMap<RequiredKey, Set<PermissionReference>>( 1024); private Map<Pair<PermissionReference, RequiredPermission.On>, Set<PermissionReference>> unconditionalRequiredPermissionsCache = new HashMap<Pair<PermissionReference, RequiredPermission.On>, Set<PermissionReference>>( 1024); private Map<QName, Set<PermissionReference>> cachedTypePermissionsExposed = new HashMap<QName, Set<PermissionReference>>( 256); private Map<QName, Set<PermissionReference>> cachedTypePermissionsUnexposed = new HashMap<QName, Set<PermissionReference>>( 256); private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); public MutableState(DictionaryService dictionaryService) { this.dictionaryService = dictionaryService; } public boolean checkPermission(PermissionReference required) { Permission permission = getPermissionOrNull(required); if (permission != null) { return true; } PermissionGroup pg = getPermissionGroupOrNull(required); if (pg != null) { if (pg.isExtends()) { if (pg.getTypeQName() != null) { return checkPermission( SimplePermissionReference.getPermissionReference(pg.getTypeQName(), pg.getName())); } else { ClassDefinition classDefinition = dictionaryService.getClass(pg.getQName()); QName parent; while ((parent = classDefinition.getParentName()) != null) { classDefinition = dictionaryService.getClass(parent); PermissionGroup attempt = getPermissionGroupOrNull( SimplePermissionReference.getPermissionReference(parent, pg.getName())); if ((attempt != null) && attempt.isAllowFullControl()) { return true; } } return false; } } else { return pg.isAllowFullControl(); } } else { return false; } } private Set<PermissionReference> getAllPermissionsImpl(QName type, boolean exposedOnly) { Map<QName, Set<PermissionReference>> cache; if (exposedOnly) { cache = cachedTypePermissionsExposed; } else { cache = cachedTypePermissionsUnexposed; } Set<PermissionReference> permissions = cache.get(type); if (permissions == null) { boolean hadWriteLock = lock.isWriteLockedByCurrentThread(); if (!hadWriteLock) { lock.readLock().unlock(); lock.writeLock().lock(); } try { permissions = cache.get(type); if (permissions == null) { permissions = new LinkedHashSet<PermissionReference>(256, 1.0f); ClassDefinition cd = dictionaryService.getClass(type); if (cd != null) { if (cd.isAspect()) { addAspectPermissions(type, permissions, exposedOnly); } else { mergeGeneralAspectPermissions(permissions, exposedOnly); addTypePermissions(type, permissions, exposedOnly); } } permissions = Collections.unmodifiableSet(permissions); cache.put(type, permissions); } } finally { if (!hadWriteLock) { lock.readLock().lock(); lock.writeLock().unlock(); } } } return permissions; } /** * Support to add permissions for types * * @param type QName * @param permissions Set<PermissionReference> * @param exposedOnly boolean */ private void addTypePermissions(QName type, Set<PermissionReference> permissions, boolean exposedOnly) { TypeDefinition typeDef = dictionaryService.getType(type); if (typeDef == null) { // the type definition is no longer in the dictionary - ignore return; } if (typeDef.getParentName() != null) { PermissionSet permissionSet = permissionSets.get(type); if (!exposedOnly || (permissionSet == null) || permissionSet.exposeAll()) { addTypePermissions(typeDef.getParentName(), permissions, exposedOnly); } } for (AspectDefinition ad : typeDef.getDefaultAspects()) { addAspectPermissions(ad.getName(), permissions, exposedOnly); } mergePermissions(permissions, type, exposedOnly, true); } /** * Support to add permissions for aspects. * * @param type QName * @param permissions Set<PermissionReference> * @param exposedOnly boolean */ private void addAspectPermissions(QName type, Set<PermissionReference> permissions, boolean exposedOnly) { AspectDefinition aspectDef = dictionaryService.getAspect(type); if (aspectDef == null) { // the aspect definition is no longer in the dictionary - ignore return; } if (aspectDef.getParentName() != null) { PermissionSet permissionSet = permissionSets.get(type); if (!exposedOnly || (permissionSet == null) || permissionSet.exposeAll()) { addAspectPermissions(aspectDef.getParentName(), permissions, exposedOnly); } } mergePermissions(permissions, type, exposedOnly, true); } /** * Support to merge permissions together. Respects extended permissions. * * @param target Set<PermissionReference> * @param type QName * @param exposedOnly boolean * @param typeRequired boolean */ private void mergePermissions(Set<PermissionReference> target, QName type, boolean exposedOnly, boolean typeRequired) { PermissionSet permissionSet = permissionSets.get(type); if (permissionSet != null) { for (PermissionGroup pg : permissionSet.getPermissionGroups()) { if (!exposedOnly || permissionSet.exposeAll() || pg.isExposed()) { if (!pg.isExtends()) { if (pg.isTypeRequired() == typeRequired) { target.add(SimplePermissionReference.getPermissionReference(pg.getQName(), pg.getName())); } } else if (exposedOnly) { if (pg.isTypeRequired() == typeRequired) { PermissionReference base = getBasePermissionGroup(pg); target.add(SimplePermissionReference.getPermissionReference(base.getQName(), base.getName())); } } } } for (Permission p : permissionSet.getPermissions()) { if (!exposedOnly || permissionSet.exposeAll() || p.isExposed()) { if (p.isTypeRequired() == typeRequired) { target.add(SimplePermissionReference.getPermissionReference(p.getQName(), p.getName())); } } } } } private void mergeGeneralAspectPermissions(Set<PermissionReference> target, boolean exposedOnly) { for (QName aspect : allAspects) { mergePermissions(target, aspect, exposedOnly, false); } } /** * Support to find permission groups * * @param target PermissionReference * @return the permission group */ private PermissionGroup getPermissionGroupOrNull(PermissionReference target) { PermissionGroup pg = permissionGroupMap.get(target); return pg == null ? null : pg; } /** * Support to get a permission group * * @param target PermissionReference * @return the permission group */ private PermissionGroup getPermissionGroup(PermissionReference target) { PermissionGroup pg = getPermissionGroupOrNull(target); if (pg == null) { throw new PermissionModelException( "There is no permission group :" + target.getQName() + " " + target.getName()); } return pg; } /** * Get the base permission group for a given permission group. * * @param pg PermissionGroup * @return the permission group */ private PermissionGroup getBasePermissionGroupOrNull(PermissionGroup pg) { if (pg == null) { return null; } PermissionGroup permissionGroup = groupsToBaseGroup.get(pg); if (permissionGroup == null) { boolean hadWriteLock = lock.isWriteLockedByCurrentThread(); if (!hadWriteLock) { lock.readLock().unlock(); lock.writeLock().lock(); } permissionGroup = groupsToBaseGroup.get(pg); if (permissionGroup == null) { permissionGroup = getBasePermissionGroupOrNullImpl(pg); groupsToBaseGroup.put(pg, permissionGroup); } if (!hadWriteLock) { lock.readLock().lock(); lock.writeLock().unlock(); } } return permissionGroup; } /** * Query the model for a base permission group Uses the Data Dictionary to reolve inheritance * * @param pg PermissionGroup * @return the permission group */ private PermissionGroup getBasePermissionGroupOrNullImpl(PermissionGroup pg) { if (pg == null) { return null; } if (pg.isExtends()) { if (pg.getTypeQName() != null) { return getPermissionGroup( SimplePermissionReference.getPermissionReference(pg.getTypeQName(), pg.getName())); } else { ClassDefinition classDefinition = dictionaryService.getClass(pg.getQName()); QName parent; if (classDefinition != null) { while ((parent = classDefinition.getParentName()) != null) { classDefinition = dictionaryService.getClass(parent); PermissionGroup attempt = getPermissionGroupOrNull( SimplePermissionReference.getPermissionReference(parent, pg.getName())); if ((attempt != null) && (!attempt.isExtends())) { return attempt; } } } return null; } } else { return pg; } } private PermissionGroup getBasePermissionGroup(PermissionGroup target) { PermissionGroup pg = getBasePermissionGroupOrNull(target); if (pg == null) { throw new PermissionModelException( "There is no parent for permission group :" + target.getQName() + " " + target.getName()); } return pg; } private Set<PermissionReference> getGrantingPermissionsImpl(PermissionReference permissionReference) { // Query the model HashSet<PermissionReference> permissions = new HashSet<PermissionReference>(256, 1.0f); permissions.add(permissionReference); for (PermissionSet ps : permissionSets.values()) { for (PermissionGroup pg : ps.getPermissionGroups()) { if (grants(pg, permissionReference)) { permissions.add(getBasePermissionGroup(pg)); } if (pg.isAllowFullControl()) { permissions.add(pg); } } for (Permission p : ps.getPermissions()) { if (p.equals(permissionReference)) { for (PermissionReference pg : p.getGrantedToGroups()) { permissions.add(getBasePermissionGroup(getPermissionGroup(pg))); } } for (RequiredPermission rp : p.getRequiredPermissions()) { if (rp.equals(permissionReference) && rp.isImplies()) { permissions.add(p); break; } } } } return permissions; } private boolean grants(PermissionGroup pg, PermissionReference permissionReference) { if (pg.getIncludedPermissionGroups().contains(permissionReference)) { return true; } if (getGranteePermissions(pg).contains(permissionReference)) { return true; } for (PermissionReference nested : pg.getIncludedPermissionGroups()) { if (grants(getPermissionGroup(nested), permissionReference)) { return true; } } return false; } private Set<PermissionReference> getGranteePermissionsImpl(PermissionReference permissionReference) { // Query the model (we have the write lock) HashSet<PermissionReference> permissions = new HashSet<PermissionReference>(256, 1.0f); permissions.add(permissionReference); for (PermissionSet ps : permissionSets.values()) { for (PermissionGroup pg : ps.getPermissionGroups()) { if (pg.equals(permissionReference)) { for (PermissionReference included : pg.getIncludedPermissionGroups()) { permissions.addAll(getGranteePermissions(included)); } if (pg.isExtends()) { if (pg.getTypeQName() != null) { permissions.addAll(getGranteePermissions(SimplePermissionReference .getPermissionReference(pg.getTypeQName(), pg.getName()))); } else { ClassDefinition classDefinition = dictionaryService.getClass(pg.getQName()); QName parent = classDefinition.getParentName(); if (parent != null) { classDefinition = dictionaryService.getClass(parent); PermissionGroup attempt = getPermissionGroupOrNull( SimplePermissionReference.getPermissionReference(parent, pg.getName())); if (attempt != null) { permissions.addAll(getGranteePermissions(attempt)); } } } } if (pg.isAllowFullControl()) { // add all available permissions.addAll(getAllPermissions()); } } } PermissionGroup baseGroup = getBasePermissionGroupOrNull( getPermissionGroupOrNull(permissionReference)); if (baseGroup != null) { for (Permission p : ps.getPermissions()) { for (PermissionReference grantedTo : p.getGrantedToGroups()) { PermissionGroup base = getBasePermissionGroupOrNull( getPermissionGroupOrNull(grantedTo)); if (baseGroup.equals(base)) { permissions.add(p); } } } } } return permissions; } private Set<PermissionReference> getImmediateGranteePermissionsImpl( PermissionReference permissionReference) { // Query the model HashSet<PermissionReference> permissions = new HashSet<PermissionReference>(256); for (PermissionSet ps : permissionSets.values()) { for (PermissionGroup pg : ps.getPermissionGroups()) { if (pg.equals(permissionReference)) { for (PermissionReference included : pg.getIncludedPermissionGroups()) { permissions.add(included); } if (pg.isExtends()) { if (pg.getTypeQName() != null) { permissions.addAll(getImmediateGranteePermissions(SimplePermissionReference .getPermissionReference(pg.getTypeQName(), pg.getName()))); } else { ClassDefinition classDefinition = dictionaryService.getClass(pg.getQName()); QName parent = classDefinition.getParentName(); if (parent != null) { classDefinition = dictionaryService.getClass(parent); PermissionGroup attempt = getPermissionGroupOrNull( SimplePermissionReference.getPermissionReference(parent, pg.getName())); if (attempt != null) { permissions.addAll(getImmediateGranteePermissions(attempt)); } } } } if (pg.isAllowFullControl()) { // add all available permissions.addAll(getAllPermissions()); } } } PermissionGroup baseGroup = getBasePermissionGroupOrNull( getPermissionGroupOrNull(permissionReference)); if (baseGroup != null) { for (Permission p : ps.getPermissions()) { for (PermissionReference grantedTo : p.getGrantedToGroups()) { PermissionGroup base = getBasePermissionGroupOrNull( getPermissionGroupOrNull(grantedTo)); if (baseGroup.equals(base)) { permissions.add(p); } } } } } return permissions; } private Set<PermissionReference> getAllPermissions() { HashSet<PermissionReference> permissions = new HashSet<PermissionReference>(256, 1.0f); for (PermissionSet ps : permissionSets.values()) { for (PermissionGroup pg : ps.getPermissionGroups()) { permissions.add(SimplePermissionReference.getPermissionReference(pg.getQName(), pg.getName())); } for (Permission p : ps.getPermissions()) { permissions.add(SimplePermissionReference.getPermissionReference(p.getQName(), p.getName())); } } return permissions; } private Set<PermissionReference> getGranteePermissions(PermissionReference permissionReference) { if (permissionReference == null) { return Collections.<PermissionReference>emptySet(); } // Cache the results Set<PermissionReference> grantees = granteePermissions.get(permissionReference); if (grantees == null) { boolean hadWriteLock = lock.isWriteLockedByCurrentThread(); if (!hadWriteLock) { lock.readLock().unlock(); lock.writeLock().lock(); } try { grantees = granteePermissions.get(permissionReference); if (grantees == null) { Set<PermissionReference> internal = getGranteePermissionsImpl(permissionReference); grantees = new HashSet<PermissionReference>(); for (PermissionReference grantee : internal) { grantees.add(SimplePermissionReference.getPermissionReference(grantee.getQName(), grantee.getName())); } grantees = Collections.unmodifiableSet(grantees); granteePermissions.put(permissionReference, grantees); } } finally { if (!hadWriteLock) { lock.readLock().lock(); lock.writeLock().unlock(); } } } return grantees; } private Set<PermissionReference> getImmediateGranteePermissions(PermissionReference permissionReference) { // Cache the results Set<PermissionReference> internal = getImmediateGranteePermissionsImpl(permissionReference); Set<PermissionReference> grantees = new HashSet<PermissionReference>(); for (PermissionReference grantee : internal) { grantees.add( SimplePermissionReference.getPermissionReference(grantee.getQName(), grantee.getName())); } grantees = Collections.unmodifiableSet(grantees); return grantees; } private Set<PermissionReference> getRequiredPermissions(PermissionReference required, QName qName, Set<QName> aspectQNames, RequiredPermission.On on) { // Cache lookup as this is static if ((required == null) || (qName == null)) { return Collections.<PermissionReference>emptySet(); } RequiredKey key = generateKey(required, qName, aspectQNames, on); Set<PermissionReference> answer = requiredPermissionsCache.get(key); if (answer == null) { boolean hadWriteLock = lock.isWriteLockedByCurrentThread(); if (!hadWriteLock) { lock.readLock().unlock(); lock.writeLock().lock(); } try { answer = requiredPermissionsCache.get(key); if (answer == null) { PermissionGroup pg = getBasePermissionGroupOrNull(getPermissionGroupOrNull(required)); if (pg == null) { answer = getRequirementsForPermission(required, on); } else { answer = getRequirementsForPermissionGroup(pg, on, qName, aspectQNames); } answer = Collections.unmodifiableSet(answer); requiredPermissionsCache.put(key, answer); } } finally { if (!hadWriteLock) { lock.readLock().lock(); lock.writeLock().unlock(); } } } return answer; } private Set<PermissionReference> getUnconditionalRequiredPermissions(PermissionReference required, RequiredPermission.On on) { // Cache lookup as this is static if (required == null) { return Collections.<PermissionReference>emptySet(); } Pair<PermissionReference, RequiredPermission.On> key = new Pair<PermissionReference, RequiredPermission.On>( required, on); Set<PermissionReference> answer = unconditionalRequiredPermissionsCache.get(key); if (answer == null) { boolean hadWriteLock = lock.isWriteLockedByCurrentThread(); if (!hadWriteLock) { lock.readLock().unlock(); lock.writeLock().lock(); } try { answer = unconditionalRequiredPermissionsCache.get(key); if (answer == null) { PermissionGroup pg = getBasePermissionGroupOrNull(getPermissionGroupOrNull(required)); if (pg == null) { answer = getRequirementsForPermission(required, on); } else { answer = getUnconditionalRequirementsForPermissionGroup(pg, on); } answer = Collections.unmodifiableSet(answer); unconditionalRequiredPermissionsCache.put(key, answer); } } finally { if (!hadWriteLock) { lock.readLock().lock(); lock.writeLock().unlock(); } } } return answer; } /** * Get the requirements for a permission * * @param required PermissionReference * @param on RequiredPermission.On * @return the set of permission references */ private Set<PermissionReference> getRequirementsForPermission(PermissionReference required, RequiredPermission.On on) { HashSet<PermissionReference> requiredPermissions = new HashSet<PermissionReference>(); Permission p = getPermissionOrNull(required); if (p != null) { for (RequiredPermission rp : p.getRequiredPermissions()) { if (!rp.isImplies() && rp.getOn().equals(on)) { requiredPermissions.add(rp); } } } return requiredPermissions; } /** * Get the requirements for a permission set * * @param target PermissionGroup * @param on RequiredPermission.On * @param qName QName * @param aspectQNames Set<QName> * @return the set of permission references */ private Set<PermissionReference> getRequirementsForPermissionGroup(PermissionGroup target, RequiredPermission.On on, QName qName, Set<QName> aspectQNames) { HashSet<PermissionReference> requiredPermissions = new HashSet<PermissionReference>(16, 1.0f); if (target == null) { return requiredPermissions; } for (PermissionSet ps : permissionSets.values()) { for (PermissionGroup pg : ps.getPermissionGroups()) { PermissionGroup base = getBasePermissionGroupOrNull(pg); if ((target.equals(base) || target.isAllowFullControl()) && (!base.isTypeRequired() || isPartOfDynamicPermissionGroup(pg, qName, aspectQNames))) { // Add includes for (PermissionReference pr : pg.getIncludedPermissionGroups()) { requiredPermissions.addAll(getRequirementsForPermissionGroup( getBasePermissionGroupOrNull(getPermissionGroupOrNull(pr)), on, qName, aspectQNames)); } } } for (Permission p : ps.getPermissions()) { for (PermissionReference grantedTo : p.getGrantedToGroups()) { PermissionGroup base = getBasePermissionGroupOrNull(getPermissionGroupOrNull(grantedTo)); if ((target.equals(base) || target.isAllowFullControl()) && (!base.isTypeRequired() || isPartOfDynamicPermissionGroup(grantedTo, qName, aspectQNames))) { if (on == RequiredPermission.On.NODE) { requiredPermissions.add(p); } } } } } return requiredPermissions; } private Set<PermissionReference> getUnconditionalRequirementsForPermissionGroup(PermissionGroup target, RequiredPermission.On on) { HashSet<PermissionReference> requiredPermissions = new HashSet<PermissionReference>(16, 1.0f); if (target == null) { return requiredPermissions; } for (PermissionSet ps : permissionSets.values()) { for (PermissionGroup pg : ps.getPermissionGroups()) { PermissionGroup base = getBasePermissionGroupOrNull(pg); if ((target.equals(base) || target.isAllowFullControl()) && (!base.isTypeRequired())) { // Add includes for (PermissionReference pr : pg.getIncludedPermissionGroups()) { requiredPermissions.addAll(getUnconditionalRequirementsForPermissionGroup( getBasePermissionGroupOrNull(getPermissionGroupOrNull(pr)), on)); } } } for (Permission p : ps.getPermissions()) { for (PermissionReference grantedTo : p.getGrantedToGroups()) { PermissionGroup base = getBasePermissionGroupOrNull(getPermissionGroupOrNull(grantedTo)); if ((target.equals(base) || target.isAllowFullControl()) && (!base.isTypeRequired())) { if (on == RequiredPermission.On.NODE) { requiredPermissions.add(p); } } } } } return requiredPermissions; } /** * Utility method to find a permission * * @param perm PermissionReference * @return the permission */ private Permission getPermissionOrNull(PermissionReference perm) { Permission p = permissionMap.get(perm); return p == null ? null : p; } /** * Check type specifc extension of permission sets. * * @param pr PermissionReference * @param typeQname QName * @param aspects Set<QName> * @return true if dynamic */ private boolean isPartOfDynamicPermissionGroup(PermissionReference pr, QName typeQname, Set<QName> aspects) { if (dictionaryService.isSubClass(typeQname, pr.getQName())) { return true; } for (QName aspect : aspects) { if (dictionaryService.isSubClass(aspect, pr.getQName())) { return true; } } return false; } public PermissionReference getPermissionReference(QName qname, String permissionName) { if (permissionName == null) { return null; } PermissionReference pr = uniqueMap.get(permissionName); if (pr == null) { pr = permissionReferenceMap.get(permissionName); if (pr == null) { throw new UnsupportedOperationException("Can not find " + permissionName); } } return pr; } public boolean isUnique(PermissionReference permissionReference) { return uniqueMap.containsKey(permissionReference.getName()); } private void buildUniquePermissionMap() { Set<String> excluded = new HashSet<String>(128, 1.0f); uniqueMap = new HashMap<String, PermissionReference>(256); permissionReferenceMap = new HashMap<String, PermissionReference>(256); permissionGroupMap = new HashMap<PermissionReference, PermissionGroup>(128); permissionMap = new HashMap<PermissionReference, Permission>(64); for (PermissionSet ps : permissionSets.values()) { for (PermissionGroup pg : ps.getPermissionGroups()) { permissionGroupMap .put(SimplePermissionReference.getPermissionReference(pg.getQName(), pg.getName()), pg); permissionReferenceMap.put(pg.toString(), SimplePermissionReference.getPermissionReference(pg.getQName(), pg.getName())); } for (Permission p : ps.getPermissions()) { permissionReferenceMap.put(p.toString(), SimplePermissionReference.getPermissionReference(p.getQName(), p.getName())); permissionMap.put(SimplePermissionReference.getPermissionReference(p.getQName(), p.getName()), p); } } for (PermissionSet ps : permissionSets.values()) { for (PermissionGroup pg : ps.getPermissionGroups()) { if (uniqueMap.containsKey(pg.getName()) && !excluded.contains(pg.getName())) { PermissionReference value = uniqueMap.get(pg.getName()); if (!value.equals(getBasePermissionGroup(pg))) { uniqueMap.remove(pg.getName()); excluded.add(pg.getName()); } } else { PermissionReference base = getBasePermissionGroup(pg); uniqueMap.put(pg.getName(), SimplePermissionReference.getPermissionReference(base.getQName(), base.getName())); } } for (Permission p : ps.getPermissions()) { if (uniqueMap.containsKey(p.getName()) && !excluded.contains(p.getName())) { PermissionReference value = uniqueMap.get(p.getName()); if (!value.equals(p)) { uniqueMap.remove(p.getName()); excluded.add(p.getName()); } } else { uniqueMap.put(p.getName(), SimplePermissionReference.getPermissionReference(p.getQName(), p.getName())); } } } // Add all permissions to the unique list if (uniqueMap.containsKey(PermissionService.ALL_PERMISSIONS)) { throw new IllegalStateException( "There must not be a permission with the same name as the ALL_PERMISSION constant: " + PermissionService.ALL_PERMISSIONS); } uniqueMap.put(PermissionService.ALL_PERMISSIONS, SimplePermissionReference.getPermissionReference( QName.createQName(NamespaceService.SECURITY_MODEL_1_0_URI, PermissionService.ALL_PERMISSIONS), PermissionService.ALL_PERMISSIONS)); } private boolean hasFull(PermissionReference permissionReference) { if (permissionReference == null) { return false; } if (permissionReference.equals(ALL)) { return true; } PermissionGroup group = getPermissionGroupOrNull(permissionReference); if (group == null) { return false; } else { if (group.isAllowFullControl()) { return true; } else { if (group.isExtends()) { if (group.getTypeQName() != null) { return hasFull(SimplePermissionReference.getPermissionReference(group.getTypeQName(), group.getName())); } else { ClassDefinition classDefinition = dictionaryService.getClass(group.getQName()); QName parent; while ((parent = classDefinition.getParentName()) != null) { classDefinition = dictionaryService.getClass(parent); PermissionGroup attempt = getPermissionGroupOrNull( SimplePermissionReference.getPermissionReference(parent, group.getName())); if ((attempt != null) && (attempt.isAllowFullControl())) { return true; } } return false; } } else { return false; } } } } } private MutableState mutableState; /** * Default constructor */ public PermissionModel() { super(); } // IOC /** * Set the model * * @param model String */ public void setModel(String model) { this.model = model; } /** * Set the dtd schema that is used to validate permission model * * @param dtdSchema String */ public void setDtdSchema(String dtdSchema) { this.dtdSchema = dtdSchema; } /** * Indicates whether model should be validated on initialization against specified dtd * * @param validate boolean */ public void setValidate(boolean validate) { this.validate = validate; } /** * Set the dictionary service * * @param dictionaryService DictionaryService */ public void setDictionaryService(DictionaryService dictionaryService) { this.dictionaryService = dictionaryService; } /** * Set the node service * * @param nodeService NodeService */ public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } /** * Adds the {@link #setModel(String) model}. */ public void init() { mutableState = new MutableState(dictionaryService); addPermissionModel(this.model); } /** * Adds a permission model * * @param model * path to the permission model to add */ public void addPermissionModel(String model) { Document document = createDocument(model); Element root = document.getRootElement(); mutableState.lock.writeLock().lock(); try { Attribute defaultPermissionAttribute = root.attribute(DEFAULT_PERMISSION); if (defaultPermissionAttribute != null) { if (defaultPermissionAttribute.getStringValue().equalsIgnoreCase(ALLOW)) { mutableState.defaultPermission = AccessStatus.ALLOWED; } else if (defaultPermissionAttribute.getStringValue().equalsIgnoreCase(DENY)) { mutableState.defaultPermission = AccessStatus.DENIED; } else { throw new PermissionModelException("The default permission must be deny or allow"); } } else { mutableState.defaultPermission = AccessStatus.DENIED; } DynamicNamespacePrefixResolver nspr = new DynamicNamespacePrefixResolver(); // Namespaces for (Iterator<Element> nsit = root.elementIterator(NAMESPACES); nsit.hasNext(); /**/) { Element namespacesElement = (Element) nsit.next(); for (Iterator<Element> it = namespacesElement.elementIterator(NAMESPACE); it.hasNext(); /**/) { Element nameSpaceElement = (Element) it.next(); nspr.registerNamespace(nameSpaceElement.attributeValue(NAMESPACE_PREFIX), nameSpaceElement.attributeValue(NAMESPACE_URI)); } } // Permission Sets for (Iterator<Element> psit = root.elementIterator(PERMISSION_SET); psit.hasNext(); /**/) { Element permissionSetElement = (Element) psit.next(); PermissionSet permissionSet = new PermissionSet(); permissionSet.initialise(permissionSetElement, nspr, this); mutableState.permissionSets.put(permissionSet.getQName(), permissionSet); } mutableState.buildUniquePermissionMap(); // NodePermissions for (Iterator<Element> npit = root.elementIterator(GLOBAL_PERMISSION); npit.hasNext(); /**/) { Element globalPermissionElement = (Element) npit.next(); GlobalPermissionEntry globalPermission = new GlobalPermissionEntry(); globalPermission.initialise(globalPermissionElement, nspr, this); mutableState.globalPermissions.add(globalPermission); } // Cache all aspect list mutableState.allAspects = dictionaryService.getAllAspects(); } finally { mutableState.lock.writeLock().unlock(); } } /* * Create the XML document from the file location */ private Document createDocument(String model) { InputStream is = this.getClass().getClassLoader().getResourceAsStream(model); URL dtdSchemaUrl = (dtdSchema == null) ? null : this.getClass().getClassLoader().getResource(dtdSchema); if (is == null) { throw new PermissionModelException("File not found: " + model); } SAXReader reader = new SAXReader(); try { if (validate) { if (dtdSchemaUrl != null) { is = processModelDocType(is, dtdSchemaUrl.toString()); reader.setValidation(true); } else { throw new PermissionModelException("Couldn't obtain DTD schema to validate permission model."); } } Document document = reader.read(is); is.close(); return document; } catch (DocumentException e) { throw new PermissionModelException("Failed to create permission model document: " + model, e); } catch (IOException e) { throw new PermissionModelException("Failed to close permission model document: " + model, e); } // TODO Do something like the following so that we don't need to modify the source xml // to validate it. The following does not work for DTDs. // InputStream is = this.getClass().getClassLoader().getResourceAsStream(model); // if (is == null) // { // throw new PermissionModelException("File not found: " + model); // } // // InputStream dtdSchemaIs = (dtdSchema == null) // ? null // : this.getClass().getClassLoader().getResourceAsStream(dtdSchema); // // try // { // Document document; // SAXReader reader; // if (validate) // { // if (dtdSchemaIs != null) // { // SAXParserFactory factory = SAXParserFactory.newInstance(); // // SchemaFactory schemaFactory = // SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema"); // // try // { // factory.setSchema(schemaFactory.newSchema( // new Source[] {new StreamSource(dtdSchemaIs)})); // SAXParser parser = factory.newSAXParser(); // reader = new SAXReader(parser.getXMLReader()); // reader.setValidation(false); // reader.setErrorHandler(new XMLErrorHandler()); // } // catch (SAXException e) // { // throw new PermissionModelException("Failed to read DTD/schema: " + dtdSchema, e); // } // catch (ParserConfigurationException e) // { // throw new PermissionModelException("Failed to configure DTD/schema: " + dtdSchema, e); // } // } // else // { // throw new PermissionModelException("Couldn't obtain DTD/schema to validate permission model."); // } // } // else // { // reader = new SAXReader(); // } // document = reader.read(is); // return document; // } // catch (DocumentException e) // { // throw new PermissionModelException("Failed to create permission model document: " + model, e); // } // finally // { // if (is != null) // { // try // { // is.close(); // } // catch (IOException e) // { // throw new PermissionModelException("Failed to close permission model document: " + model, e); // } // } // if (dtdSchemaIs != null) // { // try // { // dtdSchemaIs.close(); // } // catch (IOException e) // { // throw new PermissionModelException("Couldn't close DTD/schema to validate permission model."); // } // } // } } /* * Replace or add correct DOCTYPE to the xml to allow validation against dtd */ private InputStream processModelDocType(InputStream is, String dtdSchemaUrl) throws DocumentException, IOException { SAXReader reader = new SAXReader(); // read document without validation Document doc = reader.read(is); DocumentType docType = doc.getDocType(); if (docType != null) { // replace DOCTYPE setting the full path to the xsd docType.setSystemID(dtdSchemaUrl); } else { // add the DOCTYPE docType = new DefaultDocumentType(doc.getRootElement().getName(), dtdSchemaUrl); doc.setDocType(docType); } ByteArrayOutputStream fos = new ByteArrayOutputStream(); try { OutputFormat format = OutputFormat.createPrettyPrint(); // uses UTF-8 XMLWriter writer = new XMLWriter(fos, format); writer.write(doc); writer.flush(); } finally { fos.close(); } return new ByteArrayInputStream(fos.toByteArray()); } /** * Set the default access status * * @return the default access status */ public AccessStatus getDefaultPermission() { AccessStatus defaultPermission; mutableState.lock.readLock().lock(); defaultPermission = mutableState.defaultPermission; mutableState.lock.readLock().unlock(); return defaultPermission; } /** * Get the default acces status for the givne permission * * @param pr PermissionReference * @return the access status */ public AccessStatus getDefaultPermission(PermissionReference pr) { mutableState.lock.readLock().lock(); try { Permission p = mutableState.permissionMap.get(pr); if (p == null) { return mutableState.defaultPermission; } else { return p.getDefaultPermission(); } } finally { mutableState.lock.readLock().unlock(); } } public Set<? extends PermissionEntry> getGlobalPermissionEntries() { mutableState.lock.readLock().lock(); try { return Collections.unmodifiableSet(new HashSet<GlobalPermissionEntry>(mutableState.globalPermissions)); } finally { mutableState.lock.readLock().unlock(); } } /** * Get the permission sets by type * * @return the permission sets by type */ public Map<QName, PermissionSet> getPermissionSets() { mutableState.lock.readLock().lock(); try { return Collections.unmodifiableMap(new HashMap<QName, PermissionSet>(mutableState.permissionSets)); } finally { mutableState.lock.readLock().unlock(); } } public Set<PermissionReference> getAllPermissions(QName type) { return getAllPermissionsImpl(type, null, false); } public Set<PermissionReference> getExposedPermissions(QName type) { return getAllPermissionsImpl(type, null, true); } public Set<PermissionReference> getAllPermissions(NodeRef nodeRef) { return getAllPermissionsImpl(nodeService.getType(nodeRef), nodeService.getAspects(nodeRef), false); } public Set<PermissionReference> getExposedPermissions(NodeRef nodeRef) { return getAllPermissionsImpl(nodeService.getType(nodeRef), nodeService.getAspects(nodeRef), true); } public Set<PermissionReference> getAllPermissions(QName typeName, Set<QName> aspects) { return getAllPermissionsImpl(typeName, aspects, false); } private Set<PermissionReference> getAllPermissionsImpl(QName typeName, Set<QName> aspects, boolean exposedOnly) { Set<PermissionReference> permissions = new LinkedHashSet<PermissionReference>(128, 1.0f); ClassDefinition cd = dictionaryService.getClass(typeName); mutableState.lock.readLock().lock(); try { permissions.addAll(mutableState.getAllPermissionsImpl(typeName, exposedOnly)); if (cd != null && aspects != null) { Set<QName> defaultAspects = cd.getDefaultAspectNames(); for (QName aspect : aspects) { if (!defaultAspects.contains(aspect)) { mutableState.addAspectPermissions(aspect, permissions, exposedOnly); } } } } finally { mutableState.lock.readLock().unlock(); } return permissions; } public Set<PermissionReference> getGrantingPermissions(PermissionReference permissionReference) { if (permissionReference == null) { return Collections.<PermissionReference>emptySet(); } mutableState.lock.readLock().lock(); // Cache the results Set<PermissionReference> granters = mutableState.grantingPermissions.get(permissionReference); if (granters == null) { mutableState.lock.readLock().unlock(); mutableState.lock.writeLock().lock(); try { granters = mutableState.grantingPermissions.get(permissionReference); if (granters == null) { Set<PermissionReference> internal = mutableState .getGrantingPermissionsImpl(permissionReference); granters = new HashSet<PermissionReference>(); for (PermissionReference grantee : internal) { granters.add(SimplePermissionReference.getPermissionReference(grantee.getQName(), grantee.getName())); } granters = Collections.unmodifiableSet(granters); mutableState.grantingPermissions.put(permissionReference, granters); } } finally { mutableState.lock.writeLock().unlock(); } } else { mutableState.lock.readLock().unlock(); } return granters; } static RequiredKey generateKey(PermissionReference required, QName qName, Set<QName> aspectQNames, RequiredPermission.On on) { return RequiredKey.getRequiredKey(required, qName, aspectQNames, on); } /** * Cache key * * @author andyh */ public static class RequiredKey { PermissionReference required; QName qName; Set<QName> aspectQNames; RequiredPermission.On on; int hashCode = 0; private static ReadWriteLock lock = new ReentrantReadWriteLock(); private static Map<PermissionReference, Map<QName, Map<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>>>> instances = new HashMap<PermissionReference, Map<QName, Map<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>>>>(); /** * factory for the key * * @param required PermissionReference * @param qName QName * @param on RequiredPermission.On * @return the key */ public static RequiredKey getRequiredKey(PermissionReference required, QName qName, Set<QName> aspectQNames, RequiredPermission.On on) { lock.readLock().lock(); try { Map<QName, Map<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>>> byPermRef = instances .get(required); if (byPermRef != null) { Map<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>> byType = byPermRef.get(qName); if (byType != null) { EnumMap<RequiredPermission.On, RequiredKey> byAspects = byType.get(aspectQNames); if (byAspects != null) { RequiredKey instance = byAspects.get(on); if (instance != null) { return instance; } } } } } finally { lock.readLock().unlock(); } lock.writeLock().lock(); try { Map<QName, Map<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>>> byPermRef = instances .get(required); if (byPermRef == null) { byPermRef = new HashMap<QName, Map<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>>>(); instances.put(required, byPermRef); } Map<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>> byType = byPermRef.get(qName); if (byType == null) { byType = new HashMap<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>>(); byPermRef.put(qName, byType); } EnumMap<RequiredPermission.On, RequiredKey> byAspects = byType.get(aspectQNames); if (byAspects == null) { byAspects = new EnumMap<RequiredPermission.On, RequiredKey>(RequiredPermission.On.class); byType.put(aspectQNames, byAspects); } RequiredKey instance = byAspects.get(on); if (instance == null) { instance = new RequiredKey(required, qName, aspectQNames, on); byAspects.put(on, instance); } return instance; } finally { lock.writeLock().unlock(); } } RequiredKey(PermissionReference required, QName qName, Set<QName> aspectQNames, RequiredPermission.On on) { this.required = required; this.qName = qName; this.aspectQNames = aspectQNames; this.on = on; } @Override public int hashCode() { if (hashCode == 0) { final int PRIME = 1000003; int result = 1; result = PRIME * result + ((aspectQNames == null) ? 0 : aspectQNames.hashCode()); result = PRIME * result + ((on == null) ? 0 : on.ordinal()); result = PRIME * result + ((qName == null) ? 0 : qName.hashCode()); result = PRIME * result + ((required == null) ? 0 : required.hashCode()); hashCode = result; } return hashCode; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final RequiredKey other = (RequiredKey) obj; if (required == null) { if (other.required != null) return false; } else if (!required.equals(other.required)) return false; if (qName == null) { if (other.qName != null) return false; } else if (!qName.equals(other.qName)) return false; if (on == null) { if (other.on != null) return false; } else if (!on.equals(other.on)) return false; if (aspectQNames == null) { if (other.aspectQNames != null) return false; } else if (!aspectQNames.equals(other.aspectQNames)) return false; return true; } } public boolean checkPermission(PermissionReference required) { mutableState.lock.readLock().lock(); try { return mutableState.checkPermission(required); } finally { mutableState.lock.readLock().unlock(); } } /* (non-Javadoc) * @see org.alfresco.repo.security.permissions.impl.ModelDAO#getGranteePermissions(org.alfresco.repo.security.permissions.PermissionReference) */ public Set<PermissionReference> getGranteePermissions(PermissionReference permissionReference) { mutableState.lock.readLock().lock(); try { return mutableState.getGranteePermissions(permissionReference); } finally { mutableState.lock.readLock().unlock(); } } /* (non-Javadoc) * @see org.alfresco.repo.security.permissions.impl.ModelDAO#getImmediateGranteePermissions(org.alfresco.repo.security.permissions.PermissionReference) */ public Set<PermissionReference> getImmediateGranteePermissions(PermissionReference permissionReference) { mutableState.lock.readLock().lock(); try { return mutableState.getImmediateGranteePermissions(permissionReference); } finally { mutableState.lock.readLock().unlock(); } } /* (non-Javadoc) * @see org.alfresco.repo.security.permissions.impl.ModelDAO#getPermissionReference(org.alfresco.service.namespace.QName, java.lang.String) */ public PermissionReference getPermissionReference(QName qname, String permissionName) { mutableState.lock.readLock().lock(); try { return mutableState.getPermissionReference(qname, permissionName); } finally { mutableState.lock.readLock().unlock(); } } /* (non-Javadoc) * @see org.alfresco.repo.security.permissions.impl.ModelDAO#getRequiredPermissions(org.alfresco.repo.security.permissions.PermissionReference, org.alfresco.service.namespace.QName, java.util.Set, org.alfresco.repo.security.permissions.impl.RequiredPermission.On) */ public Set<PermissionReference> getRequiredPermissions(PermissionReference required, QName qName, Set<QName> aspectQNames, On on) { mutableState.lock.readLock().lock(); try { return mutableState.getRequiredPermissions(required, qName, aspectQNames, on); } finally { mutableState.lock.readLock().unlock(); } } /* (non-Javadoc) * @see org.alfresco.repo.security.permissions.impl.ModelDAO#getUnconditionalRequiredPermissions(org.alfresco.repo.security.permissions.PermissionReference, org.alfresco.repo.security.permissions.impl.RequiredPermission.On) */ @Override public Set<PermissionReference> getUnconditionalRequiredPermissions(PermissionReference required, On on) { mutableState.lock.readLock().lock(); try { return mutableState.getUnconditionalRequiredPermissions(required, on); } finally { mutableState.lock.readLock().unlock(); } } /* (non-Javadoc) * @see org.alfresco.repo.security.permissions.impl.ModelDAO#isUnique(org.alfresco.repo.security.permissions.PermissionReference) */ public boolean isUnique(PermissionReference permissionReference) { mutableState.lock.readLock().lock(); try { return mutableState.isUnique(permissionReference); } finally { mutableState.lock.readLock().unlock(); } } /* * (non-Javadoc) * @see org.alfresco.repo.security.permissions.impl.ModelDAO#getAllExposedPermissions() */ public Set<PermissionReference> getAllExposedPermissions() { Set<PermissionReference> permissions = new HashSet<PermissionReference>(256); mutableState.lock.readLock().lock(); try { for (PermissionSet ps : mutableState.permissionSets.values()) { for (PermissionGroup pg : ps.getPermissionGroups()) { if (pg.isExposed()) { permissions .add(SimplePermissionReference.getPermissionReference(pg.getQName(), pg.getName())); } } for (Permission p : ps.getPermissions()) { if (p.isExposed()) { permissions .add(SimplePermissionReference.getPermissionReference(p.getQName(), p.getName())); } } } return permissions; } finally { mutableState.lock.readLock().unlock(); } } public boolean hasFull(PermissionReference permissionReference) { mutableState.lock.readLock().lock(); try { return mutableState.hasFull(permissionReference); } finally { mutableState.lock.readLock().unlock(); } } /* (non-Javadoc) * @see org.alfresco.repo.security.permissions.impl.ModelDAO#getAllPermissions() */ @Override public Set<PermissionReference> getAllPermissions() { mutableState.lock.readLock().lock(); try { return mutableState.getAllPermissions(); } finally { mutableState.lock.readLock().unlock(); } } }