blob: 7cd9feef94b1ae9e4390d3c2474c512f0e86207e [file] [log] [blame]
/*
* Copyright The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.rahas.test.util;
import junit.framework.Assert;
import org.apache.axiom.om.*;
import org.apache.axiom.soap.*;
import org.apache.axis2.addressing.AddressingConstants;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.ConfigurationContextFactory;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.engine.AxisConfiguration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.rahas.RahasConstants;
import org.apache.rahas.RahasData;
import org.apache.rahas.TrustException;
import org.apache.rahas.TrustUtil;
import org.apache.rahas.impl.util.CommonUtil;
import org.apache.ws.secpolicy.Constants;
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.WSSecurityEngineResult;
import org.apache.ws.security.WSSecurityException;
import org.apache.ws.security.components.crypto.Crypto;
import org.apache.ws.security.components.crypto.CryptoFactory;
import org.apache.ws.security.components.crypto.CryptoType;
import org.apache.ws.security.handler.WSHandlerConstants;
import org.apache.ws.security.handler.WSHandlerResult;
import org.apache.ws.security.saml.ext.builder.SAML1Constants;
import org.apache.xml.security.utils.XMLUtils;
import org.opensaml.common.xml.SAMLConstants;
import org.w3c.dom.DOMConfiguration;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLStreamReader;
import java.io.*;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
/**
* Utility class for tests.
*/
public class TestUtil {
private static final Log log = LogFactory.getLog(TestUtil.class);
// Directly copied from WSS4J
public static final String SAMPLE_SOAP_MSG =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<SOAP-ENV:Envelope "
+ "xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
+ "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
+ "<SOAP-ENV:Header"
+ " xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2002/07/secext\""
+ " xmlns:wsu=\"http://schemas.xmlsoap.org/ws/2002/07/utility\"/>"
+ "<SOAP-ENV:Body>"
+ "<add xmlns=\"http://ws.apache.org/counter/counter_port_type\">"
+ "<value xmlns=\"\">15</value>"
+ "</add>"
+ "</SOAP-ENV:Body>"
+ "</SOAP-ENV:Envelope>";
/**
* Convert an SOAP Envelope as a String to a org.w3c.dom.Document.
* Directly copied from WSS4J
*/
public static org.w3c.dom.Document toSOAPPart(String xml) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
InputStream in = new ByteArrayInputStream(xml.getBytes());
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(in);
}
public static Crypto getCrypto() throws IOException, WSSecurityException, TrustException {
File file = new File("src/test/resources/crypto.config");
Assert.assertTrue(file.exists());
Properties properties = new Properties();
try {
properties.load(new FileInputStream(file));
} catch (IOException e) {
log.error("Unable to open crypto configuration file");
throw e;
}
Crypto crypto = CryptoFactory.getInstance(properties);
X509Certificate[] certificates = CommonUtil.getCertificatesByAlias(crypto, "apache");
Assert.assertEquals(certificates.length, 1);
return crypto;
}
public static X509Certificate getDefaultCertificate() throws WSSecurityException, TrustException, IOException {
Crypto crypto = getCrypto();
CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
cryptoType.setAlias("apache");
return crypto.getX509Certificates(cryptoType)[0];
}
public static Document getTestDocument() throws Exception {
InputStream is = new ByteArrayInputStream(SAMPLE_SOAP_MSG.getBytes());
SOAPEnvelope envelope = createSOAPEnvelope(is);
return TestUtil.getDocumentFromSOAPEnvelope(envelope, true);
}
public static SOAPEnvelope createSOAPEnvelope(InputStream in) throws Exception {
OMXMLParserWrapper builder = OMXMLBuilderFactory.createSOAPModelBuilder(in, null);
return (SOAPEnvelope) builder.getDocumentElement();
}
public static OMElement getRSTTemplate(String samlNamespace) throws Exception {
OMFactory fac = OMAbstractFactory.getOMFactory();
OMElement element = null;
OMElement elem = fac.createOMElement(Constants.RST_TEMPLATE);
TrustUtil.createTokenTypeElement(RahasConstants.VERSION_05_02, elem).setText(samlNamespace);
TrustUtil.createKeyTypeElement(RahasConstants.VERSION_05_02, elem,
RahasConstants.KEY_TYPE_SYMM_KEY);
TrustUtil.createKeySizeElement(RahasConstants.VERSION_05_02, elem, 256);
element = TrustUtil.createClaims(RahasConstants.VERSION_05_02, elem, "http://wso2.org");
addClaimType(element, "http://wso2.org/claims/givenname");
return elem;
}
private static void addClaimType(OMElement parent, String uri) {
OMElement element = null;
element = parent.getOMFactory().createOMElement(new QName("http://schemas.xmlsoap.org/ws/2005/05/identity", "ClaimType", "wsid"),
parent);
element.addAttribute(parent.getOMFactory().createOMAttribute("Uri", null, uri));
}
public static TestSTSClient createTestSTSClient(String samlVersion) throws Exception {
ConfigurationContext configurationContext
= ConfigurationContextFactory.createConfigurationContextFromFileSystem("src/test/resources/repo",
"src/test/resources/repo/conf/client.axis2.xml");
TestSTSClient stsClient = new TestSTSClient(configurationContext);
stsClient.setRstTemplate(getRSTTemplate(samlVersion));
stsClient.setAction(RahasConstants.WST_NS_05_02 + RahasConstants.RST_ACTION_SCT);
return stsClient;
}
public static MessageContext createDummyMessageContext(String appliesTo) throws Exception {
TestSTSClient stsClient = TestUtil.createTestSTSClient(SAMLConstants.SAML20_NS);
OMElement requestSecurityToken = stsClient.createRST(appliesTo);
MessageContext dummyMessageContext = new MessageContext();
populateReceivedResults(dummyMessageContext);
dummyMessageContext.setProperty(AddressingConstants.WS_ADDRESSING_VERSION,
AddressingConstants.Submission.WSA_NAMESPACE);
SOAPFactory factory = OMAbstractFactory.getMetaFactory(OMAbstractFactory.FEATURE_DOM).getSOAP11Factory();
SOAPEnvelope envelope = factory.createSOAPEnvelope();
SOAPBody soapBody = factory.createSOAPBody(envelope);
soapBody.addChild(requestSecurityToken);
dummyMessageContext.setEnvelope(envelope);
dummyMessageContext.setAxisService(new AxisService("TestService"));
AxisConfiguration axisConfiguration = new AxisConfiguration();
dummyMessageContext.setConfigurationContext(new ConfigurationContext(axisConfiguration));
return dummyMessageContext;
}
public static RahasData createTestRahasData(String appliesTo) throws Exception {
return new RahasData(createDummyMessageContext(appliesTo));
}
private static void populateReceivedResults(MessageContext messageContext) throws Exception {
List<WSSecurityEngineResult> wsSecEngineResults = new ArrayList<WSSecurityEngineResult>();
WSSecurityEngineResult result = new WSSecurityEngineResult(WSConstants.SIGN);
Principal principal = new Principal() {
public String getName() {
return "apache";
}
};
result.put(WSSecurityEngineResult.TAG_PRINCIPAL, principal);
result.put(WSSecurityEngineResult.TAG_X509_CERTIFICATE, getDefaultCertificate());
wsSecEngineResults.add(result);
WSHandlerResult handlerResult = new WSHandlerResult(null, wsSecEngineResults);
List<WSHandlerResult> handlerResultList = new ArrayList<WSHandlerResult>();
handlerResultList.add(handlerResult);
messageContext.setProperty(WSHandlerConstants.RECV_RESULTS, handlerResultList);
}
/**
* This is directly taken from rampart-core.
* TODO we need to move these common code to a new module. Otherwise code will be duplicated.
* We cannot use following method from rampart-core as it creates a cyclic dependency. Therefore we have
* to live with following.
* @param doc The document to convert.
* @param useDoom Whether to use doom or not.
* @return A SOAPEnvelope.
* @throws WSSecurityException If an error occurred during conversion.
*/
public static SOAPEnvelope getSOAPEnvelopeFromDOMDocument(Document doc, boolean useDoom)
throws WSSecurityException {
if(useDoom) {
try {
//Get processed headers
SOAPEnvelope env = (SOAPEnvelope)doc.getDocumentElement();
ArrayList processedHeaderQNames = new ArrayList();
SOAPHeader soapHeader = env.getHeader();
if(soapHeader != null) {
Iterator headerBlocs = soapHeader.getChildElements();
while (headerBlocs.hasNext()) {
OMElement element = (OMElement)headerBlocs.next();
SOAPHeaderBlock header = null;
if (element instanceof SOAPHeaderBlock) {
header = (SOAPHeaderBlock) element;
// If a header block is not an instance of SOAPHeaderBlock, it means that
// it is a header we have added in rampart eg. EncryptedHeader and should
// be converted to SOAPHeaderBlock for processing
} else {
header = soapHeader.addHeaderBlock(element.getLocalName(), element.getNamespace());
Iterator attrIter = element.getAllAttributes();
while (attrIter.hasNext()) {
OMAttribute attr = (OMAttribute)attrIter.next();
header.addAttribute(attr.getLocalName(), attr.getAttributeValue(), attr.getNamespace());
}
Iterator nsIter = element.getAllDeclaredNamespaces();
while (nsIter.hasNext()) {
OMNamespace ns = (OMNamespace) nsIter.next();
header.declareNamespace(ns);
}
// retrieve all child nodes (including any text nodes)
// and re-attach to header block
Iterator children = element.getChildren();
while (children.hasNext()) {
OMNode child = (OMNode)children.next();
children.remove();
header.addChild(child);
}
element.detach();
soapHeader.build();
header.setProcessed();
}
if(header.isProcessed()) {
processedHeaderQNames.add(element.getQName());
}
}
}
XMLStreamReader reader = ((OMElement) doc.getDocumentElement())
.getXMLStreamReader();
SOAPModelBuilder stAXSOAPModelBuilder = OMXMLBuilderFactory.createStAXSOAPModelBuilder(
reader);
SOAPEnvelope envelope = stAXSOAPModelBuilder.getSOAPEnvelope();
//Set the processed flag of the processed headers
SOAPHeader header = envelope.getHeader();
for (Iterator iter = processedHeaderQNames.iterator(); iter
.hasNext();) {
QName name = (QName) iter.next();
Iterator<SOAPHeaderBlock> omKids = header.getHeaderBlocksWithName(name);
if(omKids.hasNext()) {
omKids.next().setProcessed();
}
}
envelope.build();
return envelope;
} catch (FactoryConfigurationError e) {
throw new WSSecurityException(e.getMessage());
}
} else {
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
XMLUtils.outputDOM(doc.getDocumentElement(), os, true);
ByteArrayInputStream bais = new ByteArrayInputStream(os.toByteArray());
SOAPModelBuilder stAXSOAPModelBuilder = OMXMLBuilderFactory.createSOAPModelBuilder(bais, null);
return stAXSOAPModelBuilder.getSOAPEnvelope();
} catch (Exception e) {
throw new WSSecurityException(e.getMessage());
}
}
}
/**
* TODO we need to move these common code to a new module. Otherwise code will be duplicated.
* We cannot use following method from rampart-core as it creates a cyclic dependency. Therefore we have
* to live with following.
* Creates a DOM Document using the SOAP Envelope.
* @param env An org.apache.axiom.soap.SOAPEnvelope instance
* @return Returns the DOM Document of the given SOAP Envelope.
* @throws Exception If an error occurred during conversion.
*/
public static Document getDocumentFromSOAPEnvelope(SOAPEnvelope env, boolean useDoom)
throws WSSecurityException {
try {
if(env instanceof Element) {
Element element = (Element)env;
Document document = element.getOwnerDocument();
// For outgoing messages, Axis2 only creates the SOAPEnvelope, but no document. If
// the Axiom implementation also supports DOM, then the envelope (seen as a DOM
// element) will have an owner document, but the document and the envelope have no
// parent-child relationship. On the other hand, the input expected by WSS4J is
// a document with the envelope as document element. Therefore we need to set the
// envelope as document element on the owner document.
if (element.getParentNode() != document) {
document.appendChild(element);
}
// If the Axiom implementation supports DOM, then it is possible/likely that the
// DOM API was used to create the object model (or parts of it). In this case, the
// object model is not necessarily well formed with respect to namespaces because
// DOM doesn't generate namespace declarations automatically. This is an issue
// because WSS4J/Santuario expects that all namespace declarations are present.
// If this is not the case, then signature values or encryptions will be incorrect.
// To avoid this, we normalize the document. Note that if we disable the other
// normalizations supported by DOM, this is generally not a heavy operation.
// In particular, the Axiom implementation is not required to expand the object
// model (including OMSourcedElements) because the Axiom builder is required to
// perform namespace repairing, so that no modifications to unexpanded parts of
// the message are required.
DOMConfiguration domConfig = document.getDomConfig();
domConfig.setParameter("split-cdata-sections", Boolean.FALSE);
domConfig.setParameter("well-formed", Boolean.FALSE);
domConfig.setParameter("namespaces", Boolean.TRUE);
document.normalizeDocument();
return document;
}
if (useDoom) {
env.build();
// Workaround to prevent a bug in AXIOM where
// there can be an incomplete OMElement as the first child body
OMElement firstElement = env.getBody().getFirstElement();
if (firstElement != null) {
firstElement.build();
}
//Get processed headers
SOAPHeader soapHeader = env.getHeader();
ArrayList processedHeaderQNames = new ArrayList();
if(soapHeader != null) {
Iterator headerBlocs = soapHeader.getChildElements();
while (headerBlocs.hasNext()) {
SOAPHeaderBlock element = (SOAPHeaderBlock) headerBlocs.next();
if(element.isProcessed()) {
processedHeaderQNames.add(element.getQName());
}
}
}
SOAPModelBuilder stAXSOAPModelBuilder = OMXMLBuilderFactory.createStAXSOAPModelBuilder(
OMAbstractFactory.getMetaFactory(OMAbstractFactory.FEATURE_DOM),
env.getXMLStreamReader());
SOAPEnvelope envelope = (stAXSOAPModelBuilder)
.getSOAPEnvelope();
envelope.getParent().build();
//Set the processed flag of the processed headers
SOAPHeader header = envelope.getHeader();
for (Iterator iter = processedHeaderQNames.iterator(); iter
.hasNext();) {
QName name = (QName) iter.next();
Iterator<SOAPHeaderBlock> omKids = header.getHeaderBlocksWithName(name);
if(omKids.hasNext()) {
omKids.next().setProcessed();
}
}
Element envElem = (Element) envelope;
return envElem.getOwnerDocument();
} else {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
env.build();
env.serialize(baos);
ByteArrayInputStream bais = new ByteArrayInputStream(baos
.toByteArray());
DocumentBuilderFactory factory = DocumentBuilderFactory
.newInstance();
factory.setNamespaceAware(true);
return factory.newDocumentBuilder().parse(bais);
}
} catch (Exception e) {
throw new WSSecurityException(
"Error in converting SOAP Envelope to Document", e);
}
}
}