package org.jivesoftware.openfire.plugin.rest;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response.Status;

import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.admin.AdminManager;
import org.jivesoftware.openfire.auth.AuthFactory;
import org.jivesoftware.openfire.auth.ConnectionException;
import org.jivesoftware.openfire.auth.InternalUnauthenticatedException;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerRequestFilter;

/**
 * The Class AuthFilter.
 */
public class AuthFilter implements ContainerRequestFilter {

	/** The log. */
	private static Logger LOG = LoggerFactory.getLogger(AuthFilter.class);

	/** The http request. */
	@Context
	private HttpServletRequest httpRequest;

	/** The plugin. */
	private RESTServicePlugin plugin = (RESTServicePlugin) XMPPServer.getInstance().getPluginManager()
			.getPlugin("restapi");

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.sun.jersey.spi.container.ContainerRequestFilter#filter(com.sun.jersey
	 * .spi.container.ContainerRequest)
	 */
	@Override
	public ContainerRequest filter(ContainerRequest containerRequest) throws WebApplicationException {
		if (!plugin.isEnabled()) {
			throw new WebApplicationException(Status.FORBIDDEN);
		}
		
		// Let the preflight request through the authentication
		if ("OPTIONS".equals(containerRequest.getMethod())) {
			return containerRequest;
		}
		
		// To be backwards compatible to userservice 1.*
		if ("restapi/v1/userservice".equals(containerRequest.getPath())) {
			return containerRequest;
		}

		if (!plugin.getAllowedIPs().isEmpty()) {
			// Get client's IP address
			String ipAddress = httpRequest.getHeader("x-forwarded-for");
			if (ipAddress == null) {
				ipAddress = httpRequest.getHeader("X_FORWARDED_FOR");
				if (ipAddress == null) {
					ipAddress = httpRequest.getHeader("X-Forward-For");
					if (ipAddress == null) {
						ipAddress = httpRequest.getRemoteAddr();
					}
				}
			}
			if (!plugin.getAllowedIPs().contains(ipAddress)) {
				LOG.warn("REST API rejected service to IP address: " + ipAddress);
				throw new WebApplicationException(Status.UNAUTHORIZED);
			}
		}
		
		// Get the authentification passed in HTTP headers parameters
		String auth = containerRequest.getHeaderValue("authorization");

		if (auth == null) {
			throw new WebApplicationException(Status.UNAUTHORIZED);
		}

		// HTTP Basic Auth or Shared Secret key
		if ("basic".equals(plugin.getHttpAuth())) {
			String[] usernameAndPassword = BasicAuth.decode(auth);

			// If username or password fail
			if (usernameAndPassword == null || usernameAndPassword.length != 2) {
				throw new WebApplicationException(Status.UNAUTHORIZED);
			}

			boolean userAdmin = AdminManager.getInstance().isUserAdmin(usernameAndPassword[0], true);

			if (!userAdmin) {
				throw new WebApplicationException(Status.UNAUTHORIZED);
			}

			try {
				AuthFactory.authenticate(usernameAndPassword[0], usernameAndPassword[1]);
			} catch (UnauthorizedException e) {
				LOG.warn("Wrong HTTP Basic Auth authorization", e);
				throw new WebApplicationException(Status.UNAUTHORIZED);
			} catch (ConnectionException e) {
				throw new WebApplicationException(Status.UNAUTHORIZED);
			} catch (InternalUnauthenticatedException e) {
				throw new WebApplicationException(Status.UNAUTHORIZED);
			}
		} else {
			if (!auth.equals(plugin.getSecret())) {
				LOG.warn("Wrong secret key authorization. Provided key: " + auth);
				throw new WebApplicationException(Status.UNAUTHORIZED);
			}
		}
		return containerRequest;
	}
}
