package org.jboss.resteasy.keystone.core;

import org.jboss.resteasy.keystone.model.Access;
import org.jboss.resteasy.keystone.model.Role;
import org.jboss.resteasy.logging.Logger;
import org.jboss.resteasy.security.smime.PKCS7SignatureInput;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.ext.Providers;
import java.io.IOException;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.HashSet;
import java.util.Set;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
public abstract class AbstractTokenAuthFilter implements ContainerRequestFilter
{
   protected X509Certificate certificate;

   protected Logger logger = Logger.getLogger(AbstractTokenAuthFilter.class);
   protected AbstractTokenAuthFilter(X509Certificate certificate)
   {
      this.certificate = certificate;
   }

   @Context
   SecurityContext securityContext;

   @Context
   Providers providers;

   protected Access signed(String header)
   {
      PKCS7SignatureInput input = null;
      boolean verify = false;
      try
      {
         input = new PKCS7SignatureInput(header);
         input.setProviders(providers);
         verify = input.verify(certificate);
      }
      catch (Exception e)
      {
         throw new WebApplicationException(403);
      }
      if (!verify) throw new WebApplicationException(403);
      try
      {
         return (Access)input.getEntity(Access.class, MediaType.APPLICATION_JSON_TYPE);
      }
      catch (Exception e)
      {
         logger.error("Failed to unmarshall", e);
         throw new WebApplicationException(403);
      }
   }

   protected abstract Access getTokenFromServer(String header);

   @Override
   public void filter(ContainerRequestContext requestContext) throws IOException
   {
      String xAuthToken = requestContext.getHeaderString("X-Auth-Token");
      String xAuthSignedToken = requestContext.getHeaderString("X-Auth-Signed-Token");
      Access token = null;
      if (xAuthToken == null && xAuthSignedToken == null) return;
      else if (xAuthSignedToken != null && certificate != null)
      {
         token = signed(xAuthSignedToken);
      }
      else if (xAuthToken != null)
      {
         token = getTokenFromServer(xAuthToken);
      }
      if (token == null) return; // do nothing
      if (token.getToken().expired()) return; // todo maybe throw 401 with an error stating token is expired?

      final UserPrincipal principal = new UserPrincipal(token.getUser());
      final Set<String> roleSet = new HashSet<String>();
      for (Role role : token.getUser().getRoles())
      {
         roleSet.add(role.getName());
      }
      SecurityContext ctx = new SecurityContext()
      {
         @Override
         public Principal getUserPrincipal()
         {
            return principal;
         }

         @Override
         public boolean isUserInRole(String role)
         {
            return roleSet.contains(role);
         }

         @Override
         public boolean isSecure()
         {
            return securityContext.isSecure();
         }

         @Override
         public String getAuthenticationScheme()
         {
            return securityContext.getAuthenticationScheme();
         }
      };
      requestContext.setSecurityContext(ctx);
   }
}
