	package com.jax.security;
	
	import java.lang.reflect.Method;
	import java.util.Arrays;
	import java.util.HashSet;
	import java.util.List;
	import java.util.Set;
	import java.util.StringTokenizer;
	
	import javax.annotation.security.DenyAll;
	import javax.annotation.security.PermitAll;
	import javax.annotation.security.RolesAllowed;
	import javax.ws.rs.container.ContainerRequestContext;
	import javax.ws.rs.container.ResourceInfo;
	import javax.ws.rs.core.Context;
	import javax.ws.rs.core.MultivaluedMap;
	import javax.ws.rs.core.Response;
	import javax.ws.rs.ext.Provider;
	
	import org.glassfish.jersey.internal.util.Base64;
	
	/**
	 * Filtro que comprueba si un determinado usuario tiene acceso al servicio
	 * */
	@Provider
	public class AuthenticationFilter implements javax.ws.rs.container.ContainerRequestFilter
	{
		
		@Context
	    private ResourceInfo resourceInfo;
		
	    private static final String AUTHORIZATION_PROPERTY = "Authorization";
	    private static final String AUTHENTICATION_SCHEME = "Basic";
	     
	    @Override
	    public void filter(ContainerRequestContext requestContext)
	    {
	        Method method = resourceInfo.getResourceMethod();
	        
	        //Comprobar acceso permitido para todos
	        if( ! method.isAnnotationPresent(PermitAll.class))
	        {
	            if(method.isAnnotationPresent(DenyAll.class))
	            {
	                requestContext.abortWith(forbiddenResponse());
	                return;
	            }
	             
	            //Obtener cabeceras de la request
	            final MultivaluedMap<String, String> headers = requestContext.getHeaders();
	             
	            //Obtener cabecera authorization
	            final List<String> authorization = headers.get(AUTHORIZATION_PROPERTY);
	             
	            //si no envia authorization devolver respuesta 401
	            if(authorization == null || authorization.isEmpty())
	            {
	                requestContext.abortWith(unauthorizedResponse());
	                return;
	            }
	             
	            //Obtener cadena codificada de authorization
	            final String encodedUserPassword = authorization.get(0).replaceFirst(AUTHENTICATION_SCHEME + " ", "");
	             
	            //Decodificar user and password en base64
	            String usernameAndPassword = new String(Base64.decode(encodedUserPassword.getBytes()));;
	 
	            //Obtener usuario y password por separado
	            final StringTokenizer tokenizer = new StringTokenizer(usernameAndPassword, ":");
	            final String username = tokenizer.nextToken();
	            final String password = tokenizer.nextToken();
	             
	            //comprobar acceso para el usuario
	            if(method.isAnnotationPresent(RolesAllowed.class))
	            {
	                RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class);
	                Set<String> rolesSet = new HashSet<String>(Arrays.asList(rolesAnnotation.value()));
	                 
	                //Comprobar user and password
	                if( ! isUserAllowed(username, password, rolesSet))
	                {
	                    requestContext.abortWith(unauthorizedResponse());
	                    return;
	                }
	            }
	        }
	    }
	    
	    private Response unauthorizedResponse(){
	    	return Response.status(Response.Status.UNAUTHORIZED)
	    	.entity("You cannot access this resource").build();
	    }
	    
	    private Response forbiddenResponse(){
	    	return Response.status(Response.Status.FORBIDDEN)
	    	.entity("Access blocked").build();
	    }
	    
	    private boolean isUserAllowed(final String username, final String password, final Set<String> rolesSet)
	    {
	        boolean isAllowed = false;
	         

	        if(username.equals("jaxsecurity") && password.equals("jaxsecurity"))
	        {
	        	String userRole = "ADMIN";
	            
	            //Comprobar rol del usuario
	            if(rolesSet.contains(userRole))
	            {
	                isAllowed = true;
	            }
	        }
	        return isAllowed;
	    }
	}