package com.tododev.shiro;
import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.constraints.NotNull;
import org.apache.bval.guice.Validate;
import org.apache.bval.guice.ValidationModule;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.guice.aop.ShiroAopModule;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
/**
* Vamos a hacer que el test se ejecute con la clase Parameterized.
* Esta clase nos ejecutara 2 veces todos los tests, insertando en el constructor los valores del
* metodo data().
* @author jorge
*
*/
@RunWith(Parameterized.class)
public class GuiceAopTest {
private final static String ADMIN_ROLE = "admin";
private final UsernamePasswordToken ADMIN = new UsernamePasswordToken("root", "secret");
private final UsernamePasswordToken GUEST = new UsernamePasswordToken("guest", "guest");
private final Injector injector;
/**
* Cuando se crea la instancia de GuiceAopTest.class, se le inserta
* uno de los valores del metodo data().
* @param injector
*/
public GuiceAopTest(Injector injector){
this.injector = injector;
}
/**
* Devuelve una coleccion que contiene un inyector con AOP y otro que no.
* La idea es demostrar que los tests pasan satisfactoriamente de las dos formas posibles.
* @return
*/
@Parameters
public static Collection<Object[]> data() {
Injector withAop = Guice.createInjector(new ShiroAopModule(), new MyShiroModule(), new ValidationModule(), new AbstractModule() {
@Override
protected void configure() {
bind(Interface.class).to(Aop.class);
}
});
Injector noAop = Guice.createInjector(new MyShiroModule(), new AbstractModule(){
@Override
protected void configure() {
bind(Interface.class).to(NoAop.class);
}
});
Object[][] data={{noAop},{withAop}};
return Arrays.asList(data);
}
public static interface Interface{
void doSomething(String value);
}
/**
* Implementacion preparada para tener aspectos.
* @author jorge
*
*/
public static class Aop implements Interface{
@Validate
@RequiresRoles(ADMIN_ROLE)
@Override
public void doSomething(@NotNull String value) {
System.out.println(getClass()+": "+value);
}
}
/**
* Hace lo mismo que hace Aop, pero esta no usara aspectos.
* @author jorge
*
*/
public static class NoAop implements Interface{
@Override
public void doSomething(String value) {
if(value != null){
Subject currentUser = SecurityUtils.getSubject();
if(currentUser.isAuthenticated()){
if(currentUser.isPermitted(ADMIN_ROLE)){
System.out.println(getClass()+": "+value);
}else{
throw new UnauthorizedException();
}
}else{
throw new UnauthenticatedException();
}
}else{
throw new ConstraintViolationException(new HashSet<ConstraintViolation<?>>());
}
}
}
/**
* Preparamos Shiro para ser usado.
*/
@Before
public void before(){
SecurityManager securityManager = injector.getInstance(SecurityManager.class);
SecurityUtils.setSecurityManager(securityManager);
}
/**
* Deslogueamos al usuario si estuviera logueado.
*/
@After
public void after(){
Subject currentUser = SecurityUtils.getSubject();
currentUser.logout();
}
/**
* Vefirica que si no estas logueado salta una UnauthenticatedException
*/
@Test(expected = UnauthenticatedException.class)
public void unauthenticated(){
Interface classToTest = injector.getInstance(Interface.class);
classToTest.doSomething("any value");
fail("Unreachable code");
}
/**
* Verifica que si no tienes los permisos adecuados salta una UnauthorizedException
*/
@Test(expected = UnauthorizedException.class)
public void unauthorized(){
Subject currentUser = SecurityUtils.getSubject();
currentUser.login(GUEST);
Interface classToTest = injector.getInstance(Interface.class);
classToTest.doSomething("any value");
fail("Unreachable code");
}
/**
* Verifica que si llamas al metodo con el String nulo, salta una ConstraintViolationException
*/
@Test(expected = ConstraintViolationException.class)
public void constraintViolation(){
Subject currentUser = SecurityUtils.getSubject();
currentUser.login(ADMIN);
Interface classToTest = injector.getInstance(Interface.class);
classToTest.doSomething(null);
fail("Unreachable code");
}
/**
* Verifica el caso de que todo es correcto y no hay excepciones
*/
@Test
public void allowed(){
Subject currentUser = SecurityUtils.getSubject();
currentUser.login(ADMIN);
Interface classToTest = injector.getInstance(Interface.class);
classToTest.doSomething("any value");
}
}