/** * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved * * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at * https://opensso.dev.java.net/public/CDDLv1.0.html or * opensso/legal/CDDLv1.0.txt * See the License for the specific language governing * permission and limitations under the License. * * When distributing Covered Code, include this CDDL * Header Notice in each file and include the License file * at opensso/legal/CDDLv1.0.txt. * If applicable, add the following below the CDDL Header, * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * $Id: FSSOAPService.java,v 1.4 2008/11/10 22:56:58 veiming Exp $ * */ package com.sun.identity.federation.services; import com.sun.identity.federation.common.FSException; import com.sun.identity.federation.common.FSUtils; import com.sun.identity.federation.common.IFSConstants; import com.sun.identity.federation.common.LogUtil; import com.sun.identity.liberty.ws.meta.jaxb.ProviderDescriptorType; import com.sun.identity.shared.xml.XMLUtils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Map; import java.util.logging.Level; import javax.servlet.http.HttpServletResponse; import javax.xml.soap.Detail; import javax.xml.soap.MessageFactory; import javax.xml.soap.MimeHeaders; import javax.xml.soap.Name; import javax.xml.soap.SOAPBody; import javax.xml.soap.SOAPConnection; import javax.xml.soap.SOAPConnectionFactory; import javax.xml.soap.SOAPElement; import javax.xml.soap.SOAPEnvelope; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPFault; import javax.xml.soap.SOAPMessage; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * Utils to handle SOAP profiles. */ public class FSSOAPService { private static FSSOAPService instance = null; private static MessageFactory fac = null; private static SOAPConnectionFactory scf = null; static { try { fac = MessageFactory.newInstance(); } catch(Exception ex) { FSUtils.debug.error( FSUtils.bundle.getString( "missingSOAPMessageFactory"), ex); } try { scf = SOAPConnectionFactory.newInstance(); } catch(SOAPException e) { FSUtils.debug.error("FSSOAPService", e); } }; /* do nothing constructor */ private FSSOAPService() { } /* * Binds the passed object xml string to SOAP message. * The SOAP Message will then be sent across to the remote * provider for processing. * @param xmlString the request/response xml string to be bound * @return SOAPMessage constructed from the xml string */ public SOAPMessage bind(String xmlString) { SOAPMessage msg = null; MimeHeaders mimeHeaders = new MimeHeaders(); mimeHeaders.addHeader("Content-Type","text/xml"); StringBuffer envBegin = new StringBuffer(100); envBegin.append("<").append(IFSConstants.SOAP_ENV_PREFIX). append(":Envelope").append(IFSConstants.SPACE).append("xmlns:"). append(IFSConstants.SOAP_ENV_PREFIX).append("=\""). append(IFSConstants.SOAP_URI).append("\">").append(IFSConstants.NL). append("<").append(IFSConstants.SOAP_ENV_PREFIX). append(":Body>").append(IFSConstants.NL); StringBuffer envEnd = new StringBuffer(100); envEnd.append(IFSConstants.START_END_ELEMENT). append(IFSConstants.SOAP_ENV_PREFIX).append(":Body>"). append(IFSConstants.NL). append(IFSConstants.START_END_ELEMENT). append(IFSConstants.SOAP_ENV_PREFIX). append(":Envelope>").append(IFSConstants.NL); try { StringBuffer sb = new StringBuffer(300); sb.append(envBegin).append(xmlString).append(envEnd); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("response created is: "+sb.toString() + "\n--------------------"); } ByteArrayOutputStream bop = new ByteArrayOutputStream(); bop.write(sb.toString().getBytes(IFSConstants.DEFAULT_ENCODING)); msg = fac.createMessage(mimeHeaders, new ByteArrayInputStream( bop.toByteArray())); } catch(Exception e ) { FSUtils.debug.error("could not build response:", e); return null; } return msg; } /* * Sends the passed SOAPMessage to the SOAPEndpoint URL * that is passed. * @param msg the SOAPMessage to be sent * @param soapEndPoint the SOAPEndpoint URL of remote provider * @return SOAPMessage response message from remote provider * @exception IOException, SOAPException if error occurrs */ public SOAPMessage sendMessage( SOAPMessage msg, String soapEndPoint ) throws IOException, SOAPException { try { FSUtils.debug.message("just started in func sendMessage"); if (soapEndPoint == null) { FSUtils.debug.error("createSOAPReceiverURL Error!"); String[] data = { soapEndPoint }; LogUtil.error( Level.INFO, LogUtil.FAILED_SOAP_URL_END_POINT_CREATION, data); return null; } // Send the message to the provider using the connection. ByteArrayOutputStream output = new ByteArrayOutputStream(); msg.writeTo(output); String xmlString = output.toString(IFSConstants.DEFAULT_ENCODING); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("SENDING message: \n " + xmlString + "\nURLEndpoint :" + soapEndPoint + "\nSOAP CALL"); } SOAPConnection con = scf.createConnection(); SOAPMessage reply = con.call(msg, soapEndPoint); FSUtils.debug.message("SOAP CALL COMPLETED"); if (reply == null) { return null; } // check the SOAP message for any SOAP related errors // before passing control to SAML processor output = new ByteArrayOutputStream(); reply.writeTo(output); xmlString = output.toString(IFSConstants.DEFAULT_ENCODING); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("REPLIED message: \n " + xmlString); } return reply; } catch(Exception e){ FSUtils.debug.error("In catch of sendMessage" , e); return null; } } /* * Method to send the passed SOAPMessage to the SOAPEndpoint URL * that is passed. The SOAP Message will then be sent across to the remote * provider in order to perform federation termination. * @param msg the FSFederationTerminationNotification * SOAPMesage to be sent * @param soapEndPoint the SOAPEndpoint URL of remote provider * @return boolean true if successful else false */ public boolean sendTerminationMessage( SOAPMessage msg, String soapEndPoint) { try { FSUtils.debug.message("started in func sendTerminationMessage"); if(soapEndPoint == null) { FSUtils.debug.error("createSOAPReceiverURL Error!"); String[] data = { FSUtils.bundle.getString("failCreateURLEndpoint") }; LogUtil.error(Level.INFO, LogUtil.FAILED_SOAP_URL_END_POINT_CREATION, data); return false; } // Send the message to the provider using the connection. ByteArrayOutputStream output = new ByteArrayOutputStream(); msg.writeTo(output); if (FSUtils.debug.messageEnabled()) { String xmlString = output.toString( IFSConstants.DEFAULT_ENCODING); FSUtils.debug.message("SENDING message: \n " + xmlString); FSUtils.debug.message("URLEndpoint :" + soapEndPoint); } SOAPConnection con = scf.createConnection(); SOAPMessage reply = con.call(msg, soapEndPoint); FSUtils.debug.message("SOAP CALL COMPLETED"); return true; } catch(Exception e){ if(FSUtils.debug.messageEnabled()) { FSUtils.debug.message("In catch of sendTerminationMessage", e); } return false; } } /* * Parses the SOAPMessage and return the Element * corresponding to the liberty message(request/response). * @param message the SOAPMessage to be parsed * @return Element corresponding to liberty request/response */ public Element parseSOAPMessage(SOAPMessage message) { FSUtils.debug.message("FSSOAPService.parseSOAPMessage: Called"); ByteArrayOutputStream bop = null; String xmlString = null; // first check if there was any soap error during verifyHost() try { // check the SOAP message for any SOAP // related errros before passing control to SAM processor bop = new ByteArrayOutputStream(); message.writeTo(bop); xmlString = bop.toString(IFSConstants.DEFAULT_ENCODING); Document doc = XMLUtils.toDOMDocument(xmlString, FSUtils.debug); Element root = doc.getDocumentElement(); String rootName = root.getLocalName(); if ((rootName == null) ||(rootName.length() == 0)) { FSUtils.debug.error("FSSOAPService.parseSOAPMessage: " + "Local name of the SOAPElement in the" + " SOAPMessage passed seems to be missing"); return null; } if (!(rootName.equals("Envelope")) || (!(root.getNamespaceURI().equals(IFSConstants.SOAP_URI)))) { FSUtils.debug.error( "FSSOAPService.parseSOAPMessage: Could not" + "parse SOAPMessage, either root element is not Envelope" + " or invalid name space"); return null; } NodeList nlbody = root.getChildNodes(); int blength = nlbody.getLength(); if (blength <=0 ) { FSUtils.debug.error("FSSOAPService.parseSOAPMessage: Message " + "does not have body"); return null; } Node child = null; boolean found = false; for (int i = 0; i < blength; i++) { child =(Node)nlbody.item(i); if (child.getNodeType() != Node.ELEMENT_NODE) { continue; } String childName = child.getLocalName(); if (childName.equals("Body")) { found = true; break; // found the message body } } if (!found) { FSUtils.debug.error("FSSOAPService.parseSOAPMessage: Message " + "does not have body1"); return null; } Element body =(Element)child; //Is soap-env:Fault NodeList nl = body.getElementsByTagNameNS( IFSConstants.SOAP_URI, "Fault"); int length = nl.getLength(); if (length > 1) { return null; } if (length != 0 ) { child =(Node)nl.item(0); if (child.getNodeType() != Node.ELEMENT_NODE) { return null; } if (child.getLocalName().equalsIgnoreCase("Fault")) { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("FSSOAPService." + " parseSOAPMessage soap-env:Fault found in the " + "SOAPMessage"); } return(Element)child; } } try { //Is samlp:Request child = getSAMLElement(body, "Request"); if (child != null) { return (Element) child; } //Is samlp:Response child = getSAMLElement(body, "Response"); if (child != null) { return (Element) child; } //Is lib:AuthnResponseEnvelope child = getFederationElement(body, "AuthnResponseEnvelope"); if (child != null) { return (Element) child; } //Is lib:AuthnRequest child = getFederationElement(body, "AuthnRequest"); if (child != null) { return (Element) child; } //Is lib:RegisterNameIdentifierRequest child = getFederationElement(body, "RegisterNameIdentifierRequest"); if (child != null) { return (Element) child; } //Is lib:RegisterNameIdentifierResponse child = getFederationElement(body, "RegisterNameIdentifierResponse"); if (child != null) { return (Element) child; } //Is lib:FederationTerminationNotification child = getFederationElement(body, "FederationTerminationNotification"); if (child != null) { return (Element) child; } //Is lib:LogoutRequest child = getFederationElement(body, "LogoutRequest"); if (child != null) { return (Element) child; } //Is lib:LogoutResponse child = getFederationElement(body, "LogoutResponse"); if (child != null) { return (Element) child; } //Is lib:NameIdentifierMappingRequest child = getFederationElement(body, "NameIdentifierMappingRequest"); if (child != null) { return (Element) child; } //Is lib:NameIdentifierMappingResponse child = getFederationElement(body, "NameIdentifierMappingResponse"); if (child != null) { return (Element) child; } FSUtils.debug.error( "FSSOAPService.parseMessage:Invalid message type."); } catch (FSException e) { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("SOAPService.parseSOAPMessage: " + "Couldn't object protocol message element."); } } return null; } catch(Exception e){ FSUtils.debug.error("FSSOAPService.parseSOAPMessage: Exception ",e); return null; } } private Node getSAMLElement(Element body, String nodeName) throws FSException { NodeList nl = body.getElementsByTagNameNS( IFSConstants.PROTOCOL_NAMESPACE_URI, nodeName); int length = nl.getLength(); if (length > 1) { throw new FSException((String) null); } if (length != 0 ) { return (Node)nl.item(0); } return null; } private Node getFederationElement(Element body, String nodeName) throws FSException { NodeList nl = body.getElementsByTagNameNS( IFSConstants.LIB_NAMESPACE_URI, nodeName); int length = nl.getLength(); if (length == 0) { nl = body.getElementsByTagNameNS( IFSConstants.FF_12_XML_NS, nodeName); length = nl.getLength(); } if (length > 1) { throw new FSException((String) null); } if (length != 0 ) { return (Node)nl.item(0); } return null; } /* * Sends a synchronous SOAPMessage to remote provider. * @param response the http response object * @param msg the SOAPMessage to be sent * @param partnerDecriptor the remote provider meta descriptor * @param needAuthn determines forced authn * @return SOAPMessage corresponding to liberty * request/response message * @exception IOException, SOAPException if error occurrs */ public SOAPMessage doSyncCall( HttpServletResponse response, SOAPMessage msg, ProviderDescriptorType partnerDecriptor, boolean needAuthn ) throws IOException, SOAPException { FSUtils.debug.message("FSSOAPService.doSyncCall: Called"); String soapURL = createSOAPReceiverUrl( response, partnerDecriptor, false); if (soapURL == null) { FSUtils.debug.error("FSSOAPService.doSyncCall: " + "createSOAPReceiverURL Error!"); String[] data = { FSUtils.bundle.getString("failCreateURLEndpoint") }; LogUtil.error(Level.INFO, LogUtil.FAILED_SOAP_URL_END_POINT_CREATION, data); return null; } // Send the message to the provider using the connection. ByteArrayOutputStream output = new ByteArrayOutputStream(); msg.writeTo(output); String xmlString = output.toString(IFSConstants.DEFAULT_ENCODING); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message( "FSSOAPService.doSyncCall: SENDING message\n" + xmlString); } SOAPConnection con = scf.createConnection(); SOAPMessage reply = con.call(msg, soapURL); if (reply == null) { response.sendError(response.SC_INTERNAL_SERVER_ERROR, FSUtils.bundle.getString("noReplyfromSOAPReceiver")); return null; } // check the SOAP message for any SOAP related errors // before passing control to SAML processor output = new ByteArrayOutputStream(); reply.writeTo(output); xmlString = output.toString(IFSConstants.DEFAULT_ENCODING); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message( "FSSOAPService.doSyncCall: REPLIED message:\n" + xmlString); } return reply; } /* * Method to construct the URLEndpoint depending on whether basic * authentication of one provider to another is to be done. Otherwise the * SOAPEndpoint of the remote provider is returned * @param response the response object * @param partnerDecriptor the remote provider descriptor * @param needAuthn determines forced authn * @return Element corresponding to liberty request/response */ public String createSOAPReceiverUrl( HttpServletResponse response, ProviderDescriptorType partnerDecriptor, boolean needAuthn ) throws IOException { // TODO: need to handle needAuthn correctly // TODO: need to retrieve auth type, user name and password from meta //basic authentication String username = null; String password = null; String to = partnerDecriptor.getSoapEndpoint(); String authtype = null; String soapURL = null; if (needAuthn) { int idnx = -1; if ((idnx = to.indexOf("//")) == -1) { FSUtils.debug.error("FSSOAPService.createSOAPReceiverUrl: " + "createSOAPReceiverUrl: SOAP-Receiver-URL illegal format."); response.sendError(response.SC_INTERNAL_SERVER_ERROR, FSUtils.bundle.getString("illegalFormatSOAPUrl")); return null; } String protocol = to.substring(0, idnx-1); if (authtype.equalsIgnoreCase(IFSConstants.BASICAUTH) || authtype.equalsIgnoreCase(IFSConstants.NOAUTH)) { if (!protocol.equals(IFSConstants.HTTP)) { String[] data = { protocol , authtype }; LogUtil.error( Level.INFO, LogUtil.MISMATCH_AUTH_TYPE_AND_PROTOCOL, data); response.sendError(response.SC_INTERNAL_SERVER_ERROR, FSUtils.bundle.getString( "mismatchAuthTypeandProtocol")); return null; } } else if(authtype.equalsIgnoreCase(IFSConstants.SSLWITHBASICAUTH)|| authtype.equalsIgnoreCase(IFSConstants.SSL)) { if (!protocol.equals(IFSConstants.HTTPS)) { String[] data = { protocol, authtype }; LogUtil.error(Level.INFO, LogUtil.MISMATCH_AUTH_TYPE_AND_PROTOCOL, data); response.sendError(response.SC_INTERNAL_SERVER_ERROR, FSUtils.bundle.getString( "mismatchAuthTypeandProtocol")); return null; } } else { String[] data = { authtype }; LogUtil.error(Level.INFO,LogUtil.WRONG_AUTH_TYPE,data); response.sendError(response.SC_INTERNAL_SERVER_ERROR, FSUtils.bundle.getString("wrongAuthType")); return null; } if (authtype.equalsIgnoreCase(IFSConstants.BASICAUTH) || authtype.equalsIgnoreCase(IFSConstants.SSLWITHBASICAUTH)) { Map userMap = null; //partnerDecriptor.getAuthType(); username =(String) userMap.get(IFSConstants.USER); password =(String) userMap.get(IFSConstants.PASSWORD); if (username == null || password == null) { FSUtils.debug.error( "FSSOAPService.createSOAPReceiverUrl: " + "PartnerSite required basic authentication. But " + "the user name used for authentication is null."); response.sendError(response.SC_INTERNAL_SERVER_ERROR, FSUtils.bundle.getString("wrongConfigBasicAuth")); return null; } StringBuffer toSOAP = new StringBuffer(100); toSOAP.append(to.substring(0, idnx+2)).append(username). append(":").append(password).append("@"). append(to.substring(idnx+2)); soapURL = toSOAP.toString(); } return null; } else { soapURL = to; } if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message( "FSSOAPService.createSOAPReceiverUrl: Sending message to URL: " + soapURL); } String[] data = { soapURL }; LogUtil.access(Level.FINER,"SOAP_RECEIVER_URL", data); return soapURL; } /** * Forms a SOAP Fault and puts it in the SOAP Message's Body. * * @param faultcode fault code to be set in SOAPMEssage * @param faultString fault string * @param detail the details of the fault condition * @return SOAPMessage containing the SOAP fault */ public SOAPMessage formSOAPError( String faultcode, String faultString, String detail ) { SOAPMessage msg = null ; SOAPEnvelope envelope = null; SOAPFault sf = null; SOAPBody body = null; SOAPElement se = null; try { msg = fac.createMessage(); envelope = msg.getSOAPPart().getEnvelope(); body = envelope.getBody(); sf = body.addFault(); Name qname = envelope.createName( faultcode, null, IFSConstants.SOAP_URI); sf.setFaultCode(qname); sf.setFaultString(FSUtils.bundle.getString(faultString)); if ((detail != null) && !(detail.length() == 0)) { Detail det = sf.addDetail(); se =(SOAPElement)det.addDetailEntry(envelope.createName( "Problem")); se.addAttribute(envelope.createName("details"), FSUtils.bundle.getString(detail)); } } catch( SOAPException e ) { FSUtils.debug.error( "FSSOAPService.formSOAPError:", e); return null; } return msg; } /** * Returns an instance of FSSOAPService instance. * * @return an instance of FSSOAPService instance. */ public static FSSOAPService getInstance() { FSUtils.debug.message("FSSOAPService.getInstance: Called"); synchronized(FSServiceManager.class) { if (instance == null){ FSUtils.debug.message( "Constructing a new instance of FSSOAPService"); instance = new FSSOAPService(); } return instance; } } }