EXTCDI-261 EXTCDI-262 @Secured for stereotypes and support of custom meta-data

git-svn-id: https://svn.apache.org/repos/asf/myfaces/extensions/cdi/trunk@1239633 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/core/api/src/main/java/org/apache/myfaces/extensions/cdi/core/api/security/AccessDecisionVoterContext.java b/core/api/src/main/java/org/apache/myfaces/extensions/cdi/core/api/security/AccessDecisionVoterContext.java
index 958bb3e..bf33c0f 100644
--- a/core/api/src/main/java/org/apache/myfaces/extensions/cdi/core/api/security/AccessDecisionVoterContext.java
+++ b/core/api/src/main/java/org/apache/myfaces/extensions/cdi/core/api/security/AccessDecisionVoterContext.java
@@ -20,6 +20,7 @@
 

 import java.io.Serializable;

 import java.util.List;

+import java.util.Map;

 

 /**

  * Optional context which allows to get the current state as well as the results of the security check.

@@ -38,4 +39,19 @@
      * @return found violations

      */

     List<SecurityViolation> getViolations();

+

+    /**

+     * Exposes the found meta-data

+     * @return found meta-data

+     */

+    Map<String, Object> getMetaData();

+

+    /**

+     * Exposes meta-data for the given key

+     * @param key meta-data key

+     * @param targetType target type

+     * @param <T> target type

+     * @return meta-data for the given key or null if there is no value for the given key

+     */

+    <T> T getMetaDataFor(String key, Class<T> targetType);

 }

diff --git a/core/api/src/main/java/org/apache/myfaces/extensions/cdi/core/api/security/Secured.java b/core/api/src/main/java/org/apache/myfaces/extensions/cdi/core/api/security/Secured.java
index b3c5498..96e7c26 100644
--- a/core/api/src/main/java/org/apache/myfaces/extensions/cdi/core/api/security/Secured.java
+++ b/core/api/src/main/java/org/apache/myfaces/extensions/cdi/core/api/security/Secured.java
@@ -29,12 +29,13 @@
 import static java.lang.annotation.RetentionPolicy.RUNTIME;

 import static java.lang.annotation.ElementType.TYPE;

 import static java.lang.annotation.ElementType.METHOD;

+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;

 

 /**

  * Interceptor for securing beans.

  * It's also possible to use it as meta-annotation for type-safe view-configs.

  */

-@Target({TYPE, METHOD})

+@Target({TYPE, METHOD, ANNOTATION_TYPE})

 @Retention(RUNTIME)

 @Documented

 

diff --git a/core/impl/src/main/java/org/apache/myfaces/extensions/cdi/core/impl/security/spi/EditableAccessDecisionVoterContext.java b/core/impl/src/main/java/org/apache/myfaces/extensions/cdi/core/impl/security/spi/EditableAccessDecisionVoterContext.java
index f1e809c..ac88567 100644
--- a/core/impl/src/main/java/org/apache/myfaces/extensions/cdi/core/impl/security/spi/EditableAccessDecisionVoterContext.java
+++ b/core/impl/src/main/java/org/apache/myfaces/extensions/cdi/core/impl/security/spi/EditableAccessDecisionVoterContext.java
@@ -28,6 +28,14 @@
 public interface EditableAccessDecisionVoterContext extends AccessDecisionVoterContext

 {

     /**

+     * Allows to add custom meta-data. The default security strategy adds custom annotations of the intercepted method

+     * as well as class-level annotations. (Currently inherited annotations aren't supported)

+     * @param key key for the meta-data

+     * @param metaData meta-data which should be added

+     */

+    void addMetaData(String key, Object metaData);

+

+    /**

      * Updates the state of the context

      * @param accessDecisionVoterState current state

      */

diff --git a/core/impl/src/main/java/org/apache/myfaces/extensions/cdi/core/impl/util/SecurityUtils.java b/core/impl/src/main/java/org/apache/myfaces/extensions/cdi/core/impl/util/SecurityUtils.java
index b4c7821..cdfd17c 100644
--- a/core/impl/src/main/java/org/apache/myfaces/extensions/cdi/core/impl/util/SecurityUtils.java
+++ b/core/impl/src/main/java/org/apache/myfaces/extensions/cdi/core/impl/util/SecurityUtils.java
@@ -50,11 +50,13 @@
      * Helper for invoking the given {@link AccessDecisionVoter}s

      * @param invocationContext current invocation-context (might be null in case of secured views)

      * @param beanManager current bean-manager

+     * @param voterContext current access-decision-voter-context

      * @param accessDecisionVoters current access-decision-voters

      * @param errorView optional inline error view

      */

     public static void invokeVoters(InvocationContext invocationContext,

                                     BeanManager beanManager,

+                                    AccessDecisionVoterContext voterContext,

                                     List<Class<? extends AccessDecisionVoter>> accessDecisionVoters,

                                     Class<? extends ViewConfig> errorView)

     {

@@ -63,9 +65,6 @@
             return;

         }

 

-        AccessDecisionVoterContext voterContext =

-                CodiUtils.getContextualReferenceByClass(beanManager, AccessDecisionVoterContext.class, true);

-

         AccessDecisionState voterState = AccessDecisionState.VOTE_IN_PROGRESS;

         try

         {

diff --git a/jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/security/DefaultAccessDecisionVoterStateContext.java b/jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/security/DefaultAccessDecisionVoterStateContext.java
index 187e788..48b37c5 100644
--- a/jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/security/DefaultAccessDecisionVoterStateContext.java
+++ b/jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/security/DefaultAccessDecisionVoterStateContext.java
@@ -25,7 +25,9 @@
 import javax.enterprise.context.RequestScoped;

 import java.util.ArrayList;

 import java.util.Collections;

+import java.util.HashMap;

 import java.util.List;

+import java.util.Map;

 

 /**

  * {@inheritDoc}

@@ -39,6 +41,8 @@
 

     private List<SecurityViolation> securityViolations;

 

+    private Map<String, Object> metaData = new HashMap<String, Object>();

+

     /**

      * {@inheritDoc}

      */

@@ -62,13 +66,45 @@
     /**

      * {@inheritDoc}

      */

+    public Map<String, Object> getMetaData()

+    {

+        return Collections.unmodifiableMap(this.metaData);

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    public <T> T getMetaDataFor(String key, Class<T> targetType)

+    {

+        return (T)this.metaData.get(key);

+    }

+

+    public void addMetaData(String key, Object metaData)

+    {

+        //TODO specify nested security calls

+        this.metaData.put(key, metaData);

+    }

+

+    /**

+     * {@inheritDoc}

+     */

     public void setState(AccessDecisionState accessDecisionVoterState)

     {

         if(AccessDecisionState.VOTE_IN_PROGRESS.equals(accessDecisionVoterState))

         {

             this.securityViolations = new ArrayList<SecurityViolation>(); //lazy init

         }

+

         this.state = accessDecisionVoterState;

+

+        if(AccessDecisionState.INITIAL.equals(accessDecisionVoterState) ||

+                AccessDecisionState.VOTE_IN_PROGRESS.equals(accessDecisionVoterState))

+        {

+            return;

+        }

+

+        //meta-data is only needed until the end of a voting process

+        this.metaData.clear();

     }

 

     /**

diff --git a/jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/security/DefaultSecurityStrategy.java b/jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/security/DefaultSecurityStrategy.java
index 0927d1f..a1284c5 100644
--- a/jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/security/DefaultSecurityStrategy.java
+++ b/jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/security/DefaultSecurityStrategy.java
@@ -18,6 +18,9 @@
  */

 package org.apache.myfaces.extensions.cdi.jsf.impl.security;

 

+import org.apache.myfaces.extensions.cdi.core.api.security.AccessDecisionVoterContext;

+import org.apache.myfaces.extensions.cdi.core.impl.security.spi.EditableAccessDecisionVoterContext;

+import org.apache.myfaces.extensions.cdi.core.impl.util.CodiUtils;

 import org.apache.myfaces.extensions.cdi.jsf.impl.security.spi.SecurityStrategy;

 import org.apache.myfaces.extensions.cdi.core.api.security.Secured;

 import org.apache.myfaces.extensions.cdi.core.api.security.AccessDecisionVoter;

@@ -27,8 +30,11 @@
 import javax.enterprise.inject.spi.BeanManager;

 import javax.enterprise.context.Dependent;

 import javax.interceptor.InvocationContext;

+import java.lang.annotation.Annotation;

+import java.util.ArrayList;

 import java.util.Arrays;

 import java.lang.reflect.Method;

+import java.util.List;

 

 /**

  * {@inheritDoc}

@@ -47,29 +53,66 @@
      */

     public Object execute(InvocationContext invocationContext) throws Exception

     {

-        Secured secured = getSecuredAnnotation(invocationContext);

+        AccessDecisionVoterContext voterContext =

+                CodiUtils.getContextualReferenceByClass(beanManager, AccessDecisionVoterContext.class, true);

 

-        Class<? extends AccessDecisionVoter>[] voterClasses = secured.value();

+        Secured secured = null;

 

-        invokeVoters(invocationContext, this.beanManager, Arrays.asList(voterClasses), secured.errorView());

+        List<Annotation> annotatedTypeMetadata = extractMetadata(invocationContext);

+

+        for (Annotation annotation : annotatedTypeMetadata)

+        {

+            if(Secured.class.isAssignableFrom(annotation.annotationType()))

+            {

+                secured = (Secured)annotation;

+            }

+            else if(voterContext instanceof EditableAccessDecisionVoterContext)

+            {

+                ((EditableAccessDecisionVoterContext)voterContext)

+                        .addMetaData(annotation.annotationType().getName(), annotation);

+            }

+        }

+

+        if(secured != null)

+        {

+            Class<? extends AccessDecisionVoter>[] voterClasses = secured.value();

+

+            invokeVoters(invocationContext, this.beanManager, voterContext,

+                    Arrays.asList(voterClasses), secured.errorView());

+        }

 

         return invocationContext.proceed();

     }

 

-    //TODO refactor it to a generic impl. and move it to an util class

-    private Secured getSecuredAnnotation(InvocationContext invocationContext)

+    private List<Annotation> extractMetadata(InvocationContext invocationContext)

     {

-        Secured secured;

+        List<Annotation> result = new ArrayList<Annotation>();

+

         Method method = invocationContext.getMethod();

 

-        if(method.isAnnotationPresent(Secured.class))

+        result.addAll(getAllAnnotations(method.getAnnotations()));

+        result.addAll(getAllAnnotations(method.getDeclaringClass().getAnnotations()));

+

+        return result;

+    }

+

+    private List<Annotation> getAllAnnotations(Annotation[] annotations)

+    {

+        List<Annotation> result = new ArrayList<Annotation>();

+

+        String annotationName;

+        for(Annotation annotation : annotations)

         {

-            secured = method.getAnnotation(Secured.class);

+            annotationName = annotation.annotationType().getName();

+            if(annotationName.startsWith("java.") || annotationName.startsWith("javax."))

+            {

+                continue;

+            }

+

+            result.add(annotation);

+            result.addAll(getAllAnnotations(annotation.annotationType().getAnnotations()));

         }

-        else

-        {

-            secured = method.getDeclaringClass().getAnnotation(Secured.class);

-        }

-        return secured;

+

+        return result;

     }

 }

diff --git a/jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/security/SecurityAwareViewHandler.java b/jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/security/SecurityAwareViewHandler.java
index 8ccf10b..c22527c 100644
--- a/jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/security/SecurityAwareViewHandler.java
+++ b/jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/security/SecurityAwareViewHandler.java
@@ -21,8 +21,10 @@
 import org.apache.myfaces.extensions.cdi.core.api.activation.Deactivatable;

 import org.apache.myfaces.extensions.cdi.core.api.config.view.ViewConfig;

 import org.apache.myfaces.extensions.cdi.core.api.provider.BeanManagerProvider;

+import org.apache.myfaces.extensions.cdi.core.api.security.AccessDecisionVoterContext;

 import org.apache.myfaces.extensions.cdi.core.api.security.AccessDeniedException;

 import org.apache.myfaces.extensions.cdi.core.impl.util.ClassDeactivation;

+import org.apache.myfaces.extensions.cdi.core.impl.util.CodiUtils;

 import org.apache.myfaces.extensions.cdi.jsf.api.config.view.ViewConfigDescriptor;

 import org.apache.myfaces.extensions.cdi.jsf.impl.config.view.ViewConfigCache;

 import org.apache.myfaces.extensions.cdi.jsf.impl.config.view.spi.EditableViewConfigDescriptor;

@@ -92,6 +94,9 @@
             {

                 lazyInit();

 

+                AccessDecisionVoterContext voterContext =

+                        CodiUtils.getContextualReferenceByClass(beanManager, AccessDecisionVoterContext.class, true);

+

                 Class<? extends ViewConfig> errorView = null;

 

                 if(entry instanceof EditableViewConfigDescriptor)

@@ -99,7 +104,7 @@
                     errorView = ((EditableViewConfigDescriptor)entry).getErrorView();

                 }

 

-                invokeVoters(null /*TODO*/, this.beanManager, entry.getAccessDecisionVoters(), errorView);

+                invokeVoters(null /*TODO*/, this.beanManager, voterContext, entry.getAccessDecisionVoters(), errorView);

             }

         }

         catch (AccessDeniedException accessDeniedException)

diff --git a/jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/security/SecurityViewListener.java b/jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/security/SecurityViewListener.java
index a1b2f6e..6c85d7d 100644
--- a/jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/security/SecurityViewListener.java
+++ b/jee-modules/jsf-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jsf/impl/security/SecurityViewListener.java
@@ -19,6 +19,8 @@
 package org.apache.myfaces.extensions.cdi.jsf.impl.security;

 

 import org.apache.myfaces.extensions.cdi.core.api.config.view.ViewConfig;

+import org.apache.myfaces.extensions.cdi.core.api.security.AccessDecisionVoterContext;

+import org.apache.myfaces.extensions.cdi.core.impl.util.CodiUtils;

 import org.apache.myfaces.extensions.cdi.jsf.api.listener.phase.AfterPhase;

 import org.apache.myfaces.extensions.cdi.jsf.api.listener.phase.BeforePhase;

 import static org.apache.myfaces.extensions.cdi.jsf.api.listener.phase.JsfPhaseId.*;

@@ -102,11 +104,14 @@
         {

             Class<? extends ViewConfig> errorView = null;

 

+            AccessDecisionVoterContext voterContext =

+                    CodiUtils.getContextualReferenceByClass(beanManager, AccessDecisionVoterContext.class, true);

+

             if(entry instanceof EditableViewConfigDescriptor)

             {

                 errorView = ((EditableViewConfigDescriptor)entry).getErrorView();

             }

-            invokeVoters(null, beanManager, entry.getAccessDecisionVoters(), errorView);

+            invokeVoters(null, beanManager, voterContext, entry.getAccessDecisionVoters(), errorView);

         }

         catch (AccessDeniedException accessDeniedException)

         {