diff --git a/config/alfresco/hibernate-context.xml b/config/alfresco/hibernate-context.xml index 21bcaf7d78..6681ea95c7 100644 --- a/config/alfresco/hibernate-context.xml +++ b/config/alfresco/hibernate-context.xml @@ -235,18 +235,18 @@ org.alfresco.repo.security.permissions.impl.PermissionsDaoComponent - + - - - + + + - + @@ -261,7 +261,7 @@ - + @@ -288,9 +288,18 @@ - + + + + + + + + + + diff --git a/config/alfresco/messages/patch-service.properties b/config/alfresco/messages/patch-service.properties index f528faddb0..cf79df4522 100644 --- a/config/alfresco/messages/patch-service.properties +++ b/config/alfresco/messages/patch-service.properties @@ -214,9 +214,9 @@ patch.wcmPermissionPatch.result=Updated ACLs: ACLS are moved to the staging area patch.avmWebProjectInheritPermissions.description=Break inheritance of permissions on wca:webfolder object to hide access by default. patch.avmWebProjectInheritPermissions.result=Removed inheritance of permissions on all wca:webfolder objects. - patch.wcmPostPermissionSnapshotPatch.description=Snapshot stores (after fixing ACLs so they are only set on the staging area store). patch.wcmPostPermissionSnapshotPatch.result=Snapshot complete after WCM ACL changes. - +patch.updateDmPermissions.description=Update ACLs on all DM node objects to the new 3.0 permission model +patch.updateDmPermissions.result=Updated ACLs. Created {0} defining ACLs. diff --git a/config/alfresco/node-services-context.xml b/config/alfresco/node-services-context.xml index 08068b9fc5..acec6563b0 100644 --- a/config/alfresco/node-services-context.xml +++ b/config/alfresco/node-services-context.xml @@ -179,6 +179,9 @@ + + + diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml index 2d9a343a69..9bb9b99495 100644 --- a/config/alfresco/patch/patch-services-context.xml +++ b/config/alfresco/patch/patch-services-context.xml @@ -1452,5 +1452,20 @@ + + + patch.updateDmPermissions + patch.updateDmPermissions.description + 0 + 124 + 125 + + + + + + + + diff --git a/config/alfresco/public-services-security-context.xml b/config/alfresco/public-services-security-context.xml index 628c7ac4d3..9fe9c2e071 100644 --- a/config/alfresco/public-services-security-context.xml +++ b/config/alfresco/public-services-security-context.xml @@ -41,7 +41,7 @@ - + @@ -136,6 +136,16 @@ + + + + + + Unlock + CheckIn + CancelCheckOut + + diff --git a/config/alfresco/version.properties b/config/alfresco/version.properties index c074b98af5..366ad9ab30 100644 --- a/config/alfresco/version.properties +++ b/config/alfresco/version.properties @@ -19,4 +19,4 @@ version.build=@build-number@ # Schema number -version.schema=124 +version.schema=125 diff --git a/source/java/org/alfresco/repo/admin/patch/impl/DmPermissionsPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/DmPermissionsPatch.java new file mode 100644 index 0000000000..4497cb89e7 --- /dev/null +++ b/source/java/org/alfresco/repo/admin/patch/impl/DmPermissionsPatch.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.admin.patch.impl; + +import java.util.Map; + +import org.alfresco.i18n.I18NUtil; +import org.alfresco.repo.admin.patch.AbstractPatch; +import org.alfresco.repo.domain.AccessControlListDAO; +import org.alfresco.repo.domain.hibernate.AclDaoComponentImpl; +import org.alfresco.repo.security.permissions.ACLType; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; + +/** + * Migrate permissions from the OLD format to defining, shared and layered + */ +public class DmPermissionsPatch extends AbstractPatch +{ + + private static final String MSG_SUCCESS = "patch.updateDmPermissions.result"; + + private AccessControlListDAO accessControlListDao; + + private AclDaoComponentImpl aclDaoComponent; + + @Override + protected String applyInternal() throws Exception + { + Thread progressThread = null; + if (aclDaoComponent.supportsProgressTracking()) + { + Long toDo = aclDaoComponent.getDmNodeCount(); + Long maxId = aclDaoComponent.getMaxAclId(); + + progressThread = new Thread(new ProgressWatcher(toDo, maxId), "DMPatchProgressWatcher"); + progressThread.start(); + } + + Map summary = accessControlListDao.patchAcls(); + + if (progressThread != null) + { + progressThread.interrupt(); + progressThread.join(); + } + + // build the result message + String msg = I18NUtil.getMessage(MSG_SUCCESS, summary.get(ACLType.DEFINING)); + // done + return msg; + } + + /** + * Set the access control list dao + * + * @param accessControlListDao + */ + public void setAccessControlListDao(AccessControlListDAO accessControlListDao) + { + this.accessControlListDao = accessControlListDao; + } + + /** + * Set the acl dao component + * @param aclDaoComponent + */ + public void setAclDaoComponent(AclDaoComponentImpl aclDaoComponent) + { + this.aclDaoComponent = aclDaoComponent; + } + + + private class ProgressWatcher implements Runnable + { + private boolean running = true; + + Long toDo; + + Long max; + + ProgressWatcher(Long toDo, Long max) + { + this.toDo = toDo; + this.max = max; + } + + public void run() + { + while (running) + { + try + { + Thread.sleep(60000); + } + catch (InterruptedException e) + { + running = false; + } + + if (running) + { + RetryingTransactionHelper txHelper = transactionService.getRetryingTransactionHelper(); + txHelper.setMaxRetries(1); + Long done = txHelper.doInTransaction(new RetryingTransactionCallback() + { + + public Long execute() throws Throwable + { + return aclDaoComponent.getDmNodeCountWithNewACLS(max); + } + }, true, true); + + reportProgress(toDo, done); + } + } + } + + } + +} diff --git a/source/java/org/alfresco/repo/domain/hibernate/AVMAccessControlListDAO.java b/source/java/org/alfresco/repo/domain/hibernate/AVMAccessControlListDAO.java index dae3cac2e9..e0bb14810d 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/AVMAccessControlListDAO.java +++ b/source/java/org/alfresco/repo/domain/hibernate/AVMAccessControlListDAO.java @@ -83,26 +83,47 @@ public class AVMAccessControlListDAO implements AccessControlListDAO { } + /** + * Set the AVM repository + * + * @param repository + */ public void setAvmRepository(AVMRepository repository) { fAVMRepository = repository; } + /** + * Set the AVM service + * + * @param avmService + */ public void setAvmService(AVMService avmService) { fAVMService = avmService; } + /** + * Set the ACL DAO component + * + * @param aclDaoComponent + */ public void setAclDaoComponent(AclDaoComponent aclDaoComponent) { this.aclDaoComponent = aclDaoComponent; } + /** + * @param avmSnapShotTriggeredIndexingMethodInterceptor + */ public void setAvmSnapShotTriggeredIndexingMethodInterceptor(AVMSnapShotTriggeredIndexingMethodInterceptor avmSnapShotTriggeredIndexingMethodInterceptor) { this.avmSnapShotTriggeredIndexingMethodInterceptor = avmSnapShotTriggeredIndexingMethodInterceptor; } + /** + * @param hibernateSessionHelper + */ public void setHibernateSessionHelper(HibernateSessionHelper hibernateSessionHelper) { this.hibernateSessionHelper = hibernateSessionHelper; @@ -161,6 +182,7 @@ public class AVMAccessControlListDAO implements AccessControlListDAO // to the path stuffed in the NodeRef. Pair avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef); String path = avmVersionPath.getSecond(); + @SuppressWarnings("unused") List result = new ArrayList(); String[] splitPath = AVMNodeConverter.SplitBase(path); if (splitPath[0] == null) @@ -588,6 +610,16 @@ public class AVMAccessControlListDAO implements AccessControlListDAO } } + /** + * Set and cascade ACls + * + * @param descriptor + * @param mergeFrom + * @param changes + * @param mode + * @param set + * @param indirections + */ public void setFixedAcls(AVMNodeDescriptor descriptor, Long mergeFrom, List changes, SetMode mode, boolean set, Map> indirections) { if (descriptor == null) @@ -691,9 +723,21 @@ public class AVMAccessControlListDAO implements AccessControlListDAO } } + /** + * Mode to sue when setting ACLs + * @author andyh + * + */ private enum SetMode { - ALL, DIRECT_ONLY; + /** + * Set ALL + */ + ALL, + /** + * Set only direct children (not those present by layering) + */ + DIRECT_ONLY; } public Map patchAcls() @@ -893,8 +937,19 @@ public class AVMAccessControlListDAO implements AccessControlListDAO return result; } - private class CounterSet extends HashMap + /** + * + * Counter for each type of ACL change + * @author andyh + * + */ + public static class CounterSet extends HashMap { + /** + * + */ + private static final long serialVersionUID = -3682278258679211481L; + CounterSet() { super(); @@ -929,7 +984,12 @@ public class AVMAccessControlListDAO implements AccessControlListDAO } } - private class Counter + /** + * Simple counter + * @author andyh + * + */ + public static class Counter { int counter; diff --git a/source/java/org/alfresco/repo/domain/hibernate/AbstractPermissionsDaoComponentImpl.java b/source/java/org/alfresco/repo/domain/hibernate/AbstractPermissionsDaoComponentImpl.java index 4f8f5e2445..d547b1a8ba 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/AbstractPermissionsDaoComponentImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/AbstractPermissionsDaoComponentImpl.java @@ -37,6 +37,7 @@ import org.alfresco.repo.security.permissions.ACEType; 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.NodePermissionEntry; import org.alfresco.repo.security.permissions.PermissionEntry; import org.alfresco.repo.security.permissions.PermissionReference; @@ -57,6 +58,16 @@ import org.alfresco.util.GUID; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +/** + * Common suppot for permisisons dao + * + * Sub classes deteremine how ACLs are cascaded to children and how changes may COW/version children as ACLs are pushed down. + * + * TODO: remove the protocol to dao mapping + * + * @author andyh + * + */ public abstract class AbstractPermissionsDaoComponentImpl implements PermissionsDaoComponent, TransactionalDao { private static Log logger = LogFactory.getLog(AbstractPermissionsDaoComponentImpl.class); @@ -77,11 +88,19 @@ public abstract class AbstractPermissionsDaoComponentImpl implements Permissions this.uuid = GUID.generate(); } + /** + * Get the ACL DAO component + * @return - the acl dao component + */ public AclDaoComponent getAclDaoComponent() { return aclDaoComponent; } + /** + * Set the ACL DAO component + * @param aclDaoComponent + */ public void setAclDaoComponent(AclDaoComponent aclDaoComponent) { this.aclDaoComponent = aclDaoComponent; @@ -138,11 +157,19 @@ public abstract class AbstractPermissionsDaoComponentImpl implements Permissions aclDaoComponent.beforeCommit(); } + /** + * Set the mapping of protocol to DAO + * @param map + */ public void setProtocolToACLDAO(Map map) { fProtocolToACLDAO = map; } + /** + * Set the default DAO + * @param defaultACLDAO + */ public void setDefaultACLDAO(AccessControlListDAO defaultACLDAO) { fDefaultACLDAO = defaultACLDAO; @@ -435,6 +462,8 @@ public abstract class AbstractPermissionsDaoComponentImpl implements Permissions deletePermissions(nodeRef); } // create the access control list + + existing = getAccessControlList(nodeRef); CreationReport report = createAccessControlList(nodeRef, nodePermissionEntry.inheritPermissions(), existing); // add all entries @@ -594,8 +623,26 @@ public abstract class AbstractPermissionsDaoComponentImpl implements Permissions return npe; } + + + public AccessControlListProperties getAccessControlListProperties(NodeRef nodeRef) + { + DbAccessControlList acl = getACLDAO(nodeRef).getAccessControlList(nodeRef); + if(acl == null) + { + return null; + } + return aclDaoComponent.getAccessControlListProperties(acl.getId()); + } + protected abstract CreationReport createAccessControlList(NodeRef nodeRef, boolean inherit, DbAccessControlList existing); + + /** + * Internal class used for reporting the collateral damage when creating an new ACL entry + * @author andyh + * + */ static class CreationReport { DbAccessControlList created; @@ -608,21 +655,38 @@ public abstract class AbstractPermissionsDaoComponentImpl implements Permissions this.changes = changes; } + /** + * Set the change list + * + * @param changes + */ public void setChanges(List changes) { this.changes = changes; } + /** + * Set the ACL that was created + * @param created + */ public void setCreated(DbAccessControlList created) { this.created = created; } + /** + * Get the change list + * @return - the change list + */ public List getChanges() { return changes; } + /** + * Get the created ACL + * @return - the acl + */ public DbAccessControlList getCreated() { return created; diff --git a/source/java/org/alfresco/repo/domain/hibernate/AclDaoComponentImpl.java b/source/java/org/alfresco/repo/domain/hibernate/AclDaoComponentImpl.java index e77e33cf8e..0631c64777 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/AclDaoComponentImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/AclDaoComponentImpl.java @@ -41,6 +41,7 @@ import org.alfresco.repo.domain.DbAccessControlListChangeSet; import org.alfresco.repo.domain.DbAccessControlListMember; import org.alfresco.repo.domain.DbAuthority; import org.alfresco.repo.domain.DbPermission; +import org.alfresco.repo.domain.Node; import org.alfresco.repo.domain.QNameDAO; import org.alfresco.repo.domain.QNameEntity; import org.alfresco.repo.node.db.hibernate.HibernateNodeDaoServiceImpl; @@ -62,10 +63,10 @@ import org.alfresco.service.namespace.QName; import org.alfresco.util.GUID; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.hibernate.HibernateException; +import org.hibernate.Criteria; import org.hibernate.Query; import org.hibernate.Session; -import org.jgroups.tests.DeadlockTest.InRpc; +import org.hibernate.criterion.Restrictions; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; @@ -112,23 +113,59 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo private enum WriteMode { - TRUNCATE_INHERITED, ADD_INHERITED, CHANGE_INHERITED, REMOVE_INHERITED, INSERT_INHERITED, COPY_UPDATE_AND_INHERIT, COPY_ONLY; + /** + * 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, + /** + * Simlpe copy + */ + COPY_ONLY; } + /** + * + */ public AclDaoComponentImpl() { super(); + // Wire up for annoying AVM hack to support copy and setting of ACLs as nodes are created DbAccessControlListImpl.setAclDaoComponent(this); } /** * Set the DAO for accessing QName entities + * @param qnameDAO */ public void setQnameDAO(QNameDAO qnameDAO) { this.qnameDAO = qnameDAO; } + /** + * Set the ACL cache + * @param aclCache + */ public void setAclCache(SimpleCache aclCache) { this.aclCache = aclCache; @@ -344,7 +381,7 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo * @param toAdd * @param inheritsFrom * @param depth - * @return + * @return - an AclChange */ @SuppressWarnings("unchecked") private AclChange getWritable(final Long id, final Long parent, AccessControlEntry exclude, List toAdd, Long inheritsFrom, @@ -814,9 +851,26 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo @SuppressWarnings("unchecked") public List deleteAccessControlList(final Long id) { + HibernateCallback check = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Criteria criteria = getSession().createCriteria(NodeImpl.class, "node"); + criteria.createAlias("node.accessControlList", "acl"); + criteria.add(Restrictions.eq("acl.id", id)); + criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); + return criteria.list(); + } + }; + List nodes = (List) getHibernateTemplate().execute(check); + for(Node node : nodes) + { + logger.error("Found "+node.getId() +" "+node.getUuid() + " "+node.getAccessControlList() ); + } + List acls = new ArrayList(); - DbAccessControlList acl = (DbAccessControlList) getHibernateTemplate().get(DbAccessControlListImpl.class, id); + final DbAccessControlList acl = (DbAccessControlList) getHibernateTemplate().get(DbAccessControlListImpl.class, id); if (!acl.isLatest()) { throw new UnsupportedOperationException("Old ALC versions can not be updated"); @@ -828,7 +882,7 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo if ((acl.getAclType() == ACLType.DEFINING) || (acl.getAclType() == ACLType.LAYERED)) { - if (acl.getInheritedAclId() != -1) + if ((acl.getInheritedAclId() != null) && (acl.getInheritedAclId() != -1)) { final DbAccessControlList inherited = (DbAccessControlList) getHibernateTemplate().get(DbAccessControlListImpl.class, acl.getInheritedAclId()); // Will remove from the cache @@ -929,6 +983,7 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo } getHibernateTemplate().delete(acl); + getSession().flush(); } // remove the deleted acl from the cache @@ -965,6 +1020,11 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo return changes; } + /** + * Search for access control lists + * @param pattern + * @return the ids of the ACLs found + */ public Long[] findAccessControlList(AccessControlEntry pattern) { throw new UnsupportedOperationException(); @@ -986,6 +1046,10 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo return acl; } + /** + * @param id + * @return the access control list + */ @SuppressWarnings("unchecked") public AccessControlList getAccessControlListImpl(final Long id) { @@ -1025,7 +1089,7 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo entry.setContext(context); } DbPermission perm = member.getAccessControlEntry().getPermission(); - SimplePermissionReference permissionRefernce = new SimplePermissionReference(perm.getTypeQName().getQName(), perm.getName()); + SimplePermissionReference permissionRefernce = SimplePermissionReference.getPermissionReference(perm.getTypeQName().getQName(), perm.getName()); entry.setPermission(permissionRefernce); entry.setPosition(member.getPosition()); @@ -1054,6 +1118,7 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo properties.setInherits(acl.getInherits()); properties.setLatest(acl.isLatest()); properties.setVersioned(acl.isVersioned()); + properties.setId(id); return properties; } @@ -1199,8 +1264,8 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo } @SuppressWarnings("unchecked") - public List setAccessControlEntry(Long id, final AccessControlEntry ace) - { + public List setAccessControlEntry(final Long id, final AccessControlEntry ace) + { DbAccessControlList target = (DbAccessControlList) getHibernateTemplate().get(DbAccessControlListImpl.class, id); if (target.getAclType() == ACLType.SHARED) { @@ -1595,7 +1660,7 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo entry.setContext(context); } DbPermission perm = member.getAccessControlEntry().getPermission(); - SimplePermissionReference permissionRefernce = new SimplePermissionReference(perm.getTypeQName().getQName(), perm.getName()); + SimplePermissionReference permissionRefernce = SimplePermissionReference.getPermissionReference(perm.getTypeQName().getQName(), perm.getName()); entry.setPermission(permissionRefernce); entry.setPosition(Integer.valueOf(0)); @@ -1775,11 +1840,17 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo return before; } + /** + * @param after + */ public void setAfter(Long after) { this.after = after; } + /** + * @param before + */ public void setBefore(Long before) { this.before = before; @@ -1790,6 +1861,9 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo return typeAfter; } + /** + * @param typeAfter + */ public void setTypeAfter(ACLType typeAfter) { this.typeAfter = typeAfter; @@ -1800,6 +1874,9 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo return typeBefore; } + /** + * @param typeBefore + */ public void setTypeBefore(ACLType typeBefore) { this.typeBefore = typeBefore; @@ -1820,7 +1897,7 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo /** * Get the total number of head nodes in the repository * - * @return + * @return count */ public Long getAVMHeadNodeCount() { @@ -1847,6 +1924,10 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo } + /** + * Get the max acl id + * @return - max acl id + */ public Long getMaxAclId() { try @@ -1871,6 +1952,11 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo } } + /** + * Does the underlyinf connection support isolation level 1 (dirty read) + * + * @return true if we can do a dirty db read and so track changes (Oracle can not) + */ public boolean supportsProgressTracking() { try @@ -1885,6 +1971,11 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo } + /** + * Get the acl count canges so far for progress tracking + * @param above + * @return - the count + */ public Long getAVMNodeCountWithNewACLS(Long above) { try @@ -1910,6 +2001,10 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo } } + /** + * How many nodes are noew in store (approximate) + * @return - the number fo new nodes - approximate + */ public Long getNewInStore() { HibernateCallback callback = new HibernateCallback() @@ -1924,6 +2019,13 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo return count; } + + /** + * Find layered directories + * Used to imporove performance during patching and cascading the effect fo permission changes between layers + * + * @return - layered directories + */ @SuppressWarnings("unchecked") public List getLayeredDirectories() { @@ -1947,6 +2049,13 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo return indirections; } + /** + * Find layered files + * + * Used to imporove performance during patching and cascading the effect fo permission changes between layers + * + * @return - layerd files + */ @SuppressWarnings("unchecked") public List getLayeredFiles() { @@ -1985,6 +2094,11 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo getSession().flush(); } + /** + * Support to describe AVM indirections for permission performance improvements when permissions are set. + * @author andyh + * + */ public static class Indirection { Long from; @@ -2000,16 +2114,25 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo this.toVersion = toVersion; } + /** + * @return - from id + */ public Long getFrom() { return from; } + /** + * @return - to id + */ public String getTo() { return to; } + /** + * @return - version + */ public Integer getToVersion() { return toVersion; @@ -2017,4 +2140,64 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo } + /** + * Ho many DM nodes are there? + * + * @return - the count + */ + public Long getDmNodeCount() + { + try + { + Session session = getSession(); + int isolationLevel = session.connection().getTransactionIsolation(); + try + { + session.connection().setTransactionIsolation(1); + Query query = getSession().getNamedQuery("permission.GetDmNodeCount"); + Long answer = (Long) query.uniqueResult(); + return answer; + } + finally + { + session.connection().setTransactionIsolation(isolationLevel); + } + } + catch (SQLException e) + { + throw new AlfrescoRuntimeException("Failed to set TX isolation level", e); + } + + } + + /** + * How many DM nodes are three with new ACls (to track patch progress) + * @param above + * @return - the count + */ + public Long getDmNodeCountWithNewACLS(Long above) + { + try + { + Session session = getSession(); + int isolationLevel = session.connection().getTransactionIsolation(); + try + { + session.connection().setTransactionIsolation(1); + Query query = getSession().getNamedQuery("permission.GetDmNodeCountWherePermissionsHaveChanged"); + query.setParameter("above", above); + Long answer = (Long) query.uniqueResult(); + return answer; + } + finally + { + session.connection().setTransactionIsolation(isolationLevel); + } + } + catch (SQLException e) + { + throw new AlfrescoRuntimeException("Failed to set TX isolation level", e); + } + } + } diff --git a/source/java/org/alfresco/repo/domain/hibernate/DMAccessControlListDAO.java b/source/java/org/alfresco/repo/domain/hibernate/DMAccessControlListDAO.java new file mode 100644 index 0000000000..d6d8818e68 --- /dev/null +++ b/source/java/org/alfresco/repo/domain/hibernate/DMAccessControlListDAO.java @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.domain.hibernate; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.domain.AccessControlListDAO; +import org.alfresco.repo.domain.ChildAssoc; +import org.alfresco.repo.domain.DbAccessControlList; +import org.alfresco.repo.domain.Node; +import org.alfresco.repo.domain.hibernate.AVMAccessControlListDAO.CounterSet; +import org.alfresco.repo.node.db.NodeDaoService; +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.SimpleAccessControlListProperties; +import org.alfresco.repo.security.permissions.impl.AclChange; +import org.alfresco.repo.security.permissions.impl.AclDaoComponent; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; + +/** + * DAO layer for the improved ACL implemtentation. + * + * This layer is responsible for setting ACLs and any cascade behaviour required. + * It also implements the migration from the old implementation to the new. + * + * @author andyh + * + */ +public class DMAccessControlListDAO implements AccessControlListDAO +{ + /** + * The DAO for Nodes. + */ + private NodeDaoService nodeDaoService; + + private NodeService nodeService; + + private AclDaoComponent aclDaoComponent; + + private HibernateSessionHelper hibernateSessionHelper; + + /** + * Set the node dao service + * + * @param nodeDaoService + */ + public void setNodeDaoService(NodeDaoService nodeDaoService) + { + this.nodeDaoService = nodeDaoService; + } + + /** + * Set the ACL DAO components + * @param aclDaoComponent + */ + public void setAclDaoComponent(AclDaoComponent aclDaoComponent) + { + this.aclDaoComponent = aclDaoComponent; + } + + /** + * Set the hibernate session helper for session size management + * + * @param hibernateSessionHelper + */ + public void setHibernateSessionHelper(HibernateSessionHelper hibernateSessionHelper) + { + this.hibernateSessionHelper = hibernateSessionHelper; + } + + /** + * Set the node service. + * @param nodeService + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void forceCopy(NodeRef nodeRef) + { + // Nothing to do + } + + public DbAccessControlList getAccessControlList(NodeRef nodeRef) + { + Node node = nodeDaoService.getNode(nodeRef); + if (node == null) + { + throw new InvalidNodeRefException(nodeRef); + } + return node.getAccessControlList(); + } + + public DbAccessControlList getAccessControlList(StoreRef storeRef) + { + return null; + } + + public Long getIndirectAcl(NodeRef nodeRef) + { + return getAccessControlList(nodeRef).getId(); + } + + public Long getInheritedAcl(NodeRef nodeRef) + { + Node node = nodeDaoService.getNode(nodeRef); + ChildAssoc ca = nodeDaoService.getPrimaryParentAssoc(node); + if ((ca != null) && (ca.getParent() != null)) + { + DbAccessControlList acl = getAccessControlList(ca.getParent().getNodeRef()); + if (acl != null) + { + return acl.getId(); + } + else + { + return null; + } + } + else + { + return null; + } + } + + public Map patchAcls() + { + CounterSet result = new CounterSet(); + List stores = nodeService.getStores(); + + for (StoreRef store : stores) + { + @SuppressWarnings("unused") + CounterSet update; + update = fixOldDmAcls(nodeService.getRootNode(store)); + } + + HashMap toReturn = new HashMap(); + toReturn.put(ACLType.DEFINING, Integer.valueOf(result.get(ACLType.DEFINING).getCounter())); + toReturn.put(ACLType.FIXED, Integer.valueOf(result.get(ACLType.FIXED).getCounter())); + toReturn.put(ACLType.GLOBAL, Integer.valueOf(result.get(ACLType.GLOBAL).getCounter())); + toReturn.put(ACLType.LAYERED, Integer.valueOf(result.get(ACLType.LAYERED).getCounter())); + toReturn.put(ACLType.OLD, Integer.valueOf(result.get(ACLType.OLD).getCounter())); + toReturn.put(ACLType.SHARED, Integer.valueOf(result.get(ACLType.SHARED).getCounter())); + return toReturn; + } + + private CounterSet fixOldDmAcls(NodeRef nodeRef) + { + hibernateSessionHelper.mark(); + try + { + return fixOldDmAclsImpl(nodeRef); + } + finally + { + hibernateSessionHelper.resetAndRemoveMark(); + } + } + + private CounterSet fixOldDmAclsImpl(NodeRef nodeRef) + { + CounterSet result = new CounterSet(); + // Do the children first + + for (ChildAssociationRef child : nodeService.getChildAssocs(nodeRef)) + { + CounterSet update = fixOldDmAcls(child.getChildRef()); + result.add(update); + } + + DbAccessControlList existingAcl = getAccessControlList(nodeRef); + + if (existingAcl != null) + { + if (existingAcl.getAclType() == ACLType.OLD) + { + result.increment(ACLType.DEFINING); + // + SimpleAccessControlListProperties properties = DMPermissionsDaoComponentImpl.getDefaultProperties(); + // Accept default versioning + Long id = aclDaoComponent.createAccessControlList(properties); + DbAccessControlList newAcl = aclDaoComponent.getDbAccessControlList(id); + + AccessControlList existing = aclDaoComponent.getAccessControlList(existingAcl.getId()); + for (AccessControlEntry entry : existing.getEntries()) + { + if (entry.getPosition() == 0) + { + aclDaoComponent.setAccessControlEntry(id, entry); + } + } + setAccessControlList(nodeRef, newAcl); + + // Cascade to children - changes should all be 1-1 so we do not have to post fix + + List changes = new ArrayList(); + + setFixedAcls(nodeRef, aclDaoComponent.getInheritedAccessControlList(id), changes, false); + + } + else + { + // Already fixed up :-) + } + } + + return result; + } + + public void setAccessControlList(NodeRef nodeRef, DbAccessControlList acl) + { + Node node = nodeDaoService.getNode(nodeRef); + if (node == null) + { + throw new InvalidNodeRefException(nodeRef); + } + node.setAccessControlList(acl); + } + + public void setAccessControlList(StoreRef storeRef, DbAccessControlList acl) + { + throw new UnsupportedOperationException(); + } + + public List setInheritanceForChildren(NodeRef parent, Long mergeFrom) + { + List changes = new ArrayList(); + setFixedAcls(parent, mergeFrom, changes, false); + return changes; + } + + public void updateChangedAcls(NodeRef startingPoint, List changes) + { + // Nothing to do: no nodes change as a result of ACL changes + } + + /** + * Support to set ACLs and cascade fo required + * + * @param nodeRef + * @param mergeFrom + * @param changes + * @param set + */ + public void setFixedAcls(NodeRef nodeRef, Long mergeFrom, List changes, boolean set) + { + if (nodeRef == null) + { + return; + } + else + { + if (set) + { + setAccessControlList(nodeRef, aclDaoComponent.getDbAccessControlList(mergeFrom)); + } + + List children = nodeService.getChildAssocs(nodeRef); + + for (ChildAssociationRef child : children) + { + DbAccessControlList acl = getAccessControlList(child.getChildRef()); + + if (acl == null) + { + hibernateSessionHelper.mark(); + try + { + setFixedAcls(child.getChildRef(), mergeFrom, changes, true); + } + finally + { + hibernateSessionHelper.resetAndRemoveMark(); + } + } + else if (acl.getAclType() == ACLType.LAYERED) + { + throw new UnsupportedOperationException(); + } + else if (acl.getAclType() == ACLType.DEFINING) + { + @SuppressWarnings("unused") + List newChanges = aclDaoComponent.mergeInheritedAccessControlList(mergeFrom, acl.getId()); + } + else + { + hibernateSessionHelper.mark(); + try + { + setFixedAcls(child.getChildRef(), mergeFrom, changes, true); + } + finally + { + hibernateSessionHelper.resetAndRemoveMark(); + } + } + + } + } + + } + + /** + * Static support to set ACLs - required for use by the dbNodeService + * + * @param nodeRef + * @param mergeFrom + * @param set + * @param nodeService + * @param aclDaoComponent + * @param nodeDaoService + */ + public static void setFixedAcls(NodeRef nodeRef, Long mergeFrom, boolean set, NodeService nodeService, AclDaoComponent aclDaoComponent, NodeDaoService nodeDaoService) + { + if (nodeRef == null) + { + return; + } + else + { + if (set) + { + setAccessControlList(nodeRef, aclDaoComponent.getDbAccessControlList(mergeFrom), nodeDaoService); + } + + List children = nodeService.getChildAssocs(nodeRef); + + for (ChildAssociationRef child : children) + { + DbAccessControlList acl = getAccessControlList(child.getChildRef(), nodeDaoService); + + if (acl == null) + { + setFixedAcls(child.getChildRef(), mergeFrom, true, nodeService, aclDaoComponent, nodeDaoService); + } + else if (acl.getAclType() == ACLType.LAYERED) + { + throw new UnsupportedOperationException(); + } + else if (acl.getAclType() == ACLType.DEFINING) + { + @SuppressWarnings("unused") + List newChanges = aclDaoComponent.mergeInheritedAccessControlList(mergeFrom, acl.getId()); + } + else + { + setFixedAcls(child.getChildRef(), mergeFrom, true, nodeService, aclDaoComponent, nodeDaoService); + } + } + } + } + + private static DbAccessControlList getAccessControlList(NodeRef nodeRef, NodeDaoService nodeDaoService) + { + Node node = nodeDaoService.getNode(nodeRef); + if (node == null) + { + throw new InvalidNodeRefException(nodeRef); + } + return node.getAccessControlList(); + } + + private static void setAccessControlList(NodeRef nodeRef, DbAccessControlList acl, NodeDaoService nodeDaoService) + { + Node node = nodeDaoService.getNode(nodeRef); + if (node == null) + { + throw new InvalidNodeRefException(nodeRef); + } + node.setAccessControlList(acl); + } + +} diff --git a/source/java/org/alfresco/repo/domain/hibernate/DMPermissionsDaoComponentImpl.java b/source/java/org/alfresco/repo/domain/hibernate/DMPermissionsDaoComponentImpl.java new file mode 100644 index 0000000000..c449b9005a --- /dev/null +++ b/source/java/org/alfresco/repo/domain/hibernate/DMPermissionsDaoComponentImpl.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.domain.hibernate; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.alfresco.repo.domain.DbAccessControlList; +import org.alfresco.repo.security.permissions.ACLType; +import org.alfresco.repo.security.permissions.SimpleAccessControlListProperties; +import org.alfresco.repo.security.permissions.impl.AclChange; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Manage creation and deletion of ACL entries for the new DM ACL implementation + * + * @author andyh + * + */ +public class DMPermissionsDaoComponentImpl extends AbstractPermissionsDaoComponentImpl +{ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(DMPermissionsDaoComponentImpl.class); + + @Override + protected CreationReport createAccessControlList(NodeRef nodeRef, boolean inherit, DbAccessControlList existing) + { + if (existing == null) + { + SimpleAccessControlListProperties properties = new SimpleAccessControlListProperties(); + properties.setAclType(ACLType.DEFINING); + properties.setInherits(inherit); + properties.setVersioned(false); + // Accept default versioning + Long id = aclDaoComponent.createAccessControlList(properties); + List changes = new ArrayList(); + DbAccessControlList acl = aclDaoComponent.getDbAccessControlList(id); + changes.add(new AclDaoComponentImpl.AclChangeImpl(null, id, null, acl.getAclType())); + changes.addAll(getACLDAO(nodeRef).setInheritanceForChildren(nodeRef, aclDaoComponent.getInheritedAccessControlList(id))); + getACLDAO(nodeRef).setAccessControlList(nodeRef, acl); + return new CreationReport(acl, changes); + } + SimpleAccessControlListProperties properties; + Long id; + List changes; + DbAccessControlList acl; + switch (existing.getAclType()) + { + case OLD: + throw new IllegalStateException("Can not mix old and new style permissions"); + case DEFINING: + return new CreationReport(existing, Collections. emptyList()); + case FIXED: + case GLOBAL: + case SHARED: + // create new defining, wire up and report changes to acl required. + properties = new SimpleAccessControlListProperties(); + properties.setAclType(ACLType.DEFINING); + properties.setInherits(existing.getInherits()); + properties.setVersioned(false); + id = aclDaoComponent.createAccessControlList(properties); + changes = new ArrayList(); + acl = aclDaoComponent.getDbAccessControlList(id); + changes.add(new AclDaoComponentImpl.AclChangeImpl(existing.getId(), id, existing.getAclType(), acl.getAclType())); + changes.addAll(aclDaoComponent.mergeInheritedAccessControlList(existing.getId(), id)); + // set this to inherit to children + changes.addAll(getACLDAO(nodeRef).setInheritanceForChildren(nodeRef, aclDaoComponent.getInheritedAccessControlList(id))); + + getACLDAO(nodeRef).setAccessControlList(nodeRef, acl); + return new CreationReport(acl, changes); + case LAYERED: + throw new IllegalStateException("Layering is not supported for DM permissions"); + default: + throw new IllegalStateException("Unknown type " + existing.getAclType()); + } + + } + + public void deletePermissions(NodeRef nodeRef) + { + DbAccessControlList acl = null; + try + { + acl = getAccessControlList(nodeRef); + } + catch (InvalidNodeRefException e) + { + return; + } + System.out.println("Deleting "+acl+" on "+nodeRef); + if (acl != null) + { + if (acl.getInheritsFrom() != null) + { + @SuppressWarnings("unused") + Long deleted = acl.getId(); + Long inheritsFrom = acl.getInheritsFrom(); + getACLDAO(nodeRef).setAccessControlList(nodeRef, aclDaoComponent.getDbAccessControlList(inheritsFrom)); + List changes = new ArrayList(); + changes.addAll(getACLDAO(nodeRef).setInheritanceForChildren(nodeRef, inheritsFrom)); + getACLDAO(nodeRef).updateChangedAcls(nodeRef, changes); + aclDaoComponent.deleteAccessControlList(acl.getId()); + } + else + { + // TODO: could just cear out existing + @SuppressWarnings("unused") + Long deleted = acl.getId(); + SimpleAccessControlListProperties properties = new SimpleAccessControlListProperties(); + properties = new SimpleAccessControlListProperties(); + properties.setAclType(ACLType.DEFINING); + properties.setInherits(Boolean.FALSE); + properties.setVersioned(false); + + Long id = aclDaoComponent.createAccessControlList(properties); + getACLDAO(nodeRef).setAccessControlList(nodeRef, aclDaoComponent.getDbAccessControlList(id)); + List changes = new ArrayList(); + changes.addAll(getACLDAO(nodeRef).setInheritanceForChildren(nodeRef, aclDaoComponent.getInheritedAccessControlList(id))); + getACLDAO(nodeRef).updateChangedAcls(nodeRef, changes); + aclDaoComponent.deleteAccessControlList(acl.getId()); + } + } + + } + + + /** + * Get the default ACL properties + * + * @return the default properties + */ + public static SimpleAccessControlListProperties getDefaultProperties() + { + SimpleAccessControlListProperties properties = new SimpleAccessControlListProperties(); + properties.setAclType(ACLType.DEFINING); + properties.setInherits(true); + properties.setVersioned(false); + return properties; + } + +} diff --git a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml index b26f404e1f..661bd60506 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml +++ b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml @@ -62,7 +62,7 @@ fetch="select" unique="false" not-null="false" - cascade="delete" /> + cascade="none" /> singletonList(new AclDaoComponentImpl.AclChangeImpl(null, id, null, acl.getAclType()))); + + AbstractPermissionsDaoComponentImpl.CreationReport report = new AbstractPermissionsDaoComponentImpl.CreationReport(acl, Collections + . singletonList(new AclDaoComponentImpl.AclChangeImpl(null, id, null, acl.getAclType()))); return report; - + } public void deletePermissions(NodeRef nodeRef) @@ -96,7 +97,4 @@ public class OldADMPermissionsDaoComponentImpl extends AbstractPermissionsDaoCom aclDaoComponent.deleteAccessControlList(acl.getId()); } } - - - } diff --git a/source/java/org/alfresco/repo/domain/hibernate/Permission.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/Permission.hbm.xml index c4c4d4bc57..209534f3c5 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/Permission.hbm.xml +++ b/source/java/org/alfresco/repo/domain/hibernate/Permission.hbm.xml @@ -433,6 +433,21 @@ + + + + + + :above + ]]> + +