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.domain.permissions; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.domain.node.NodeDAO; import org.alfresco.repo.domain.qname.QNameDAO; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.permissions.ACEType; import org.alfresco.repo.security.permissions.ACLCopyMode; 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.SimpleAccessControlEntry; import org.alfresco.repo.security.permissions.SimpleAccessControlList; import org.alfresco.repo.security.permissions.SimpleAccessControlListProperties; import org.alfresco.repo.security.permissions.impl.AclChange; import org.alfresco.repo.security.permissions.impl.SimplePermissionReference; import org.alfresco.repo.tenant.TenantService; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.TransactionListenerAdapter; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.namespace.QName; import org.alfresco.util.GUID; import org.alfresco.util.Pair; import org.alfresco.util.ParameterCheck; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * DAO to manage ACL persistence * * Note: based on earlier AclDaoComponentImpl * * @author Andy Hind, janv * @since 3.4 */ public class AclDAOImpl implements AclDAO { private static Log logger = LogFactory.getLog(AclDAOImpl.class); private QNameDAO qnameDAO; private AclCrudDAO aclCrudDAO; private NodeDAO nodeDAO; private TenantService tenantService; private SimpleCache<Serializable, AccessControlList> aclCache; private enum WriteMode { /** * Remove inherited ACEs after that set */ TRUNCATE_INHERITED, /** * Add inherited ACEs */ ADD_INHERITED, /** * The source of inherited ACEs is changing */ CHANGE_INHERITED, /** * Remove all inherited ACEs */ REMOVE_INHERITED, /** * Insert inherited ACEs */ INSERT_INHERITED, /** * Copy ACLs and update ACEs and inheritance */ COPY_UPDATE_AND_INHERIT, /** * Simple copy */ COPY_ONLY, CREATE_AND_INHERIT; } public void setQnameDAO(QNameDAO qnameDAO) { this.qnameDAO = qnameDAO; } public void setTenantService(TenantService tenantService) { this.tenantService = tenantService; } public void setAclCrudDAO(AclCrudDAO aclCrudDAO) { this.aclCrudDAO = aclCrudDAO; } public void setNodeDAO(NodeDAO nodeDAO) { this.nodeDAO = nodeDAO; } /** * Set the ACL cache * */ public void setAclCache(SimpleCache<Serializable, AccessControlList> aclCache) { this.aclCache = aclCache; } /** * {@inheritDoc} */ @Override public Long createAccessControlList() { return createAccessControlList(getDefaultProperties()).getId(); } /** * {@inheritDoc} */ @Override public AccessControlListProperties getDefaultProperties() { SimpleAccessControlListProperties properties = new SimpleAccessControlListProperties(); properties.setAclType(ACLType.DEFINING); properties.setInherits(true); properties.setVersioned(false); return properties; } /** * {@inheritDoc} */ @Override public Acl createAccessControlList(AccessControlListProperties properties) { if (properties == null) { throw new IllegalArgumentException("Properties cannot be null"); } if (properties.getAclType() == null) { throw new IllegalArgumentException("ACL Type must be defined"); } switch (properties.getAclType()) { case OLD: if (properties.isVersioned() == Boolean.TRUE) { throw new IllegalArgumentException("Old acls can not be versioned"); } break; case SHARED: throw new IllegalArgumentException("Can not create shared acls direct - use get inherited"); case DEFINING: case LAYERED: break; case FIXED: if (properties.getInherits() == Boolean.TRUE) { throw new IllegalArgumentException("Fixed ACLs can not inherit"); } case GLOBAL: if (properties.getInherits() == Boolean.TRUE) { throw new IllegalArgumentException("Fixed ACLs can not inherit"); } default: break; } return createAccessControlList(properties, null, null); } /** * {@inheritDoc} */ @Override public Acl createAccessControlList(AccessControlListProperties properties, List<AccessControlEntry> aces, Long inherited) { if (properties == null) { throw new IllegalArgumentException("Properties cannot be null"); } AclEntity acl = new AclEntity(); if (properties.getAclId() != null) { acl.setAclId(properties.getAclId()); } else { acl.setAclId(GUID.generate()); } acl.setAclType(properties.getAclType()); acl.setAclVersion(Long.valueOf(1l)); switch (properties.getAclType()) { case FIXED: case GLOBAL: acl.setInherits(Boolean.FALSE); case OLD: case SHARED: case DEFINING: case LAYERED: default: if (properties.getInherits() != null) { acl.setInherits(properties.getInherits()); } else { acl.setInherits(Boolean.TRUE); } break; } acl.setLatest(Boolean.TRUE); switch (properties.getAclType()) { case OLD: acl.setVersioned(Boolean.FALSE); break; case LAYERED: if (properties.isVersioned() != null) { acl.setVersioned(properties.isVersioned()); } else { acl.setVersioned(Boolean.TRUE); } break; case FIXED: case GLOBAL: case SHARED: case DEFINING: default: if (properties.isVersioned() != null) { acl.setVersioned(properties.isVersioned()); } else { acl.setVersioned(Boolean.FALSE); } break; } acl.setAclChangeSetId(getCurrentChangeSetId()); acl.setRequiresVersion(false); Acl createdAcl = (AclEntity) aclCrudDAO.createAcl(acl); long created = createdAcl.getId(); List<Ace> toAdd = new ArrayList<Ace>(); List<AccessControlEntry> excluded = new ArrayList<AccessControlEntry>(); List<AclChange> changes = new ArrayList<AclChange>(); if ((aces != null) && aces.size() > 0) { for (AccessControlEntry ace : aces) { if ((ace.getPosition() != null) && (ace.getPosition() != 0)) { throw new IllegalArgumentException("Invalid position"); } // Find authority Authority authority = aclCrudDAO.getOrCreateAuthority(ace.getAuthority()); Permission permission = aclCrudDAO.getOrCreatePermission(ace.getPermission()); // Find context if (ace.getContext() != null) { throw new UnsupportedOperationException(); } // Find ACE Ace entry = aclCrudDAO.getOrCreateAce(permission, authority, ace.getAceType(), ace.getAccessStatus()); // Wire up // COW and remove any existing matches SimpleAccessControlEntry exclude = new SimpleAccessControlEntry(); // match any access status exclude.setAceType(ace.getAceType()); exclude.setAuthority(ace.getAuthority()); exclude.setPermission(ace.getPermission()); exclude.setPosition(0); toAdd.add(entry); excluded.add(exclude); // Will remove from the cache } } Long toInherit = null; if (inherited != null) { toInherit = getInheritedAccessControlList(inherited); } getWritable(created, toInherit, excluded, toAdd, toInherit, false, changes, WriteMode.CREATE_AND_INHERIT); // Fetch an up-to-date version return getAcl(created); } private void getWritable(final Long id, final Long parent, List<? extends AccessControlEntry> exclude, List<Ace> toAdd, Long inheritsFrom, boolean cascade, List<AclChange> changes, WriteMode mode) { List<Ace> inherited = null; List<Integer> positions = null; if ((mode == WriteMode.ADD_INHERITED) || (mode == WriteMode.INSERT_INHERITED) || (mode == WriteMode.CHANGE_INHERITED) || (mode == WriteMode.CREATE_AND_INHERIT)) { inherited = new ArrayList<Ace>(); positions = new ArrayList<Integer>(); // get aces for acl (via acl member) List<AclMember> members; if (parent != null) { members = aclCrudDAO.getAclMembersByAcl(parent); } else { members = Collections.<AclMember>emptyList(); } for (AclMember member : members) { Ace aceEntity = aclCrudDAO.getAce(member.getAceId()); if ((mode == WriteMode.INSERT_INHERITED) && (member.getPos() == 0)) { inherited.add(aceEntity); positions.add(member.getPos()); } else { inherited.add(aceEntity); positions.add(member.getPos()); } } } getWritable(id, parent, new HashSet<Long>(), exclude, toAdd, inheritsFrom, inherited, positions, cascade, 0, changes, mode, false); } /** * Make a whole tree of ACLs copy on write if required Includes adding and removing ACEs which can be optimised * slightly for copy on write (no need to add and then remove) */ private void getWritable(final Long id, final Long parent, Set<Long> visitedAcls, List<? extends AccessControlEntry> exclude, List<Ace> toAdd, Long inheritsFrom, List<Ace> inherited, List<Integer> positions, boolean cascade, int depth, List<AclChange> changes, WriteMode mode, boolean requiresVersion) { AclChange current = getWritable(id, parent, exclude, toAdd, inheritsFrom, inherited, positions, depth, mode, requiresVersion); changes.add(current); boolean cascadeVersion = requiresVersion; if (!cascadeVersion) { cascadeVersion = !current.getBefore().equals(current.getAfter()); } if (cascade) { List<Long> inheritors = aclCrudDAO.getAclsThatInheritFromAcl(id); for (Long nextId : inheritors) { if (visitedAcls.contains(nextId)) { if (logger.isWarnEnabled()) { StringBuilder message = new StringBuilder("ACL cycle detected! Repeated ALC id = '") .append(nextId).append("', inherited ACL id = '").append(id) .append("', already visited ACLs: '").append(visitedAcls) .append("'. Skipping processing of the ACL id..."); logger.warn(message.toString()); } } else { // Check for those that inherit themselves to other nodes ... getWritable(nextId, current.getAfter(), visitedAcls, exclude, toAdd, current.getAfter(), inherited, positions, cascade, depth + 1, changes, mode, cascadeVersion); } } } } /** * COW for an individual ACL * @return - an AclChange */ private AclChange getWritable(final Long id, final Long parent, List<? extends AccessControlEntry> exclude, List<Ace> acesToAdd, Long inheritsFrom, List<Ace> inherited, List<Integer> positions, int depth, WriteMode mode, boolean requiresVersion) { AclUpdateEntity acl = aclCrudDAO.getAclForUpdate(id); if (!acl.isLatest()) { return new AclChangeImpl(id, id, acl.getAclType(), acl.getAclType()); } List<Long> toAdd = new ArrayList<Long>(0); if (acesToAdd != null) { for (Ace ace : acesToAdd) { toAdd.add(ace.getId()); } } if (!acl.isVersioned()) { switch (mode) { case COPY_UPDATE_AND_INHERIT: removeAcesFromAcl(id, exclude, depth); aclCrudDAO.addAclMembersToAcl(acl.getId(), toAdd, depth); break; case CHANGE_INHERITED: replaceInherited(id, acl, inherited, positions, depth); break; case ADD_INHERITED: addInherited(acl, inherited, positions, depth); break; case TRUNCATE_INHERITED: truncateInherited(id, depth); break; case INSERT_INHERITED: insertInherited(id, acl, inherited, positions, depth); break; case REMOVE_INHERITED: removeInherited(id, depth); break; case CREATE_AND_INHERIT: aclCrudDAO.addAclMembersToAcl(acl.getId(), toAdd, depth); addInherited(acl, inherited, positions, depth); case COPY_ONLY: default: break; } if (inheritsFrom != null) { acl.setInheritsFrom(inheritsFrom); } acl.setAclChangeSetId(getCurrentChangeSetId()); aclCrudDAO.updateAcl(acl); return new AclChangeImpl(id, id, acl.getAclType(), acl.getAclType()); } else if ((acl.getAclChangeSetId() == getCurrentChangeSetId()) && (!requiresVersion) && (!acl.getRequiresVersion())) { switch (mode) { case COPY_UPDATE_AND_INHERIT: removeAcesFromAcl(id, exclude, depth); aclCrudDAO.addAclMembersToAcl(acl.getId(), toAdd, depth); break; case CHANGE_INHERITED: replaceInherited(id, acl, inherited, positions, depth); break; case ADD_INHERITED: addInherited(acl, inherited, positions, depth); break; case TRUNCATE_INHERITED: truncateInherited(id, depth); break; case INSERT_INHERITED: insertInherited(id, acl, inherited, positions, depth); break; case REMOVE_INHERITED: removeInherited(id, depth); break; case CREATE_AND_INHERIT: aclCrudDAO.addAclMembersToAcl(acl.getId(), toAdd, depth); addInherited(acl, inherited, positions, depth); case COPY_ONLY: default: break; } if (inheritsFrom != null) { acl.setInheritsFrom(inheritsFrom); } aclCrudDAO.updateAcl(acl); return new AclChangeImpl(id, id, acl.getAclType(), acl.getAclType()); } else { AclEntity newAcl = new AclEntity(); newAcl.setAclChangeSetId(getCurrentChangeSetId()); newAcl.setAclId(acl.getAclId()); newAcl.setAclType(acl.getAclType()); newAcl.setAclVersion(acl.getAclVersion() + 1); newAcl.setInheritedAcl(-1l); newAcl.setInherits(acl.getInherits()); newAcl.setInheritsFrom((inheritsFrom != null) ? inheritsFrom : acl.getInheritsFrom()); newAcl.setLatest(Boolean.TRUE); newAcl.setVersioned(Boolean.TRUE); newAcl.setRequiresVersion(Boolean.FALSE); AclEntity createdAcl = (AclEntity) aclCrudDAO.createAcl(newAcl); long created = createdAcl.getId(); // Create new membership entries - excluding those in the given pattern // AcePatternMatcher excluder = new AcePatternMatcher(exclude); // get aces for acl (via acl member) List<AclMember> members = aclCrudDAO.getAclMembersByAcl(id); if (members.size() > 0) { List<Pair<Long, Integer>> aceIdsWithDepths = new ArrayList<Pair<Long, Integer>>(members.size()); for (AclMember member : members) { aceIdsWithDepths.add(new Pair<Long, Integer>(member.getAceId(), member.getPos())); } // copy acl members to new acl aclCrudDAO.addAclMembersToAcl(newAcl.getId(), aceIdsWithDepths); } // add new switch (mode) { case COPY_UPDATE_AND_INHERIT: // Done above removeAcesFromAcl(newAcl.getId(), exclude, depth); aclCrudDAO.addAclMembersToAcl(newAcl.getId(), toAdd, depth); break; case CHANGE_INHERITED: replaceInherited(newAcl.getId(), newAcl, inherited, positions, depth); break; case ADD_INHERITED: addInherited(newAcl, inherited, positions, depth); break; case TRUNCATE_INHERITED: truncateInherited(newAcl.getId(), depth); break; case INSERT_INHERITED: insertInherited(newAcl.getId(), newAcl, inherited, positions, depth); break; case REMOVE_INHERITED: removeInherited(newAcl.getId(), depth); break; case CREATE_AND_INHERIT: aclCrudDAO.addAclMembersToAcl(acl.getId(), toAdd, depth); addInherited(acl, inherited, positions, depth); case COPY_ONLY: default: break; } // Fix up inherited ACL if required if (newAcl.getAclType() == ACLType.SHARED) { if (parent != null) { Long writableParentAcl = getWritable(parent, null, null, null, null, null, null, 0, WriteMode.COPY_ONLY, false).getAfter(); AclUpdateEntity parentAcl = aclCrudDAO.getAclForUpdate(writableParentAcl); parentAcl.setInheritedAcl(created); aclCrudDAO.updateAcl(parentAcl); } } // fix up old version acl.setLatest(Boolean.FALSE); acl.setRequiresVersion(Boolean.FALSE); aclCrudDAO.updateAcl(acl); return new AclChangeImpl(id, created, acl.getAclType(), newAcl.getAclType()); } } /** * Helper to remove ACEs from an ACL */ private void removeAcesFromAcl(final Long id, final List<? extends AccessControlEntry> exclude, final int depth) { if (exclude == null) { // cascade delete all acl members - no exclusion aclCrudDAO.deleteAclMembersByAcl(id); } else { AcePatternMatcher excluder = new AcePatternMatcher(exclude); List<Map<String, Object>> results = aclCrudDAO.getAcesAndAuthoritiesByAcl(id); List<Long> memberIds = new ArrayList<Long>(results.size()); for (Map<String, Object> result : results) { Long result_aclmemId = (Long) result.get("aclmemId"); if ((exclude != null) && excluder.matches(aclCrudDAO, result, depth)) { memberIds.add(result_aclmemId); } } // delete list of acl members aclCrudDAO.deleteAclMembers(memberIds); } } private void replaceInherited(Long id, Acl acl, List<Ace> inherited, List<Integer> positions, int depth) { truncateInherited(id, depth); addInherited(acl, inherited, positions, depth); } private void truncateInherited(final Long id, int depth) { List<AclMember> members = aclCrudDAO.getAclMembersByAcl(id); List<Long> membersToDelete = new ArrayList<Long>(members.size()); for (AclMember member : members) { if (member.getPos() > depth) { membersToDelete.add(member.getId()); } } if (membersToDelete.size() > 0) { // delete list of acl members aclCrudDAO.deleteAclMembers(membersToDelete); } } private void removeInherited(final Long id, int depth) { List<AclMemberEntity> members = aclCrudDAO.getAclMembersByAclForUpdate(id); List<Long> membersToDelete = new ArrayList<Long>(members.size()); for (AclMemberEntity member : members) { if (member.getPos() == depth + 1) { membersToDelete.add(member.getId()); } else if (member.getPos() > (depth + 1)) { member.setPos(member.getPos() - 1); aclCrudDAO.updateAclMember(member); } } if (membersToDelete.size() > 0) { // delete list of acl members aclCrudDAO.deleteAclMembers(membersToDelete); } } private void addInherited(Acl acl, List<Ace> inherited, List<Integer> positions, int depth) { if ((inherited != null) && (inherited.size() > 0)) { List<Pair<Long, Integer>> aceIdsWithDepths = new ArrayList<Pair<Long, Integer>>(inherited.size()); for (int i = 0; i < inherited.size(); i++) { Ace add = inherited.get(i); Integer position = positions.get(i); aceIdsWithDepths.add(new Pair<Long, Integer>(add.getId(), position.intValue() + depth + 1)); } aclCrudDAO.addAclMembersToAcl(acl.getId(), aceIdsWithDepths); } } private void insertInherited(final Long id, AclEntity acl, List<Ace> inherited, List<Integer> positions, int depth) { // get aces for acl (via acl member) List<AclMemberEntity> members = aclCrudDAO.getAclMembersByAclForUpdate(id); for (AclMemberEntity member : members) { if (member.getPos() > depth) { member.setPos(member.getPos() + 1); aclCrudDAO.updateAclMember(member); } } addInherited(acl, inherited, positions, depth); } /** * {@inheritDoc} */ @Override public List<AclChange> deleteAccessControlEntries(final String authority) { List<AclChange> aclChanges = new LinkedList<AclChange>(); // get authority Authority authEntity = aclCrudDAO.getAuthority(authority); if (authEntity == null) { return aclChanges; } List<Long> aces = new ArrayList<Long>(); List<AclMember> members = aclCrudDAO.getAclMembersByAuthority(authority); boolean leaveAuthority = false; if (members.size() > 0) { Set<AclUpdateEntity> acls = new HashSet<AclUpdateEntity>(members.size() * 2); List<Long> membersToDelete = new ArrayList<Long>(members.size()); // fix up members and extract acls and aces for (AclMember member : members) { // Delete acl entry Long aclMemberId = member.getId(); Long aclId = member.getAclId(); Long aceId = member.getAceId(); boolean hasAnotherTenantNodes = false; if (AuthenticationUtil.isMtEnabled()) { // ALF-3563 // Retrieve dependent nodes List<Long> nodeIds = aclCrudDAO.getADMNodesByAcl(aclId, -1); if (nodeIds.size() > 0) { for (Long nodeId : nodeIds) { Pair<Long, NodeRef> nodePair = nodeDAO.getNodePair(nodeId); if (nodePair == null) { logger.warn("Node does not exist: " + nodeId); continue; } else { NodeRef nodeRef = nodePair.getSecond(); try { // Throws AlfrescoRuntimeException in case of domain mismatch tenantService.checkDomain(nodeRef.getStoreRef().getIdentifier()); } catch (AlfrescoRuntimeException e) { hasAnotherTenantNodes = true; leaveAuthority = true; break; } } } } } if (!hasAnotherTenantNodes) { AclUpdateEntity list = aclCrudDAO.getAclForUpdate(aclId); aclChanges.add(new AclChangeImpl(aclId, aclId, list.getAclType(), list.getAclType())); acls.add(list); membersToDelete.add(aclMemberId); aces.add((Long) aceId); } } // delete list of acl members aclCrudDAO.deleteAclMembers(membersToDelete); // Remember to 'touch' all affected ACLs for (AclUpdateEntity acl : acls) { acl.setAclChangeSetId(getCurrentChangeSetId()); aclCrudDAO.updateAcl(acl); } } if (!leaveAuthority) { // remove ACEs aclCrudDAO.deleteAces(aces); // Tidy up any unreferenced ACEs // get aces by authority List<Ace> unreferenced = aclCrudDAO.getAcesByAuthority(authEntity.getId()); if (unreferenced.size() > 0) { List<Long> unrefencedAcesToDelete = new ArrayList<Long>(unreferenced.size()); for (Ace ace : unreferenced) { unrefencedAcesToDelete.add(ace.getId()); } aclCrudDAO.deleteAces(unrefencedAcesToDelete); } // remove authority if (authEntity != null) { aclCrudDAO.deleteAuthority(authEntity.getId()); } } return aclChanges; } /** * {@inheritDoc} */ @Override public void deleteAclForNode(long aclId) { Acl dbAcl = getAcl(aclId); if (dbAcl.getAclType() == ACLType.DEFINING) { // delete acl members & acl aclCrudDAO.deleteAclMembersByAcl(aclId); aclCrudDAO.deleteAcl(aclId); } if (dbAcl.getAclType() == ACLType.SHARED) { // check unused Long defining = dbAcl.getInheritsFrom(); if (aclCrudDAO.getAcl(defining) == null) { // ADM if (getADMNodesByAcl(aclId, 1).size() == 0) { // delete acl members & acl aclCrudDAO.deleteAclMembersByAcl(aclId); aclCrudDAO.deleteAcl(aclId); } } } } /** * {@inheritDoc} */ @Override public List<AclChange> deleteAccessControlList(final Long id) { if (logger.isDebugEnabled()) { // debug only int maxForDebug = 11; List<Long> nodeIds = getADMNodesByAcl(id, maxForDebug); for (Long nodeId : nodeIds) { logger.debug("deleteAccessControlList: Found nodeId=" + nodeId + ", aclId=" + id); } } List<AclChange> acls = new ArrayList<AclChange>(); final AclUpdateEntity acl = aclCrudDAO.getAclForUpdate(id); if (!acl.isLatest()) { throw new UnsupportedOperationException("Old ACL versions can not be updated"); } if (acl.getAclType() == ACLType.SHARED) { throw new UnsupportedOperationException( "Delete is not supported for shared acls - they are deleted with the defining acl"); } if ((acl.getAclType() == ACLType.DEFINING) || (acl.getAclType() == ACLType.LAYERED)) { if ((acl.getInheritedAcl() != null) && (acl.getInheritedAcl() != -1)) { final Acl inherited = aclCrudDAO.getAcl(acl.getInheritedAcl()); // Will remove from the cache getWritable(inherited.getId(), acl.getInheritsFrom(), null, null, null, true, acls, WriteMode.REMOVE_INHERITED); Acl unusedInherited = null; for (AclChange change : acls) { if (change.getBefore() != null && change.getBefore().equals(inherited.getId())) { unusedInherited = aclCrudDAO.getAcl(change.getAfter()); } } final Long newId = unusedInherited.getId(); List<Long> inheritors = aclCrudDAO.getAclsThatInheritFromAcl(newId); for (Long nextId : inheritors) { // Will remove from the cache getWritable(nextId, acl.getInheritsFrom(), null, null, acl.getInheritsFrom(), true, acls, WriteMode.REMOVE_INHERITED); } // delete acl members aclCrudDAO.deleteAclMembersByAcl(newId); // delete 'unusedInherited' acl aclCrudDAO.deleteAcl(unusedInherited.getId()); if (inherited.isVersioned()) { AclUpdateEntity inheritedForUpdate = aclCrudDAO.getAclForUpdate(inherited.getId()); if (inheritedForUpdate != null) { inheritedForUpdate.setLatest(Boolean.FALSE); aclCrudDAO.updateAcl(inheritedForUpdate); } } else { // delete 'inherited' acl aclCrudDAO.deleteAcl(inherited.getId()); } } } else { List<Long> inheritors = aclCrudDAO.getAclsThatInheritFromAcl(id); for (Long nextId : inheritors) { // Will remove from the cache getWritable(nextId, acl.getInheritsFrom(), null, null, null, true, acls, WriteMode.REMOVE_INHERITED); } } // delete if (acl.isVersioned()) { acl.setLatest(Boolean.FALSE); acl.setAclChangeSetId(getCurrentChangeSetId()); aclCrudDAO.updateAcl(acl); } else { // delete acl members & acl aclCrudDAO.deleteAclMembersByAcl(id); aclCrudDAO.deleteAcl(acl.getId()); } acls.add(new AclChangeImpl(id, null, acl.getAclType(), null)); return acls; } /** * {@inheritDoc} */ @Override public List<AclChange> deleteLocalAccessControlEntries(Long id) { List<AclChange> changes = new ArrayList<AclChange>(); SimpleAccessControlEntry pattern = new SimpleAccessControlEntry(); pattern.setPosition(Integer.valueOf(0)); // Will remove from the cache getWritable(id, null, Collections.singletonList(pattern), null, null, true, changes, WriteMode.COPY_UPDATE_AND_INHERIT); return changes; } /** * {@inheritDoc} */ @Override public List<AclChange> deleteInheritedAccessControlEntries(Long id) { List<AclChange> changes = new ArrayList<AclChange>(); SimpleAccessControlEntry pattern = new SimpleAccessControlEntry(); pattern.setPosition(Integer.valueOf(-1)); // Will remove from the cache getWritable(id, null, Collections.singletonList(pattern), null, null, true, changes, WriteMode.COPY_UPDATE_AND_INHERIT); return changes; } /** * {@inheritDoc} */ @Override public List<AclChange> deleteAccessControlEntries(Long id, AccessControlEntry pattern) { List<AclChange> changes = new ArrayList<AclChange>(); // Will remove from the cache getWritable(id, null, Collections.singletonList(pattern), null, null, true, changes, WriteMode.COPY_UPDATE_AND_INHERIT); return changes; } /** * {@inheritDoc} */ @Override public Acl getAcl(Long id) { return aclCrudDAO.getAcl(id); } /** * {@inheritDoc} */ @Override public AccessControlListProperties getAccessControlListProperties(Long id) { ParameterCheck.mandatory("id", id); // Prevent unboxing failures return aclCrudDAO.getAcl(id); } @Override public void setCheckAclConsistency() { aclCrudDAO.setCheckAclConsistency(); } /** * {@inheritDoc} */ @Override public AccessControlList getAccessControlList(Long id) { // Used the cached properties as our cache key AccessControlListProperties properties = getAccessControlListProperties(id); if (properties == null) { return null; } AccessControlList aclCached = aclCache.get((Serializable) properties); if (aclCached != null) { return aclCached; } SimpleAccessControlList acl = new SimpleAccessControlList(); acl.setProperties(properties); List<Map<String, Object>> results = aclCrudDAO.getAcesAndAuthoritiesByAcl(id); List<AccessControlEntry> entries = new ArrayList<AccessControlEntry>(results.size()); for (Map<String, Object> result : results) // for (AclMemberEntity member : members) { Boolean aceIsAllowed = (Boolean) result.get("allowed"); Integer aceType = (Integer) result.get("applies"); String authority = (String) result.get("authority"); Long permissionId = (Long) result.get("permissionId"); Integer position = (Integer) result.get("pos"); //Long result_aclmemId = (Long) result.get("aclmemId"); // not used here SimpleAccessControlEntry sacEntry = new SimpleAccessControlEntry(); sacEntry.setAccessStatus(aceIsAllowed ? AccessStatus.ALLOWED : AccessStatus.DENIED); sacEntry.setAceType(ACEType.getACETypeFromId(aceType)); sacEntry.setAuthority(authority); // if (entry.getContext() != null) // { // SimpleAccessControlEntryContext context = new SimpleAccessControlEntryContext(); // context.setClassContext(entry.getContext().getClassContext()); // context.setKVPContext(entry.getContext().getKvpContext()); // context.setPropertyContext(entry.getContext().getPropertyContext()); // sacEntry.setContext(context); // } Permission perm = aclCrudDAO.getPermission(permissionId); QName permTypeQName = qnameDAO.getQName(perm.getTypeQNameId()).getSecond(); // Has an ID so must exist SimplePermissionReference permissionRefernce = SimplePermissionReference .getPermissionReference(permTypeQName, perm.getName()); sacEntry.setPermission(permissionRefernce); sacEntry.setPosition(position); entries.add(sacEntry); } Collections.sort(entries); acl.setEntries(entries); // Cache it for next time aclCache.put((Serializable) properties, acl); return acl; } /** * {@inheritDoc} */ @Override public Long getInheritedAccessControlList(Long id) { AclUpdateEntity acl = aclCrudDAO.getAclForUpdate(id); if (acl.getAclType() == ACLType.OLD) { return null; } if ((acl.getInheritedAcl() != null) && (acl.getInheritedAcl() != -1)) { return acl.getInheritedAcl(); } Long inheritedAclId = null; if ((acl.getAclType() == ACLType.DEFINING) || (acl.getAclType() == ACLType.LAYERED)) { List<AclChange> changes = new ArrayList<AclChange>(); // created shared acl SimpleAccessControlListProperties properties = new SimpleAccessControlListProperties(); properties.setAclType(ACLType.SHARED); properties.setInherits(Boolean.TRUE); properties.setVersioned(acl.isVersioned()); Long sharedId = createAccessControlList(properties, null, null).getId(); getWritable(sharedId, id, null, null, id, true, changes, WriteMode.ADD_INHERITED); acl.setInheritedAcl(sharedId); inheritedAclId = sharedId; } else { acl.setInheritedAcl(acl.getId()); inheritedAclId = acl.getId(); } acl.setAclChangeSetId(getCurrentChangeSetId()); aclCrudDAO.updateAcl(acl); return inheritedAclId; } /** * {@inheritDoc} */ @Override public List<AclChange> mergeInheritedAccessControlList(Long inherited, Long target) { // TODO: For now we do a replace - we could do an insert if both inherit from the same acl List<AclChange> changes = new ArrayList<AclChange>(); Acl targetAcl = aclCrudDAO.getAcl(target); Acl inheritedAcl = null; if (inherited != null) { inheritedAcl = aclCrudDAO.getAcl(inherited); } else { // Assume we are just resetting it to inherit as before if (targetAcl.getInheritsFrom() != null) { inheritedAcl = aclCrudDAO.getAcl(targetAcl.getInheritsFrom()); if (inheritedAcl == null) { // TODO: Try previous versions throw new IllegalStateException("No old inheritance definition to use"); } else { // find the latest version of the acl if (!inheritedAcl.isLatest()) { final String searchAclId = inheritedAcl.getAclId(); Long actualInheritor = (Long) aclCrudDAO.getLatestAclByGuid(searchAclId); inheritedAcl = aclCrudDAO.getAcl(actualInheritor); if (inheritedAcl == null) { // TODO: Try previous versions throw new IllegalStateException("No ACL found"); } } } } else { // There is no inheritance to set return changes; } } // recursion test // if inherited already inherits from the target Acl test = inheritedAcl; Set<Long> visitedAcls = new HashSet<Long>(); while (test != null) { Long testId = test.getId(); if (testId != null && testId.equals(target)) { throw new IllegalStateException("Cyclical ACL detected"); } if (visitedAcls.contains(testId)) { throw new IllegalStateException("Cyclical InheritsFrom detected. AclId: " + testId); } else { visitedAcls.add(testId); } Long parent = test.getInheritsFrom(); if ((parent == null) || (parent == -1l)) { test = null; } else { test = aclCrudDAO.getAcl(test.getInheritsFrom()); } } if ((targetAcl.getAclType() != ACLType.DEFINING) && (targetAcl.getAclType() != ACLType.LAYERED)) { throw new IllegalArgumentException("Only defining ACLs can have their inheritance set"); } if (!targetAcl.getInherits()) { return changes; } Long actualInheritedId = inheritedAcl.getId(); if ((inheritedAcl.getAclType() == ACLType.DEFINING) || (inheritedAcl.getAclType() == ACLType.LAYERED)) { actualInheritedId = getInheritedAccessControlList(actualInheritedId); } // Will remove from the cache getWritable(target, actualInheritedId, null, null, actualInheritedId, true, changes, WriteMode.CHANGE_INHERITED); return changes; } /** * {@inheritDoc} */ @Override public List<AclChange> setAccessControlEntry(final Long id, final AccessControlEntry ace) { Acl target = aclCrudDAO.getAcl(id); if (target.getAclType() == ACLType.SHARED) { throw new IllegalArgumentException("Shared ACLs are immutable"); } List<AclChange> changes = new ArrayList<AclChange>(); if ((ace.getPosition() != null) && (ace.getPosition() != 0)) { throw new IllegalArgumentException("Invalid position"); } // Find authority Authority authority = aclCrudDAO.getOrCreateAuthority(ace.getAuthority()); Permission permission = aclCrudDAO.getOrCreatePermission(ace.getPermission()); // Find context if (ace.getContext() != null) { throw new UnsupportedOperationException(); } // Find ACE Ace entry = aclCrudDAO.getOrCreateAce(permission, authority, ace.getAceType(), ace.getAccessStatus()); // Wire up // COW and remove any existing matches SimpleAccessControlEntry exclude = new SimpleAccessControlEntry(); // match any access status exclude.setAceType(ace.getAceType()); exclude.setAuthority(ace.getAuthority()); exclude.setPermission(ace.getPermission()); exclude.setPosition(0); List<Ace> toAdd = new ArrayList<Ace>(1); toAdd.add(entry); // Will remove from the cache getWritable(id, null, Collections.singletonList(exclude), toAdd, null, true, changes, WriteMode.COPY_UPDATE_AND_INHERIT); return changes; } /** * {@inheritDoc} */ @Override public List<AclChange> enableInheritance(Long id, Long parent) { List<AclChange> changes = new ArrayList<AclChange>(); AclUpdateEntity acl = aclCrudDAO.getAclForUpdate(id); switch (acl.getAclType()) { case FIXED: case GLOBAL: throw new IllegalArgumentException("Fixed and global permissions can not inherit"); case OLD: acl.setInherits(Boolean.TRUE); acl.setAclChangeSetId(getCurrentChangeSetId()); aclCrudDAO.updateAcl(acl); changes.add(new AclChangeImpl(id, id, acl.getAclType(), acl.getAclType())); return changes; case SHARED: // TODO support a list of children and casacade if given throw new IllegalArgumentException( "Shared acls should be replace by creating a definig ACL, wiring it up for inhertitance, and then applying inheritance to any children. It can not be done by magic "); case DEFINING: case LAYERED: default: if (!acl.getInherits()) { // Will remove from the cache getWritable(id, null, null, null, null, false, changes, WriteMode.COPY_ONLY); acl = aclCrudDAO.getAclForUpdate(changes.get(0).getAfter()); acl.setInherits(Boolean.TRUE); acl.setAclChangeSetId(getCurrentChangeSetId()); aclCrudDAO.updateAcl(acl); } else { // Will remove from the cache getWritable(id, null, null, null, null, false, changes, WriteMode.COPY_ONLY); } List<AclChange> merged = mergeInheritedAccessControlList(parent, changes.get(0).getAfter()); changes.addAll(merged); return changes; } } /** * {@inheritDoc} */ @Override public List<AclChange> disableInheritance(Long id, boolean setInheritedOnAcl) { AclUpdateEntity acl = aclCrudDAO.getAclForUpdate(id); List<AclChange> changes = new ArrayList<AclChange>(1); switch (acl.getAclType()) { case FIXED: case GLOBAL: return Collections .<AclChange>singletonList(new AclChangeImpl(id, id, acl.getAclType(), acl.getAclType())); case OLD: acl.setInherits(Boolean.FALSE); acl.setAclChangeSetId(getCurrentChangeSetId()); aclCrudDAO.updateAcl(acl); changes.add(new AclChangeImpl(id, id, acl.getAclType(), acl.getAclType())); return changes; case SHARED: // TODO support a list of children and casacade if given throw new IllegalArgumentException("Shared ACL must inherit"); case DEFINING: case LAYERED: default: return disableInheritanceImpl(id, setInheritedOnAcl, acl); } } private Long getCopy(Long toCopy, Long toInheritFrom, ACLCopyMode mode) { AclUpdateEntity aclToCopy; Long inheritedId; Acl aclToInheritFrom; switch (mode) { case INHERIT: if (toCopy.equals(toInheritFrom)) { return getInheritedAccessControlList(toCopy); } else { throw new UnsupportedOperationException(); } case COW: aclToCopy = aclCrudDAO.getAclForUpdate(toCopy); aclToCopy.setRequiresVersion(true); aclToCopy.setAclChangeSetId(getCurrentChangeSetId()); aclCrudDAO.updateAcl(aclToCopy); inheritedId = getInheritedAccessControlList(toCopy); if ((inheritedId != null) && (!inheritedId.equals(toCopy))) { AclUpdateEntity inheritedAcl = aclCrudDAO.getAclForUpdate(inheritedId); inheritedAcl.setRequiresVersion(true); inheritedAcl.setAclChangeSetId(getCurrentChangeSetId()); aclCrudDAO.updateAcl(inheritedAcl); } return toCopy; case REDIRECT: if ((toInheritFrom != null) && (toInheritFrom.equals(toCopy))) { return getInheritedAccessControlList(toInheritFrom); } aclToCopy = aclCrudDAO.getAclForUpdate(toCopy); aclToInheritFrom = null; if (toInheritFrom != null) { aclToInheritFrom = aclCrudDAO.getAcl(toInheritFrom); } switch (aclToCopy.getAclType()) { case DEFINING: // This is not called on the redirecting node as only LAYERED change permissions when redirected // So this needs to make a copy in the same way layered does case LAYERED: if (toInheritFrom == null) { return toCopy; } // manages cache clearing beneath List<AclChange> changes = mergeInheritedAccessControlList(toInheritFrom, toCopy); for (AclChange change : changes) { if (change.getBefore().equals(toCopy)) { return change.getAfter(); } } throw new UnsupportedOperationException(); case SHARED: if (aclToInheritFrom != null) { return getInheritedAccessControlList(toInheritFrom); } else { throw new UnsupportedOperationException(); } case FIXED: case GLOBAL: case OLD: return toCopy; default: throw new UnsupportedOperationException(); } case COPY: aclToCopy = aclCrudDAO.getAclForUpdate(toCopy); aclToInheritFrom = null; if (toInheritFrom != null) { aclToInheritFrom = aclCrudDAO.getAcl(toInheritFrom); } switch (aclToCopy.getAclType()) { case DEFINING: SimpleAccessControlListProperties properties = new SimpleAccessControlListProperties(); properties.setAclType(ACLType.DEFINING); properties.setInherits(aclToCopy.getInherits()); properties.setVersioned(true); Long id = createAccessControlList(properties).getId(); AccessControlList indirectAcl = getAccessControlList(toCopy); for (AccessControlEntry entry : indirectAcl.getEntries()) { if (entry.getPosition() == 0) { setAccessControlEntry(id, entry); } } if (aclToInheritFrom != null) { mergeInheritedAccessControlList(toInheritFrom, id); } return id; case SHARED: if (aclToInheritFrom != null) { return getInheritedAccessControlList(toInheritFrom); } else { return null; } case FIXED: case GLOBAL: case LAYERED: case OLD: return toCopy; default: throw new UnsupportedOperationException(); } default: throw new UnsupportedOperationException(); } } /** * {@inheritDoc} */ @Override public Acl getAclCopy(Long toCopy, Long toInheritFrom, ACLCopyMode mode) { return getAclEntityCopy(toCopy, toInheritFrom, mode); } private Acl getAclEntityCopy(Long toCopy, Long toInheritFrom, ACLCopyMode mode) { Long id = getCopy(toCopy, toInheritFrom, mode); if (id == null) { return null; } return aclCrudDAO.getAcl(id); } /** * {@inheritDoc} */ @Override public List<Long> getADMNodesByAcl(long aclEntityId, int maxResults) { return aclCrudDAO.getADMNodesByAcl(aclEntityId, maxResults); } /** * {@inheritDoc} */ @Override public Acl createLayeredAcl(Long indirectedAcl) { SimpleAccessControlListProperties properties = new SimpleAccessControlListProperties(); properties.setAclType(ACLType.LAYERED); Acl acl = createAccessControlList(properties); long id = acl.getId(); if (indirectedAcl != null) { mergeInheritedAccessControlList(indirectedAcl, id); } return acl; } private List<AclChange> disableInheritanceImpl(Long id, boolean setInheritedOnAcl, AclEntity aclIn) { List<AclChange> changes = new ArrayList<AclChange>(); if (!aclIn.getInherits()) { return Collections.<AclChange>emptyList(); } // Manages caching getWritable(id, null, null, null, null, false, changes, WriteMode.COPY_ONLY); AclUpdateEntity acl = aclCrudDAO.getAclForUpdate(changes.get(0).getAfter()); final Long inheritsFrom = acl.getInheritsFrom(); acl.setInherits(Boolean.FALSE); acl.setAclChangeSetId(getCurrentChangeSetId()); aclCrudDAO.updateAcl(acl); // Keep inherits from so we can reinstate if required // acl.setInheritsFrom(-1l); // Manages caching getWritable(acl.getId(), null, null, null, null, true, changes, WriteMode.TRUNCATE_INHERITED); // set Inherited - TODO: UNTESTED if ((inheritsFrom != null) && (inheritsFrom != -1) && setInheritedOnAcl) { // get aces for acl (via acl member) List<AclMember> members = aclCrudDAO.getAclMembersByAcl(inheritsFrom); for (AclMember member : members) { // TODO optimise Ace ace = aclCrudDAO.getAce(member.getAceId()); Authority authority = aclCrudDAO.getAuthority(ace.getAuthorityId()); SimpleAccessControlEntry entry = new SimpleAccessControlEntry(); entry.setAccessStatus(ace.isAllowed() ? AccessStatus.ALLOWED : AccessStatus.DENIED); entry.setAceType(ace.getAceType()); entry.setAuthority(authority.getAuthority()); /* NOTE: currently unused - intended for possible future enhancement if (ace.getContextId() != null) { AceContext aceContext = aclCrudDAO.getAceContext(ace.getContextId()); SimpleAccessControlEntryContext context = new SimpleAccessControlEntryContext(); context.setClassContext(aceContext.getClassContext()); context.setKVPContext(aceContext.getKvpContext()); context.setPropertyContext(aceContext.getPropertyContext()); entry.setContext(context); } */ Permission perm = aclCrudDAO.getPermission(ace.getPermissionId()); QName permTypeQName = qnameDAO.getQName(perm.getTypeQNameId()).getSecond(); // Has an ID so must exist SimplePermissionReference permissionRefernce = SimplePermissionReference .getPermissionReference(permTypeQName, perm.getName()); entry.setPermission(permissionRefernce); entry.setPosition(Integer.valueOf(0)); setAccessControlEntry(id, entry); } } return changes; } private static final String RESOURCE_KEY_ACL_CHANGE_SET_ID = "acl.change.set.id"; private UpdateChangeSetListener updateChangeSetListener = new UpdateChangeSetListener(); /** * Wrapper to update the current changeset to get the change time correct * * @author Derek Hulley * @since 4.0 */ private class UpdateChangeSetListener extends TransactionListenerAdapter { @Override public void beforeCommit(boolean readOnly) { if (readOnly) { return; } Long changeSetId = (Long) AlfrescoTransactionSupport.getResource(RESOURCE_KEY_ACL_CHANGE_SET_ID); if (changeSetId == null) { // There has not been a change return; } // Update it long commitTimeMs = System.currentTimeMillis(); aclCrudDAO.updateAclChangeSet(changeSetId, commitTimeMs); } } /** * Support to get the current ACL change set and bind this to the transaction. So we only make one new version of an * ACL per change set. If something is in the current change set we can update it. */ private long getCurrentChangeSetId() { Long changeSetId = (Long) AlfrescoTransactionSupport.getResource(RESOURCE_KEY_ACL_CHANGE_SET_ID); if (changeSetId == null) { changeSetId = aclCrudDAO.createAclChangeSet(); // bind the ID and the listener AlfrescoTransactionSupport.bindResource(RESOURCE_KEY_ACL_CHANGE_SET_ID, changeSetId); AlfrescoTransactionSupport.bindListener(updateChangeSetListener); if (logger.isDebugEnabled()) { logger.debug("New change set = " + changeSetId); } } return changeSetId; } private static class AcePatternMatcher { private List<? extends AccessControlEntry> patterns; AcePatternMatcher(List<? extends AccessControlEntry> patterns) { this.patterns = patterns; } boolean matches(AclCrudDAO aclCrudDAO, Map<String, Object> result, int position) { if (patterns == null) { return true; } for (AccessControlEntry pattern : patterns) { if (checkPattern(aclCrudDAO, result, position, pattern)) { return true; } } return false; } private boolean checkPattern(AclCrudDAO aclCrudDAO, Map<String, Object> result, int position, AccessControlEntry pattern) { Boolean result_aceIsAllowed = (Boolean) result.get("allowed"); Integer result_aceType = (Integer) result.get("applies"); String result_authority = (String) result.get("authority"); Long result_permissionId = (Long) result.get("permissionId"); Integer result_position = (Integer) result.get("pos"); //Long result_aclmemId = (Long) result.get("aclmemId"); // not used if (pattern.getAccessStatus() != null) { if (pattern .getAccessStatus() != (result_aceIsAllowed ? AccessStatus.ALLOWED : AccessStatus.DENIED)) { return false; } } if (pattern.getAceType() != null) { if (pattern.getAceType() != ACEType.getACETypeFromId(result_aceType)) { return false; } } if (pattern.getAuthority() != null) { if ((pattern.getAuthorityType() != AuthorityType.WILDCARD) && !pattern.getAuthority().equals(result_authority)) { return false; } } if (pattern.getContext() != null) { throw new IllegalArgumentException("Context not yet supported"); } if (pattern.getPermission() != null) { Long permId = aclCrudDAO.getPermission(pattern.getPermission()).getId(); if (!permId.equals(result_permissionId)) { return false; } } if (pattern.getPosition() != null) { if (pattern.getPosition().intValue() >= 0) { if (result_position != position) { return false; } } else if (pattern.getPosition().intValue() == -1) { if (result_position <= position) { return false; } } } return true; } } static class AclChangeImpl implements AclChange { private Long before; private Long after; private ACLType typeBefore; private ACLType typeAfter; public AclChangeImpl(Long before, Long after, ACLType typeBefore, ACLType typeAfter) { this.before = before; this.after = after; this.typeAfter = typeAfter; this.typeBefore = typeBefore; } public Long getAfter() { return after; } public Long getBefore() { return before; } /** * @param after Long */ public void setAfter(Long after) { this.after = after; } /** * @param before Long */ public void setBefore(Long before) { this.before = before; } public ACLType getTypeAfter() { return typeAfter; } /** * @param typeAfter ACLType */ public void setTypeAfter(ACLType typeAfter) { this.typeAfter = typeAfter; } public ACLType getTypeBefore() { return typeBefore; } /** * @param typeBefore ACLType */ public void setTypeBefore(ACLType typeBefore) { this.typeBefore = typeBefore; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("(").append(getBefore()).append(",").append(getTypeBefore()).append(")"); builder.append(" - > "); builder.append("(").append(getAfter()).append(",").append(getTypeAfter()).append(")"); return builder.toString(); } } /** * {@inheritDoc} */ @Override public void renameAuthority(String before, String after) { aclCrudDAO.renameAuthority(before, after); aclCache.clear(); } /** * {@inheritDoc} */ @Override public void fixSharedAcl(Long shared, Long defining) { if (defining == null) { throw new IllegalArgumentException("Null defining acl"); } if (shared == null) { throw new IllegalArgumentException("Null shared acl"); } List<AclChange> changes = new ArrayList<AclChange>(); getWritable(shared, defining, null, null, defining, true, changes, WriteMode.CHANGE_INHERITED); } /* (non-Javadoc) * @see org.alfresco.repo.domain.permissions.AclDAO#getMaxChangeSetCommitTime() */ @Override public Long getMaxChangeSetCommitTime() { return aclCrudDAO.getMaxChangeSetCommitTime(); } /* (non-Javadoc) * @see org.alfresco.repo.domain.permissions.AclDAO#getMaxChangeSetIdByCommitTime(long) */ @Override public Long getMaxChangeSetIdByCommitTime(long maxCommitTime) { return aclCrudDAO.getMaxChangeSetIdByCommitTime(maxCommitTime); } }