I want to give certain permission for the logged user on some entities on their creation.
I am trying to use an Interceptor to add Permission object for the current user on creation of AuthorizedAccessEntity.
Now I tried implementing the interceptor using
http://www.hibernate.org/318.html as a model but i ran into some trouble.
Here is the mapping for the collection which holds the permissions for each user in AuthorizedAccessEntity.
Code:
@OneToMany(
targetEntity=PermissionResource.class,
fetch=FetchType.LAZY,
cascade={CascadeType.ALL}
)
@JoinColumn(name="RESOURCE_SUBJECT_ID")
@MapKey(name="user")
@Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
public Map<User, IdentityPermission<Resource>> getAuthorizedUsers() {
return authorizedUsers;
}
public void setAuthorizedUsers(Map<User, IdentityPermission<Resource>> authorizedUsers) {
this.authorizedUsers = authorizedUsers;
}
Here is the interceptor.
Code:
class RPXInterceptor extends EmptyInterceptor {
/* This set holds the newly created AuthorizedAccessEntity objects (except User) we have to add user rights to.
* We fill it in onSave and actually add the logged user rights in postFlush.
*/
private Set inserts = new HashSet();
private Session session = null;
/**
* If the object which is going to be saved is an AuthorizedAccessEntity we add it to
* our set so we can set the control rights for the current user in postFlush.
* This method is called only when new object is created.
*/
public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
if ((entity instanceof AuthorizedAccessEntity) && !(entity instanceof User)) {
Set<AuthorizedAccessEntity> sInserts = Collections.synchronizedSet(inserts);
synchronized (sInserts) {
sInserts.add((AuthorizedAccessEntity) entity);
}
}
return false;
}
/**
* Iterate over the collection of freshly saved AuthorizedAccessEntity objects and add CONTROL rights for the current user.
*/
public void postFlush(Iterator entities) {
for (Iterator iter = inserts.iterator(); iter.hasNext(); ) {
Session tempSession = HibernateUtil.createSession(session.connection());
AuthorizedAccessEntity subject = (AuthorizedAccessEntity) iter.next();
User user = UserService.getLoggedUser();
if (!subject.getAuthorizedUsers().containsKey(user)) {
try {
subject.getAuthorizedUsers().put(user, SecurityService.createPermission(user, subject, PermissionType.CONTROL));
tempSession.save(subject);
tempSession.flush();
} catch (Exception ex) {
throw new CallbackException(ex);
} finally {
try {
tempSession.close();
} catch (HibernateException ex) {
throw new CallbackException(ex);
}
}
}
}
inserts.clear();
}
public void setSession(Session session) {
this.session = session;
}
public void afterTransactionCompletion(Transaction tx) {
//in case we had a rollback.
inserts.clear();
}
}
Note: SecurityService.createPermission(user, subject, PermissionType.CONTROL) creates a Permission object (let's say ResourcePersmission) like the one below:
Code:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Table(name="T_PERMISSION")
@DiscriminatorColumn(
name = "DATA_TYPE",
discriminatorType = DiscriminatorType.STRING
)
@ForceDiscriminator
public class Permission extends Identity {
private PermissionType permissionType;
private User user;
/**
* Creates a new instance of Permission
*/
public Permission() {
}
public PermissionType getPermissionType() {
return permissionType;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "USER_ID")
public User getUser() {
return user;
}
public void setPermissionType(PermissionType permissionType) {
this.permissionType = permissionType;
}
public void setUser(User user) {
this.user = user;
}
}
@MappedSuperclass
public class IdentityPermission<E extends Identity> extends Permission {
protected E subject;
public IdentityPermission() {
setName("");
}
public IdentityPermission(User user, PermissionType permissionType, E subject) {
setUser(user);
setPermissionType(permissionType);
setSubject(subject);
setName("");
}
public IdentityPermission(User user, PermissionType permissionType) {
this(user, permissionType, null);
setName("");
}
@ManyToOne(fetch=FetchType.LAZY)
public E getSubject() {
return subject;
}
public void setSubject(E subject) {
this.subject = subject;
}
}
@Entity
@DiscriminatorValue("RESOURCE")
@AttributeOverride(name="subject", column=@Column(name="RESOURCE_SUBJECT_ID"))
@AssociationOverride(name="subject", joinColumns=@JoinColumn(name="RESOURCE_SUBJECT_ID"))
public class PermissionResource extends IdentityPermission<Resource> {
public PermissionResource() {
}
public PermissionResource(User user, PermissionType permissionType, Resource subject) {
super(user, permissionType, subject);
}
public PermissionResource(User user, PermissionType permissionType) {
super(user, permissionType);
}
}
I get the following exception:
Code:
Caused by: org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions
at org.hibernate.collection.AbstractPersistentCollection.setCurrentSession(AbstractPersistentCollection.java:410)
at org.hibernate.event.def.WrapVisitor.processCollection(WrapVisitor.java:44)
at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:101)
at org.hibernate.event.def.WrapVisitor.processValue(WrapVisitor.java:98)
at org.hibernate.event.def.AbstractVisitor.processEntityPropertyValues(AbstractVisitor.java:55)
at org.hibernate.event.def.AbstractSaveEventListener.visitCollectionsBeforeSave(AbstractSaveEventListener.java:371)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:273)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:181)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:107)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:187)
at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:33)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:172)
at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:27)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:535)
at org.hibernate.impl.SessionImpl.save(SessionImpl.java:523)
at org.hibernate.impl.SessionImpl.save(SessionImpl.java:519)
at com.recoveryplanner.entity.util.RPXInterceptor.postFlush(RPXInterceptor.java:137)
So I guess I can't modify some collection property of the entity and save again in postFlush. Any other ideas how to implement this?
I tried saving only Permission object however no association between the AuthorizedAccessEntity and the row in t_permission table is made. (null in the field in t_permission where the entity's id should be.) so it's like no subject is set.
Any ideas?