| 1 |
|
package org.trails.hibernate; |
| 2 |
|
|
| 3 |
|
import java.io.Serializable; |
| 4 |
|
|
| 5 |
|
import ognl.Ognl; |
| 6 |
|
import ognl.OgnlException; |
| 7 |
|
|
| 8 |
|
import org.acegisecurity.AuthenticationCredentialsNotFoundException; |
| 9 |
|
import org.acegisecurity.GrantedAuthority; |
| 10 |
|
import org.acegisecurity.context.SecurityContext; |
| 11 |
|
import org.acegisecurity.context.SecurityContextHolder; |
| 12 |
|
import org.acegisecurity.userdetails.UserDetails; |
| 13 |
|
import org.apache.commons.logging.Log; |
| 14 |
|
import org.apache.commons.logging.LogFactory; |
| 15 |
|
import org.hibernate.CallbackException; |
| 16 |
|
import org.hibernate.Transaction; |
| 17 |
|
import org.hibernate.type.Type; |
| 18 |
|
import org.trails.security.EntityModificationInterception; |
| 19 |
|
import org.trails.security.RestrictionType; |
| 20 |
|
import org.trails.security.TrailsSecurityException; |
| 21 |
|
import org.trails.security.annotation.RemoveRequiresAssociation; |
| 22 |
|
import org.trails.security.annotation.RemoveRequiresRole; |
| 23 |
|
import org.trails.security.annotation.UpdateRequiresAssociation; |
| 24 |
|
import org.trails.security.annotation.UpdateRequiresRole; |
| 25 |
|
|
| 26 |
9 |
public class TrailsSecurityInterceptor extends TrailsInterceptor { |
| 27 |
3 |
private static final Log log = LogFactory.getLog(TrailsSecurityInterceptor.class); |
| 28 |
|
|
| 29 |
|
private void checkRestriction(final Object entity, final RestrictionType restrictionType) { |
| 30 |
15 |
log.info("Check restriction for entity : " + entity); |
| 31 |
|
|
| 32 |
15 |
if (entity == null || restrictionType == null) return; |
| 33 |
15 |
SecurityContext context = SecurityContextHolder.getContext(); |
| 34 |
|
|
| 35 |
|
|
| 36 |
15 |
if (context == null || context.getAuthentication() == null) return; |
| 37 |
|
|
| 38 |
15 |
boolean roleRestriction = false; |
| 39 |
|
|
| 40 |
15 |
String[] requiredRole = null; |
| 41 |
15 |
switch (restrictionType) { |
| 42 |
|
case UPDATE : |
| 43 |
15 |
UpdateRequiresRole updateRestriction = entity.getClass().getAnnotation(UpdateRequiresRole.class ); |
| 44 |
15 |
if (updateRestriction != null) requiredRole = updateRestriction.value(); |
| 45 |
|
break; |
| 46 |
|
case REMOVE : |
| 47 |
0 |
RemoveRequiresRole removeRestriction = entity.getClass().getAnnotation(RemoveRequiresRole.class ); |
| 48 |
0 |
if (removeRestriction != null) requiredRole = removeRestriction.value(); |
| 49 |
9 |
break; |
| 50 |
|
} |
| 51 |
15 |
if (requiredRole != null) { |
| 52 |
6 |
GrantedAuthority[] authorities = context.getAuthentication().getAuthorities(); |
| 53 |
12 |
for (GrantedAuthority authority : authorities) |
| 54 |
6 |
for (String role : requiredRole) if (role.equals(authority.getAuthority()) ) return; |
| 55 |
6 |
roleRestriction = true; |
| 56 |
|
} |
| 57 |
|
|
| 58 |
15 |
String ownerPropertyAssociation = null; |
| 59 |
15 |
switch (restrictionType) { |
| 60 |
|
case UPDATE : |
| 61 |
15 |
UpdateRequiresAssociation updateRestriction = entity.getClass().getAnnotation(UpdateRequiresAssociation.class ); |
| 62 |
15 |
if (updateRestriction != null) ownerPropertyAssociation = updateRestriction.value(); |
| 63 |
|
break; |
| 64 |
|
case REMOVE : |
| 65 |
0 |
RemoveRequiresAssociation removeRestriction = entity.getClass().getAnnotation(RemoveRequiresAssociation.class ); |
| 66 |
0 |
if (removeRestriction != null) ownerPropertyAssociation = removeRestriction.value(); |
| 67 |
|
break; |
| 68 |
|
} |
| 69 |
|
|
| 70 |
15 |
if (ownerPropertyAssociation != null) if (checkOwnershipRestriction(entity, ownerPropertyAssociation) ) return; |
| 71 |
|
|
| 72 |
|
|
| 73 |
|
|
| 74 |
0 |
if (roleRestriction) throw new EntityModificationInterception(entity, "Authenticated user does not have a required role or ownership"); |
| 75 |
|
|
| 76 |
0 |
} |
| 77 |
|
|
| 78 |
|
private boolean checkOwnershipRestriction(final Object entity, final String associatedProperty) { |
| 79 |
15 |
if (entity == null || associatedProperty == null) return false; |
| 80 |
|
|
| 81 |
|
try { |
| 82 |
15 |
SecurityContext context = SecurityContextHolder.getContext(); |
| 83 |
15 |
if (context.getAuthentication() == null) throw new AuthenticationCredentialsNotFoundException("Entity requires an authenticated user as owner"); |
| 84 |
15 |
String currentUserName = context.getAuthentication().getName(); |
| 85 |
15 |
if (currentUserName == null) currentUserName = ""; |
| 86 |
|
|
| 87 |
|
|
| 88 |
15 |
if ("".equals(associatedProperty) ) { |
| 89 |
6 |
if (!(entity instanceof UserDetails) ) throw new TrailsSecurityException("Entity is not of type UserDetails"); |
| 90 |
6 |
UserDetails userDetails = (UserDetails)entity; |
| 91 |
6 |
if (currentUserName.equals(userDetails.getUsername()) ) return true; |
| 92 |
3 |
else throw new EntityModificationInterception(entity, "Entity does not represent the authenticated user"); |
| 93 |
|
} |
| 94 |
|
|
| 95 |
9 |
Object value = Ognl.getValue(associatedProperty, entity); |
| 96 |
6 |
if (value == null) throw new EntityModificationInterception(entity, "Associated owner property is null"); |
| 97 |
|
|
| 98 |
6 |
if (value instanceof Iterable) { |
| 99 |
|
try { |
| 100 |
0 |
Iterable<UserDetails> iterable = (Iterable<UserDetails>)value; |
| 101 |
0 |
for (UserDetails userDetails : iterable) { |
| 102 |
0 |
if (currentUserName.equals(userDetails.getUsername()) ) { |
| 103 |
0 |
value = null; |
| 104 |
0 |
break; |
| 105 |
|
} |
| 106 |
|
} |
| 107 |
|
} |
| 108 |
0 |
catch (ClassCastException e) { |
| 109 |
0 |
throw new TrailsSecurityException("Associated collection doesn't contain UserDetails objects"); |
| 110 |
0 |
} |
| 111 |
|
|
| 112 |
0 |
if (value != null) throw new EntityModificationInterception(entity, "Authenticated user is not in the owners collection"); |
| 113 |
|
} |
| 114 |
|
else { |
| 115 |
6 |
if (!(value instanceof UserDetails) ) throw new TrailsSecurityException("Associate property is not of type UserDetails"); |
| 116 |
6 |
UserDetails userDetails = (UserDetails)value; |
| 117 |
6 |
if (!currentUserName.equals(userDetails.getUsername()) ) throw new EntityModificationInterception(entity, "Authenticated user is not the owner"); |
| 118 |
|
} |
| 119 |
|
} |
| 120 |
3 |
catch(OgnlException e) { |
| 121 |
3 |
throw new TrailsSecurityException("Could not evaluate the owner association", e); |
| 122 |
3 |
} |
| 123 |
3 |
return true; |
| 124 |
|
} |
| 125 |
|
|
| 126 |
|
|
| 127 |
|
|
| 128 |
|
|
| 129 |
|
public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, |
| 130 |
|
Object[] previousState, String[] propertyNames, Type[] types) throws CallbackException |
| 131 |
|
{ |
| 132 |
6 |
checkRestriction(entity, RestrictionType.UPDATE); |
| 133 |
3 |
return super.onFlushDirty(entity, id, currentState, previousState, propertyNames, types); |
| 134 |
|
} |
| 135 |
|
|
| 136 |
|
|
| 137 |
|
|
| 138 |
|
|
| 139 |
|
public boolean onSave(Object entity, Serializable id, Object[] state, |
| 140 |
|
String[] propertyNames, Type[] types) throws CallbackException |
| 141 |
|
{ |
| 142 |
9 |
checkRestriction(entity, RestrictionType.UPDATE); |
| 143 |
3 |
return super.onSave(entity, id, state, propertyNames, types); |
| 144 |
|
} |
| 145 |
|
|
| 146 |
|
|
| 147 |
|
|
| 148 |
|
|
| 149 |
|
public void onDelete(Object entity, Serializable arg1, Object[] arg2, |
| 150 |
|
String[] arg3, Type[] arg4) throws CallbackException |
| 151 |
|
{ |
| 152 |
0 |
checkRestriction(entity, RestrictionType.REMOVE); |
| 153 |
0 |
super.onDelete(entity, arg1, arg2, arg3, arg4); |
| 154 |
0 |
} |
| 155 |
|
} |