package gov.va.med.fw.ui.filter;

// Java imports
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.context.WebApplicationContext;

// Framework imports
import gov.va.med.fw.ui.DelegatingActionUtils;
import gov.va.med.fw.security.EncryptionServiceException;
import gov.va.med.fw.ui.security.UIEncryptionService;

/**
 * This is the servlet filter to decrypt the encrypted parameters and query
 * string.
 * 
 * @author Muddaiah Ranga
 * @version 3.0
 */
public class DecryptionFilter extends BaseFilter {
	protected UIEncryptionService encryptionService;

	/**
	 * This is the overridden method. See Servlet API javadoc for further
	 * details.
	 * 
	 * @param request
	 *           is the request object
	 * @param response
	 *           is the response object
	 * @param filterChain
	 *           is the filter chain object
	 * @throws ServletException
	 * @throws IOException
	 */
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain filterChain) throws ServletException, IOException {
		HttpServletRequest req = (HttpServletRequest) request;
		HttpServletResponse res = (HttpServletResponse) response;

		try {
			// If the encryption service is initialised, initialise it.
			if (encryptionService == null) {
				WebApplicationContext ac = 
					DelegatingActionUtils.findRequiredWebApplicationContext( req.getSession().getServletContext() );
				
				encryptionService = (UIEncryptionService)ac.getBean( "encryptionService" );
			}
		}
		catch (Exception e ) {
			logger.error( "Failed to obtain an encryption service", e );
			throw new ServletException( e );
		}
		// If the encryption is not enabled, do nothing and return.
		if (encryptionService == null || !encryptionService.isEncryptionEnabled()) {
			filterChain.doFilter(request, response);
			return;
		}

		// If the request is already decrypted, don't do anything
		if (request instanceof DecryptedRequest) {
			filterChain.doFilter(request, response);
			return;
		}

		try {
			// Query string is encrypted, decrypt and wrap the Request object.
			DecryptedRequest wrapper = new DecryptedRequest(req, encryptionService);
			filterChain.doFilter(wrapper, response);
		}
		catch (EncryptionServiceException decryptoEx) {
			logger.error(decryptoEx);
			handleEncryptionError(req, res, decryptoEx);
		}
	}

	/** ***************************************************************************** */
	/**
	 * ************************** Private Methods
	 * **********************************
	 */
	/** ***************************************************************************** */

	/**
	 * Displays the crypto error page.
	 * 
	 * @param request
	 *           the request object
	 * @param response
	 *           the response object
	 * @param exception
	 *           the exception throws ServletException
	 */
	private void handleEncryptionError(HttpServletRequest request,
			HttpServletResponse response, Throwable exception)
			throws ServletException {
		try {
			if (logger.isWarnEnabled()) {
				StringBuffer userInfo = new StringBuffer();
				userInfo.append(" User IP = " + request.getRemoteHost());
				//Commented out to resolve Fortify High Finding Log Forging
				//userInfo.append(" User Id = " + request.getRemoteUser());
				//userInfo.append(" User URL = " + request.getRequestURL().toString());
				logger.warn(userInfo, exception);
			}
			String encryptionErrorUrl = encryptionService.getEncryptionErrorUrl();
			RequestDispatcher reqDispatcher = request
					.getRequestDispatcher(encryptionErrorUrl);
			reqDispatcher.forward(new DecryptedRequest(request), response);
		}
		catch (Exception ex) {
			logger.error("Error displaying error page", ex);
			throw new ServletException(ex);
		}
	}
}
