package org.jboss.resteasy.skeleton.key.jaxrs;

import org.jboss.resteasy.logging.Logger;
import org.jboss.resteasy.skeleton.key.RSATokenVerifier;
import org.jboss.resteasy.skeleton.key.ResourceMetadata;
import org.jboss.resteasy.skeleton.key.SkeletonKeyPrincipal;
import org.jboss.resteasy.skeleton.key.SkeletonKeySession;
import org.jboss.resteasy.skeleton.key.VerificationException;
import org.jboss.resteasy.skeleton.key.representations.SkeletonKeyToken;
import org.jboss.resteasy.spi.ResteasyProviderFactory;

import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import java.io.IOException;
import java.security.Principal;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
@Priority(Priorities.AUTHENTICATION)
public class JaxrsBearerTokenFilter implements ContainerRequestFilter
{
   protected ResourceMetadata resourceMetadata;
   private static Logger log = Logger.getLogger(JaxrsBearerTokenFilter.class);

   public JaxrsBearerTokenFilter(ResourceMetadata resourceMetadata)
   {
      this.resourceMetadata = resourceMetadata;
   }

   protected void challengeResponse(ContainerRequestContext request, String error, String description)
   {
      StringBuilder header = new StringBuilder("Bearer realm=\"");
      header.append(resourceMetadata.getRealm()).append("\"");
      if (error != null)
      {
         header.append(", error=\"").append(error).append("\"");
      }
      if (description != null)
      {
         header.append(", error_description=\"").append(description).append("\"");
      }
      request.abortWith(Response.status(401).header("WWW-Authenticate", header.toString()).build());
      return;
   }

   @Context
   protected SecurityContext securityContext;

   @Override
   public void filter(ContainerRequestContext request) throws IOException
   {
      String authHeader = request.getHeaderString(HttpHeaders.AUTHORIZATION);
      if (authHeader == null)
      {
         challengeResponse(request, null, null);
         return;
      }

      String[] split = authHeader.trim().split("\\s+");
      if (split == null || split.length != 2) challengeResponse(request, null, null);
      if (!split[0].equalsIgnoreCase("Bearer")) challengeResponse(request, null, null);


      String tokenString = split[1];


      try
      {
         SkeletonKeyToken token = RSATokenVerifier.verifyToken(tokenString, resourceMetadata);
         SkeletonKeySession skSession = new SkeletonKeySession(tokenString, resourceMetadata);
         ResteasyProviderFactory.pushContext(SkeletonKeySession.class, skSession);
         String callerPrincipal = securityContext.getUserPrincipal() != null ? securityContext.getUserPrincipal().getName() : null;

         final SkeletonKeyPrincipal principal = new SkeletonKeyPrincipal(token.getPrincipal(), callerPrincipal);
         final boolean isSecure = securityContext.isSecure();
         final SkeletonKeyToken.Access access;
         if (resourceMetadata.getResourceName() != null)
         {
            access = token.getResourceAccess(resourceMetadata.getResourceName());
         }
         else
         {
            access = token.getRealmAccess();
         }
         SecurityContext ctx = new SecurityContext()
         {
            @Override
            public Principal getUserPrincipal()
            {
               return principal;
            }

            @Override
            public boolean isUserInRole(String role)
            {
               if (access.getRoles() == null) return false;
               return access.getRoles().contains(role);
            }

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

            @Override
            public String getAuthenticationScheme()
            {
               return "OAUTH_BEARER";
            }
         };
         request.setSecurityContext(ctx);
      }
      catch (VerificationException e)
      {
         log.error("Failed to verify token", e);
         challengeResponse(request, "invalid_token", e.getMessage());
      }
   }

}
