blob: 1e9d1a5cf96611b3ed83a049499fb9e7f2611da9 [file] [log] [blame]
/*******************************************************************************
* Copyright (C) 2007 The University of Manchester
*
* Modifications to the initial code base are copyright of their
* respective authors, or their employers as appropriate.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
******************************************************************************/
package net.sf.taverna.t2.activities.wsdl;
import java.io.IOException;
import java.io.StringReader;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import net.sf.taverna.t2.activities.wsdl.security.SecurityProfiles;
import net.sf.taverna.t2.security.credentialmanager.CMException;
import net.sf.taverna.t2.security.credentialmanager.CredentialManager;
import net.sf.taverna.t2.security.credentialmanager.UsernamePassword;
import net.sf.taverna.wsdl.parser.WSDLParser;
import net.sf.taverna.wsdl.soap.WSDLSOAPInvoker;
import org.apache.axis.AxisProperties;
import org.apache.axis.EngineConfiguration;
import org.apache.axis.MessageContext;
import org.apache.axis.client.Call;
import org.apache.axis.configuration.XMLStringProvider;
import org.apache.axis.message.SOAPHeaderElement;
import org.apache.log4j.Logger;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.jdom.output.DOMOutputter;
import com.fasterxml.jackson.databind.JsonNode;
/**
* Invokes SOAP based Web Services from T2.
*
* Subclasses WSDLSOAPInvoker used for invoking Web Services from Taverna 1.x
* and extends it to provide support for invoking secure Web services.
*
* @author Stuart Owen
* @author Alex Nenadic
* @author Stian Soiland-Reyes
*
*/
public class T2WSDLSOAPInvoker extends WSDLSOAPInvoker {
private static final String REFERENCE_PROPERTIES = "ReferenceProperties";
private static final String ENDPOINT_REFERENCE = "EndpointReference";
private static Logger logger = Logger.getLogger(T2WSDLSOAPInvoker.class);
private static final Namespace wsaNS = Namespace.getNamespace("wsa",
"http://schemas.xmlsoap.org/ws/2004/03/addressing");
private String wsrfEndpointReference = null;
private CredentialManager credentialManager;
public T2WSDLSOAPInvoker(WSDLParser parser, String operationName,
List<String> outputNames, CredentialManager credentialManager) {
super(parser, operationName, outputNames);
this.credentialManager = credentialManager;
}
public T2WSDLSOAPInvoker(WSDLParser parser, String operationName,
List<String> outputNames, String wsrfEndpointReference, CredentialManager credentialManager) {
this(parser, operationName, outputNames, credentialManager);
this.wsrfEndpointReference = wsrfEndpointReference;
}
@SuppressWarnings("unchecked")
protected void addEndpointReferenceHeaders(
List<SOAPHeaderElement> soapHeaders) {
// Extract elements
// Add WSA-stuff
// Add elements
Document wsrfDoc;
try {
wsrfDoc = parseWsrfEndpointReference(wsrfEndpointReference);
} catch (JDOMException e) {
logger.warn("Could not parse endpoint reference, ignoring:\n"
+ wsrfEndpointReference, e);
return;
} catch (IOException e) {
logger.error("Could not read endpoint reference, ignoring:\n"
+ wsrfEndpointReference, e);
return;
}
Element endpointRefElem = null;
Element wsrfRoot = wsrfDoc.getRootElement();
if (wsrfRoot.getNamespace().equals(wsaNS)
&& wsrfRoot.getName().equals(ENDPOINT_REFERENCE)) {
endpointRefElem = wsrfRoot;
} else {
// Only look for child if the parent is not an EPR
Element childEndpoint = wsrfRoot
.getChild(ENDPOINT_REFERENCE, wsaNS);
if (childEndpoint != null) {
// Support wrapped endpoint reference for backward compatibility
// and convenience (T2-677)
endpointRefElem = childEndpoint;
} else {
logger
.warn("Unexpected element name for endpoint reference, but inserting anyway: "
+ wsrfRoot.getQualifiedName());
endpointRefElem = wsrfRoot;
}
}
Element refPropsElem = endpointRefElem.getChild(REFERENCE_PROPERTIES,
wsaNS);
if (refPropsElem == null) {
logger.warn("Could not find " + REFERENCE_PROPERTIES);
return;
}
List<Element> refProps = refPropsElem.getChildren();
// Make a copy of the list as it would be modified by
// prop.detach();
for (Element prop : new ArrayList<Element>(refProps)) {
DOMOutputter domOutputter = new DOMOutputter();
SOAPHeaderElement soapElem;
prop.detach();
try {
org.w3c.dom.Document domDoc = domOutputter.output(new Document(
prop));
soapElem = new SOAPHeaderElement(domDoc.getDocumentElement());
} catch (JDOMException e) {
logger.warn(
"Could not translate wsrf element to DOM:\n" + prop, e);
continue;
}
soapElem.setMustUnderstand(false);
soapElem.setActor(null);
soapHeaders.add(soapElem);
}
// soapHeaders.add(new SOAPHeaderElement((Element) wsrfDoc
// .getDocumentElement()));
}
protected void configureSecurity(Call call, JsonNode bean) throws Exception {
// If security settings require WS-Security - configure the axis call
// with appropriate properties
URI securityProfile = new URI(bean.get("securityProfile").textValue());
if (securityProfile
.equals(SecurityProfiles.WSSECURITY_USERNAMETOKEN_PLAINTEXTPASSWORD)
|| securityProfile
.equals(SecurityProfiles.WSSECURITY_USERNAMETOKEN_DIGESTPASSWORD)
|| securityProfile
.equals(SecurityProfiles.WSSECURITY_TIMESTAMP_USERNAMETOKEN_PLAINTEXTPASSWORD)
|| securityProfile
.equals(SecurityProfiles.WSSECURITY_TIMESTAMP_USERNAMETOKEN_DIGESTPASSWORD)) {
UsernamePassword usernamePassword = getUsernameAndPasswordForService(bean, false);
call.setProperty(Call.USERNAME_PROPERTY, usernamePassword.getUsername());
call.setProperty(Call.PASSWORD_PROPERTY, usernamePassword.getPasswordAsString());
usernamePassword.resetPassword();
} else if (securityProfile.equals(SecurityProfiles.HTTP_BASIC_AUTHN)){
// Basic HTTP AuthN - set HTTP headers
// pathrecursion allowed
UsernamePassword usernamePassword = getUsernameAndPasswordForService(bean, true);
MessageContext context = call.getMessageContext();
context.setUsername(usernamePassword.getUsername());
context.setPassword(usernamePassword.getPasswordAsString());
usernamePassword.resetPassword();
} else {
logger.error("Unknown security profile " + securityProfile);
}
}
/**
* Get username and password from Credential Manager or ask user to supply
* one. Username is the first element of the returned array, and the
* password is the second.
*/
protected UsernamePassword getUsernameAndPasswordForService(
JsonNode bean, boolean usePathRecursion) throws CMException {
// Try to get username and password for this service from Credential
// Manager (which should pop up UI if needed)
URI serviceUri = URI.create(bean.get("operation").get("wsdl").textValue());
UsernamePassword username_password = credentialManager.getUsernameAndPasswordForService(serviceUri, usePathRecursion, null);
if (username_password == null) {
throw new CMException("No username/password provided for service " + serviceUri);
}
return username_password;
}
@Override
protected List<SOAPHeaderElement> makeSoapHeaders() {
List<SOAPHeaderElement> soapHeaders = new ArrayList<SOAPHeaderElement>(
super.makeSoapHeaders());
if (wsrfEndpointReference != null && getParser().isWsrfService()) {
addEndpointReferenceHeaders(soapHeaders);
}
return soapHeaders;
}
protected org.jdom.Document parseWsrfEndpointReference(
String wsrfEndpointReference) throws JDOMException, IOException {
SAXBuilder builder = new SAXBuilder();
return builder.build(new StringReader(wsrfEndpointReference));
}
public Map<String, Object> invoke(Map<String, Object> inputMap,
JsonNode bean) throws Exception {
EngineConfiguration wssEngineConfiguration = null;
if (bean.has("securityProfile")) {
URI securityProfile = new URI(bean.get("securityProfile").textValue());
// If security settings require WS-Security and not just e.g. Basic HTTP
// AuthN - configure the axis engine from the appropriate config strings
if (securityProfile
.equals(SecurityProfiles.WSSECURITY_USERNAMETOKEN_PLAINTEXTPASSWORD)) {
wssEngineConfiguration = new XMLStringProvider(
SecurityProfiles.WSSECURITY_USERNAMETOKEN_PLAINTEXTPASSWORD_CONFIG);
} else if (securityProfile
.equals(SecurityProfiles.WSSECURITY_USERNAMETOKEN_DIGESTPASSWORD)) {
wssEngineConfiguration = new XMLStringProvider(
SecurityProfiles.WSSECURITY_USERNAMETOKEN_DIGESTPASSWORD_CONFIG);
} else if (securityProfile
.equals(SecurityProfiles.WSSECURITY_TIMESTAMP_USERNAMETOKEN_PLAINTEXTPASSWORD)) {
wssEngineConfiguration = new XMLStringProvider(
SecurityProfiles.WSSECURITY_TIMESTAMP_USERNAMETOKEN_PLAINTETPASSWORD_CONFIG);
} else if (securityProfile
.equals(SecurityProfiles.WSSECURITY_TIMESTAMP_USERNAMETOKEN_DIGESTPASSWORD)) {
wssEngineConfiguration = new XMLStringProvider(
SecurityProfiles.WSSECURITY_TIMESTAMP_USERNAMETOKEN_DIGESTPASSWORD_CONFIG);
}
}
// This does not work
// ClassUtils.setClassLoader("net.sf.taverna.t2.activities.wsdl.security.TavernaAxisCustomSSLSocketFactory",TavernaAxisCustomSSLSocketFactory.class.getClassLoader());
// Setting Axis property only works when we also set the Thread's classloader as below
// (we do it from the net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke.requestRun())
// Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
if (AxisProperties.getProperty("axis.socketSecureFactory")== null || !AxisProperties.getProperty("axis.socketSecureFactory").equals("net.sf.taverna.t2.activities.wsdl.security.TavernaAxisCustomSSLSocketFactory")){
AxisProperties.setProperty("axis.socketSecureFactory", "net.sf.taverna.t2.activities.wsdl.security.TavernaAxisCustomSSLSocketFactory");
logger.info("Setting axis.socketSecureFactory property to " + AxisProperties.getProperty("axis.socketSecureFactory"));
}
// This also does not work
//AxisProperties.setClassDefault(SecureSocketFactory.class, "net.sf.taverna.t2.activities.wsdl.security.TavernaAxisCustomSSLSocketFactory");
Call call = super.getCall(wssEngineConfiguration);
// Now that we have an axis Call object, configure any additional
// security properties on it (or its message context or its Transport
// handler),
// such as WS-Security UsernameToken or HTTP Basic AuthN
if (bean.has("securityProfile")) {
configureSecurity(call, bean);
}
return invoke(inputMap, call);
}
}