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)
{