Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
languagejava
package dk.itp.portalprotect.wss.dispatcher;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Properties;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import dk.itp.portalprotect.wss.agent.WSSAgent;
import dk.itp.security.passticket.PTException;
import dk.itp.security.utils.HtmlEncoder;
import dk.itp.security.utils.Listed;
import dk.itp.security.utils.StringMatcher;
import dk.itp.security.utils.UniqueId;
import dk.itp.statistics.Statistics;
import dk.itp.tunnel.AbstractTunnelPlugin;
import dk.itp.tunnel.TunnelServlet;
import dk.itp.tunnel.TunnelServlet.ServerEntry;

/**
 * Plugin to the dispatcher/tunnel which handes WS-Security, i.e. it modifies the request/response contents to add
 * or remove WS-Security encryption.
 *  
 * @author Kim Rasmussen
 * @version $Revision$
 *
 * <pre>
 * PortalProtect - Security infrastructure
 * Copyright(c) 2010, IT Practice A/S, All rights reserved.

* 
 * This source code is confidential.
 * </pre>
 */
public class WSSPlugin extends AbstractTunnelPlugin {
	private Logger cat = LoggerFactory.getLogger(getClass());
	protected String[] urlPatterns;
	protected String[] wsdlUrlPatterns;
	protected String signer;
	protected Statistics statistics;
	
	protected class RequestInfo {
		ByteArrayOutputStream bout = new ByteArrayOutputStream();
		PTException loginError;
	}
	private static ThreadLocal<RequestInfo> requestInfoTL = new ThreadLocal<RequestInfo>();
	
	private boolean isRequestForUs(HttpServletRequest request) {
		if (request.getHeader("SOAPAction") == null) {
			return false;
		}
			
		// Copy it so if setConfiguration is called we do not risk the array size being changed.
		String[] urlPatterns = this.urlPatterns;
		
		String uri = request.getRequestURI();
		for(int i = 0; i < urlPatterns.length; i++) {
			if (StringMatcher.match(urlPatterns[i], uri)) {
				return true;
			}
		}
		return false;
	}
	
	private boolean isWSDL(HttpServletRequest request) {
		// Copy it so if setConfiguration is called we do not risk the array size being changed.
		String[] urlPatterns = this.wsdlUrlPatterns;
		
		String uri = request.getRequestURI();
		String qstr = request.getQueryString();
		if (qstr != null && qstr.length() > 0)
			uri += "?"+qstr;
		
		for(int i = 0; i < urlPatterns.length; i++) {
			if (StringMatcher.match(urlPatterns[i], uri)) {
				return true;
			}
		}
		return false;		
	}
	
	public byte[] modifyRequestContent(HttpServletRequest request, HttpServletResponse response, byte[] content, UniqueId sessionId, ServerEntry serverEntry,
			TunnelServlet tunnel) throws IOException {
		requestInfoTL.set(null);
		
		if (!isRequestForUs(request))
			return content;
		
		// This request seems to be useful for us, so lets process it.
		String xml = new String(content, "UTF8");
		try {
			if (cat.isDebugEnabled()) {
				cat.debug("XML Input: " + xml);
			}
			xml = WSSAgent.logonWithSOAP(tunnel.getAgent(), sessionId.toString(), xml);
			if (xml != null) {
				if (cat.isDebugEnabled()) {
					cat.debug("XML input modified to: " + xml);
				}
				return xml.getBytes("UTF8");
			}
		} catch (PTException e) {
			cat.warn("Unable to login with SOAP request contents", e);
		}
		return content;		
	}

	public boolean modifyRequest(HttpServletRequest request, HttpServletResponse response, byte[] content, UniqueId sessionId, StringBuffer requestToWebServer,
			ServerEntry serverEntry, TunnelServlet tunnel) throws IOException {
		RequestInfo info = requestInfoTL.get();
		
		if (info != null && info.loginError != null) {
			String fault = generateSoapFault(info.loginError);
			response.getOutputStream().print(fault);
			return true;
		}
		return false;
	}
	
	public boolean isInterestedInResponseContents(HttpServletRequest request, HttpServletResponse response, byte[] content, UniqueId sessionId,
			ServerEntry serverEntry, TunnelServlet tunnel) throws IOException {
		if (!isWSDL(request) && !isRequestForUs(request))
			return false;
		
		RequestInfo info = requestInfoTL.get();
		if (info == null)
			requestInfoTL.set(new RequestInfo());
		return true;
	}

	public void responseContentRead(HttpServletRequest request, HttpServletResponse response, byte[] content, UniqueId sessionId, ServerEntry serverEntry,
			TunnelServlet tunnel, byte[] responseBuffer, int responseBufferSize) throws IOException {
		
		RequestInfo info = requestInfoTL.get();
		info.bout.write(responseBuffer, 0, responseBufferSize);
	}

	public void responseContentFinished(HttpServletRequest request, HttpServletResponse response, byte[] content, UniqueId sessionId, ServerEntry serverEntry,
			TunnelServlet tunnel) throws IOException {
		RequestInfo info = requestInfoTL.get();
		
		String xml = new String(info.bout.toByteArray(), "UTF8");
		if (cat.isDebugEnabled()) {
			cat.debug("Response: " + xml);
		}
		
		String sessionID = sessionId.toString();
		
		if (isWSDL(request)) {
			// TODO: Cache the result for another time
			try {
				xml = WSSAgent.attachPoliciesToWSDL(tunnel.getAgent(), sessionID, xml, request.getRequestURL().toString());
				response.getOutputStream().write(xml.getBytes("UTF8"));
			} catch(PTException e) {
				cat.warn("Problem attaching policy to WSDL", e);
				
				// Write proper soapfault here
				@SuppressWarnings("unused")
				String fault = generateSoapFault(e);
				response.getOutputStream().print("<html><body>Problem attaching policy to WSDL: " + HtmlEncoder.encode(e.toString()) + "</body></html>"fault);
			}
		} else {
			try {
				String encryptionKey = tunnel.getAgent().getStateVariable(sessionID, "encryptedWithKey");
				if (encryptionKey != null)
					xml = WSSAgent.signAndEncryptSOAP(tunnel.getAgent(), sessionId.toString(), xml, signer, encryptionKey);
				else
					xml = WSSAgent.signSOAP(tunnel.getAgent(), sessionID, xml, signer);
				if (cat.isDebugEnabled()) {
					cat.debug("XML Output modified to: " + xml);
				}
				response.getOutputStream().write(xml.getBytes("UTF8"));
			} catch(PTException e) {
				cat.warn("Problem signing response XML", e);
				
				// Write proper soapfault here
				String fault = generateSoapFault(e);
				response.getOutputStream().print(fault);
			}
		}
		requestInfoTL.set(null);
	}

	public void setStatistics(Statistics stats) {
		super.setStatistics(stats);
		
		this.statistics = stats;
	}

	public void setConfiguration(Properties props) {
		super.setConfiguration(props);
		
		urlPatterns = new Listed<String>(props.getProperty("wss.urls", "")).toArray();
		wsdlUrlPatterns = new Listed<String>(props.getProperty("wss.wsdlurls", "")).toArray();
		signer = props.getProperty("wss.signer", "unknown");
	}
	
	protected String generateSoapFault(PTException e) {
		String fault = "<env:Envelope xmlns:env=\"http://www.w3.org/2003/05/soap-envelope\">\n"+
		"<env:Header/><env:Body>\n<env:Fault>\n<env:Code><env:Value>env:Sender</env:Value></env:Code>\n"+
		"<env:Reason><env:Text>\n"+
		e.toString()+
		"</env:Text></env:Reason>\n</env:Fault></env:Body></env:Envelope>";
		
		return fault;
	}
}