Java and JBoss related stuff
RSS icon Home icon
  • Simple security interceptor in Weld/JSF2

    Posted on March 31st, 2010 Adam Warski 7 comments

    Waiting for the Seam3 security module, I wrote a simple security interceptor (inspired by the Seam2 security annotation). You can use it like this:

    
    @Secure("#{loggedInUser.name == arg0.name}")
    public List<Message> listMessages(User owner) { ... }
    

    Here, loggedInUser is a @Named Weld (CDI) bean, and arg0 is the first argument of the method. This is a slight modification to the Seam2 security annotation, where it wasn’t possible to reference arguments. If the EL expression doesn’t evaluate to true, an exception is thrown.

    The implementation is pretty straightforward, as adding an interceptor in Weld is really simple. First, we need the annotation:

    
    /**
     * @author Adam Warski (adam at warski dot org)
     */
    @Retention(RUNTIME)
    @Target({TYPE, METHOD})
    @InterceptorBinding
    public @interface Secure {
        /**
         * @return The EL expression that should be evaluated. If it evaluates to
         * {@code true}, access will be granted. The EL expression may reference
         * any objects that are in any context, as well as the arguments of the method,
         * under the names {@code arg0, arg1, arg2, ...}.
         */
        @Nonbinding
        String  value();
    }
    

    The key components here is the @InterceptorBinding meta-annotation, and specifying the value of to be @Nonbinding. If the value element was binding, then we would need to define an interceptor for each possible String value.

    Next, the interceptor itself:

    
    @Interceptor
    @Secure("")
    public class SecurityInterceptor {
        private String getArgName(int index) { return "arg" + index; }
    
        @AroundInvoke
        public Object invoke(InvocationContext ctx) throws Exception {
            FacesContext facesCtx = FacesContext.getCurrentInstance();
            ELContext elCtx = facesCtx.getELContext();
    
            Secure secure = getSecureAnnotation(ctx.getMethod());
            String expression = secure.value();
    
            // Populating the request map so that parameters are available (arg0, ...)
            Map<String, Object> requestMap = facesCtx.getExternalContext()
                    .getRequestMap();
            for (int i = 0; i < ctx.getParameters().length; i++) {
                Object parameter = ctx.getParameters()[i];
                requestMap.put(getArgName(i), parameter);
            }
    
            Boolean expressionValue = (Boolean) facesCtx.getApplication()
                    .getExpressionFactory()
                    .createValueExpression(elCtx, expression, Boolean.class)
                    .getValue(elCtx);
    
            // Removing the parameters (arg0, arg1, ...)
            for (int i = 0; i < ctx.getParameters().length; i++) {
                requestMap.remove(getArgName(i));
            }
    
            if (expressionValue == null || !expressionValue) {
                throw new SecurityException();
            }
    
            return ctx.proceed();
        }
    
        private Secure getSecureAnnotation(Method m) {
            for (Annotation a: m.getAnnotations()) {
                if (a instanceof Secure) { return (Secure) a; }
            }
            for (Annotation a: m.getDeclaringClass().getAnnotations()) {
                if (a instanceof Secure) { return (Secure) a; }
            }
    
            throw new RuntimeException("@Secure not found on method " + m.getName() +
                    " or its class " + m.getClass().getName());
        }
    }
    

    And finally, we need an entry in beans.xml, enabling the interceptor:

    
    <interceptors>
       <class>util.security.SecurityInterceptor</class>
    </interceptors>
    

    One shortcoming is that it’s not currently possible to place one @Secure annotation on the class, and another on the methods (see this thread on the forum). The idea is that then the class-level annotation expresses general security constraints, which can be later refined on the method level.

    Another missing feature, which could be easily added, is a message parameter to the annotation, which would be included in the exception in case the check fails.

    Adam

     

    5 responses to “Simple security interceptor in Weld/JSF2” RSS icon

    • Hey Adam,

      Great post! It nicely demonstrates how simple it is to implement powerful features from Seam 2 in just a couple lines of code.

      I have two suggestions:

      Rather than referencing arg0, arg1, etc in the EL expression, it would perhaps be cleaner and clearer to use annotations on designated method arguments to alias them to EL variables. Something like:

      @Secure(“#{loggedInUser.name == owner.name}”)
      public List listMessages(@ELVar(“owner”) User owner) { … }

      That brings me to my next suggestion. Rather than putting the temporary EL variables in request scope, the more formal way of doing it is to register them with the VariableMapper. That is how the iteration variables work in the c:forEach tag, for example:

      See ELContext#getVariableMapper()

      Cheers!

      Dan

    • Hey!

      Thanks :). Regarding your suggestions, I was thinking about naming the variables also, but left it out for simplicity. And thanks for the second suggestion, I was wondering what is the “correct” way to map those variables temporarily. I’ll give the VariableMapper a try.

      A challenge that also remains, is to make this work outside of JSF requests, e.g. from a call from an MDB. One problem is that there’s no FacesContext, so I would have to get the ELContext from someplace else, second is that there’s no user in the session (if the session scope is accessible at all) – so I would need a @RunAs there. But that’s future work :). Maybe seam3-security will be working by then, so I’ll try hacking it instead.

      Adam

      Adam

    • Thanks for the interesting article!

    • Joselito Sem Noção

      Hi!

      It’s possible to put a SQL query inside annotation?

      I like it! I will put all my code inside annotation… doing this I will not have any compilation errors!!

      Thanks for the brilliant idea!!

    • Sure, if you write an appropriate interceptor, you can put SQL, I think that even with sufficient time you could put some assembler code in annotations! ;)


    2 Trackbacks / Pingbacks

    Leave a reply