package com.softwaremill.common.cdi.security; import com.google.common.collect.ImmutableMap; import com.softwaremill.common.cdi.el.ELEvaluator; import javax.inject.Inject; import javax.interceptor.AroundInvoke; import javax.interceptor.Interceptor; import javax.interceptor.InvocationContext; import java.io.Serializable; import java.lang.annotation.Annotation; import java.util.HashMap; import java.util.Map; /** * @author Adam Warski (adam at warski dot org) */ @Interceptor @InterceptSecure({}) public class SecurityInterceptor implements Serializable { @Inject private SecurityExtension se; @Inject private ELEvaluator elEvaluator; @Inject private SecurityFlags securityFlags; @AroundInvoke public Object invoke(InvocationContext ctx) throws Exception { // Getting the generated @InterceptSecure annotation for the method. // After the CDI Maintenance Release is released, it should be possible to get the annotated type of the // currently invoked bean, see: // http://old.nabble.com/Retrieving-the-Bean-object-for-an-interceptor-td28147499.html // For now, we just use a map in the extension. One limitation is that this doesn't allow different security // annotations for methods which are used in several beans. InterceptSecure is = se.getInterceptSecure(ctx.getMethod()); // First checking the security flags AllowWithFlag[] allowWithFlags = is == null? new AllowWithFlag[0] : is.flags(); for (AllowWithFlag allowWithFlag : allowWithFlags) { for (String flag : allowWithFlag.value()) { if (securityFlags.hasFlag(flag)) { return ctx.proceed(); } } } // Then checking the restrictions Secure[] constraints = is == null ? new Secure[0] : is.value(); // Getting the parameter values // TODO: compute at the beginning Map<String, Object> secureVars = computeParameterValues(ctx); for (Secure constraint : constraints) { Boolean expressionValue = elEvaluator.evaluate(constraint.value(), Boolean.class, secureVars); if (expressionValue == null || !expressionValue) { // TODO: message throw new SecurityConditionException(); } } return ctx.proceed(); } private Map<String, Object> computeParameterValues(InvocationContext ctx) { Annotation[][] parametersAnnotations = ctx.getMethod().getParameterAnnotations(); Map<String, Object> secureVars = new HashMap<String, Object>(); for (int i = 0; i < parametersAnnotations.length; i++) { Annotation[] parameterAnnotations = parametersAnnotations[i]; for (Annotation parameterAnnotation : parameterAnnotations) { if (SecureVar.class.isAssignableFrom(parameterAnnotation.annotationType())) { SecureVar secureVar = (SecureVar) parameterAnnotation; Object paramValue = ctx.getParameters()[i]; // If there's an expression associated with the variable, use its value instead of the // parameter. if (!"".equals(secureVar.exp())) { paramValue = evaluateSecureVarExp(paramValue, secureVar); } secureVars.put(secureVar.value(), paramValue); } } } return secureVars; } public Object evaluateSecureVarExp(Object base, SecureVar secureVar) { return elEvaluator.evaluate(secureVar.exp(), Object.class, ImmutableMap.of("p", base)); } }