diff --git a/pom.xml b/pom.xml
index cbd4225..254afb6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -66,6 +66,12 @@
1.1
provided
+
+ javax.ejb
+ javax.ejb-api
+ 3.2
+ provided
+
diff --git a/src/main/java/br/com/caelum/vraptor/security/interceptor/SecurityInterceptor.java b/src/main/java/br/com/caelum/vraptor/security/interceptor/SecurityInterceptor.java
index 368f956..aaffd80 100644
--- a/src/main/java/br/com/caelum/vraptor/security/interceptor/SecurityInterceptor.java
+++ b/src/main/java/br/com/caelum/vraptor/security/interceptor/SecurityInterceptor.java
@@ -1,15 +1,22 @@
package br.com.caelum.vraptor.security.interceptor;
+import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
+import java.util.Arrays;
import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
-import org.apache.shiro.aop.MethodInvocation;
import org.apache.shiro.authz.AuthorizationException;
+import org.apache.shiro.authz.annotation.RequiresAuthentication;
+import org.apache.shiro.authz.annotation.RequiresGuest;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.apache.shiro.authz.annotation.RequiresRoles;
+import org.apache.shiro.authz.annotation.RequiresUser;
import org.apache.shiro.authz.aop.AnnotationsAuthorizingMethodInterceptor;
+import org.apache.shiro.subject.Subject;
import br.com.caelum.vraptor.security.AuthorizationRestrictionListener;
import br.com.caelum.vraptor.security.annotation.Secured;
@@ -17,40 +24,57 @@
@Interceptor
@Secured
public class SecurityInterceptor extends AnnotationsAuthorizingMethodInterceptor {
-
+
@Inject private AuthorizationRestrictionListener listener;
+ @Inject private Subject subject;
+
+ public SecurityInterceptor() { super(); }
@AroundInvoke
public Object check(InvocationContext ctx) throws Exception {
try {
- assertAuthorized(new InvocationContextToMethodInvocationConverter(ctx));
+ Class> c = ctx.getTarget().getClass();
+ Method m = ctx.getMethod();
+
+ if (!subject.isAuthenticated() && hasAnnotation(c, m, RequiresAuthentication.class)) {
+ throw new AuthorizationException("Authentication required");
+ }
+
+ if (subject.getPrincipal() != null && hasAnnotation(c, m, RequiresGuest.class)) {
+ throw new AuthorizationException("Guest required");
+ }
+
+ if (subject.getPrincipal() == null && hasAnnotation(c, m, RequiresUser.class)) {
+ throw new AuthorizationException("User required");
+ }
+
+ RequiresRoles roles = getAnnotation(c, m, RequiresRoles.class);
+
+ if (roles != null) {
+ subject.checkRoles(Arrays.asList(roles.value()));
+ }
+
+ RequiresPermissions permissions = getAnnotation(c, m, RequiresPermissions.class);
+
+ if (permissions != null) {
+ subject.checkPermissions(permissions.value());
+ }
+ return ctx.proceed();
} catch(AuthorizationException e) {
listener.onAuthorizationRestriction(e);
}
- return ctx.proceed();
+ return null;
}
-
- private static class InvocationContextToMethodInvocationConverter implements MethodInvocation {
- private final InvocationContext context;
-
- public InvocationContextToMethodInvocationConverter(InvocationContext ctx) {
- context = ctx;
- }
- public Object proceed() throws Throwable {
- return context.proceed();
- }
-
- public Method getMethod() {
- return context.getMethod();
- }
-
- public Object[] getArguments() {
- return context.getParameters();
- }
+ private static boolean hasAnnotation(Class> c, Method m, Class extends Annotation> a) {
+ return m.isAnnotationPresent(a)
+ || c.isAnnotationPresent(a)
+ || c.getSuperclass().isAnnotationPresent(a);
+ }
- public Object getThis() {
- return context.getTarget();
- }
+ private static A getAnnotation(Class> c, Method m, Class a) {
+ return m.isAnnotationPresent(a) ? m.getAnnotation(a)
+ : c.isAnnotationPresent(a) ? c.getAnnotation(a)
+ : c.getSuperclass().getAnnotation(a);
}
}
diff --git a/src/main/java/br/com/caelum/vraptor/security/produces/SafeSubject.java b/src/main/java/br/com/caelum/vraptor/security/produces/SafeSubject.java
new file mode 100644
index 0000000..b1aa7c8
--- /dev/null
+++ b/src/main/java/br/com/caelum/vraptor/security/produces/SafeSubject.java
@@ -0,0 +1,191 @@
+package br.com.caelum.vraptor.security.produces;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authz.AuthorizationException;
+import org.apache.shiro.authz.Permission;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.subject.ExecutionException;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.apache.shiro.subject.Subject;
+
+public class SafeSubject implements Serializable, Subject {
+
+ private Subject subject;
+
+ public SafeSubject(Subject subject) {
+ this.subject = subject;
+ }
+
+ @Override
+ public Object getPrincipal() {
+ return subject.getPrincipal();
+ }
+
+ @Override
+ public PrincipalCollection getPrincipals() {
+ return subject.getPrincipals();
+ }
+
+ @Override
+ public boolean isPermitted(String permission) {
+ return subject.isPermitted(permission);
+ }
+
+ @Override
+ public boolean isPermitted(Permission permission) {
+ return subject.isPermitted(permission);
+ }
+
+ @Override
+ public boolean[] isPermitted(String... permissions) {
+ return subject.isPermitted(permissions);
+ }
+
+ @Override
+ public boolean[] isPermitted(List permissions) {
+ return subject.isPermitted(permissions);
+ }
+
+ @Override
+ public boolean isPermittedAll(String... permissions) {
+ return subject.isPermittedAll(permissions);
+ }
+
+ @Override
+ public boolean isPermittedAll(Collection permissions) {
+ return subject.isPermittedAll(permissions);
+ }
+
+ @Override
+ public void checkPermission(String permission)
+ throws AuthorizationException {
+ this.subject.checkPermission(permission);
+ }
+
+ @Override
+ public void checkPermission(Permission permission)
+ throws AuthorizationException {
+ this.subject.checkPermission(permission);
+ }
+
+ @Override
+ public void checkPermissions(String... permissions)
+ throws AuthorizationException {
+ this.subject.checkPermissions(permissions);
+ }
+
+ @Override
+ public void checkPermissions(Collection permissions)
+ throws AuthorizationException {
+ this.subject.checkPermissions(permissions);
+ }
+
+ @Override
+ public boolean hasRole(String roleIdentifier) {
+ return this.subject.hasRole(roleIdentifier);
+ }
+
+ @Override
+ public boolean[] hasRoles(List roleIdentifiers) {
+ return this.subject.hasRoles(roleIdentifiers);
+ }
+
+ @Override
+ public boolean hasAllRoles(Collection roleIdentifiers) {
+ return this.subject.hasAllRoles(roleIdentifiers);
+ }
+
+ @Override
+ public void checkRole(String roleIdentifier) throws AuthorizationException {
+ this.subject.checkRole(roleIdentifier);
+ }
+
+ @Override
+ public void checkRoles(Collection roleIdentifiers)
+ throws AuthorizationException {
+ this.subject.checkRoles(roleIdentifiers);
+ }
+
+ @Override
+ public void checkRoles(String... roleIdentifiers)
+ throws AuthorizationException {
+ this.subject.checkRoles(roleIdentifiers);
+ }
+
+ @Override
+ public void login(AuthenticationToken token) throws AuthenticationException {
+ this.subject.login(token);
+ }
+
+ @Override
+ public boolean isAuthenticated() {
+ return this.subject.isAuthenticated();
+ }
+
+ @Override
+ public boolean isRemembered() {
+ return this.subject.isRemembered();
+ }
+
+ @Override
+ public Session getSession() {
+ return this.subject.getSession();
+ }
+
+ @Override
+ public Session getSession(boolean create) {
+ return this.subject.getSession(create);
+ }
+
+ @Override
+ public void logout() {
+ this.subject.logout();
+ }
+
+ @Override
+ public V execute(Callable callable) throws ExecutionException {
+ return this.subject.execute(callable);
+ }
+
+ @Override
+ public void execute(Runnable runnable) {
+ this.subject.execute(runnable);
+ }
+
+ @Override
+ public Callable associateWith(Callable callable) {
+ return this.subject.associateWith(callable);
+ }
+
+ @Override
+ public Runnable associateWith(Runnable runnable) {
+ return this.subject.associateWith(runnable);
+ }
+
+ @Override
+ public void runAs(PrincipalCollection principals)
+ throws NullPointerException, IllegalStateException {
+ this.subject.runAs(principals);
+ }
+
+ @Override
+ public boolean isRunAs() {
+ return this.subject.isRunAs();
+ }
+
+ @Override
+ public PrincipalCollection getPreviousPrincipals() {
+ return this.subject.getPreviousPrincipals();
+ }
+
+ @Override
+ public PrincipalCollection releaseRunAs() {
+ return this.subject.releaseRunAs();
+ }
+}
diff --git a/src/main/java/br/com/caelum/vraptor/security/produces/SecurityFacade.java b/src/main/java/br/com/caelum/vraptor/security/produces/SecurityFacade.java
index 723ce96..81b249e 100644
--- a/src/main/java/br/com/caelum/vraptor/security/produces/SecurityFacade.java
+++ b/src/main/java/br/com/caelum/vraptor/security/produces/SecurityFacade.java
@@ -5,12 +5,13 @@
import java.util.Collection;
import javax.annotation.PostConstruct;
-import javax.enterprise.context.ApplicationScoped;
+import javax.ejb.Startup;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Instance;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
+import javax.inject.Singleton;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationListener;
@@ -30,7 +31,8 @@
import br.com.caelum.vraptor.security.strategy.ShiroInitConfigStrategy;
-@ApplicationScoped
+@Startup
+@Singleton
public class SecurityFacade {
@Inject @Any private Instance authenticationListeners;
@@ -75,14 +77,14 @@ public SecurityManager getSecurityManager() {
return SecurityUtils.getSecurityManager();
}
- @Produces
+ @Produces @SessionScoped
public Subject getSubject() {
- return SecurityUtils.getSubject();
+ return new SafeSubject( SecurityUtils.getSubject() );
}
@Produces @SessionScoped
public Session getSession() {
- return new SafeSession(SecurityUtils.getSubject().getSession());
+ return new SafeSession( getSubject().getSession() );
}
@Produces
diff --git a/src/main/resources/META-INF/beans.xml b/src/main/resources/META-INF/beans.xml
index 680a7d9..c81de25 100644
--- a/src/main/resources/META-INF/beans.xml
+++ b/src/main/resources/META-INF/beans.xml
@@ -2,8 +2,4 @@
-
-
- br.com.caelum.vraptor.security.interceptor.SecurityInterceptor
-
\ No newline at end of file