| /* |
| * 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 omKids = header.getChildrenWithName(name); |
| if(omKids.hasNext()) { |
| ((SOAPHeaderBlock)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 omKids = header.getChildrenWithName(name); |
| if(omKids.hasNext()) { |
| ((SOAPHeaderBlock)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); |
| } |
| } |
| } |