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: FSRequest.java,v 1.3 2008/06/25 05:46:45 qcheng Exp $
026 *
027 */
028
029/**
030 * Portions Copyrighted 2012 ForgeRock AS
031 */
032package com.sun.identity.federation.message;
033
034import java.util.List;
035import java.util.Collections;
036import java.util.ArrayList;
037import java.util.Iterator;
038import java.util.StringTokenizer;
039import org.w3c.dom.Attr;
040import org.w3c.dom.Element;
041import org.w3c.dom.Node;
042import org.w3c.dom.NodeList;
043import org.w3c.dom.NamedNodeMap;
044import org.w3c.dom.Document;
045import com.sun.identity.saml.common.SAMLConstants;
046import com.sun.identity.saml.common.SAMLException;
047import com.sun.identity.saml.common.SAMLResponderException;
048import com.sun.identity.saml.common.SAMLRequesterException;
049import com.sun.identity.saml.common.SAMLRequestVersionTooHighException;
050import com.sun.identity.saml.common.SAMLRequestVersionTooLowException;
051import com.sun.identity.saml.xmlsig.XMLSignatureManager;
052import com.sun.identity.saml.protocol.AssertionArtifact;
053import com.sun.identity.saml.protocol.AttributeQuery;
054import com.sun.identity.saml.protocol.AuthenticationQuery;
055import com.sun.identity.saml.protocol.AuthorizationDecisionQuery;
056import com.sun.identity.saml.protocol.Request;
057import com.sun.identity.saml.protocol.Query;
058import com.sun.identity.shared.DateUtils;
059import com.sun.identity.saml.assertion.AssertionIDReference;
060import com.sun.identity.federation.common.FSUtils;
061import com.sun.identity.federation.common.IFSConstants;
062import com.sun.identity.shared.xml.XMLUtils;
063
064/**
065 * This class represents a Liberty <code>Request</code>.
066 * It extends from the abstract base class <code>AbstractRequest</code>.
067 *
068 * @supported.all.api
069 */
070public class FSRequest extends Request {
071    /*
072     * data members
073     */
074    protected String id = null;
075    
076    
077    /**
078     * Returns the value of <code>id</code> attribute.
079     *
080     * @return the value of <code>id</code> attribute.
081     * @see #setID(String)
082     */
083    public String getID() {
084        return id;
085    }
086    
087    /**
088     * Sets the value of <code>id</code> attribute.
089     *
090     * @param id the value of <code>id</code> attribute.
091     * @see #getID()
092     */
093    
094    public void setID(String id) {
095        this.id = id;
096    }
097    
098    /*
099     * Default Constructor.
100     */
101    protected FSRequest() {
102    }
103    
104    
105    /**
106     * Constructor creates <code>Request</code> object. This
107     * shall only be used at the client side to construct a
108     * <code>Request</code> object.
109     * NOTE: The content here is just the body for the Request. The
110     * constructor will add <code>MajorVersion</code>,
111     * <code>MinorVersion</code>, etc. to form a complete Request.
112     *
113     * @param requestId the request identifier, if this
114     *        value is null then one will be generated.
115     * @param query <code>AuthenticationQuery</code> to be included in
116     *        the Request.
117     * @throws <code>SAMLException</code> on error.
118     */
119    public FSRequest(String requestId, Query query) throws SAMLException {
120        super(requestId, query);
121    }
122    
123    /**
124     * Constructor creates <code>Request</code> object. This
125     * shall only be used at the client side to construct a
126     * <code>Request</code> object.
127     * NOTE: The content here is just the body for the Request. The
128     * constructor will add <code>MajorVersion</code>,
129     * <code>MinorVersion</code>, etc. to form a complete Request.
130     *
131     * @param requestId the request identifier, if this
132     *        value is null then one will be generated.
133     * @param contents a <code>List</code> of objects that are the contents
134     *        of Request that the client wants to send to the server.
135     *        It could be an :
136     *            <code>AuthenticationQuery</code>,
137     *            <code>AuthorizationDecisionQuery</code>,
138     *            <code>AttributeQuery</code>, 1 or more
139     *            <code>AssertionIDReference</code>, or 1 or more of
140     *            <code>AssertionArtifact</code>.
141     * @throws <code>SAMLException</code> throws errors on exception.
142     */
143    public FSRequest(String requestId, List contents) throws SAMLException {
144        super(requestId, contents);
145    }
146    
147    
148    /**
149     * Returns the <code>MinorVersion</code>.
150     *
151     * @return the <code>MinorVersion</code>.
152     * @see #setMinorVersion(int)
153     */
154    public int getMinorVersion() {
155        return minorVersion;
156    }
157    
158    /**
159     * Sets the <code>MinorVersion</code>.
160     *
161     * @param version the <code>MinorVersion</code>.
162     * @see #getMinorVersion()
163     */
164    public void setMinorVersion(int version) {
165        minorVersion = version;
166    }
167    
168    /**
169     * Parses the <code>XML</code> Document String to construct a
170     * <code>Request</code> object. This method shall only be used at the server
171     * side to reconstruct a Request object based on the XML document
172     * received from client.
173     *
174     * @param xml the <code>XML</code> Document string.
175     * @return the <code>Request</code> object.
176     * @throws <code>SAMLException</code> on error.
177     */
178    public static Request parseXML(String xml) throws SAMLException {
179        // parse the xml string
180        Document doc = XMLUtils.toDOMDocument(xml, FSUtils.debug);
181        Element root = doc.getDocumentElement();
182        return new FSRequest(root);
183    }
184    
185    /**
186     * Constructor creates a <code>FSRequest</code> object from
187     * a <code>XML</code> Document Element.
188     *
189     * @param root the <code>XML</code> Document Element.
190     * @throws <code>SAMLException</code> on error.
191     */
192    public FSRequest(Element root) throws SAMLException {
193        // Make sure this is a Request
194        String tag = null;
195        if (root == null) {
196            FSUtils.debug.message("Request(Element): null input.");
197            throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
198                    "nullInput",null);
199        }
200        if (((tag = root.getLocalName()) == null) ||
201                (!tag.equals("Request"))) {
202            FSUtils.debug.message("Request(Element): wrong input");
203            throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
204                    "wrongInput",null);
205        }
206        
207        id = root.getAttribute("id");
208        List signs = XMLUtils.getElementsByTagNameNS1(root,
209                SAMLConstants.XMLSIG_NAMESPACE_URI,
210                SAMLConstants.XMLSIG_ELEMENT_NAME);
211        int signsSize = signs.size();
212        if (signsSize == 1) {
213            XMLSignatureManager manager = XMLSignatureManager.getInstance();
214            if (id == null) {
215                valid = manager.verifyXMLSignature(root,
216                        IFSConstants.REQUEST_ID, null);
217            } else {
218                valid = manager.verifyXMLSignature(root);
219            }
220            if (!valid) {
221                if (FSUtils.debug.messageEnabled()) {
222                    FSUtils.debug.message("Request(Element): couldn't verify"
223                            + " Request's signature.");
224                }
225            }
226            xmlString = XMLUtils.print(root);
227            signed = true;
228        } else if (signsSize != 0) {
229            if (FSUtils.debug.messageEnabled()) {
230                FSUtils.debug.message("Request(Element): included more than"
231                        + " one Signature element.");
232            }
233            throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
234                    "moreElement",null);
235        }
236        
237        // Attribute RequestID
238        requestID = root.getAttribute("RequestID");
239        if ((requestID == null) || (requestID.length() == 0)) {
240            if (FSUtils.debug.messageEnabled()) {
241                FSUtils.debug.message("Request(Element): Request doesn't "
242                        + "have a RequestID.");
243            }
244            String[] args = { IFSConstants.REQUEST_ID };
245            throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
246                    "missingAttribute",args);
247        }
248        
249        // Attribute MajorVersion
250        parseMajorVersion(root.getAttribute("MajorVersion"));
251        
252        // Attribute MinorVersion
253        parseMinorVersion(root.getAttribute("MinorVersion"));
254        
255        // Attribute IssueInstant
256        String instantString = root.getAttribute("IssueInstant");
257        if ((instantString == null) || (instantString.length() == 0)) {
258            FSUtils.debug.message("Request(Element): missing IssueInstant");
259            String[] args = { IFSConstants.ISSUE_INSTANT };
260            throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
261                    "missingAttribute",args);
262        } else {
263            try {
264                issueInstant = DateUtils.stringToDate(instantString);
265            } catch (Exception e) {
266                FSUtils.debug.message(
267                        "Request(Element): could not parse IssueInstant", e);
268                throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
269                        "wrongInput",null);
270            }
271        }
272        
273        // get the contents of the request
274        NodeList contentnl = root.getChildNodes();
275        Node child;
276        String nodeName;
277        String respondWith;
278        for (int i = 0, length = contentnl.getLength(); i < length; i++) {
279            child = contentnl.item(i);
280            if ((nodeName = child.getLocalName()) != null) {
281                if (nodeName.equals("RespondWith")) {
282                    respondWith = XMLUtils.getElementValue((Element) child);
283                    if (respondWith.length() == 0) {
284                        if (FSUtils.debug.messageEnabled()) {
285                            FSUtils.debug.message("Request(Element): wrong "
286                                    + "RespondWith value.");
287                        }
288                        throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
289                                "wrongInput",null);
290                    }
291                    if (respondWiths == Collections.EMPTY_LIST) {
292                        respondWiths = new ArrayList();
293                    }
294                    respondWiths.add(respondWith);
295                } else if (nodeName.equals("Signature")) {
296                    signature = (Element) child;
297                } else if (nodeName.equals("AuthenticationQuery")) {
298                    // make sure the content is not assigned already
299                    if (contentType != NOT_SUPPORTED) {
300                        if (FSUtils.debug.messageEnabled()) {
301                            FSUtils.debug.message("Request(Element): should"
302                                    + "contain only one AuthenticationQuery.");
303                        }
304                        throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
305                                "wrongInput",null);
306                    }
307                    contentType = AUTHENTICATION_QUERY;
308                    query = new AuthenticationQuery((Element) child);
309                } else if (nodeName.equals("AuthorizationDecisionQuery")) {
310                    // make sure content is not assigned already
311                    if (contentType != NOT_SUPPORTED) {
312                        if (FSUtils.debug.messageEnabled()) {
313                            FSUtils.debug.message("Request(Element): should"
314                                    + "contain only one "
315                                    + "AuthorizationDecisionQuery.");
316                        }
317                        throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
318                                "wrongInput",null);
319                    }
320                    contentType = AUTHORIZATION_DECISION_QUERY;
321                    query = new AuthorizationDecisionQuery((Element) child);
322                } else if (nodeName.equals("AttributeQuery")) {
323                    // make sure content is not assigned already
324                    if (contentType != NOT_SUPPORTED) {
325                        if (FSUtils.debug.messageEnabled()) {
326                            FSUtils.debug.message("Request(Element): should"
327                                    + "contain only one AttributeQuery.");
328                        }
329                        throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
330                                "wrongInput",null);
331                    }
332                    contentType = ATTRIBUTE_QUERY;
333                    query = new AttributeQuery((Element) child);
334                } else if (nodeName.equals("AssertionIDReference")) {
335                    // make sure the content has no other elements assigned
336                    if ((contentType != NOT_SUPPORTED) &&
337                            (contentType != ASSERTION_ID_REFERENCE)) {
338                        if (FSUtils.debug.messageEnabled()) {
339                            FSUtils.debug.message("Request(Element): "
340                                    + "contained mixed contents.");
341                        }
342                        throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
343                                "wrongInput",null);
344                    }
345                    contentType = ASSERTION_ID_REFERENCE;
346                    if (assertionIDRefs == Collections.EMPTY_LIST) {
347                        assertionIDRefs = new ArrayList();
348                    }
349                    assertionIDRefs.add(new AssertionIDReference(
350                            XMLUtils.getElementValue((Element) child)));
351                } else if (nodeName.equals("AssertionArtifact")) {
352                    // make sure the content has no other elements assigned
353                    if ((contentType != NOT_SUPPORTED) &&
354                            (contentType != ASSERTION_ARTIFACT)) {
355                        if (FSUtils.debug.messageEnabled()) {
356                            FSUtils.debug.message("Request(Element): "
357                                    + "contained mixed contents.");
358                        }
359                        throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
360                                "wrongInput",null);
361                    }
362                    contentType = ASSERTION_ARTIFACT;
363                    if (artifacts == Collections.EMPTY_LIST) {
364                        artifacts = new ArrayList();
365                    }
366                    artifacts.add(new AssertionArtifact(
367                            XMLUtils.getElementValue((Element) child)));
368                } else if (nodeName.equals("Query") ||
369                        nodeName.equals("SubjectQuery")) {
370                    parseQuery(child);
371                } else {
372                    if (FSUtils.debug.messageEnabled()) {
373                        FSUtils.debug.message("Request(Element): invalid"
374                                + " node" + nodeName);
375                    }
376                    throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
377                            "wrongInput",null);
378                } // check nodeName
379            } // if nodeName != null
380        } // done for the nodelist loop
381        
382        if (contentType == NOT_SUPPORTED) {
383            FSUtils.debug.message("Request: empty content.");
384            throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
385                    "wrongInput",null);
386        }
387    }
388    
389    /* Returns the value of <code>RespondWith</code> attribute.
390     *
391     * @return value of the <code>RespondWith</code> attribute.
392     * @throws <code>SAMLException</code> on error.
393     */
394    private String checkAndGetRespondWith(String respondWith)
395    throws SAMLException {
396        if ((respondWith == null) || (respondWith.length() == 0)) {
397            FSUtils.debug.message("Request: empty RespondWith Value.");
398            throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
399                    "wrongInput",null);
400        }
401        
402        if (respondWith.indexOf(":") == -1) {
403            return (SAMLConstants.ASSERTION_PREFIX + respondWith);
404        } else {
405            StringTokenizer st = new StringTokenizer(respondWith, ":");
406            if (st.countTokens() != 2) {
407                FSUtils.debug.message("Request: wrong RespondWith value.");
408                throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
409                        "wrongInput",null);
410            }
411            st.nextToken();
412            String temp = st.nextToken().trim();
413            if (temp.length() == 0) {
414                FSUtils.debug.message("Request: wrong RespondWith value.");
415                throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
416                        "wrongInput",null);
417            }
418            return (SAMLConstants.ASSERTION_PREFIX + temp);
419        }
420    }
421    
422    
423    /**
424     * Sets the <code>MajorVersion</code> by parsing the version string.
425     *
426     * @param majorVer a String representing the <code>MajorVersion</code> to
427     *        be set.
428     * @throws <code>FSMsgException</code> on error.
429     */
430    
431    private void parseMajorVersion(String majorVer) throws SAMLException {
432        try {
433            majorVersion = Integer.parseInt(majorVer);
434        } catch (NumberFormatException e) {
435            if (FSUtils.debug.messageEnabled()) {
436                FSUtils.debug.message("Request(Element): invalid "
437                        + "MajorVersion", e);
438            }
439            throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
440                    "wrongInput",null);
441        }
442        
443        if (majorVersion != SAMLConstants.PROTOCOL_MAJOR_VERSION) {
444            if (majorVersion > SAMLConstants.PROTOCOL_MAJOR_VERSION) {
445                if (FSUtils.debug.messageEnabled()) {
446                    FSUtils.debug.message("Request(Element):MajorVersion of "
447                            + "the Request is too high.");
448                }
449                throw new SAMLRequestVersionTooHighException(
450                        FSUtils.BUNDLE_NAME,"requestVersionTooHigh",null);
451            } else {
452                if (FSUtils.debug.messageEnabled()) {
453                    FSUtils.debug.message("Request(Element):MajorVersion of "
454                            + "the Request is too low.");
455                }
456                throw new SAMLRequestVersionTooLowException(FSUtils.BUNDLE_NAME,
457                        "requestVersionTooLow",null);
458            }
459        }
460    }
461    
462    /**
463     * Sets the <code>MinorVersion</code> by parsing the version string.
464     *
465     * @param minorVer a String representing the <code>MinorVersion</code> to
466     *        be set.
467     * @throws <code>SAMLException</code> when the version mismatchs.
468     */
469    private void parseMinorVersion(String minorVer) throws SAMLException {
470        try {
471            minorVersion = Integer.parseInt(minorVer);
472        } catch (NumberFormatException e) {
473            if (FSUtils.debug.messageEnabled()) {
474                FSUtils.debug.message("Request(Element): invalid "
475                        + "MinorVersion", e);
476            }
477            throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
478                    "wrongInput",null);
479        }
480        
481        if(minorVersion > IFSConstants.FF_12_SAML_PROTOCOL_MINOR_VERSION) {
482            FSUtils.debug.error("Request(Element): MinorVersion"
483                    + " of the Request is too high.");
484            throw new SAMLRequestVersionTooHighException(FSUtils.BUNDLE_NAME,
485                    "requestVersionTooHigh",null);
486        } else if (minorVersion <
487                IFSConstants.FF_11_SAML_PROTOCOL_MINOR_VERSION) {
488            FSUtils.debug.error("Request(Element): MinorVersion"
489                    + " of the Request is too low.");
490            throw new SAMLRequestVersionTooLowException(FSUtils.BUNDLE_NAME,
491                    "requestVersionTooLow",null);
492        }
493    }
494    
495    /**
496     * Parses the Query or <code>SubjectQuery</code> represented by
497     * a DOM tree Node. It then checks and sets data members if it is a
498     * supported query, such as <code>AuthenticationQuery</code>,
499     * <code>AttributeQeury</code>, or <code>AuthorizationDecisionQuery</code>.
500     *
501     * @param child a <code>DOM</code> Node.
502     * @throws <code>SAMLException</code> if the <code>Query</code> is invalid.
503     */
504    private void parseQuery(Node child) throws SAMLException {
505        NamedNodeMap nm = child.getAttributes();
506        int len = nm.getLength();
507        String attrName;
508        String attrValue;
509        Attr attr;
510        boolean found = false;
511        for (int j = 0; j < len; j++) {
512            attr = (Attr) nm.item(j);
513            attrName = attr.getLocalName();
514            if ((attrName != null) && (attrName.equals("type"))) {
515                attrValue = attr.getNodeValue();
516                if (attrValue.equals("AuthenticationQueryType")) {
517                    if (contentType != NOT_SUPPORTED) {
518                        if (FSUtils.debug.messageEnabled()) {
519                            FSUtils.debug.message("Request(Element): should"
520                                    + " contain only one AuthenticationQuery.");
521                        }
522                        throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
523                                "wrongInput",null);
524                    }
525                    contentType = AUTHENTICATION_QUERY;
526                    query = new AuthenticationQuery((Element) child);
527                } else if (attrValue.equals(
528                        "AuthorizationDecisionQueryType")) {
529                    if (contentType != NOT_SUPPORTED) {
530                        if (FSUtils.debug.messageEnabled()) {
531                            FSUtils.debug.message("Request(Element): should "
532                                    + "contain one "
533                                    + "AuthorizationDecisionQuery.");
534                        }
535                        throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
536                                "wrongInput",null);
537                    }
538                    contentType = AUTHORIZATION_DECISION_QUERY;
539                    query = new AuthorizationDecisionQuery((Element) child);
540                } else if (attrValue.equals("AttributeQueryType")) {
541                    if (contentType != NOT_SUPPORTED) {
542                        if (FSUtils.debug.messageEnabled()) {
543                            FSUtils.debug.message("Request(Element): should "
544                                    + "contain one AttributeQuery.");
545                        }
546                        throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
547                                "wrongInput",null);
548                    }
549                    contentType = ATTRIBUTE_QUERY;
550                    query = new AttributeQuery((Element) child);
551                } else {
552                    if (FSUtils.debug.messageEnabled()) {
553                        FSUtils.debug.message("Request(Element): This type of"
554                                + " " + attrName + " is not supported.");
555                    }
556                    throw new SAMLResponderException(FSUtils.BUNDLE_NAME,
557                            "queryNotSupported",null);
558                } // check typevalue
559                found = true;
560                break;
561            } // if found type attribute
562        } // end attribute for loop
563        // if not found type
564        if (!found) {
565            if (FSUtils.debug.messageEnabled()) {
566                FSUtils.debug.message("Request(Element): missing"
567                        + " xsi:type definition in " + child.getLocalName());
568            }
569            throw new SAMLRequesterException(FSUtils.BUNDLE_NAME,
570                    "wrongInput",null);
571        }
572    }
573    
574    /**
575     * Creates a String representation of the <code>&lt;samlp:Request&gt;</code>
576     * element.
577     *
578     * @return a <code>XML</code> String representing the request.
579     */
580    public String toXMLString() {
581        return toXMLString(true, true);
582    }
583    
584    /**
585     * Creates a String representation of the <code>&lt;samlp:Request&gt;</code>
586     * element.
587     *
588     * @param includeNS Determines whether or not the names pace qualifier
589     *        is prepended to the Element when converted
590     * @param declareNS Determines whether or not the name space is declared
591     *        within the Element.
592     * @return a string containing the valid XML for this object.
593     */
594    public String toXMLString(boolean includeNS, boolean declareNS) {
595        return toXMLString(includeNS, declareNS, false);
596    }
597    
598    /**
599     * Creates a String representation of the <code>&lt;samlp:Request&gt;</code>
600     * element.
601     *
602     * @param includeNS Determines whether or not the name space qualifier
603     *        is prepended to the Element when converted
604     * @param declareNS Determines whether or not the name space is declared
605     *        within the Element.
606     * @param includeHeader Determines whether the output include the XML
607     *              declaration header.
608     * @return a string containing the valid XML for this object.
609     */
610    public String toXMLString(boolean includeNS,boolean declareNS,
611            boolean includeHeader) {
612        if (signed && (xmlString != null)) {
613            return xmlString;
614        }
615        
616        StringBuffer xml = new StringBuffer(300);
617        if (includeHeader) {
618            xml.append("<?xml version=\"1.0\" encoding=\"").
619                    append(SAMLConstants.DEFAULT_ENCODING).append("\" ?>\n");
620        }
621        String prefix = "";
622        String libprefix = "";
623        String uri = "";
624        String liburi = "";
625        String uriXSI="";
626        if (includeNS) {
627            prefix = SAMLConstants.PROTOCOL_PREFIX;
628            libprefix = IFSConstants.LIB_PREFIX;
629        }
630        if (declareNS) {
631            uri = SAMLConstants.PROTOCOL_NAMESPACE_STRING;
632            if(minorVersion == IFSConstants.FF_12_SAML_PROTOCOL_MINOR_VERSION){
633                liburi = IFSConstants.LIB_12_NAMESPACE_STRING;
634            } else {
635                liburi = IFSConstants.LIB_NAMESPACE_STRING;
636            }
637            uriXSI = IFSConstants.XSI_NAMESPACE_STRING;
638        }
639        
640        String instantString = DateUtils.toUTCDateFormat(issueInstant);
641        
642        xml.append("<").append(prefix).append("Request").append(uri).
643                //append(" xmlns=\"http://www.w3.org/2000/xmlns/\"").append(uri).
644                append(" ").append(liburi).append(" ").append(uriXSI);
645        if(minorVersion == IFSConstants.FF_11_SAML_PROTOCOL_MINOR_VERSION) {
646            if(id != null && !(id.length() == 0)){
647                xml.append(" id=\"").append(id).append("\"");
648            }
649        }
650        xml.append(" RequestID=\"").append(requestID).append("\"").
651                append(" MajorVersion=\"").append(majorVersion).append("\"").
652                append(" MinorVersion=\"").append(minorVersion).append("\"").
653                append(" IssueInstant=\"").append(instantString).append("\"");
654        
655        if(minorVersion == IFSConstants.FF_11_SAML_PROTOCOL_MINOR_VERSION) {
656            xml.append(" xsi:type").append("=\"").append(libprefix).
657                    append("SignedSAMLRequestType").append("\"");
658        }
659        xml.append(">");
660        if((respondWiths != null) && (respondWiths != Collections.EMPTY_LIST)){
661            Iterator i = respondWiths.iterator();
662            String respondWith = null;
663            while (i.hasNext()) {
664                respondWith = (String) i.next();
665                xml.append("<").append(prefix).append("RespondWith>");
666                if (respondWith.startsWith(SAMLConstants.ASSERTION_PREFIX)) {
667                    xml.append(respondWith);
668                } else {
669                    try {
670                        xml.append(checkAndGetRespondWith(respondWith));
671                    } catch (SAMLException e) {
672                        FSUtils.debug.error("Request.toString: ", e);
673                        xml.append(respondWith);
674                    }
675                }
676                xml.append("</").append(prefix).append("RespondWith>");
677            }
678        }
679        
680        if (signed) {
681            if (signatureString != null) {
682                xml.append(signatureString);
683            } else if (signature != null) {
684                signatureString = XMLUtils.print(signature);
685                xml.append(signatureString);
686            }
687        }
688        Iterator j;
689        switch (contentType) {
690            case AUTHENTICATION_QUERY:
691                xml.append(((AuthenticationQuery)query)
692                   .toString(includeNS, false));
693                break;
694            case AUTHORIZATION_DECISION_QUERY:
695                xml.append(((AuthorizationDecisionQuery)query)
696                   .toString(includeNS,false));
697                break;
698            case ATTRIBUTE_QUERY:
699                xml.append(((AttributeQuery)query).toString(includeNS, false));
700                break;
701            case ASSERTION_ID_REFERENCE:
702                j = assertionIDRefs.iterator();
703                while (j.hasNext()) {
704                    xml.append(((AssertionIDReference) j.next()).
705                            toString(true, true));
706                }
707                break;
708            case ASSERTION_ARTIFACT:
709                j = artifacts.iterator();
710                while (j.hasNext()) {
711                    xml.append(((AssertionArtifact)
712                    j.next()).toString(includeNS, false));
713                }
714                break;
715            default:
716                break;
717        }
718        
719        xml.append("</").append(prefix).append("Request>");
720        return xml.toString();
721    }
722}