Add new model objects
To work with ACLs, we need some new domain objects.
Base class for secured objects
The class BaseObjectAclAware serves as a base class for all domain objects, we want to secure.
package org.appfuse.model.acl; import java.io.Serializable; import org.appfuse.model.BaseObject; /** * Base object for all ACL aware model objects */ public abstract class BaseObjectAclAware extends BaseObject { /** * This methods returns the unique key of the object (the primary key) * * @return Unique key of the object (the primary key) */ public abstract Serializable getUniqueKey(); }
Domain objects for ACLs
To persist our ACLs in the database, we add two new domain objects to our project.
Object identity
The class BasicAclObjectIdentity describes the identity of an object. The object identity is assembled using the fully qualified class name of an object and – sperated by a colon – its id.
package org.appfuse.model.acl; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.Table; import org.acegisecurity.acl.basic.AclObjectIdentity; import org.acegisecurity.acls.Permission; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.appfuse.model.BaseObject; /** * A class for preserving the identity of an object. */ @Entity @Table(name="acl_object_identity") public class BasicAclObjectIdentity extends BaseObject implements AclObjectIdentity { /** version uid */ private static final long serialVersionUID = -3198369597782899163L; private Long id; private String objectIdentity; private String aclClass; private BasicAclObjectIdentity parentObject; private Set<Permission> permissions = new HashSet<Permission>(); public BasicAclObjectIdentity() { super(); } public BasicAclObjectIdentity(String identity) { super(); objectIdentity = identity; } @Id @GeneratedValue(strategy=GenerationType.AUTO) public Long getId() { return id; } @Column(name="object_identity", length=60, nullable=false, unique=true) public String getObjectIdentity() { return objectIdentity; } @Column(name="acl_class", length=50, nullable=false) public String getAclClass() { return aclClass; } @ManyToOne @JoinColumn(name="parent_object") public BasicAclObjectIdentity getParentObject() { return parentObject; } @OneToMany(cascade=CascadeType.ALL, mappedBy="basicAclObjectIdentity", targetEntity=org.appfuse.model.acl.BasicAclPermission.class) @JoinColumn(name="acl_object_identity") public Set<Permission> getPermissions() { return permissions; } public void setId(Long id) { this.id = id; } public void setObjectIdentity(String objectIdentity) { this.objectIdentity = objectIdentity; } public void setAclClass(String aclClass) { this.aclClass = aclClass; } public void setParentObject(BasicAclObjectIdentity parentObject) { this.parentObject = parentObject; } public void setPermissions(Set<Permission> permissions) { this.permissions = permissions; } /* (non-Javadoc) * @see org.appfuse.model.BaseObject#equals(java.lang.Object) */ public boolean equals(Object object) { BasicAclObjectIdentity rhs = (BasicAclObjectIdentity) object; return new EqualsBuilder() .append(this.getObjectIdentity(), rhs.getObjectIdentity()) .isEquals(); } /* (non-Javadoc) * @see org.appfuse.model.BaseObject#hashCode() */ public int hashCode() { return new HashCodeBuilder(1330891477, -1591801705) .append(this.getObjectIdentity()) .toHashCode(); } /* (non-Javadoc) * @see org.appfuse.model.BaseObject#toString() */ public String toString() { return objectIdentity; } }
Permissions
Permissions get assigned for a specific object identity and are valid for exaclty one Authority (a Role) or a Principal (username).
package org.appfuse.model.acl; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import javax.persistence.Transient; import org.acegisecurity.acl.basic.AclObjectIdentity; import org.acegisecurity.acl.basic.BasicAclEntry; import org.acegisecurity.acl.basic.SimpleAclEntry; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.lang.builder.ToStringBuilder; import org.appfuse.model.BaseObject; @Entity @Table(name="acl_permission") public class BasicAclPermission extends BaseObject implements BasicAclEntry { /** version uid */ private static final long serialVersionUID = 3790157885600360115L; private Long id; private String objRecipient; private Integer objMask; private AclObjectIdentity basicAclObjectIdentity; @Id @GeneratedValue(strategy=GenerationType.AUTO) public Long getId() { return id; } @Column(name="mask", nullable=false) public Integer getObjMask() { return objMask; } @Column(name="recipient", length=100, nullable=false) public String getObjRecipient() { return objRecipient; } @ManyToOne(targetEntity=org.appfuse.model.acl.BasicAclObjectIdentity.class) @JoinColumn(name="acl_object_identity", nullable=false) public AclObjectIdentity getBasicAclObjectIdentity() { return basicAclObjectIdentity; } public void setId(Long id) { this.id = id; } public void setObjRecipient(String objRecipient) { this.objRecipient = objRecipient; } public void setObjMask(Integer objMask) { this.objMask = objMask; } public void setBasicAclObjectIdentity( BasicAclObjectIdentity basicAclObjectIdentity) { this.basicAclObjectIdentity = basicAclObjectIdentity; } // ~~~ implements BasicAclEntry ~~~ @Transient public AclObjectIdentity getAclObjectIdentity() { return this.basicAclObjectIdentity; } public void setAclObjectIdentity(AclObjectIdentity aclObjectIdentity) { this.basicAclObjectIdentity = aclObjectIdentity; } @Transient public AclObjectIdentity getAclObjectParentIdentity() { return ((BasicAclObjectIdentity) basicAclObjectIdentity).getParentObject(); } public void setAclObjectParentIdentity(AclObjectIdentity aclObjectParentIdentity) { BasicAclObjectIdentity identity = (BasicAclObjectIdentity) aclObjectParentIdentity; identity.setParentObject((BasicAclObjectIdentity) aclObjectParentIdentity); } @Transient public int getMask() { return objMask.intValue(); } public void setMask(int mask) { this.objMask = new Integer(mask); } @Transient public Object getRecipient() { return objRecipient; } public void setRecipient(Object recipient) { this.objRecipient = recipient.toString(); } @Transient public boolean isPermitted(int mask) { SimpleAclEntry aclEntry = new SimpleAclEntry(); aclEntry.setMask(this.getMask()); return aclEntry.isPermitted(mask); } // ~~~ end of implements ~~~ /* (non-Javadoc) * @see org.appfuse.model.BaseObject#equals(java.lang.Object) */ public boolean equals(Object object) { if (!(object instanceof BasicAclPermission)) { return false; } BasicAclPermission rhs = (BasicAclPermission) object; return new EqualsBuilder() .append(this.basicAclObjectIdentity, rhs.basicAclObjectIdentity) .append(this.objRecipient, rhs.objRecipient) .isEquals(); } /* (non-Javadoc) * @see org.appfuse.model.BaseObject#hashCode() */ public int hashCode() { return new HashCodeBuilder(-623867371, 1205873953) .append(this.basicAclObjectIdentity) .append(this.objRecipient) .toHashCode(); } /* (non-Javadoc) * @see org.appfuse.model.BaseObject#toString() */ public String toString() { return new ToStringBuilder(this) .append("id", this.id) .append("objMask", this.objMask) .append("recipient", this.getRecipient()) .append("basicAclObjectIdentity", this.basicAclObjectIdentity) .toString(); } }
Modify Hibernate configuration
As usual, we have to make Hibernate aware of the new domain objects by adding entries to the file hibernate.cfg.xml:
<!-- ACL classes --> <mapping class="org.appfuse.model.acl.BasicAclObjectIdentity"/> <mapping class="org.appfuse.model.acl.BasicAclPermission"/>
Modify sample data
We also want some test data to be available and thus modify the file sample-data.xml (don't forget to copy this file to the web-module when using a modular archetype).
<table name='acl_object_identity'> <column>id</column> <column>object_identity</column> <column>acl_class</column> <column>parent_object</column> <row> <value>1</value> <value>org.appfuse.model.Person</value> <value>org.acegisecurity.acl.basic.SimpleAclEntry</value> <null/> </row> <row> <value>2</value> <value>org.appfuse.model.Person:1</value> <value>org.acegisecurity.acl.basic.SimpleAclEntry</value> <null/> </row> </table> <table name='acl_permission'> <column>id</column> <column>recipient</column> <column>acl_object_identity</column> <column>mask</column> <row> <value>1</value> <value>ROLE_ADMIN</value> <value>1</value> <value>1</value> </row> <row> <value>2</value> <value>ROLE_USER</value> <value>1</value> <value>14</value> </row> <row> <value>3</value> <value>ROLE_ADMIN</value> <value>2</value> <value>1</value> </row> </table>
Secure Person domain
For Person to be a secured domain object, it has to extend BaseObjectAclAware:
public class Person extends BaseObjectAclAware
As a result the method getUniqueKey() has to be implemented.
/* (non-Javadoc) * @see org.appfuse.model.acl.BaseObjectAclAware#getUniqueKey() */ @Transient @Override public Serializable getUniqueKey() { return getId(); }