001/**
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved
005 *
006 * The contents of this file are subject to the terms
007 * of the Common Development and Distribution License
008 * (the License). You may not use this file except in
009 * compliance with the License.
010 *
011 * You can obtain a copy of the License at
012 * https://opensso.dev.java.net/public/CDDLv1.0.html or
013 * opensso/legal/CDDLv1.0.txt
014 * See the License for the specific language governing
015 * permission and limitations under the License.
016 *
017 * When distributing Covered Code, include this CDDL
018 * Header Notice in each file and include the License file
019 * at opensso/legal/CDDLv1.0.txt.
020 * If applicable, add the following below the CDDL Header,
021 * with the fields enclosed by brackets [] replaced by
022 * your own identifying information:
023 * "Portions Copyrighted [year] [name of copyright owner]"
024 *
025 * $Id: SAMLClient.java,v 1.6 2008/08/19 19:11:11 veiming Exp $
026 *
027 */
028
029
030package com.sun.identity.saml; 
031
032import java.io.*;
033import java.util.*;
034import java.net.URL;
035import javax.servlet.http.*;
036import com.sun.identity.common.SystemConfigurationUtil;
037import com.sun.identity.common.SystemConfigurationException;
038import com.sun.identity.plugin.session.SessionException;
039import com.sun.identity.plugin.session.SessionManager;
040import com.sun.identity.plugin.session.SessionProvider;
041import com.sun.identity.saml.common.*;
042import com.sun.identity.saml.assertion.*;
043import com.sun.identity.saml.protocol.*;
044import com.sun.identity.saml.servlet.SAMLSOAPReceiver;
045import com.sun.identity.shared.jaxrpc.SOAPClient;
046import com.sun.identity.shared.xml.XMLUtils;
047import org.w3c.dom.Document;
048import org.w3c.dom.Node;
049import org.w3c.dom.NodeList;
050import org.w3c.dom.Element;
051
052/**
053 * The class <code>SAMLClient</code> provides interfaces
054 * to do Web and POST profile as specified by SAML specification. It
055 * also provides methods to get Assertions based on Artifacts.
056 * @supported.api
057 */
058
059public class SAMLClient {   
060    /**
061     * This private method is designed to do the SAML Single-Sign-On. 
062     * It is called internally by doWebArtifact and doWebPOST methods. 
063     * @param request HTTP Servlet Request
064     * @param response HTTP Servlet Response
065     * @param target the target URL
066     * @param service the service name 
067     * @exception IOException if an input or output exception occurs when 
068     *     redirecting to service <code>URL</code>
069     * @exception SAMLException if SAML error occurs during Single-Sign-On.
070     */
071    private static void doSSO(HttpServletRequest request,
072                              HttpServletResponse response, 
073                              String target, String service) 
074                              throws IOException, SAMLException {
075        if (request == null || response == null || target == null) {
076            SAMLUtils.debug.error("SAMLClient:Input parameter is null.");
077            throw new SAMLException(SAMLUtils.bundle.getString("nullInput")); 
078        }
079        if ((!service.equals(SAMLConstants.SAML_AWARE_NAMING)) &&
080            (!service.equals(SAMLConstants.SAML_POST_NAMING)) &&
081            (!service.equals(SAMLConstants.SAML_SOAP_NAMING))) {
082            SAMLUtils.debug.error("SAMLClient:illegal naming service name.");
083            throw new SAMLException(
084                SAMLUtils.bundle.getString("illegalNamingService")); 
085        }
086        Object ssoToken = null;  
087        SessionProvider sessionProvider;
088        try {
089            sessionProvider = SessionManager.getProvider();
090            ssoToken =sessionProvider.getSession(request);
091            if (ssoToken == null) {
092                SAMLUtils.debug.error("SAMLClient:SSOToken is null.");
093                throw new SAMLException( 
094                    SAMLUtils.bundle.getString("nullSSOToken")); 
095            }
096            if (!sessionProvider.isValid(ssoToken)) {
097                SAMLUtils.debug.error("SAMLClient:Session is invalid."); 
098                throw new SAMLException(
099                    SAMLUtils.bundle.getString("invalidSSOToken")); 
100            }
101        } catch (SessionException se) {
102            SAMLUtils.debug.error("SAMLClient", se);
103            throw new SAMLException("SAMLClient:doSSO:" + se.getMessage()); 
104        }
105        URL weburl = null;
106        try { 
107            URL serverurl = new URL(SAMLServiceManager.getServerURL()); 
108            weburl = SystemConfigurationUtil.getServiceURL(service,  
109                serverurl.getProtocol(), serverurl.getHost(),
110                serverurl.getPort(), serverurl.getPath());  
111            
112        } catch(SystemConfigurationException ue) {
113            SAMLUtils.debug.error("SAMLClient", ue); 
114            throw new SAMLException(
115                SAMLUtils.bundle.getString("URLNotFoundException")); 
116        }
117        StringBuffer redirectedurl = new StringBuffer(200); 
118        String tname = (String) SAMLServiceManager.
119             getAttribute(SAMLConstants.TARGET_SPECIFIER);   
120        redirectedurl.append(weburl).append("?").append(tname).append("=").
121             append(target);
122        response.sendRedirect(redirectedurl.toString()); 
123    }             
124
125    /**
126     * This method is designed to do the SAML web-browser profile with 
127     * Artifact. Once the browser (user) authenticated to OpenSSO, 
128     * it can call this method to complete the single sign on to the
129     * target host and be redirected to the specified target site.
130     * @param request HTTP Servlet Request
131     * @param response HTTP Servlet Response
132     * @param target A String representing the target URL
133     * @exception IOException if an input or output exception occurs when
134     *     redirecting to service <code>URL</code>
135     * @exception SAMLException if SAML error occurs during the process 
136     * @supported.api
137     */
138    public static void doWebArtifact(HttpServletRequest request, 
139                                     HttpServletResponse response, 
140                                     String target) 
141                                     throws IOException, SAMLException {
142        doSSO(request, response, target, SAMLConstants.SAML_AWARE_NAMING);
143    }
144
145    /**
146     * This method is designed to do the SAML web-browser POST profile. 
147     * Once the browser (user) authenticated to OpenSSO, 
148     * it can call this method
149     * to complete the single sign on to the target host and be
150     * redirected to the target site.
151     * @param request HTTP Servlet Request
152     * @param response HTTP Servlet Response
153     * @param target A String representing the target URL
154     * @exception IOException if an input or output exception occurs when
155     *     redirecting to service <code>URL</code>
156     * @exception SAMLException if SAML error occurs during the process 
157     * @supported.api
158     */
159    public static void doWebPOST(HttpServletRequest request,
160                                 HttpServletResponse response, 
161                                 String target)
162                                 throws IOException, SAMLException {
163        doSSO(request, response, target, SAMLConstants.SAML_POST_NAMING);
164    }
165    
166    /**
167     * This method returns the Assertion for the corresponding artifact.
168     * It sends an <code>ArtifactQuery</code> SAML message to the
169     * destination identified by the source ID in the artifact and
170     * returns the Assertion contained in the SAML response message.
171     *
172     * @param artifact An <code>AssertionArtifact</code> representing the
173     *                 artifact 
174     * @return An Assertion corresponding to the artifact
175     * @exception IOException if an input or output exception occurs when
176     *     connecting to SAML service <code>URL</code>
177     * @exception SAMLException if SAML error occurs during the process 
178     * @supported.api
179     */
180    public static Assertion getAssertionByArtifact(AssertionArtifact artifact)
181        throws IOException, SAMLException {          
182        return getAssertionByArtifact(artifact.getAssertionArtifact());
183    }
184
185    /**
186     * This method returns the Assertion for the corresponding artifact.
187     * It sends an <code>ArtifactQuery</code> SAML message to the destination
188     * identified by the source ID in the artifact and returns the Assertion
189     * contained in the SAML response message.
190     *
191     * @param artifact A String representing the artifact
192     * @return An Assertion corresponding to the artifact
193     * @exception IOException if an input or output exception occurs when
194     *     connecting to SAML service <code>URL</code>
195     * @exception SAMLException if SAML error occurs during the process
196     * @supported.api
197     */
198    public static Assertion getAssertionByArtifact(String artifact)
199        throws IOException, SAMLException {
200        if (artifact == null || artifact.length() == 0) {
201            if (SAMLUtils.debug.messageEnabled()) {
202                SAMLUtils.debug.message("SAMLClient: input is null.");
203            }
204            throw new SAMLException(
205                SAMLUtils.bundle.getString("nullInput"));
206        }
207        // first, check if the sourceid contained in the artifact has an entry 
208        // in SAML config
209        AssertionArtifact aa = new AssertionArtifact(artifact); 
210        String sid = aa.getSourceID(); 
211        String ssurl = getSamlSoapUrl(sid); 
212        // if not, query naming service to get the soap url in case of local 
213        URL samlsoap = null; 
214        try {
215            if (ssurl == null) {
216                Map instances= (Map)
217                  SAMLServiceManager.getAttribute(SAMLConstants.INSTANCE_LIST); 
218                if (instances == null || instances.size() == 0) {
219                    throw new SAMLException( 
220                        SAMLUtils.bundle.getString("instancemapNull")); 
221                }
222                String server= (String) instances.get(sid); 
223                if (server == null || server.length() == 0) {
224                    throw new SAMLException(
225                        SAMLUtils.bundle.getString("instanceNotFound")); 
226                }
227                URL serverurl = new URL(server); 
228                samlsoap = SystemConfigurationUtil.getServiceURL(
229                    SAMLConstants.SAML_SOAP_NAMING,
230                    serverurl.getProtocol(), serverurl.getHost(),
231                    serverurl.getPort(), serverurl.getPath());   
232            } else {
233                samlsoap = new URL(ssurl);
234            }
235            if (SAMLUtils.debug.messageEnabled()) {
236                SAMLUtils.debug.message("SAMLClient:SOAPUrl=" +
237                    samlsoap.toString());
238            }
239        } catch (SystemConfigurationException ue) {
240            SAMLUtils.debug.error("SAMLClient", ue);
241            throw new SAMLException(
242                SAMLUtils.bundle.getString("URLNotFoundException"));
243        }
244        if (!setLocalFlag(samlsoap)) {
245            throw new SAMLException( 
246                SAMLUtils.bundle.getString("failSetLocalFlag"));
247        }
248     
249        if (SAMLUtils.debug.messageEnabled()) {
250            SAMLUtils.debug.message("SAMLClient:getAssertionByArtifact: " +
251                                    "check localFlag : " +
252                                     SAMLServiceManager.localFlag);
253        }
254        String encodedSourceid = (String) SAMLServiceManager.getAttribute(
255                                                SAMLConstants.SITE_ID);
256        boolean isMySite = sid.equals(encodedSourceid.trim()); 
257        if (SAMLServiceManager.localFlag && isMySite)  {
258            // if the localFlag is true and the Artifact's source id is 
259            // the same as my site_id, (means SAMLClient and AssertionManager 
260            // in the same JVM, call AssertionManager directly.
261            if (SAMLUtils.debug.messageEnabled()) {
262                SAMLUtils.debug.message("SAMLClient:getAssertionByArtifact" +
263                                ":call AssertionManager.getAssertion(" +
264                                "AssertionArtifact)");      
265            }
266            AssertionManager assertManager = AssertionManager.getInstance(); 
267            Assertion assertion = assertManager.getAssertion(aa);  
268            return assertion;
269        }
270        String[] strarray = new String[1];
271        strarray[0]= artifact;
272        List asserts = null; 
273        if (isMySite && ssurl == null) {
274            asserts = artifactQueryHandler(strarray, samlsoap.toString()); 
275        } else {
276            asserts = artifactQueryHandler(strarray, null); 
277        }
278        if (asserts == null || asserts.isEmpty()) {
279            if (SAMLUtils.debug.messageEnabled()) {
280                SAMLUtils.debug.message("SAMLClient:getAssertionByArtifact" +
281                                    ":returned assertion list is null.");      
282               }
283            return null; 
284        }
285        return ((Assertion) asserts.get(0)); 
286    }
287
288    private static String getSamlSoapUrl(String sourceid) {
289        String soapurl = null;
290        try {
291            Map partner = (Map) SAMLServiceManager.getAttribute(
292                                                   SAMLConstants.PARTNER_URLS); 
293            if (partner == null) {
294                SAMLUtils.debug.error("SAMLClient:Partner URL is null.");
295                return null;
296            }
297            SAMLServiceManager.SOAPEntry partnerdest = 
298                           (SAMLServiceManager.SOAPEntry) partner.get(sourceid);
299            if (partnerdest != null) {
300                soapurl = partnerdest.getSOAPUrl(); 
301            } else {
302                if (SAMLUtils.debug.messageEnabled()) {
303                    SAMLUtils.debug.message("SAMLClient: " + sourceid + 
304                                            " is not on trusted site list.");
305                }
306            }
307            return soapurl;
308        } catch (Exception se) {
309            SAMLUtils.debug.error("SAMLClient: ", se);
310            return null;
311        } 
312    }
313    
314    public static boolean setLocalFlag(URL url) {
315        if (url == null) {
316            SAMLUtils.debug.error("SAMLClient:setLocalFlag has null input.");
317            return false; 
318        }
319        try {
320             // Preload class SAMLSOAPReceiver since it wouldn't be included 
321             // in the remote sdk. If the class SAMLSOAPReceiver isn't 
322             // presented, we consider it is client application. 
323             Class.forName("com.sun.identity.saml.servlet.SAMLSOAPReceiver");
324             if (SAMLUtils.debug.messageEnabled()) {
325                SAMLUtils.debug.message("in setLocalFlag(), url : " + 
326                                        url.toString());
327                SAMLUtils.debug.message("SAMLSOAPReceiver.localSAMLServiceID : "
328                                        + SAMLSOAPReceiver.localSAMLServiceID);
329            }
330            if (SAMLSOAPReceiver.localSAMLServiceID != null) { 
331                URL samlservice =
332                    new URL(SAMLSOAPReceiver.localSAMLServiceID);   
333                if ((url.getHost().equalsIgnoreCase(samlservice.getHost())) && 
334                    (url.getPort() == samlservice.getPort())) {
335                    SAMLServiceManager.localFlag = true;
336                    return true; 
337                }
338            }
339        } catch (ClassNotFoundException cnfe) {
340          if (SAMLUtils.debug.messageEnabled()) {
341              SAMLUtils.debug.message("SAMLClient::setLocalFlag: ",  
342                                       cnfe); 
343           }
344           SAMLServiceManager.localFlag = false; 
345           return true; 
346        } catch (Exception e) {
347            SAMLUtils.debug.error("SAMLClient::setLocalFlag:: ", e);
348            return false; 
349        }
350        SAMLServiceManager.localFlag = false; 
351        return true;
352    }
353
354    /**
355     * This private method takes a SAML request object and returns a SOAPMessage
356     * wrapped around the request object. 
357     * @param req A SAML request object 
358     * @return a SOAPMessage 
359     * @exception SAMLException
360     */
361    private static String createSOAPMessage(Request req)
362                               throws SAMLException {
363        if (req == null){
364            throw new SAMLException(SAMLUtils.bundle.getString("nullInput"));
365        }
366
367        try {
368            StringBuffer envBegin = new StringBuffer(100);
369            envBegin.append("<").append(SAMLConstants.SOAP_ENV_PREFIX).
370                append(":Envelope").append(SAMLConstants.SPACE).
371                append("xmlns:").append(SAMLConstants.SOAP_ENV_PREFIX).
372                append("=\"").append(SAMLConstants.SOAP_URI).append("\">").
373                append(SAMLConstants.NL).append("<").
374                append(SAMLConstants.SOAP_ENV_PREFIX).append(":Body>").
375                append(SAMLConstants.NL);
376
377            StringBuffer envEnd = new StringBuffer(100);
378            envEnd.append(SAMLConstants.START_END_ELEMENT).
379                append(SAMLConstants.SOAP_ENV_PREFIX).append(":Body>").
380                append(SAMLConstants.NL).
381                append(SAMLConstants.START_END_ELEMENT).
382                append(SAMLConstants.SOAP_ENV_PREFIX).
383                append(":Envelope>").append(SAMLConstants.NL);
384
385            StringBuffer sb = new StringBuffer(300);
386            sb.append(envBegin).append(req.toString(true, true)).append(envEnd);
387            return(sb.toString());
388        } catch (Exception e) {
389            throw new SAMLException(e.getMessage()); 
390        }   
391    }
392    
393    /**
394     * This private method is designed to get the URLEndpoint which points to 
395     * the partner's SOAP Receiver service, such as the URLEndpoint of 
396     * SAMLSOAPReceiver servlet in OpenSSO context. 
397     * @param destSite  A object of 
398     *                com.sun.identity.saml.common.SAMLServiceManager.SOAPEntry
399     * @param to An URLEndpoint object 
400     * @exception IOException if <code>URL</code> is invalid
401     * @exception SAMLException if SAML error occurs during the process
402     */
403    private static String createSOAPReceiverUrl(
404            com.sun.identity.saml.common.SAMLServiceManager.SOAPEntry destSite, 
405            String to) throws IOException, SAMLException {
406        if (destSite == null || to == null || to.length() == 0) {
407            throw new SAMLException(SAMLUtils.bundle.getString("nullInput"));
408        }
409        //get authentication type 
410        String authtype = destSite.getAuthType();    
411        String urlEndpoint  = null;
412        int idnx = -1; 
413        if ((idnx = to.indexOf("//")) == -1) {
414            SAMLUtils.debug.error("SAMLClient:createSOAPReceiverUrl:" +
415                                  "Illegal format of input parameter.");  
416            throw new SAMLException(
417                      SAMLUtils.bundle.getString("illegalFormatSOAPUrl")); 
418        }
419        String protocol = to.substring(0, idnx-1);  
420        // check if the authentication type matches the protocol specified in 
421        // input parameter "to". 
422        if (authtype.equalsIgnoreCase(SAMLConstants.BASICAUTH) || 
423            authtype.equalsIgnoreCase(SAMLConstants.NOAUTH)) {
424            if (!protocol.equals(SAMLConstants.HTTP)) {    
425                if (SystemConfigurationUtil.isServerMode()) {
426                    String[] data = {SAMLUtils.bundle.getString(
427                      "mismatchAuthTypeandProtocol")};
428                    LogUtils.error(java.util.logging.Level.INFO,
429                        LogUtils.AUTH_PROTOCOL_MISMATCH, data);
430                }
431                throw new SAMLException(    
432                    SAMLUtils.bundle.getString("mismatchAuthTypeandProtocol"));
433            }
434        } else if (authtype.equalsIgnoreCase(SAMLConstants.SSLWITHBASICAUTH)  
435                || authtype.equalsIgnoreCase(SAMLConstants.SSL)) {
436            if (!protocol.equals(SAMLConstants.HTTPS)) {    
437                if (SystemConfigurationUtil.isServerMode()) {
438                    String[] data = {SAMLUtils.bundle.getString(
439                      "mismatchAuthTypeandProtocol")};
440                    LogUtils.error(java.util.logging.Level.INFO,
441                        LogUtils.AUTH_PROTOCOL_MISMATCH, data);
442                }
443                throw new SAMLException(
444                  SAMLUtils.bundle.getString("mismatchAuthTypeandProtocol"));
445            }
446        } else {
447            if (SystemConfigurationUtil.isServerMode()) {
448                String[] data = {SAMLUtils.bundle.getString(
449                   "wrongAuthType")};
450                LogUtils.error(java.util.logging.Level.INFO,
451                    LogUtils.INVALID_AUTH_TYPE, data);
452            }
453            throw new SAMLException(
454                      SAMLUtils.bundle.getString("wrongAuthType"));
455        }
456
457        // If the authentication type is BASICAUTH or SSLWITHBASICAUTH, 
458        // call ServiceManager to retrieve the partner's user name and password 
459        // which protects the partner's SOAPReceiverURL. 
460        if (authtype.equalsIgnoreCase(SAMLConstants.BASICAUTH) || 
461            authtype.equalsIgnoreCase(SAMLConstants.SSLWITHBASICAUTH)) {
462            String username = destSite.getBasicAuthUserID();
463            String password = destSite.getBasicAuthPassword();
464            if (username == null || password == null) {
465                SAMLUtils.debug.error("SAMLClient:createSOAPReceiverUrl:" +
466                    "PartnerSite required basic authentication. But the " +
467                    "user name or password used for authentication is null.");
468                throw new SAMLException(
469                    SAMLUtils.bundle.getString("wrongConfigBasicAuth"));
470            }
471            String toSOAP = to.substring(0, idnx+2) + username + ":" +
472                            password + "@" + to.substring(idnx+2); 
473            urlEndpoint = toSOAP;
474        } else {
475            urlEndpoint = to; 
476        }
477        if (SAMLUtils.debug.messageEnabled()) {
478            SAMLUtils.debug.message("Sending message to URL: " +
479                                    urlEndpoint);
480        }
481        if (SystemConfigurationUtil.isServerMode()) {
482            String[] data = {SAMLUtils.bundle.getString("SOAPReceiverURL"),
483                urlEndpoint};
484            LogUtils.access(java.util.logging.Level.FINE, 
485                LogUtils.SOAP_RECEIVER_URL, data);
486        }
487        return urlEndpoint;
488    }
489    
490    /**
491     * This private method is designed to get the SAML response object from  
492     * a SOAPMessage string. 
493     * @param xmlString A String representing a string of SOAPMessage 
494     * @return a SAML Response object
495     * @exception IOException if an input or output exception occurs when
496     *     connecting to SAML service <code>URL</code>
497     * @exception SAMLException if SAML error occurs during the process
498     */
499    private static Response getSAMLResponse(String xmlString) 
500                            throws IOException, SAMLException {
501        if (xmlString == null || xmlString.length() == 0) {
502            throw new SAMLException(SAMLUtils.bundle.getString("nullInput"));
503        }   
504        Response samlResp = null;                                
505        Document doc = XMLUtils.toDOMDocument(xmlString, SAMLUtils.debug);
506        Element root= doc.getDocumentElement();
507        String rootName  = root.getLocalName();
508        if ((rootName == null) || (rootName.length() == 0)) {
509            SAMLUtils.debug.error("Missing Envelope tag.");
510            throw new SAMLException (
511                      SAMLUtils.bundle.getString("missingSOAPEnvTag"));
512        }
513        if (!(rootName.equals("Envelope")) ||
514            (!(root.getNamespaceURI().equals(SAMLConstants.SOAP_URI)))) {
515            SAMLUtils.debug.error("Wrong Envelope tag or namespace.");   
516            throw new SAMLException(
517                SAMLUtils.bundle.getString("serverError"));
518        }
519        //exam the child element of <SOAP-ENV:Envelope>
520        NodeList  nodes = root.getChildNodes();
521        int nodeCount = nodes.getLength();
522        if (nodeCount <= 0) {
523            SAMLUtils.debug.error("Envelope does not contain a SOAP body."); 
524            throw new SAMLException(
525                      SAMLUtils.bundle.getString("missingSOAPBody"));
526        }
527        String tagName = null; 
528        String ctagName = null; 
529        Node currentNode = null; 
530        Node cnode = null; 
531        for (int i = 0; i < nodeCount; i++) {
532            currentNode = nodes.item(i);
533            if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
534                tagName = currentNode.getLocalName();
535                if ((tagName == null) || tagName.length() == 0) {
536                    SAMLUtils.debug.error("Missing tag name of child element");
537                    throw new SAMLException(
538                             SAMLUtils.bundle.getString("missingChildTagName"));
539                }
540                if (tagName.equals("Body")) {
541                    NodeList cNodes = currentNode.getChildNodes(); 
542                    int cnodeCount = cNodes.getLength(); 
543                    for (int j = 0; j < cnodeCount; j++) {
544                        cnode = cNodes.item(j); 
545                        if (cnode.getNodeType() == Node.ELEMENT_NODE){ 
546                            ctagName = cnode.getLocalName();
547                            if ((ctagName == null) || ctagName.length() == 0) {
548                                SAMLUtils.debug.error("Missing tag name of " +
549                                            "child element of <SOAP-ENV:Body>");
550                                throw new SAMLException(      
551                                          SAMLUtils.bundle.getString(
552                                                    "missingChildTagName"));
553                            }
554                            if (ctagName.equals("Fault")) {
555                                SAMLUtils.debug.error("SOAPFault error."); 
556                                throw new SAMLException(
557                                          XMLUtils.print(cnode)); 
558                            } else if (ctagName.equals("Response")) {
559                                samlResp = new Response((Element) cnode); 
560                                if (SAMLUtils.debug.messageEnabled()) {
561                                    SAMLUtils.debug.message("SAML Response:" +
562                                                        samlResp.toString());
563                                }
564                                break;
565                            } else {
566                                SAMLUtils.debug.error("Wrong child element " +
567                                                      "in SOAPBody");
568                                throw new SAMLException(
569                                          SAMLUtils.bundle.getString(
570                                          "wrongSOAPBody")); 
571                            }
572                        }
573                    } // end of for(int j=0; j <cnodeCount; j++) 
574                } else if (tagName.equals("Header")) {
575                    if (SAMLUtils.debug.messageEnabled()) {
576                        SAMLUtils.debug.message("Inside SOAP Response:" +
577                                                 " SOAP Header");
578                    }
579                } else {
580                    SAMLUtils.debug.error("Wrong child element in Envelope"); 
581                    throw new SAMLException(
582                              SAMLUtils.bundle.getString("wrongSOAPElement"));  
583                }
584            } // end of if (currentNode.getNodeType() == Node.ELEMENT_NODE) 
585        } // end of for (int i = 0; i < nodeCount; i++)
586        return samlResp; 
587    }
588
589    /**
590     * This method is designed to get a list of assertion from the 
591     * SAML Response.
592     * @param samlresponse A SAML Response object
593     * @param alist a List  
594     * @return a List object representing a list of Assertion
595     * @exception SAMLException
596     */
597    private static List getAssertionList(Response samlresponse, List alist) 
598                                         throws SAMLException {
599        if (samlresponse == null || alist == null) {
600            throw new SAMLException(SAMLUtils.bundle.getString("nullInput"));
601        }
602        // get a list of SAML assertion 
603        List assertions = new ArrayList(); 
604        assertions = samlresponse.getAssertion();
605        if (assertions == null || assertions.isEmpty()) {      
606            if (SystemConfigurationUtil.isServerMode()) {
607                String[] data = {SAMLUtils.bundle.getString(
608                "noAssertioninResponse"), samlresponse.toString(true, true)};
609                LogUtils.error(java.util.logging.Level.INFO, 
610                    LogUtils.NO_ASSERTION_IN_RESPONSE, data);
611            }
612            throw new SAMLException(
613                 SAMLUtils.displayXML(samlresponse.getStatus().toString()));
614        }  
615        if (assertions.size() != alist.size()) {
616            SAMLUtils.debug.error("The SAML response containing assertions !=" 
617                             + "the number of artifacts in SAML request");
618            if (SystemConfigurationUtil.isServerMode()) {                       
619                String[] data = {SAMLUtils.bundle.getString(
620                    "wrongNumberAssertions"),
621                    samlresponse.toString(true, true)};
622                LogUtils.error(java.util.logging.Level.INFO, 
623                    LogUtils.MISMATCHED_ASSERTION_AND_ARTIFACT, data);
624            }
625            throw new SAMLException(
626                      SAMLUtils.bundle.getString("wrongNumberAssertions")); 
627        }   
628        return assertions; 
629    }
630    
631    /**
632     * This method is designed to get a list of assertion based on the input 
633     * <code>AssertionArtifact</code>(s). 
634     *
635     * @param arti An array of String 
636     * @return a List object representing a list of Assertions
637     * @exception IOException if an input or output exception occurs when
638     *     connecting to SAML service <code>URL</code>
639     * @exception SAMLException if SAML error occurs during the process
640     */
641    public static List artifactQueryHandler(String[] arti, String connecto) 
642                       throws IOException, SAMLException {
643        if ((arti == null) || (arti.length == 0)) {
644            SAMLUtils.debug.message("artifactQueryHandler: null input.");
645            throw new SAMLException(SAMLUtils.bundle.getString("nullInput"));
646        }
647
648        String firstSourceID = null;
649        com.sun.identity.saml.common.SAMLServiceManager.SOAPEntry dest= null;
650        Response samlresponse = null; 
651        List al = new ArrayList(); 
652        List artl = new ArrayList();
653        
654        AssertionArtifact firstArtifact = new AssertionArtifact(arti[0]); 
655        firstSourceID = firstArtifact.getSourceID();
656        if (SystemConfigurationUtil.isServerMode()) {
657            String[] data = {SAMLUtils.bundle.getString("Artifact") + " " + 0, 
658                arti[0]};
659            LogUtils.access(java.util.logging.Level.INFO,
660                LogUtils.ARTIFACT_TO_SEND, data);
661        }
662        artl.add(firstArtifact);
663        al.add(arti[0]);
664        AssertionArtifact assertArtifact = null; 
665        String destination = null; 
666        for (int k = 1; k < arti.length; k++) {
667            // check if all Artifact come from the same source id             
668            assertArtifact = new AssertionArtifact(arti[k]);
669            destination = assertArtifact.getSourceID(); 
670            if (SAMLUtils.debug.messageEnabled()) {
671                SAMLUtils.debug.message("SourceID within the Artifact is " +
672                                        destination);
673            }
674            if (!destination.equals(firstSourceID)) {
675                if (SAMLUtils.debug.messageEnabled()) {
676                    SAMLUtils.debug.message("Received multiple Artifacts " +
677                                                "have different source id.");
678                }
679                throw new SAMLException(
680                          SAMLUtils.bundle.getString("sourceidDifferent")); 
681            }
682            if (SystemConfigurationUtil.isServerMode()) {
683                String[] data = {SAMLUtils.bundle.getString("Artifact") + " " 
684                    + k, arti[k]};
685                LogUtils.access(java.util.logging.Level.FINE,
686                    LogUtils.ARTIFACT_TO_SEND, data);
687            }
688            artl.add(assertArtifact);
689            al.add(arti[k]);
690        }
691        
692        try {
693            //Retrieve the soap-receiver-url using the sourceid inside of 
694            //the AssertionArtifact 
695            String to = null; 
696            Map soaps = (Map) SAMLServiceManager.getAttribute(
697                                                   SAMLConstants.PARTNER_URLS); 
698            if (soaps == null) {
699                SAMLUtils.debug.error(
700                                  SAMLUtils.bundle.getString("nullPartnerUrl"));
701                throw new SAMLException(
702                                  SAMLUtils.bundle.getString("nullPartnerUrl"));
703            }
704            String urlEndpoint = null; 
705            if (soaps.containsKey(firstSourceID)) {
706                dest = (SAMLServiceManager.SOAPEntry) soaps.get(firstSourceID);
707                to = dest.getSOAPUrl(); 
708                if (to==null) {
709                    if (connecto == null || connecto.length() == 0) {
710                        if (SystemConfigurationUtil.isServerMode()) {
711                            String[] data = {SAMLUtils.bundle.getString(
712                                "wrongPartnerSOAPUrl")};
713                            LogUtils.error(java.util.logging.Level.INFO,
714                                LogUtils.WRONG_SOAP_URL, data);
715                        }
716                        throw new SAMLException(
717                          SAMLUtils.bundle.getString("wrongPartnerSOAPUrl")); 
718                    } else {
719                        urlEndpoint = connecto;
720                    }
721                } else {
722                    urlEndpoint = createSOAPReceiverUrl(dest, to); 
723                }
724            } else {
725                if (SAMLUtils.debug.messageEnabled()) {
726                    SAMLUtils.debug.message("SAMLClient:artifactQueryHandler: " 
727                                 + "Failed to locate SOAP-Receiver-URL " +
728                                 "using the source id from AssertionArtifact.");
729                }
730                if (connecto == null || connecto.length() == 0) {
731                    throw new SAMLException(
732                             SAMLUtils.bundle.getString("failedLocateSOAPUrl"));
733                } else { 
734                    urlEndpoint = connecto; 
735                }
736            }   
737         
738            if (urlEndpoint == null) {
739                SAMLUtils.debug.error("SAMLClient:artifactQueryHandler:" + 
740                                      "createSOAPReceiverURL Error!");
741                if (SystemConfigurationUtil.isServerMode()) {
742                    String[] data = {SAMLUtils.bundle.getString(
743                        "wrongPartnerSOAPUrl")};
744                    LogUtils.error(java.util.logging.Level.INFO,
745                        LogUtils.WRONG_SOAP_URL, data);
746                }
747                throw new SAMLException(SAMLUtils.bundle.getString(
748                                        "wrongPartnerSOAPUrl")); 
749            }
750            //generate SAML Request    
751            Request req = new Request(null, artl);
752            String ver = dest.getVersion(); 
753            if (ver != null) {
754                StringTokenizer st = new StringTokenizer(ver,".");
755                if (st.countTokens() == 2) {
756                    req.setMajorVersion(
757                        Integer.parseInt(st.nextToken().trim()));
758                    req.setMinorVersion(
759                        Integer.parseInt(st.nextToken().trim()));
760                }
761            }
762
763            if (((Boolean) SAMLServiceManager.getAttribute(
764                SAMLConstants.SIGN_REQUEST)).booleanValue())
765            {
766                req.signXML();
767            }
768            // SOAPMessage msg = createSOAPMessage(req);  
769            String xmlString = createSOAPMessage(req);
770
771            // Send the message to the provider using the connection.
772            if (SAMLUtils.debug.messageEnabled()) {
773                SAMLUtils.debug.message("SENDING message: \n " + xmlString);  
774            }
775            if (SystemConfigurationUtil.isServerMode()) {
776                String[] data = {SAMLUtils.bundle.getString(
777                    "sendingSAMLRequest"), xmlString};
778                LogUtils.access(java.util.logging.Level.FINE, 
779                    LogUtils.SAML_ARTIFACT_QUERY, data); 
780            }
781            
782            // SOAPMessage  reply = con.call(msg, urlEndpoint); 
783            String[] urls = { urlEndpoint };
784            SOAPClient client = new SOAPClient(urls);
785            InputStream inbuf = client.call(xmlString, null, null);
786            StringBuffer reply = new StringBuffer();
787            String line;
788            BufferedReader reader = new BufferedReader(new InputStreamReader(
789                inbuf, "UTF-8"));
790            while ((line = reader.readLine()) != null) {
791                reply.append(line).append("\n");
792            }
793
794            //reply should contain SAML response 
795            if (reply == null) {
796                if (SystemConfigurationUtil.isServerMode()) {
797                    String[] data = {SAMLUtils.bundle.getString(
798                        "noReplyfromSOAPReceiver")};
799                    LogUtils.error(java.util.logging.Level.INFO, 
800                        LogUtils.NO_REPLY_FROM_SOAP_RECEIVER, data);
801                }
802                throw new SAMLException( 
803                         SAMLUtils.bundle.getString("noReplyfromSOAPReceiver"));
804            }
805            
806            // check the SOAP message for any SOAP related errors
807            // before passing control to SAML processor
808            xmlString = reply.toString();
809            if (SAMLUtils.debug.messageEnabled()) {
810                SAMLUtils.debug.message("REPLIED message: \n " + xmlString);  
811            }
812            if (SystemConfigurationUtil.isServerMode()) {
813                String[] data = {SAMLUtils.bundle.getString(
814                    "repliedSOAPMessage"), xmlString};
815                LogUtils.access(java.util.logging.Level.FINE, 
816                    LogUtils.REPLIED_SOAP_MESSAGE, data);
817            }
818            samlresponse = getSAMLResponse(xmlString); 
819            if (samlresponse == null) {
820                SAMLUtils.debug.error("SAMLClient:artifactQueryHandler:"+
821                            "No SAML Response contained in SOAPMessage.");
822                if (SystemConfigurationUtil.isServerMode()) {
823                    String[] data = {SAMLUtils.bundle.getString(
824                        "noSAMLResponse")};
825                    LogUtils.error(java.util.logging.Level.INFO, 
826                        LogUtils.NULL_SAML_RESPONSE, data);
827                }
828                throw new SAMLException(SAMLUtils.bundle.getString(
829                                        "noSAMLResponse"));
830            }
831        } catch (Exception e) {
832            SAMLUtils.debug.error("SAMLClient:artifactQueryHandler", e); 
833            throw new SAMLException(e.getMessage()); 
834        }
835        if (SAMLUtils.debug.messageEnabled()) {
836            SAMLUtils.debug.message("Start to process SAML Response..."); 
837        }
838        // Process saml Response 
839        if (!samlresponse.isSignatureValid()) {
840            if (SystemConfigurationUtil.isServerMode()) {
841                String[] data = {SAMLUtils.bundle.getString(
842                   "cannotVerifyResponse")};
843                LogUtils.error(java.util.logging.Level.INFO, 
844                   LogUtils.INVALID_RESPONSE_SIGNATURE, data);
845            }
846            throw new SAMLException(
847                      SAMLUtils.bundle.getString("cannotVerifyResponse"));
848        }       
849        try {        
850            String statuscode= samlresponse.getStatus().getStatusCode().
851                               getValue(); 
852            int idex=0; 
853            if ((idex=statuscode.indexOf(":")) == -1) {
854                throw new SAMLException(
855                      SAMLUtils.bundle.getString("wrongformatStatusCode")); 
856            }
857            if (!(statuscode.substring(idex).equals(":Success"))) {
858               SAMLUtils.debug.error("Error:SAML StatusCode is not Success");
859               throw new SAMLException(
860                   SAMLUtils.displayXML(samlresponse.getStatus().toString())); 
861            }      
862        } catch (Exception e) {
863            if (SystemConfigurationUtil.isServerMode()) {
864                String[] data = {SAMLUtils.bundle.getString(
865                    "errorSAMLStatusCode")};
866                LogUtils.error(java.util.logging.Level.INFO, 
867                    LogUtils.ERROR_RESPONSE_STATUS, data);
868            }
869            throw new SAMLException(e.getMessage());
870        } 
871        // retrieve SAML Assertion
872        List asserts = new ArrayList(); 
873        asserts = getAssertionList(samlresponse, al); 
874        return asserts; 
875    }
876}




























































Copyright © 2010-2017, ForgeRock All Rights Reserved.