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;
}
} |