/**
 * Magnolia and its source-code is licensed under the LGPL. You may copy, adapt,
 * and redistribute this file for commercial or non-commercial use. When
 * copying, adapting, or redistributing this document in keeping with the
 * guidelines above, you are required to provide proper attribution to obinary.
 * If you reproduce or distribute the document without making any substantive
 * modifications to its content, please use the following attribution line:
 * Copyright 1993-2005 obinary Ltd. (http://www.obinary.com) All rights
 * reserved.
 */
package info.magnolia.cms.filters;

import info.magnolia.cms.core.Path;
import info.magnolia.cms.security.Authenticator;
import info.magnolia.cms.security.Listener;
import info.magnolia.cms.security.Lock;
import info.magnolia.cms.security.SecureURI;
import info.magnolia.cms.security.SessionAccessControl;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import edu.yale.its.tp.cas.client.filter.CASFilter;

/**
 * @author Roberto Cosenza
 */
public class CASSecurityFilter implements Filter {

	protected Logger logger = Logger.getLogger(getClass());
	private CASFilter casFilter;

	public void init(FilterConfig filterConfig) throws ServletException {
		try {
			casFilter = new CASFilter();
			casFilter.init(filterConfig);
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public void destroy() {
		casFilter.destroy();
	}

	protected boolean authenticate(HttpServletRequest request,
			HttpServletResponse response, FilterChain chain)
			throws ServletException, IOException {
		logger.debug("authenticate");

		boolean authenticate = Authenticator.authenticate(request);
		if (logger.isDebugEnabled()) {
			logger.debug("authenticate = " + authenticate);
		}

		if (!authenticate) {
			logger.debug("Invalidating user");
			SessionAccessControl.invalidateUser(request);
			return false;
		}
		return true;
	}

	/**
	 * @delegates authentication to a CAS filter before processing as
	 *            SecurityFilter
	 * @see SecurityFilter
	 */
	public void doFilter(ServletRequest req, ServletResponse res,
			FilterChain chain) throws IOException, ServletException {
		logger.debug("doFilter");
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;

		if(passThroughURI(request)) {
			logger.debug("Skipping CAS authentication");
			chain.doFilter(request, response);
		} else if (SecureURI.isProtected(Path.getURI(request))) {
			logger.debug("Protected URL, delegate to CAS");
			casFilter.doFilter(request, response, chain);
		} else {
			chain.doFilter(request, response);
		}

		checkAccess(request, response, chain);
	}

	/**
	 * Tells if authentication has to be skipped. Activation needs proxy authentication 
	 * which is currently not implemented. This method will make authentication be skipped 
	 * for the ActivationHandler URI. Warning: THIS IS A SECURITY RISK. You should protected 
	 * ActivationHandler url with other means (Suggestion: use activation on localhost and 
	 * restrict the ActivationHandler URI through Apache)
	 * @return true if no CAS authentication has to be performed
	 **/
	protected boolean passThroughURI(ServletRequest req) throws ServletException {
		String passThroughURI = ".*/ActivationHandler";
		String requestURI = ((HttpServletRequest)req).getRequestURI();
		logger.debug("passThroughURI = " + passThroughURI);
		logger.debug("requestURI = " + requestURI);
		boolean match = false;
		if(passThroughURI != null && requestURI.matches(passThroughURI)) {
				match =  true;
		}	
				
		logger.debug("match = " + match);
		return match;
	}

	/**
	 * Checks access from Listener / Authenticator / AccessLock.
	 * 
	 * @param req
	 *            HttpServletRequest as received by the service method
	 * @param res
	 *            HttpServletResponse as received by the service method
	 * @return boolean <code>true</code> if access to the resource is allowed
	 * @throws IOException
	 *             can be thrown when the servlet is unable to write to the
	 *             response stream
	 */
	protected boolean checkAccess(HttpServletRequest req,
			HttpServletResponse res, FilterChain chain) throws IOException,
			ServletException {
		logger.debug("checkAccess");
		if (Lock.isSystemLocked()) {
			res.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
			return false;
		} else if (SessionAccessControl.isSecuredSession(req)) {
			return true;
		} else if (SecureURI.isProtected(Path.getURI(req))) {
			return authenticate(req, res, chain);
		} else if (!Listener.isAllowed(req)) {
			res.sendError(HttpServletResponse.SC_FORBIDDEN);
			return false;
		}
		return true;
	}

}
