package com.andypemberton.spring.mvc.portlet.security.aspect;

import java.lang.reflect.Method;
import java.util.Arrays;

import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.portlet.PortletRequest;
import javax.portlet.PortletSecurityException;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

/**
 * An AspectJ aspect that checks JSR250 security annotations (PermitAll, DenyAll, RolesAllowed)
 * against annotation-driven Spring Portlet MVC controllers.
 * 
 * @author Andy Pemberton
 *
 */
@Aspect
public class SpringJSR250SecurityAspect {

	@Pointcut("execution(java.lang.reflect.Method org.springframework.web.portlet.mvc.annotation..*.resolveHandlerMethod(..)) && args(request,..)")
	public void resolveHandlerMethodCall(PortletRequest request) {
	}

	@AfterReturning(pointcut = "resolveHandlerMethodCall(request)", returning = "method")
	public void applyAuthorization(JoinPoint joinPoint, Method method, PortletRequest request) throws PortletSecurityException {
		if (method != null) {
			PermitAll permitAll = method.getAnnotation(PermitAll.class);
			DenyAll denyAll = method.getAnnotation(DenyAll.class);
			RolesAllowed rolesAllowed = method.getAnnotation(RolesAllowed.class);

			if (permitAll != null && denyAll != null) {
				throw new IllegalStateException(method.toString() + " marked with both DenyAll and PermitAll.");
			} else if (denyAll != null) {
				throw new PortletSecurityException("Cannot access method: " + method.toString() + "; it is secured with DenyAll.");
			} else if (rolesAllowed != null) {
				boolean authorized = false;
				for (String role : rolesAllowed.value()) {
					if (request.isUserInRole(role)) {
						authorized = true;
						break;
					}
				}
				if (!authorized) { throw new PortletSecurityException("Cannot access: " + method.toString()
						+ "; it is secured with RolesAllowed and the current user is not in the list of allowed roles: "
						+ Arrays.toString(rolesAllowed.value())); }
			}
		}
	}
}
