diff --git a/config/alfresco/ehcache-default.xml b/config/alfresco/ehcache-default.xml index 0c3902f42c..84015637eb 100644 --- a/config/alfresco/ehcache-default.xml +++ b/config/alfresco/ehcache-default.xml @@ -151,14 +151,14 @@ - + @@ -167,9 +167,15 @@ maxElementsInMemory="25000" eternal="true" overflowToDisk="false"/> + + diff --git a/source/java/org/alfresco/repo/domain/DbAccessControlEntry.java b/source/java/org/alfresco/repo/domain/DbAccessControlEntry.java index 9983a57bc8..2df59c8b59 100644 --- a/source/java/org/alfresco/repo/domain/DbAccessControlEntry.java +++ b/source/java/org/alfresco/repo/domain/DbAccessControlEntry.java @@ -72,4 +72,10 @@ public interface DbAccessControlEntry * @param allowed */ public void setAllowed(boolean allowed); + + /** + * Helper method to delete the instance and make sure that all + * inverse associations are properly maintained. + */ + public void delete(); } diff --git a/source/java/org/alfresco/repo/domain/DbAccessControlList.java b/source/java/org/alfresco/repo/domain/DbAccessControlList.java index 891c98b77f..dad02839a7 100644 --- a/source/java/org/alfresco/repo/domain/DbAccessControlList.java +++ b/source/java/org/alfresco/repo/domain/DbAccessControlList.java @@ -16,6 +16,10 @@ */ package org.alfresco.repo.domain; +import java.util.Set; + +import org.alfresco.repo.domain.hibernate.DbAccessControlEntryImpl; + /** * The interface to support persistence of node access control entries in hibernate @@ -30,17 +34,29 @@ public interface DbAccessControlList public void setNode(Node node); + /** + * + * @return Returns the access control entries for this access control list + */ + public Set getEntries(); + /** * Get inheritance behaviour - * @return + * @return Returns the inheritance status of this list */ public boolean getInherits(); /** * Set inheritance behaviour - * @param inherits + * @param inherits true to set the permissions to inherit */ public void setInherits(boolean inherits); + + public int deleteEntriesForAuthority(String authorityKey); + + public int deleteEntriesForPermission(DbPermissionKey permissionKey); + + public int deleteEntry(String authorityKey, DbPermissionKey permissionKey); /** * Delete the entries related to this access control list @@ -48,4 +64,18 @@ public interface DbAccessControlList * @return Returns the number of entries deleted */ public int deleteEntries(); + + public DbAccessControlEntry getEntry(String authorityKey, DbPermissionKey permissionKey); + + /** + * Factory method to create an entry and wire it up. + * Note that the returned value may still be transient. Saving it should be fine, but + * is not required. + * + * @param permission the mandatory permission association with this entry + * @param authority the mandatory authority. Must not be transient. + * @param allowed allowed or disallowed. Must not be transient. + * @return Returns the new entry + */ + public DbAccessControlEntryImpl newEntry(DbPermission permission, DbAuthority authority, boolean allowed); } diff --git a/source/java/org/alfresco/repo/domain/DbPermission.java b/source/java/org/alfresco/repo/domain/DbPermission.java index d1e741c2b5..5973c813ec 100644 --- a/source/java/org/alfresco/repo/domain/DbPermission.java +++ b/source/java/org/alfresco/repo/domain/DbPermission.java @@ -51,4 +51,10 @@ public interface DbPermission extends Serializable * @param name the name of the permission */ public void setName(String name); + + /** + * @return Returns a key combining the {@link #getTypeQname() type} + * and {@link #getName() name} + */ + public DbPermissionKey getKey(); } diff --git a/source/java/org/alfresco/repo/domain/DbPermissionKey.java b/source/java/org/alfresco/repo/domain/DbPermissionKey.java new file mode 100644 index 0000000000..654b37d8cd --- /dev/null +++ b/source/java/org/alfresco/repo/domain/DbPermissionKey.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.domain; + +import java.io.Serializable; + +import org.alfresco.service.namespace.QName; +import org.alfresco.util.EqualsHelper; + +/** + * Compound key for persistence of {@link org.alfresco.repo.domain.DbPermission}. + * + * @author Derek Hulley + */ +public class DbPermissionKey implements Serializable +{ + private static final long serialVersionUID = -1667797216480779296L; + + private QName typeQname; + private String name; + + public DbPermissionKey() + { + } + + public DbPermissionKey(QName typeQname, String name) + { + this.typeQname = typeQname; + this.name = name; + } + + public String toString() + { + return ("DbPermissionKey" + + "[ type=" + typeQname + + ", name=" + name + + "]"); + } + + public int hashCode() + { + return this.name.hashCode(); + } + + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else if (!(obj instanceof DbPermissionKey)) + { + return false; + } + DbPermissionKey that = (DbPermissionKey) obj; + return (EqualsHelper.nullSafeEquals(this.typeQname, that.typeQname) + && EqualsHelper.nullSafeEquals(this.name, that.name) + ); + } + + public QName getTypeQname() + { + return typeQname; + } + + /** + * Tamper-proof method only to be used by introspectors + */ + @SuppressWarnings("unused") + private void setTypeQname(QName typeQname) + { + this.typeQname = typeQname; + } + + public String getName() + { + return name; + } + + /** + * Tamper-proof method only to be used by introspectors + */ + @SuppressWarnings("unused") + private void setName(String name) + { + this.name = name; + } +} diff --git a/source/java/org/alfresco/repo/domain/Node.java b/source/java/org/alfresco/repo/domain/Node.java index c85eb73e07..f988b5554d 100644 --- a/source/java/org/alfresco/repo/domain/Node.java +++ b/source/java/org/alfresco/repo/domain/Node.java @@ -80,5 +80,5 @@ public interface Node public DbAccessControlList getAccessControlList(); -// public void setAccessControlList(DbAccessControlList accessControlList); + public void setAccessControlList(DbAccessControlList accessControlList); } diff --git a/source/java/org/alfresco/repo/domain/hibernate/DbAccessControlEntryImpl.java b/source/java/org/alfresco/repo/domain/hibernate/DbAccessControlEntryImpl.java index 88b2b76a0b..f9d67561dc 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/DbAccessControlEntryImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/DbAccessControlEntryImpl.java @@ -27,7 +27,7 @@ import org.alfresco.util.EqualsHelper; * * @author andyh */ -public class DbAccessControlEntryImpl implements DbAccessControlEntry +public class DbAccessControlEntryImpl extends LifecycleAdapter implements DbAccessControlEntry { /** The object id */ private long id; @@ -56,7 +56,7 @@ public class DbAccessControlEntryImpl implements DbAccessControlEntry sb.append("DbAccessControlEntryImpl") .append("[ id=").append(id) .append(", acl=").append(accessControlList.getId()) - .append(", permission=").append(permission.getId()) + .append(", permission=").append(permission.getKey()) .append(", authority=").append(authority.getRecipient()) .append("]"); return sb.toString(); @@ -74,16 +74,14 @@ public class DbAccessControlEntryImpl implements DbAccessControlEntry return false; } DbAccessControlEntry other = (DbAccessControlEntry) o; - return (this.allowed == other.isAllowed()) - && EqualsHelper.nullSafeEquals(this.accessControlList, other.getAccessControlList()) - && EqualsHelper.nullSafeEquals(this.permission, other.getPermission()) - && EqualsHelper.nullSafeEquals(this.authority, other.getAuthority()); + return (EqualsHelper.nullSafeEquals(this.permission, other.getPermission()) + && EqualsHelper.nullSafeEquals(this.authority, other.getAuthority())); } @Override public int hashCode() { - int hashCode = accessControlList.hashCode(); + int hashCode = 0; if (permission != null) { hashCode = hashCode * 37 + permission.hashCode(); @@ -92,33 +90,9 @@ public class DbAccessControlEntryImpl implements DbAccessControlEntry { hashCode = hashCode * 37 + authority.hashCode(); } - hashCode = hashCode * 37 + (allowed ? 1 : 0); return hashCode; } - - /** - * Factory method to create an entry and wire it in to the contained nodePermissionEntry - * - * @param accessControlList the list of entries that this one belongs to - * @param permission the mandatory permission association with this entry - * @param authority the mandatory authority - * @param allowed allowed or disallowed - * @return Returns an unpersisted entity - */ - public static DbAccessControlEntryImpl create( - DbAccessControlList accessControlList, - DbPermission permission, - DbAuthority authority, - boolean allowed) - { - DbAccessControlEntryImpl accessControlEntry = new DbAccessControlEntryImpl(); - accessControlEntry.setAccessControlList(accessControlList); - accessControlEntry.setPermission(permission); - accessControlEntry.setAuthority(authority); - accessControlEntry.setAllowed(allowed); - return accessControlEntry; - } - + public long getId() { return id; @@ -171,4 +145,13 @@ public class DbAccessControlEntryImpl implements DbAccessControlEntry { this.allowed = allowed; } + + public void delete() + { + // remove the instance from the access control list + @SuppressWarnings("unused") + boolean removed = getAccessControlList().getEntries().remove(this); + // delete the instance + getSession().delete(this); + } } diff --git a/source/java/org/alfresco/repo/domain/hibernate/DbAccessControlListImpl.java b/source/java/org/alfresco/repo/domain/hibernate/DbAccessControlListImpl.java index 06692722e7..4fa4f1725e 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/DbAccessControlListImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/DbAccessControlListImpl.java @@ -16,13 +16,20 @@ */ package org.alfresco.repo.domain.hibernate; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.alfresco.repo.domain.DbAccessControlEntry; import org.alfresco.repo.domain.DbAccessControlList; +import org.alfresco.repo.domain.DbAuthority; +import org.alfresco.repo.domain.DbPermission; +import org.alfresco.repo.domain.DbPermissionKey; import org.alfresco.repo.domain.Node; import org.alfresco.util.EqualsHelper; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.hibernate.CallbackException; -import org.hibernate.Query; import org.hibernate.Session; /** @@ -36,8 +43,14 @@ public class DbAccessControlListImpl extends LifecycleAdapter implements DbAcces private long id; private Node node; + private Set entries; private boolean inherits; + public DbAccessControlListImpl() + { + entries = new HashSet(5); + } + @Override public String toString() { @@ -45,6 +58,7 @@ public class DbAccessControlListImpl extends LifecycleAdapter implements DbAcces sb.append("DbAccessControlListImpl") .append("[ id=").append(id) .append(", node=").append(node) + .append(", entries=").append(entries.size()) .append(", inherits=").append(inherits) .append("]"); return sb.toString(); @@ -73,35 +87,6 @@ public class DbAccessControlListImpl extends LifecycleAdapter implements DbAcces return (node == null ? 0 : node.hashCode()); } - public int deleteEntries() - { - /* - * This can use a delete direct to the database as well, but then care must be taken - * to evict the instances from the session. - */ - - // bypass L2 cache and get all entries for this list - Query query = getSession() - .getNamedQuery(PermissionsDaoComponentImpl.QUERY_GET_AC_ENTRIES_FOR_AC_LIST) - .setLong("accessControlListId", this.id); - int count = HibernateHelper.deleteQueryResults(getSession(), query); - // done - if (logger.isDebugEnabled()) - { - logger.debug("Deleted " + count + " access entries for access control list " + this.id); - } - return count; - } - - /** - * Ensures that all this access control list's entries have been deleted. - */ - public boolean onDelete(Session session) throws CallbackException - { - deleteEntries(); - return super.onDelete(session); - } - public long getId() { return id; @@ -126,9 +111,18 @@ public class DbAccessControlListImpl extends LifecycleAdapter implements DbAcces this.node = node; } - public DbAccessControlListImpl() + public Set getEntries() { - super(); + return entries; + } + + /** + * For Hibernate use + */ + @SuppressWarnings("unused") + private void setEntries(Set entries) + { + this.entries = entries; } public boolean getInherits() @@ -140,4 +134,112 @@ public class DbAccessControlListImpl extends LifecycleAdapter implements DbAcces { this.inherits = inherits; } + + /** + * @see #deleteEntry(String, DbPermissionKey) + */ + public int deleteEntriesForAuthority(String authority) + { + return deleteEntry(authority, null); + } + + /** + * @see #deleteEntry(String, DbPermissionKey) + */ + public int deleteEntriesForPermission(DbPermissionKey permissionKey) + { + return deleteEntry(null, permissionKey); + } + + public int deleteEntry(String authority, DbPermissionKey permissionKey) + { + List toDelete = new ArrayList(2); + for (DbAccessControlEntry entry : entries) + { + if (authority != null && !authority.equals(entry.getAuthority().getRecipient())) + { + // authority is not a match + continue; + } + else if (permissionKey != null && !permissionKey.equals(entry.getPermission().getKey())) + { + // permission is not a match + continue; + } + toDelete.add(entry); + } + // delete them + for (DbAccessControlEntry entry : toDelete) + { + // remove from the entry list + entry.delete(); + } + // done + if (logger.isDebugEnabled()) + { + logger.debug("Deleted " + toDelete.size() + " access entries: \n" + + " access control list: " + id + "\n" + + " authority: " + authority + "\n" + + " permission: " + permissionKey); + } + return toDelete.size(); + } + + public int deleteEntries() + { + /* + * We don't do the full delete-remove-from-set thing here. Just delete each child entity + * and then clear the entry set. + */ + + Session session = getSession(); + List toDelete = new ArrayList(entries); + // delete each entry + for (DbAccessControlEntry entry : toDelete) + { + session.delete(entry); + } + // clear the list + int count = entries.size(); + entries.clear(); + // done + if (logger.isDebugEnabled()) + { + logger.debug("Deleted " + count + " access entries for access control list " + this.id); + } + return count; + } + + public DbAccessControlEntry getEntry(String authority, DbPermissionKey permissionKey) + { + for (DbAccessControlEntry entry : entries) + { + DbAuthority authorityEntity = entry.getAuthority(); + DbPermission permissionEntity = entry.getPermission(); + // check for a match + if (authorityEntity.getRecipient().equals(authority) + && permissionEntity.getKey().equals(permissionKey)) + { + // found it + return entry; + } + } + return null; + } + + public DbAccessControlEntryImpl newEntry(DbPermission permission, DbAuthority authority, boolean allowed) + { + DbAccessControlEntryImpl accessControlEntry = new DbAccessControlEntryImpl(); + // fill + accessControlEntry.setAccessControlList(this); + accessControlEntry.setPermission(permission); + accessControlEntry.setAuthority(authority); + accessControlEntry.setAllowed(allowed); + // save it + getSession().save(accessControlEntry); + // maintain inverse set on the acl + getEntries().add(accessControlEntry); + // done + return accessControlEntry; + } } diff --git a/source/java/org/alfresco/repo/domain/hibernate/DbAuthorityImpl.java b/source/java/org/alfresco/repo/domain/hibernate/DbAuthorityImpl.java index 5194057763..9a793b80d0 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/DbAuthorityImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/DbAuthorityImpl.java @@ -38,11 +38,11 @@ public class DbAuthorityImpl extends LifecycleAdapter implements DbAuthority private static Log logger = LogFactory.getLog(DbAuthorityImpl.class); private String recipient; - private Set externalKeys = new HashSet(); + private Set externalKeys; public DbAuthorityImpl() { - super(); + externalKeys = new HashSet(); } @Override @@ -76,8 +76,8 @@ public class DbAuthorityImpl extends LifecycleAdapter implements DbAuthority // bypass L2 cache and get all entries for this list Query query = getSession() .getNamedQuery(PermissionsDaoComponentImpl.QUERY_GET_AC_ENTRIES_FOR_AUTHORITY) - .setString("recipient", this.recipient); - int count = HibernateHelper.deleteQueryResults(getSession(), query); + .setString("authorityRecipient", this.recipient); + int count = HibernateHelper.deleteDbAccessControlEntries(getSession(), query); // done if (logger.isDebugEnabled()) { @@ -115,4 +115,16 @@ public class DbAuthorityImpl extends LifecycleAdapter implements DbAuthority { this.externalKeys = externalKeys; } + + /** + * Helper method to find an authority based on its natural key + * + * @param session the Hibernate session to use + * @param authority the authority name + * @return Returns an existing instance or null if not found + */ + public static DbAuthority find(Session session, String authority) + { + return (DbAuthority) session.get(DbAuthorityImpl.class, authority); + } } diff --git a/source/java/org/alfresco/repo/domain/hibernate/DbPermissionImpl.java b/source/java/org/alfresco/repo/domain/hibernate/DbPermissionImpl.java index 1e270483db..501d7f419a 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/DbPermissionImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/DbPermissionImpl.java @@ -17,6 +17,7 @@ package org.alfresco.repo.domain.hibernate; import org.alfresco.repo.domain.DbPermission; +import org.alfresco.repo.domain.DbPermissionKey; import org.alfresco.service.namespace.QName; import org.alfresco.util.EqualsHelper; import org.apache.commons.logging.Log; @@ -90,7 +91,7 @@ public class DbPermissionImpl extends LifecycleAdapter implements DbPermission Query query = getSession() .getNamedQuery(PermissionsDaoComponentImpl.QUERY_GET_AC_ENTRIES_FOR_PERMISSION) .setSerializable("permissionId", this.id); - int count = HibernateHelper.deleteQueryResults(getSession(), query); + int count = HibernateHelper.deleteDbAccessControlEntries(getSession(), query); // done if (logger.isDebugEnabled()) { @@ -142,6 +143,11 @@ public class DbPermissionImpl extends LifecycleAdapter implements DbPermission this.name = name; } + public DbPermissionKey getKey() + { + return new DbPermissionKey(typeQname, name); + } + /** * Helper method to find a permission based on its natural key * diff --git a/source/java/org/alfresco/repo/domain/hibernate/HibernateHelper.java b/source/java/org/alfresco/repo/domain/hibernate/HibernateHelper.java index 86070d90a8..8da807a8fe 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/HibernateHelper.java +++ b/source/java/org/alfresco/repo/domain/hibernate/HibernateHelper.java @@ -16,8 +16,7 @@ */ package org.alfresco.repo.domain.hibernate; -import org.hibernate.CacheMode; -import org.hibernate.ObjectDeletedException; +import org.alfresco.repo.domain.DbAccessControlEntry; import org.hibernate.Query; import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; @@ -32,36 +31,25 @@ public class HibernateHelper { /** * Helper method to scroll through the results of a query and delete all the - * results, performing batch flushes. This will handle large resultsets by - * pulling the results directly in from the query. For certain circumstances, it - * may be better to perform a bulk delete directly instead. + * resulting access control entries, performing batch flushes. * * @param session the session to use for the deletions - * @param query the query with all parameters set - * @return Returns the number of entities deleted, regardless of type + * @param query the query with all parameters set and that will return + * {@link org.alfresco.repo.domain.DbAccessControlEntry access control entry} instances + * @return Returns the number of entries deleted */ - public static int deleteQueryResults(Session session, Query query) + public static int deleteDbAccessControlEntries(Session session, Query query) { - ScrollableResults entities = query.setCacheMode(CacheMode.IGNORE).scroll(ScrollMode.FORWARD_ONLY); + ScrollableResults entities = query.scroll(ScrollMode.FORWARD_ONLY); int count = 0; while (entities.next()) { - Object[] entityResults = entities.get(); - for (Object object : entityResults) + DbAccessControlEntry entry = (DbAccessControlEntry) entities.get(0); + entry.delete(); + if (++count % 50 == 0) { - try - { - session.delete(object); - } - catch (ObjectDeletedException e) - { - // ignore - it's what we wanted - } - if (++count % 50 == 0) - { - session.flush(); - session.clear(); - } + session.flush(); + session.clear(); } } return count; diff --git a/source/java/org/alfresco/repo/domain/hibernate/LifecycleAdapter.java b/source/java/org/alfresco/repo/domain/hibernate/LifecycleAdapter.java index 54c202accc..7e9740809c 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/LifecycleAdapter.java +++ b/source/java/org/alfresco/repo/domain/hibernate/LifecycleAdapter.java @@ -18,6 +18,7 @@ package org.alfresco.repo.domain.hibernate; import java.io.Serializable; +import org.alfresco.error.AlfrescoRuntimeException; import org.hibernate.CallbackException; import org.hibernate.Session; import org.hibernate.classic.Lifecycle; @@ -37,6 +38,10 @@ public abstract class LifecycleAdapter implements Lifecycle */ protected Session getSession() { + if (session == null) + { + throw new AlfrescoRuntimeException("Hibernate entity is not part of a session: " + this); + } return session; } 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 0701924b99..917f39484f 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml +++ b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml @@ -28,9 +28,9 @@ name="store" class="org.alfresco.repo.domain.hibernate.StoreImpl" not-null="true" - lazy="no-proxy" + lazy="proxy" optimistic-lock="true" - fetch="select"> + fetch="join"> @@ -43,8 +43,8 @@ name="accessControlList" class="org.alfresco.repo.domain.hibernate.DbAccessControlListImpl" property-ref="node" - lazy="no-proxy" - fetch="select" + lazy="false" + fetch="join" cascade="delete" /> diff --git a/source/java/org/alfresco/repo/domain/hibernate/NodeImpl.java b/source/java/org/alfresco/repo/domain/hibernate/NodeImpl.java index df770553b8..5bf438f904 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/NodeImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/NodeImpl.java @@ -34,6 +34,7 @@ import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.domain.Store; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; +import org.alfresco.util.EqualsHelper; /** * Bean containing all the persistence data representing a node. @@ -134,12 +135,13 @@ public class NodeImpl extends LifecycleAdapter implements Node return false; } Node that = (Node) obj; - return (this.getNodeRef().equals(that.getNodeRef())); + return (EqualsHelper.nullSafeEquals(getStore(), that.getStore()) + && EqualsHelper.nullSafeEquals(getUuid(), that.getUuid())); } public int hashCode() { - return getNodeRef().hashCode(); + return getUuid().hashCode(); } // @Override @@ -316,11 +318,7 @@ public class NodeImpl extends LifecycleAdapter implements Node return accessControlList; } - /** - * For Hibernate use - */ - @SuppressWarnings("unused") - private void setAccessControlList(DbAccessControlList accessControlList) + public void setAccessControlList(DbAccessControlList accessControlList) { this.accessControlList = accessControlList; } 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 f4032016f1..9d72a4fe85 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/Permission.hbm.xml +++ b/source/java/org/alfresco/repo/domain/hibernate/Permission.hbm.xml @@ -26,6 +26,16 @@ not-null="true"> + + + + + @@ -47,29 +57,29 @@ + name="accessControlList" + class="org.alfresco.repo.domain.hibernate.DbAccessControlListImpl" + column="acl_id" + lazy="no-proxy" + fetch="select" + optimistic-lock="true" + not-null="true" /> + name="permission" + class="org.alfresco.repo.domain.hibernate.DbPermissionImpl" + column="permission_id" + lazy="no-proxy" + fetch="select" + optimistic-lock="true" + not-null="true" /> + name="authority" + class="org.alfresco.repo.domain.hibernate.DbAuthorityImpl" + column="authority_id" + lazy="no-proxy" + fetch="select" + optimistic-lock="true" + not-null="true" /> @@ -83,7 +93,7 @@ dynamic-insert="false" dynamic-update="false" select-before-update="false" - lazy="true" + lazy="false" optimistic-lock="version" > @@ -104,7 +114,7 @@ dynamic-insert="false" dynamic-update="false" select-before-update="false" - lazy="true" + lazy="false" optimistic-lock="version" > @@ -133,24 +143,6 @@ permission.name = :permissionName - - select - ace - from - org.alfresco.repo.domain.hibernate.DbAccessControlEntryImpl as ace - where - ace.accessControlList.id = :accessControlListId - - - - select - ace - from - org.alfresco.repo.domain.hibernate.DbAccessControlEntryImpl as ace - where - ace.permission.id = :permissionId - - select ace @@ -160,41 +152,4 @@ ace.authority.recipient = :authorityRecipient - - select - ace - from - org.alfresco.repo.domain.hibernate.DbAccessControlEntryImpl as ace - where - ace.authority.recipient = :authorityRecipient and - ace.accessControlList.node.store.key.protocol = :storeProtocol and - ace.accessControlList.node.store.key.identifier = :storeIdentifier and - ace.accessControlList.node.uuid = :nodeUuid - - - - select - ace - from - org.alfresco.repo.domain.hibernate.DbAccessControlEntryImpl as ace - where - ace.permission.typeQname = :permissionTypeQName and - ace.permission.name = :permissionName and - ace.authority.recipient = :authorityRecipient and - ace.accessControlList.node.store.key.protocol = :storeProtocol and - ace.accessControlList.node.store.key.identifier = :storeIdentifier and - ace.accessControlList.node.uuid = :nodeUuid - - - - select - acl - from - org.alfresco.repo.domain.hibernate.DbAccessControlListImpl as acl - where - acl.node.store.key.protocol = :storeProtocol and - acl.node.store.key.identifier = :storeIdentifier and - acl.node.uuid = :nodeUuid - - \ No newline at end of file diff --git a/source/java/org/alfresco/repo/domain/hibernate/PermissionsDaoComponentImpl.java b/source/java/org/alfresco/repo/domain/hibernate/PermissionsDaoComponentImpl.java index 8dc7a42357..1a8d0b337b 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/PermissionsDaoComponentImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/PermissionsDaoComponentImpl.java @@ -16,9 +16,9 @@ */ package org.alfresco.repo.domain.hibernate; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Set; import org.alfresco.repo.cache.SimpleCache; @@ -26,6 +26,7 @@ import org.alfresco.repo.domain.DbAccessControlEntry; import org.alfresco.repo.domain.DbAccessControlList; import org.alfresco.repo.domain.DbAuthority; import org.alfresco.repo.domain.DbPermission; +import org.alfresco.repo.domain.DbPermissionKey; import org.alfresco.repo.domain.Node; import org.alfresco.repo.node.db.NodeDaoService; import org.alfresco.repo.security.permissions.NodePermissionEntry; @@ -56,12 +57,8 @@ import org.springframework.orm.hibernate3.support.HibernateDaoSupport; public class PermissionsDaoComponentImpl extends HibernateDaoSupport implements PermissionsDaoComponent { public static final String QUERY_GET_PERMISSION = "permission.GetPermission"; - public static final String QUERY_GET_AC_LIST_FOR_NODE = "permission.GetAccessControlListForNode"; - public static final String QUERY_GET_AC_ENTRIES_FOR_AC_LIST = "permission.GetAccessControlEntriesForAccessControlList"; public static final String QUERY_GET_AC_ENTRIES_FOR_AUTHORITY = "permission.GetAccessControlEntriesForAuthority"; public static final String QUERY_GET_AC_ENTRIES_FOR_PERMISSION = "permission.GetAccessControlEntriesForPermission"; - public static final String QUERY_GET_AC_ENTRIES_FOR_AUTHORITY_AND_NODE = "permission.GetAccessControlEntriesForAuthorityAndNode"; - public static final String QUERY_GET_AC_ENTRY_FOR_ALL = "permission.GetAccessControlEntryForAll"; private NodeDaoService nodeDaoService; private SimpleCache nullPermissionCache; @@ -93,13 +90,16 @@ public class PermissionsDaoComponentImpl extends HibernateDaoSupport implements { return npe; } + Node node = getNode(nodeRef); // get the persisted version - DbAccessControlList acl = getAccessControlList(nodeRef, false); + DbAccessControlList acl = getAccessControlList(node, false); if (acl == null) { // there isn't an access control list for the node - spoof a null one SimpleNodePermissionEntry snpe = new SimpleNodePermissionEntry( - nodeRef, true, Collections. emptySet()); + nodeRef, + true, + Collections. emptySet()); npe = snpe; nullPermissionCache.put(nodeRef, snpe); } @@ -122,46 +122,18 @@ public class PermissionsDaoComponentImpl extends HibernateDaoSupport implements * @param create - create the object if it is missing * @return Returns the current access control list or null if not found */ - private DbAccessControlList getAccessControlList(final NodeRef nodeRef, boolean create) + private DbAccessControlList getAccessControlList(Node node, boolean create) { - // get the access control list for the node - HibernateCallback callback = new HibernateCallback() + DbAccessControlList acl = node.getAccessControlList(); + if (acl == null && create) { - public Object doInHibernate(Session session) - { - Query query = session.getNamedQuery(PermissionsDaoComponentImpl.QUERY_GET_AC_LIST_FOR_NODE); - query.setString("storeProtocol", nodeRef.getStoreRef().getProtocol()) - .setString("storeIdentifier", nodeRef.getStoreRef().getIdentifier()) - .setString("nodeUuid", nodeRef.getId()); - return query.list(); - } - }; - @SuppressWarnings("unchecked") - List results = (List) getHibernateTemplate().execute(callback); - DbAccessControlList acl = null; - if (results.size() == 0) - { - // create it - if (create) - { - acl = createAccessControlList(nodeRef); - } - // else null will be returned - } - else if (results.size() > 0) - { - acl = (DbAccessControlList) results.get(0); - } - else if (results.size() > 1) - { - logger.warn("Duplicate access control lists for node: " + nodeRef); - acl = (DbAccessControlList) results.get(0); + acl = createAccessControlList(node); } // done if (logger.isDebugEnabled()) { logger.debug("Retrieved access control list: \n" + - " node: " + nodeRef + "\n" + + " node: " + node.getNodeRef() + "\n" + " list: " + acl); } return acl; @@ -171,16 +143,17 @@ public class PermissionsDaoComponentImpl extends HibernateDaoSupport implements * Creates an access control list for the node and removes the entry from * the nullPermsionCache. */ - private DbAccessControlList createAccessControlList(final NodeRef nodeRef) + private DbAccessControlList createAccessControlList(Node node) { - // get the node referenced - Node node = getNode(nodeRef); - DbAccessControlList acl = new DbAccessControlListImpl(); acl.setNode(node); acl.setInherits(true); getHibernateTemplate().save(acl); + // maintain inverse + node.setAccessControlList(acl); + + NodeRef nodeRef = node.getNodeRef(); nullPermissionCache.remove(nodeRef); // done @@ -209,17 +182,21 @@ public class PermissionsDaoComponentImpl extends HibernateDaoSupport implements public void deletePermissions(NodeRef nodeRef) { - DbAccessControlList acl = getAccessControlList(nodeRef, false); + Node node = getNode(nodeRef); + DbAccessControlList acl = getAccessControlList(node, false); if (acl != null) { // delete the access control list - it will cascade to the entries getHibernateTemplate().delete(acl); + // maintain inverse + node.setAccessControlList(null); } } @SuppressWarnings("unchecked") public void deletePermissions(final String authority) { + // get the authority HibernateCallback callback = new HibernateCallback() { public Object doInHibernate(Session session) @@ -227,7 +204,7 @@ public class PermissionsDaoComponentImpl extends HibernateDaoSupport implements Query query = session .getNamedQuery(QUERY_GET_AC_ENTRIES_FOR_AUTHORITY) .setString("authorityRecipient", authority); - return (Integer) HibernateHelper.deleteQueryResults(session, query); + return (Integer) HibernateHelper.deleteDbAccessControlEntries(session, query); } }; Integer deletedCount = (Integer) getHibernateTemplate().execute(callback); @@ -240,20 +217,13 @@ public class PermissionsDaoComponentImpl extends HibernateDaoSupport implements public void deletePermissions(final NodeRef nodeRef, final String authority) { - HibernateCallback callback = new HibernateCallback() + Node node = getNode(nodeRef); + DbAccessControlList acl = node.getAccessControlList(); + int deletedCount = 0; + if (acl != null) { - public Object doInHibernate(Session session) - { - Query query = session - .getNamedQuery(QUERY_GET_AC_ENTRIES_FOR_AUTHORITY_AND_NODE) - .setString("authorityRecipient", authority) - .setString("storeProtocol", nodeRef.getStoreRef().getProtocol()) - .setString("storeIdentifier", nodeRef.getStoreRef().getIdentifier()) - .setString("nodeUuid", nodeRef.getId()); - return HibernateHelper.deleteQueryResults(session, query); - } - }; - Integer deletedCount = (Integer) getHibernateTemplate().execute(callback); + deletedCount = acl.deleteEntriesForAuthority(authority); + } // done if (logger.isDebugEnabled()) { @@ -268,38 +238,39 @@ public class PermissionsDaoComponentImpl extends HibernateDaoSupport implements * the given criteria. Note that the access control list for the node is * not deleted. */ - public void deletePermission(final NodeRef nodeRef, final String authority, final PermissionReference permission) + public void deletePermission(NodeRef nodeRef, String authority, PermissionReference permission) { - // get the entry - DbAccessControlEntry entry = getAccessControlEntry(nodeRef, authority, permission); - if (entry != null) + Node node = getNode(nodeRef); + DbAccessControlList acl = node.getAccessControlList(); + int deletedCount = 0; + if (acl != null) { - getHibernateTemplate().delete(entry); - // done - if (logger.isDebugEnabled()) - { - logger.debug("Deleted entry for criteria: \n" + - " node: " + nodeRef + "\n" + - " authority: " + authority + "\n" + - " permission: " + permission); - } + DbPermissionKey permissionKey = new DbPermissionKey(permission.getQName(), permission.getName()); + deletedCount = acl.deleteEntry(authority, permissionKey); + } + // done + if (logger.isDebugEnabled()) + { + logger.debug("Deleted " + deletedCount + "entries for criteria: \n" + + " node: " + nodeRef + "\n" + + " permission: " + permission + "\n" + + " authority: " + authority); } } public void setPermission(NodeRef nodeRef, String authority, PermissionReference permission, boolean allow) { + Node node = getNode(nodeRef); // get the entry - DbAccessControlEntry entry = getAccessControlEntry(nodeRef, authority, permission); + DbAccessControlEntry entry = getAccessControlEntry(node, authority, permission); if (entry == null) { // need to create it - DbAccessControlList dbAccessControlList = getAccessControlList(nodeRef, true); + DbAccessControlList dbAccessControlList = getAccessControlList(node, true); DbPermission dbPermission = getPermission(permission, true); DbAuthority dbAuthority = getAuthority(authority, true); // set persistent objects - entry = DbAccessControlEntryImpl.create(dbAccessControlList, dbPermission, dbAuthority, allow); - // save it - getHibernateTemplate().save(entry); + entry = dbAccessControlList.newEntry(dbPermission, dbAuthority, allow); // drop the entry from the null cache nullPermissionCache.remove(nodeRef); // done @@ -326,31 +297,22 @@ public class PermissionsDaoComponentImpl extends HibernateDaoSupport implements * @return Returns all access control entries that match the criteria */ private DbAccessControlEntry getAccessControlEntry( - final NodeRef nodeRef, - final String authority, - final PermissionReference permission) + Node node, + String authority, + PermissionReference permission) { - HibernateCallback callback = new HibernateCallback() + DbAccessControlList acl = getAccessControlList(node, false); + DbAccessControlEntry entry = null; + if (acl != null) { - public Object doInHibernate(Session session) - { - Query query = session - .getNamedQuery(QUERY_GET_AC_ENTRY_FOR_ALL) - .setString("permissionTypeQName", permission.getQName().toString()) - .setString("permissionName", permission.getName()) - .setString("authorityRecipient", authority) - .setString("storeProtocol", nodeRef.getStoreRef().getProtocol()) - .setString("storeIdentifier", nodeRef.getStoreRef().getIdentifier()) - .setString("nodeUuid", nodeRef.getId()); - return (DbAccessControlEntry) query.uniqueResult(); - } - }; - DbAccessControlEntry entry = (DbAccessControlEntry) getHibernateTemplate().execute(callback); + DbPermissionKey permissionKey = new DbPermissionKey(permission.getQName(), permission.getName()); + entry = acl.getEntry(authority, permissionKey); + } // done if (logger.isDebugEnabled()) { logger.debug("" + (entry == null ? "Did not find" : "Found") + "entry for criteria: \n" + - " node: " + nodeRef + "\n" + + " node: " + node.getId() + "\n" + " authority: " + authority + "\n" + " permission: " + permission); } @@ -383,15 +345,9 @@ public class PermissionsDaoComponentImpl extends HibernateDaoSupport implements { final QName qname = permissionRef.getQName(); final String name = permissionRef.getName(); + Session session = getSession(); - HibernateCallback callback = new HibernateCallback() - { - public Object doInHibernate(Session session) - { - return DbPermissionImpl.find(session, qname, name); - } - }; - DbPermission dbPermission = (DbPermission) getHibernateTemplate().execute(callback); + DbPermission dbPermission = DbPermissionImpl.find(session, qname, name); // create if necessary if ((dbPermission == null) && create) @@ -416,24 +372,21 @@ public class PermissionsDaoComponentImpl extends HibernateDaoSupport implements public void setPermission(NodePermissionEntry nodePermissionEntry) { NodeRef nodeRef = nodePermissionEntry.getNodeRef(); - // get the access control list - DbAccessControlList acl = getAccessControlList(nodeRef, false); - if (acl == null) + Node node = getNode(nodeRef); + + // Get the access control list + // Note the logic here requires to know whether it was created or not + DbAccessControlList acl = getAccessControlList(node, false); + if (acl != null) { - // create the access control list - acl = createAccessControlList(nodeRef); - } - else - { - // remove entries associated with the list - int deleted = acl.deleteEntries(); - if (logger.isDebugEnabled()) - { - logger.debug("Deleted " + deleted + " entries for access control list: \n" + - " acl: " + acl); - } - getSession().flush(); + // drop the list + getHibernateTemplate().delete(acl); + // update node + node.setAccessControlList(null); } + // create the access control list + acl = createAccessControlList(node); + // set attributes acl.setInherits(nodePermissionEntry.inheritPermissions()); @@ -447,24 +400,37 @@ public class PermissionsDaoComponentImpl extends HibernateDaoSupport implements DbPermission permissionEntity = getPermission(permission, true); DbAuthority authorityEntity = getAuthority(authority, true); - DbAccessControlEntryImpl entry = DbAccessControlEntryImpl.create( - acl, - permissionEntity, - authorityEntity, - isAllowed); - getHibernateTemplate().save(entry); + @SuppressWarnings("unused") + DbAccessControlEntryImpl entry = acl.newEntry(permissionEntity, authorityEntity, isAllowed); } } public void setInheritParentPermissions(NodeRef nodeRef, boolean inheritParentPermissions) { - DbAccessControlList acl = getAccessControlList(nodeRef, true); - acl.setInherits(inheritParentPermissions); + Node node = getNode(nodeRef); + + DbAccessControlList acl = null; + if (!inheritParentPermissions) + { + // Inheritance == true is the default, so only force a create of the ACL if the value false + acl = getAccessControlList(node, true); + acl.setInherits(false); + } + else + { + acl = getAccessControlList(node, false); + if (acl != null) + { + acl.setInherits(true); + } + } } public boolean getInheritParentPermissions(NodeRef nodeRef) { - DbAccessControlList acl = getAccessControlList(nodeRef, false); + Node node = getNode(nodeRef); + + DbAccessControlList acl = getAccessControlList(node, false); if (acl == null) { return true; @@ -484,46 +450,19 @@ public class PermissionsDaoComponentImpl extends HibernateDaoSupport implements { ParameterCheck.mandatory("acl", acl); } - List entries = getEntriesForList(acl); + Set entries = acl.getEntries(); SimpleNodePermissionEntry snpe = new SimpleNodePermissionEntry( acl.getNode().getNodeRef(), acl.getInherits(), createSimplePermissionEntries(entries)); return snpe; } - - /** - * Executes a query to retrieve the access control list's entries - * - * @param acl the access control list - * @return Returns a list of the entries - */ - @SuppressWarnings("unchecked") - private List getEntriesForList(final DbAccessControlList acl) - { - HibernateCallback callback = new HibernateCallback() - { - public Object doInHibernate(Session session) - { - Query query = session.getNamedQuery(QUERY_GET_AC_ENTRIES_FOR_AC_LIST); - query.setLong("accessControlListId", acl.getId()); - return query.list(); - } - }; - List entries = (List) getHibernateTemplate().execute(callback); - // done - if (logger.isDebugEnabled()) - { - logger.debug("Found " + entries.size() + " entries for access control list " + acl.getId()); - } - return entries; - } /** * @param entries access control entries * @return Returns a unique set of entries that can be given back to the outside world */ - private Set createSimplePermissionEntries(List entries) + private Set createSimplePermissionEntries(Collection entries) { if (entries == null) { diff --git a/source/java/org/alfresco/repo/domain/hibernate/Store.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/Store.hbm.xml index 07da354377..6691a9937a 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/Store.hbm.xml +++ b/source/java/org/alfresco/repo/domain/hibernate/Store.hbm.xml @@ -23,9 +23,9 @@ + fetch="select" > diff --git a/source/java/org/alfresco/repo/domain/hibernate/StoreImpl.java b/source/java/org/alfresco/repo/domain/hibernate/StoreImpl.java index 1a700a588e..c469858bd4 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/StoreImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/StoreImpl.java @@ -123,7 +123,7 @@ public class StoreImpl implements Store return key; } - public synchronized void setKey(StoreKey key) + public void setKey(StoreKey key) { refWriteLock.lock(); try diff --git a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java index 95d6d050fa..091e99c42d 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java @@ -181,6 +181,23 @@ public class PermissionServiceTest extends AbstractPermissionTest testSetNodePermissionEntry(); testSetNodePermissionEntry2(); } + + public void testDoubleSetAllowDeny() + { + Set permissionEntries = null; + // add-remove andy-all + permissionService.setPermission(rootNodeRef, "andy", permissionService.getAllPermission(), true); + permissionService.setPermission(rootNodeRef, "andy", permissionService.getAllPermission(), false); + permissionService.deletePermission(rootNodeRef, "andy", permissionService.getAllPermission()); + permissionEntries = permissionService.getSetPermissions(rootNodeRef).getPermissionEntries(); + assertEquals(0, permissionEntries.size()); + // add-remove andy-read + permissionService.setPermission(rootNodeRef, "andy", PermissionService.READ, true); + permissionService.setPermission(rootNodeRef, "andy", PermissionService.READ, false); + permissionService.deletePermission(rootNodeRef, "andy", PermissionService.READ); + permissionEntries = permissionService.getSetPermissions(rootNodeRef).getPermissionEntries(); + assertEquals(0, permissionEntries.size()); + } public void testSetPermissionEntryElements() { diff --git a/source/java/org/alfresco/repo/security/permissions/impl/hibernate/HibernatePermissionTest.java b/source/java/org/alfresco/repo/security/permissions/impl/hibernate/HibernatePermissionTest.java index 4d84696c38..87933c23d7 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/hibernate/HibernatePermissionTest.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/hibernate/HibernatePermissionTest.java @@ -119,13 +119,6 @@ public class HibernatePermissionTest extends BaseSpringTest // throw the reference away and get the a new one for the id permission = (DbPermission) getSession().load(DbPermissionImpl.class, id); assertNotNull("Permission not found", permission); - assertEquals("Test", permission.getName()); - assertEquals(qname, permission.getTypeQname()); - - // Test key - permission = (DbPermission) getSession().load(DbPermissionImpl.class, id); - assertNotNull("Permission not found", permission); - assertEquals("Test", permission.getName()); assertEquals(qname, permission.getTypeQname()); } @@ -175,29 +168,29 @@ public class HibernatePermissionTest extends BaseSpringTest DbAccessControlList accessControlList = new DbAccessControlListImpl(); accessControlList.setNode(node); accessControlList.setInherits(true); + Serializable nodeAclId = getSession().save(accessControlList); DbAuthority recipient = new DbAuthorityImpl(); recipient.setRecipient("Test"); recipient.getExternalKeys().add("One"); + getSession().save(recipient); DbPermission permission = new DbPermissionImpl(); permission.setTypeQname(qname); permission.setName("Test"); - - DbAccessControlEntry accessControlEntry = DbAccessControlEntryImpl.create(accessControlList, permission, recipient, true); - - Serializable nodeAclId = getSession().save(accessControlList); - getSession().save(recipient); getSession().save(permission); - Serializable aceEntryId = getSession().save(accessControlEntry); - accessControlEntry = (DbAccessControlEntry) getSession().load(DbAccessControlEntryImpl.class, aceEntryId); + DbAccessControlEntry accessControlEntry = accessControlList.newEntry(permission, recipient, true); + Long aceEntryId = accessControlEntry.getId(); + assertNotNull("Entry is still transient", aceEntryId); + + accessControlEntry = (DbAccessControlEntry) getSession().load(DbAccessControlEntryImpl.class, aceEntryId); assertNotNull("Permission entry not found", accessControlEntry); assertTrue(accessControlEntry.isAllowed()); assertNotNull(accessControlEntry.getAccessControlList()); assertTrue(accessControlEntry.getAccessControlList().getInherits()); assertNotNull(accessControlEntry.getPermission()); - assertEquals("Test", accessControlEntry.getPermission().getName()); + assertEquals("Test", accessControlEntry.getPermission().getKey().getName()); assertNotNull(accessControlEntry.getAuthority()); assertEquals("Test", accessControlEntry.getAuthority().getRecipient()); assertEquals(1, accessControlEntry.getAuthority().getExternalKeys().size());