449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * The contents of this file are subject to the terms of the Common Development and
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * Distribution License (the License). You may not use this file except in compliance with the
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * specific language governing permission and limitations under the License.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * When distributing Covered Software, include this CDDL Header Notice in each file and include
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * Header, with the fields enclosed by brackets [] replaced by your own identifying
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * information: "Portions copyright [year] [name of copyright owner]".
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * Copyright 2015 ForgeRock AS.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbingsimport com.sun.identity.saml.common.SAMLConstants;
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * The SOAPCommunicator is a utility package to assist in SAML2 communication over SOAP.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings private static final Debug debug = Debug.getInstance("libSAML2");
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings private SOAPConnectionFactory soapConnectionFactory; // TODO: use Guice
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings private MessageFactory messageFactory; // TODO: use Guice
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings private static SOAPCommunicator instance = new SOAPCommunicator(); // TODO: use Guice
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings soapConnectionFactory = SOAPConnectionFactory.newInstance();
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings messageFactory = MessageFactory.newInstance();
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings debug.error("SOAPCommunicator: Unable to create SOAP MessageFactory", se);
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * Gets the singleton instance of the SOAPCommunicator.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @return the SOAPCommunicator instance.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings public static SOAPCommunicator getInstance() {
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * Opens a SOAP Connection.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @return a new <code>SOAPConnection</code>
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @throws if there was an exception creating the
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * <code>SOAPConnection</code> object.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings public SOAPConnection openSOAPConnection() throws SOAPException {
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings return soapConnectionFactory.createConnection();
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * Creates <code>SOAPMessage</code> with the input XML String
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * as message body.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @param xmlString XML string to be put into <code>SOAPMessage</code> body.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @param isClientMessage true if the message is sent from SOAP client to
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @return newly created <code>SOAPMessage</code>.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @throws SOAPException if it cannot create the
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * <code>SOAPMessage</code>.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings public SOAPMessage createSOAPMessage(final String xmlString,
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings return createSOAPMessage(null, xmlString, isClientMessage);
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * Creates <code>SOAPMessage</code> with the input XML String
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * as message header and body.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @param header XML string to be put into <code>SOAPMessage</code> header.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @param body XML string to be put into <code>SOAPMessage</code> body.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @param isClientMessage true if the message is sent from SOAP client to
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @return newly created <code>SOAPMessage</code>.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @throws SOAPException if it cannot create the <code>SOAPMessage</code>.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings public SOAPMessage createSOAPMessage(final String header,
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings mimeHeaders.addHeader("Content-Type", "text/xml");
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings debug.message("SOAPCommunicator.createSOAPMessage: header = " +
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings sb.append("<").append(SAMLConstants.SOAP_ENV_PREFIX)
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings .append(":Envelope").append(SAMLConstants.SPACE)
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings .append("xmlns:").append(SAMLConstants.SOAP_ENV_PREFIX)
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings .append("=\"").append(SAMLConstants.SOAP_URI).append("\">");
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings .append(SAMLConstants.SOAP_ENV_PREFIX).append(":Header>")
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings .append(SAMLConstants.SOAP_ENV_PREFIX).append(":Body>")
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings .append(":Envelope>").append(SAMLConstants.NL);
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings debug.message("SOAPCommunicator.createSOAPMessage: soap message = " +
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings return messageFactory.createMessage(mimeHeaders, new ByteArrayInputStream(
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings sb.toString().getBytes(SAML2Constants.DEFAULT_ENCODING)));
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings debug.error("SOAPCommunicator.createSOAPMessage: IOE", io);
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * Returns SOAP body as DOM Element from SOAPMessage.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @param message SOAPMessage object.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @return SOAP body, return null if unable to get the SOAP body element.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings public Element getSOAPBody(final SOAPMessage message)
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings debug.message("SOAPCommunicator.getSOAPBody : start");
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings // check the SOAP message for any SOAP
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings // related errors before passing control to SAML processor
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings debug.error("SOAPCommunicator.getSOAPBody : writeTo IO", ie);
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings debug.error("SOAPCommunicator.getSOAPBody : writeTo SOAP", se);
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings ByteArrayInputStream byteArrayIn = new ByteArrayInputStream(byteArrayOut.toByteArray());
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings Document doc = XMLUtils.toDOMDocument(byteArrayIn, debug);
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings debug.message("SOAPCommunicator.getSOAPBody : soap body =\n"
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings String rootName = doc.getDocumentElement().getLocalName();
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings debug.error("SOAPCommunicator.getSOAPBody : no local name");
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings throw new SAML2Exception(SAML2Utils.bundle.getString("missingLocalName"));
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings (!(SAMLConstants.SOAP_URI.equals(root.getNamespaceURI())))) {
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings debug.error("SOAPCommunicator.getSOAPBody : either root " +
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings "element is not Envelope or invalid name space or prefix");
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings throw new SAML2Exception(SAML2Utils.bundle.getString("invalidSOAPElement"));
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings debug.error("SOAPCommunicator.getSOAPBody: no msg body");
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings throw new SAML2Exception(SAML2Utils.bundle.getString("missingSOAPBody"));
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings if (child.getNodeType() != Node.ELEMENT_NODE) {
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings debug.message("SOAPCommunicator.getSOAPBody: " + child);
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings debug.message("SOAPCommunicator.getSOAPBody: local name= " + childName);
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings if (childName.equals("Body") && SAMLConstants.SOAP_URI.equals(child.getNamespaceURI())) {
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings // found the Body element
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings throw new SAML2Exception(SAML2Utils.bundle.getString(
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings "missingSOAPBody"));
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * Forms a SOAP Fault and puts it in the SOAP Message Body.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @param faultCode Fault code.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @param faultString Fault string.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @param detail Fault details.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @return SOAP Fault in the SOAP Message Body or null if unable to generate the message.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings public SOAPMessage createSOAPFault(final String faultCode,
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings SOAPMessage message = messageFactory.createMessage();
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings SOAPFault fault = envelope.getBody().addFault();
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings fault.setFaultCode(envelope.createName(faultCode, null, SOAPConstants.URI_NS_SOAP_ENVELOPE));
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings fault.setFaultString(SAML2Utils.bundle.getString(faultString));
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings SOAPElement faultDetailEntry = (SOAPElement) faultDetail.addDetailEntry(envelope.createName(
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings faultDetailEntry.addAttribute(envelope.createName("details"), SAML2Utils.bundle.getString(detail));
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * Returns SOAP Message from <code>HttpServletRequest</code>.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @param request <code>HttpServletRequest</code> includes SOAP Message.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @return SOAPMessage if request include any soap message in the header.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @throws IOException if error in creating input stream.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @throws SOAPException if error in creating soap message.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings public SOAPMessage getSOAPMessage(final HttpServletRequest request)
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings // Get all the headers from the HTTP request
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings // Get the body of the HTTP request
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings // Create a SOAPMessage
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings return messageFactory.createMessage(headers, is);
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * Send SOAP Message to specified url and returns message from peer.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @param xmlMessage <code>String</code> will be sent.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @param soapUrl URL the mesaage send to.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @param isClientMessage true if the message is sent from SOAP client to
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @return SOAPMessage if the peer send back any reply.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @throws SOAPException if error in creating soap message.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @throws SAML2Exception if error in creating soap message.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings public SOAPMessage sendSOAPMessage(final String xmlMessage,
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings SOAPConnection con = soapConnectionFactory.createConnection();
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings SOAPMessage msg = createSOAPMessage(xmlMessage, isClientMessage);
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * Converts a <code>SOAPMessage</code> to a <code>String</code>.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @param message SOAPMessage object.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @return the <code>String</code> converted from the
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * <code>SOAPMessage</code> or null if an error ocurred.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings public String soapMessageToString(final SOAPMessage message) {
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings ByteArrayOutputStream bop = new ByteArrayOutputStream();
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings debug.error("SOAPCommunicator.soapMessageToString:", ie);
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings debug.error("SOAPCommunicator.soapMessageToString:", soapex);
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * Returns first Element with given local name in samlp name space inside
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * SOAP message.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @param message SOAP message.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @param localName local name of the Element to be returned.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @return first Element matching the local name.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @throws SAML2Exception if the Element could not be found or there is
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * SOAP Fault present.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings public Element getSamlpElement(final SOAPMessage message,
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings final String localName) throws SAML2Exception {
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings NodeList bodyChildNodes = body.getChildNodes();
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings int childNodeLength = bodyChildNodes.getLength();
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings debug.error("SOAPCommunicator.getSamlpElement: empty body");
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings throw new SAML2Exception(SAML2SDKUtils.bundle.getString("missingBody"));
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings if (node.getNodeType() != Node.ELEMENT_NODE) {
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings debug.message("SOAPCommunicator.getSamlpElement: node={}, nsURI={}",
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings throw new SAML2Exception(SAML2Utils.bundle.getString(
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings "soapFaultInSOAPResponse"));
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings throw new SAML2Exception(SAML2SDKUtils.bundle.getString("elementNotFound") +
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * Returns mime headers in HTTP servlet request.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @param req HTTP servlet request.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @return mime headers in HTTP servlet request.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings public MimeHeaders getHeaders(final HttpServletRequest req) {
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings String headerValue = req.getHeader(headerName);
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings debug.message("SOAPCommunicator.getHeaders: Header name={}, value={}",
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings debug.message("SOAPCommunicator.getHeaders: Header=" + headers.toString());