package es.internna.jee.security.impl;
import java.lang.reflect.AnnotatedElement;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import es.internna.jee.security.api.RoleMembershipManagement;
/**
*
* Interceptor that implements the security roles defined using the annotations from the JSR 250
* Annotations currently supported are: @RolesAllowed, @PermitAll, @DenyAll
*
* @author Jose Noheda
*
*/
@Aspect
public class SecurityInterceptor {
private Log log = LogFactory.getLog(SecurityInterceptor.class);
private final String[] executionResults = {"Undefined", "Forbidden", "Permitted"};
private RoleMembershipManagement roleMembershipManagement;
/**
* Detect the current roles of the user
*/
public void setRoleMembershipManagement(RoleMembershipManagement roleMembershipManagement) {
this.roleMembershipManagement = roleMembershipManagement;
}
/**
* Only a Method can be annoted with @DenyAll (<a href='http://java.sun.com/javaee/5/docs/api/javax/annotation/security/DenyAll.html' target='_top'>@DenyAll</a>)
* Methods with this annotation should always throw a SecurityException.
*/
@Before("@annotation(javax.annotation.security.DenyAll)")
public void deny(JoinPoint jp) throws Throwable {
if (log.isDebugEnabled()) log.debug("Method is annotated with @DenyAll. Cannot proceed");
throw new SecurityException();
}
/**
* RolesAllowed can be applied to a Method or a Class (<a href='http://java.sun.com/javaee/5/docs/api/javax/annotation/security/RolesAllowed.html' target='_top'>@RolesAllowed</a>)
* Methods with this annotation should check the current user roles before deciding to allow access.
*/
@Before("@annotation(javax.annotation.security.RolesAllowed) || @annotation(javax.annotation.security.PermitAll)")
public void checkRoles(JoinPoint jp) throws SecurityException {
if (log.isDebugEnabled()) log.debug("Aspect: SecurityInterceptor [" + this.hashCode() + "] has been fired");
int permitted = -1;
try {
MethodSignature met = (MethodSignature) jp.getSignature();
permitted = checkPermission(jp.getSourceLocation().getWithinType().getMethod(met.getMethod().getName(), met.getMethod().getParameterTypes()));
if(permitted == -1) permitted = checkPermission(jp.getSourceLocation().getWithinType());
} catch (Exception ex) {
if (log.isDebugEnabled()) log.debug(ex);
} finally {
if (log.isDebugEnabled()) log.debug("Aspect: SecurityInterceptor [" + this.hashCode() + "] execution result is " + executionResults[permitted + 1]);
if(permitted == 0) throw new SecurityException();
}
}
/**
* This function returns
* -1 if the object is not annotated (so no operation should be done)
* 0 if access should be denied
* 1 if the access should be allowed
*/
private int checkPermission(AnnotatedElement targetObject) {
int perm = -1;
if (log.isDebugEnabled()) log.debug("Checking user credentials to execute " + targetObject);
if (targetObject != null) {
RolesAllowed rolesAllowed = targetObject.getAnnotation(RolesAllowed.class);
if (rolesAllowed != null) {
if (log.isDebugEnabled()) log.debug("Detected roles for object " + rolesAllowed);
for (String role : rolesAllowed.value()) {
if (perm != 1) {
perm = this.roleMembershipManagement.isInRole(role) ? 1 : 0;
if (log.isDebugEnabled() & (perm == 1)) log.debug("Authenticated user is member of role: " + role);
}
}
if (log.isDebugEnabled() & (perm == 0)) log.debug("User is NOT member of any of the authorized roles");
} else {
if (targetObject.getAnnotation(PermitAll.class) != null) {
perm = 1;
if (log.isDebugEnabled()) log.debug("Object (" + targetObject + ") allowed by @PermitAll annotation");
}
}
}
return perm;
}
}