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