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: FSLogoutResponse.java,v 1.4 2008/06/25 05:46:44 qcheng Exp $
026 *
027 */
028
029package com.sun.identity.federation.message;
030
031import com.sun.identity.shared.encode.Base64;
032import com.sun.identity.shared.encode.URLEncDec;
033import com.sun.identity.shared.xml.XMLUtils;
034
035import com.sun.identity.shared.DateUtils;
036
037import com.sun.identity.federation.message.common.FSMsgException;
038import com.sun.identity.federation.common.FSUtils;
039import com.sun.identity.federation.common.IFSConstants;
040
041import com.sun.identity.saml.common.SAMLConstants;
042import com.sun.identity.saml.common.SAMLException;
043import com.sun.identity.saml.common.SAMLResponderException;
044import com.sun.identity.saml.common.SAMLVersionMismatchException;
045
046import com.sun.identity.saml.xmlsig.XMLSignatureManager;
047
048import com.sun.identity.saml.protocol.AbstractResponse;
049import com.sun.identity.saml.protocol.Status;
050import com.sun.identity.saml.protocol.StatusCode;
051
052import java.io.IOException;
053
054import java.text.ParseException;
055
056import java.util.Date;
057import java.util.List;
058
059import javax.servlet.http.HttpServletRequest;
060
061import org.w3c.dom.Element;
062import org.w3c.dom.Node;
063import org.w3c.dom.NodeList;
064import org.w3c.dom.Document;
065
066/**
067 * This class has methods to create a Liberty <code>LogoutResponse</code>.
068 *
069 * @supported.all.api
070 */
071
072public class FSLogoutResponse extends AbstractResponse {
073    private String providerId;
074    private String relayState;
075    private Status status;
076    protected String xmlString;
077    protected String signatureString;
078    protected String id;
079    private String inResponseTo;
080    
081    /**
082     * Default Constructor.
083     */
084    public FSLogoutResponse() {
085        try {
086            setIssueInstant(new Date());
087            providerId = new String();
088            StatusCode statusCode = new StatusCode(IFSConstants.SAML_SUCCESS);
089            status = new Status(statusCode);
090            relayState = new String();
091        } catch (SAMLException e){
092            FSUtils.debug.error("FSLogoutResponse.constructor:", e);
093        }
094        
095    }
096    
097    /**
098     * Constructor creates <code>FSLogoutResponse</code> object.
099     *
100     * @param responseID the value of <code>ResponseID</code> attribute.
101     * @param inResponseTo the value of <code>inResponseTo</code> attribute.
102     * @param status the  Logout <code>Status</code>  object.
103     * @param providerId the value of <code>ProviderID</code> attribute.
104     * @param relayState the value of <code>RelayState</code> attribute.
105     * @throws <code>FSMsgException</code> if this object cannot be created.
106     */
107    public FSLogoutResponse(String responseID,
108            String inResponseTo,
109            Status status,
110            String providerId,
111            String relayState)
112            throws FSMsgException {
113        if ((responseID == null) || (responseID.length() == 0)) {
114            this.responseID = FSUtils.generateID();
115            if (this.responseID == null) {
116                throw new FSMsgException("errorGenerateID",null);
117            }
118        } else {
119            this.responseID = responseID;
120        }
121        
122        if (inResponseTo == null) {
123            FSUtils.debug.message("Response: inResponseTo is null.");
124            throw new FSMsgException("nullInput",null);
125        }
126        this.inResponseTo = inResponseTo;
127        if (status == null) {
128            FSUtils.debug.message("Response: missing <Status>.");
129            throw new FSMsgException("missingElement",null);
130        }
131        this.status = status;
132        this.providerId = providerId;
133        this.relayState = relayState;
134        setIssueInstant(new Date());
135    }
136    
137    /**
138     * Constructor creates <code>FSLogoutResponse</code> object from
139     * a Document element.
140     *
141     * @param root the Document element object.
142     * @throws FSMsgException if this object cannot be created.
143     * @throws SAMLException if there is an error.
144     */
145    public FSLogoutResponse(Element root) throws FSMsgException, SAMLException {
146        if (root == null) {
147            FSUtils.debug.message("FSLogoutResponse.parseXML: null input.");
148            throw new FSMsgException("nullInput",null);
149        }
150        String tag = null;
151        if (((tag = root.getLocalName()) == null) ||
152                (!tag.equals(IFSConstants.LOGOUT_RESPONSE))) {
153            FSUtils.debug.message("FSLogoutResponse.parseXML: wrong input.");
154            throw new FSMsgException("wrongInput",null);
155        }
156        
157        id = root.getAttribute(IFSConstants.ID);
158        
159        // Attribute ResponseID
160        responseID = root.getAttribute(IFSConstants.RESPONSE_ID);
161        if ((responseID == null) || (responseID.length() == 0)) {
162            if (FSUtils.debug.messageEnabled()) {
163                FSUtils.debug.message("FSLogoutResponse.parseXML: "
164                        + "Reponse doesn't have ResponseID.");
165            }
166            String[] args = { IFSConstants.RESPONSE_ID };
167            throw new FSMsgException("missingAttribute",args);
168        }
169        parseMajorVersion(root.getAttribute(IFSConstants.MAJOR_VERSION));
170        parseMinorVersion(root.getAttribute(IFSConstants.MINOR_VERSION));
171        // Attribute InResponseTo
172        inResponseTo = root.getAttribute(IFSConstants.IN_RESPONSE_TO);
173        if (inResponseTo == null) {
174            if (FSUtils.debug.messageEnabled()) {
175                FSUtils.debug.message("FSLogoutResponse.parseXML: "
176                        + "Response doesn't have InResponseTo.");
177            }
178            String[] args = { IFSConstants.IN_RESPONSE_TO };
179            throw new FSMsgException("missingAttribute",args);
180        }
181        // Attribute IssueInstant
182        String instantString = root.getAttribute(IFSConstants.ISSUE_INSTANT);
183        if ((instantString == null) || (instantString.length() == 0)) {
184            FSUtils.debug.message("FSLogoutResponse(Element): "
185                    + "missing IssueInstant");
186            String[] args = { IFSConstants.ISSUE_INSTANT };
187            throw new FSMsgException("missingAttribute",args);
188        } else {
189            try {
190                issueInstant = DateUtils.stringToDate(instantString);
191            } catch (ParseException e) {
192                if (FSUtils.debug.messageEnabled()) {
193                    FSUtils.debug.message(
194                            "FSLogoutResponse(Element): could not " +
195                            "parse IssueInstant", e);
196                }
197                throw new FSMsgException("wrongInput", null);
198            }
199        }
200        
201        NodeList nl = root.getChildNodes();
202        Node child;
203        String childName;
204        int length = nl.getLength();
205        for (int i = 0; i < length; i++) {
206            child = nl.item(i);
207            if ((childName = child.getLocalName()) != null) {
208                if (childName.equals(IFSConstants.STATUS)) {
209                    if (status != null) {
210                        if (FSUtils.debug.messageEnabled()) {
211                            FSUtils.debug.message(
212                                    "FSLogoutResponse: included more"
213                                    + " than one <Status>");
214                        }
215                        throw new FSMsgException("moreElement",null);
216                    }
217                    status = new Status((Element) child);
218                } else if (childName.equals(IFSConstants.SIGNATURE)) {
219                } else if (childName.equals(IFSConstants.PROVIDER_ID)) {
220                    if (providerId != null) {
221                        if (FSUtils.debug.messageEnabled()) {
222                            FSUtils.debug.message(
223                                    "FSLogoutResponse: included more"
224                                    + " than one providerId");
225                        }
226                        throw new FSMsgException("moreElement",null);
227                    }
228                    providerId = XMLUtils.getElementValue((Element) child);
229                } else if (childName.equals(IFSConstants.RELAY_STATE)) {
230                    relayState = XMLUtils.getElementValue((Element) child);
231                } else {
232                    if (FSUtils.debug.messageEnabled()) {
233                        FSUtils.debug.message("FSLogoutResponse: included wrong"
234                                + " element:" + childName);
235                    }
236                    throw new FSMsgException("wrongInput",null);
237                }
238            } // end if childName != null
239        } // end for loop
240        
241        if (status == null) {
242            FSUtils.debug.message("FSLogoutResponse:missing element <Status>.");
243            throw new FSMsgException("oneElement",null);
244        }
245        
246        if (providerId == null) {
247            FSUtils.debug.message(
248                    "FSLogoutResponse: missing element providerId.");
249            throw new FSMsgException("oneElement",null);
250        }
251        
252        //check for signature
253        List signs = XMLUtils.getElementsByTagNameNS1(root,
254                SAMLConstants.XMLSIG_NAMESPACE_URI,
255                SAMLConstants.XMLSIG_ELEMENT_NAME);
256        int signsSize = signs.size();
257        if (signsSize == 1) {
258            Element elem = (Element)signs.get(0);
259            setSignature(elem);
260            xmlString = XMLUtils.print(root);
261            signed = true;
262        } else if (signsSize != 0) {
263            if (FSUtils.debug.messageEnabled()) {
264                FSUtils.debug.message(
265                        "FSLogoutResponse(Element): included more than"
266                        + " one Signature element.");
267            }
268            throw new FSMsgException("moreElement",null);
269        }
270    }
271    
272    /**
273     * Returns the value of <code>RelayState</code> attribute.
274     *
275     * @return the value of <code>RelayState</code> attribute.
276     * @see #setRelayState(String)
277     */
278    public String getRelayState(){
279        return relayState;
280    }
281    
282    /**
283     * Set the value of <code>RelayState</code> attribute.
284     *
285     * @param relayState the value of <code>RelayState</code> attribute.
286     * @see #getRelayState()
287     */
288    public void setRelayState(String relayState){
289        this.relayState = relayState;
290    }
291    
292    /**
293     * Returns the value of <code>InResponseTo</code> attribute.
294     *
295     * @return the value of <code>InResponseTo</code> attribute.
296     * @see #setResponseTo(String)
297     */
298    public String getResponseTo(){
299        return inResponseTo;
300    }
301    
302    /**
303     * Sets the value of <code>InResponseTo</code> attribute.
304     *
305     * @param inResponseTo the value of <code>InResponseTo</code> attribute.
306     * @see #getResponseTo
307     */
308    public void setResponseTo(String inResponseTo){
309        this.inResponseTo = inResponseTo;
310    }
311    
312    /**
313     * Returns the value of <code>id</code> attribute.
314     *
315     * @return the value of <code>id</code> attribute.
316     * @see #setID(String)
317     */
318    public String getID(){
319        return id;
320    }
321    
322    /**
323     * Sets the value of <code>id</code> attribute.
324     *
325     * @param id the value of <code>id</code> attribute.
326     * @see #getID()
327     */
328    public void setID(String id){
329        this.id = id;
330    }
331    
332    /**
333     * Returns the value of <code>ProviderID</code> attribute.
334     *
335     * @return the value of <code>ProviderID</code> attribute.
336     * @see #setProviderId(String).
337     */
338    
339    public String getProviderId(){
340        return providerId;
341    }
342    
343    /**
344     * Sets the value of  <code>ProviderID</code> attribute.
345     *
346     * @param providerId the value of  <code>ProviderID</code> attribute.
347     * @see #getProviderId()
348     */
349    public void setProviderId(String providerId){
350        this.providerId = providerId;
351    }
352    
353    /**
354     * Returns the Signed <code>LogoutResponse</code> string.
355     *
356     * @return signatureString the Signed <code>LogoutResponse</code> string.
357     */
358    public String getSignatureString(){
359        return signatureString;
360    }
361    
362    /**
363     * Returns the value of <code>MinorVersion</code> attribute.
364     *
365     * @return the value of <code>MinorVersion</code> attribute.
366     * @see #setMinorVersion(int)
367     */
368    public int getMinorVersion() {
369        return minorVersion;
370    }
371    
372    /**
373     * Sets the value of <code>MinorVersion</code> attribute.
374     *
375     * @param version thevalue of <code>MinorVersion</code> attribute.
376     * @see #getMinorVersion()
377     */
378    public void setMinorVersion(int version) {
379        minorVersion = version;
380    }
381    
382    /**
383     * Returns the Logout <code>Status</code>.
384     *
385     * @return the Logout <code>Status</code>.
386     * @see #setStatus(String)
387     * @see #setStatus(Status)
388     */
389    public Status getStatus() {
390        return status;
391    }
392    
393    /**
394     * Sets the Logout <code>Status</code>.
395     *
396     * @param status the Logout <code>Status</code code.
397     * @see #getStatus
398     */
399    public void setStatus(String status) {
400        try {
401            StatusCode statusCode = new StatusCode(status);
402            this.status = new Status(statusCode);
403        } catch (Exception e) {
404            if (FSUtils.debug.messageEnabled()) {
405                FSUtils.debug.message("FSLogoutResponse(Element): could not "
406                        + "set attribute:", e);
407            }
408        }
409    }
410    
411    /**
412     * Sets the Logout <code>Status</code>.
413     *
414     * @param status the Logout <code>Status</code object.
415     * @see #getStatus
416     */
417    public void setStatus(Status status) {
418        this.status=status;
419    }
420    
421    /**
422     * Sets the <code>MajorVersion</code> by parsing the version string.
423     *
424     * @param majorVer a String representing the <code>MajorVersion</code> to
425     *        be set.
426     * @throws FSMsgException on error.
427     * @throws SAMLException when the version mismatchs.
428     */
429    private void parseMajorVersion(String majorVer)
430    throws FSMsgException, SAMLException {
431        try {
432            majorVersion = Integer.parseInt(majorVer);
433        } catch (NumberFormatException e) {
434            if (FSUtils.debug.messageEnabled()) {
435                FSUtils.debug.message("Response(Element): invalid "
436                        + "MajorVersion", e);
437            }
438            throw new FSMsgException("wrongInput",null);
439        }
440        
441        if (majorVersion != SAMLConstants.PROTOCOL_MAJOR_VERSION) {
442            if (majorVersion > SAMLConstants.PROTOCOL_MAJOR_VERSION) {
443                if (FSUtils.debug.messageEnabled()) {
444                    FSUtils.debug.message("Response(Element):MajorVersion of"
445                            + " the Response is too high.");
446                }
447                throw new SAMLVersionMismatchException(FSUtils.BUNDLE_NAME,
448                        "responseVersionTooHigh",null);
449            } else {
450                if (FSUtils.debug.messageEnabled()) {
451                    FSUtils.debug.message("Response(Element):MajorVersion of"
452                            + " the Response is too low.");
453                }
454                throw new SAMLVersionMismatchException(FSUtils.BUNDLE_NAME,
455                        "responseVersionTooLow",null);
456            }
457        }
458    }
459    
460    /**
461     * Sets the <code>MinorVersion</code> by parsing the version string.
462     *
463     * @param minorVer a String representing the <code>MinorVersion</code> to
464     *        be set.
465     * @throws SAMLException when the version mismatchs.
466     */
467    private void parseMinorVersion(String minorVer)
468    throws FSMsgException, SAMLException {
469        try {
470            minorVersion = Integer.parseInt(minorVer);
471        } catch (NumberFormatException e) {
472            if (FSUtils.debug.messageEnabled()) {
473                FSUtils.debug.message("Response(Element): invalid "
474                        + "MinorVersion", e);
475            }
476            throw new FSMsgException("wrongInput",null);
477        }
478        if (minorVersion > IFSConstants.FF_12_PROTOCOL_MINOR_VERSION) {
479            FSUtils.debug.error("Response(Element):MinorVersion of"
480                               + " the Response is too high.");
481            throw new SAMLVersionMismatchException(FSUtils.BUNDLE_NAME,
482                        "responseVersionTooHigh",null);
483        } else if (minorVersion < IFSConstants.FF_11_PROTOCOL_MINOR_VERSION) {
484            FSUtils.debug.error("Response(Element):MinorVersion of"
485                                  + " the Response is too low.");
486            throw new SAMLVersionMismatchException(FSUtils.BUNDLE_NAME,
487                        "responseVersionTooLow",null);
488        }
489    }
490    
491    /**
492     * Returns the <code>FSLogoutResponse</code> object.
493     *
494     * @param xml the XML string to be parsed.
495     * @return <code>FSLogoutResponse</code> object created from the XML string.
496     * @throws FSMsgException if there is
497     *         error creating the object.
498     */
499    
500    public static FSLogoutResponse parseXML(String xml) throws FSMsgException {
501        FSLogoutResponse logoutResponse = null;
502        try{
503            Document doc = XMLUtils.toDOMDocument(xml, FSUtils.debug);
504            Element root = doc.getDocumentElement();
505            logoutResponse = new FSLogoutResponse(root);
506        }catch(SAMLException ex){
507            if (FSUtils.debug.messageEnabled()) {
508                FSUtils.debug.message("FSLogoutResponse.parseXML: "
509                        + "Error while parsing input xml string");
510            }
511            throw new FSMsgException("parseError",null, ex);
512        }
513        return logoutResponse;
514    }
515    
516    /**
517     * Returns a String representation of the <code>LogoutResponse</code>
518     * object. This method translates the response to an XML string.
519     *
520     * @return An XML String representing the logout response.
521     */
522    public String toXMLString()  throws FSMsgException {
523        return this.toXMLString(true, true);
524    }
525    
526    /**
527     * Returns a String representation of the <code>LogoutResponse</code>
528     * object.
529     *
530     * @return An XML String representing the logout response.
531     * @throws FSMsgException if there is an error converting
532     *         this object ot a string.
533     */
534    public String toXMLString(boolean includeNS, boolean declareNS)
535    throws FSMsgException {
536        return toXMLString(includeNS, declareNS, false);
537    }
538    
539    /**
540     * Returns a String representation of the <code>LogoutResponse</code>
541     * object.
542     *
543     * @param includeNS Determines whether or not the namespace qualifier
544     *        is prepended to the Element when converted
545     * @param declareNS Determines whether or not the namespace is declared
546     *        within the Element.
547     * @param includeHeader Determines whether the output include the xml
548     *        declaration header.
549     * @return a string containing the valid XML for this element
550     * @throws FSMsgException if there is an error converting
551     *         this object ot a string.
552     */
553    
554    public String toXMLString(boolean includeNS, boolean declareNS,
555            boolean includeHeader)  throws FSMsgException {
556        StringBuffer xml = new StringBuffer(300);
557        if (includeHeader) {
558            xml.append(IFSConstants.XML_PREFIX)
559            .append(SAMLConstants.DEFAULT_ENCODING)
560            .append(IFSConstants.QUOTE)
561            .append(IFSConstants.SPACE)
562            .append(IFSConstants.QUESTION_MARK)
563            .append(IFSConstants.RIGHT_ANGLE)
564            .append(IFSConstants.NL);
565        }
566        String prefixLIB = "";
567        String uriLIB = "";
568        if (includeNS) {
569            prefixLIB = IFSConstants.LIB_PREFIX;
570        }
571        
572        if (declareNS) {
573            if(minorVersion == IFSConstants.FF_12_PROTOCOL_MINOR_VERSION) {
574                uriLIB = IFSConstants.LIB_12_NAMESPACE_STRING;
575            } else {
576                uriLIB = IFSConstants.LIB_NAMESPACE_STRING;
577            }
578        }
579        
580        String instantString = DateUtils.toUTCDateFormat(issueInstant);
581        
582        if((providerId == null) || (providerId.length() == 0)){
583            FSUtils.debug.error("FSLogoutResponse.toXMLString: "
584                    + "providerId is null in the response with responseId:"
585                    + responseID);
586            String[] args = { responseID };
587            throw new FSMsgException("nullProviderIdWResponseId",args);
588        }
589        
590        xml.append(IFSConstants.LEFT_ANGLE)
591        .append(prefixLIB)
592        .append(IFSConstants.LOGOUT_RESPONSE)
593        .append(uriLIB)
594        .append(IFSConstants.SPACE);
595        
596        if (minorVersion == IFSConstants.FF_11_PROTOCOL_MINOR_VERSION &&
597                id != null && !(id.length() == 0)) {
598            xml.append(IFSConstants.ID)
599            .append(IFSConstants.EQUAL_TO)
600            .append(IFSConstants.QUOTE)
601            .append(id)
602            .append(IFSConstants.QUOTE)
603            .append(IFSConstants.SPACE)
604            .append(IFSConstants.SPACE);
605        }
606        
607        if (responseID != null) {
608            xml.append(IFSConstants.RESPONSE_ID)
609            .append(IFSConstants.EQUAL_TO)
610            .append(IFSConstants.QUOTE)
611            .append(responseID)
612            .append(IFSConstants.QUOTE)
613            .append(IFSConstants.SPACE)
614            .append(IFSConstants.SPACE);
615        }
616        
617        if (inResponseTo != null) {
618            xml.append(IFSConstants.IN_RESPONSE_TO)
619            .append(IFSConstants.EQUAL_TO)
620            .append(IFSConstants.QUOTE)
621            .append(inResponseTo)
622            .append(IFSConstants.QUOTE)
623            .append(IFSConstants.SPACE)
624            .append(IFSConstants.SPACE);
625        }
626        
627        xml.append(IFSConstants.MAJOR_VERSION)
628        .append(IFSConstants.EQUAL_TO)
629        .append(IFSConstants.QUOTE)
630        .append(majorVersion)
631        .append(IFSConstants.QUOTE)
632        .append(IFSConstants.SPACE)
633        .append(IFSConstants.SPACE)
634        .append(IFSConstants.MINOR_VERSION)
635        .append(IFSConstants.EQUAL_TO)
636        .append(IFSConstants.QUOTE)
637        .append(minorVersion)
638        .append(IFSConstants.QUOTE)
639        .append(IFSConstants.SPACE)
640        .append(IFSConstants.SPACE)
641        .append(IFSConstants.ISSUE_INSTANT)
642        .append(IFSConstants.EQUAL_TO)
643        .append(IFSConstants.QUOTE)
644        .append(instantString)
645        .append(IFSConstants.QUOTE)
646        .append(IFSConstants.RIGHT_ANGLE);
647
648        if (signed) {
649            if (signatureString != null) {
650                xml.append(signatureString);
651            } else if (signature != null) {
652                signatureString = XMLUtils.print(signature);
653                xml.append(signatureString);
654            }
655        }
656        
657        if (providerId != null) {
658            xml.append(IFSConstants.LEFT_ANGLE)
659            .append(prefixLIB)
660            .append(IFSConstants.PROVIDER_ID)
661            .append(IFSConstants.RIGHT_ANGLE)
662            .append(providerId)
663            .append(IFSConstants.START_END_ELEMENT)
664            .append(prefixLIB)
665            .append(IFSConstants.PROVIDER_ID)
666            .append(IFSConstants.RIGHT_ANGLE);
667        }
668        
669        if (status != null) {
670            xml.append(status.toString(includeNS, true));
671        }
672        
673        if (relayState != null) {
674            xml.append(IFSConstants.LEFT_ANGLE)
675            .append(prefixLIB)
676            .append(IFSConstants.RELAY_STATE)
677            .append(IFSConstants.RIGHT_ANGLE)
678            .append(relayState)
679            .append(IFSConstants.START_END_ELEMENT)
680            .append(prefixLIB)
681            .append(IFSConstants.RELAY_STATE)
682            .append(IFSConstants.RIGHT_ANGLE);
683        }
684        
685        xml.append(IFSConstants.START_END_ELEMENT)
686        .append(prefixLIB)
687        .append(IFSConstants.LOGOUT_RESPONSE)
688        .append(IFSConstants.RIGHT_ANGLE);
689        
690        return xml.toString();
691    }
692    
693    /**
694     * Returns <code>FSLogoutResponse</code> object. The object
695     * is created by parsing an Base64 encode authentication
696     * request String.
697     *
698     * @param encodedRes the encode string
699     * @throws FSMsgException if there is an error creating this object.
700     * @throws SAMLException if there is an error creating this object.
701     */
702    public static FSLogoutResponse parseBASE64EncodedString(String encodedRes)
703    throws FSMsgException, SAMLException {
704        if (encodedRes != null){
705            String decodedNameRegRes = new String(Base64.decode(encodedRes));
706            if (FSUtils.debug.messageEnabled()) {
707                FSUtils.debug.message("FSLogoutResponse."
708                        + "parseBASE64EncodedString: decoded input string: "
709                        + decodedNameRegRes);
710            }
711            return parseXML(decodedNameRegRes);
712        } else {
713            if (FSUtils.debug.messageEnabled()) {
714                FSUtils.debug.message(
715                        "FSLogoutResponse.parseBASE64EncodedString"
716                        + ": null String passed in as argument.");
717            }
718            throw new FSMsgException("nullInput",null);
719        }
720    }
721    
722    /**
723     * Returns a Base64 Encoded String.
724     *
725     * @return a Base64 Encoded String.
726     * @throws FSMsgException if there is an error encoding the string.
727     */
728    public String toBASE64EncodedString() throws FSMsgException {
729        if ((responseID == null) || (responseID.length() == 0)){
730            responseID = FSUtils.generateID();
731            if (responseID == null) {
732                FSUtils.debug.error("FSLogoutResponse.toBASE64EncodedString: "
733                        + "couldn't generate ResponseID.");
734                throw new FSMsgException("errorGenerateID",null);
735            }
736        }
737        return Base64.encode(this.toXMLString().getBytes());
738    }
739    
740    /**
741     * Unsupported operation.
742     */
743    public void signXML() {
744    }
745    
746    /**
747     * Signs the <code>LogoutResponse</code>.
748     *
749     * @param certAlias the Certificate Alias.
750     * @throws XMLSignatureException if this object cannot be signed.
751     */
752    public void signXML(String certAlias) throws SAMLException {
753        FSUtils.debug.message("FSLogoutResponse.signXML: Called");
754        if (signed) {
755            if (FSUtils.debug.messageEnabled()) {
756                FSUtils.debug.message("FSLogoutResponse.signXML: "
757                        + "the assertion is already signed.");
758            }
759            throw new SAMLResponderException(FSUtils.BUNDLE_NAME,
760                    "alreadySigned",null);
761        }
762        if (certAlias == null || certAlias.length() == 0) {
763            throw new SAMLResponderException(FSUtils.BUNDLE_NAME,
764                    "cannotFindCertAlias",null);
765        }
766        try{
767            XMLSignatureManager manager = XMLSignatureManager.getInstance();
768            if (minorVersion == IFSConstants.FF_11_PROTOCOL_MINOR_VERSION) {
769                signatureString = manager.signXML(this.toXMLString(true, true),
770                        certAlias,
771                        IFSConstants.DEF_SIG_ALGO,
772                        IFSConstants.ID,
773                        this.id, false);
774            } else if (minorVersion ==
775                    IFSConstants.FF_12_PROTOCOL_MINOR_VERSION) {
776                signatureString =
777                        manager.signXML(this.toXMLString(true, true),
778                        certAlias, IFSConstants.DEF_SIG_ALGO,
779                        IFSConstants.RESPONSE_ID,
780                        this.getResponseID(), false);
781            } else {
782                if (FSUtils.debug.messageEnabled()) {
783                    FSUtils.debug.message("invalid minor version.");
784                }
785            }
786            
787            signature = XMLUtils.toDOMDocument(signatureString, FSUtils.debug)
788            .getDocumentElement();
789            
790            signed = true;
791            xmlString = this.toXMLString(true, true);
792        } catch (Exception e) {
793            throw new SAMLResponderException(FSUtils.BUNDLE_NAME,
794                    "signFailed",null);
795        }
796    }
797    
798    /**
799     * Sets the Signature.
800     *
801     * @param elem the Document Element.
802     * @return true if success otherwise false.
803     */
804    public boolean setSignature(Element elem) {
805        signatureString = XMLUtils.print(elem);
806        return super.setSignature(elem);
807    }
808    
809    /**
810     * Returns an URL Encoded String.
811     *
812     * @return a url encoded query string.
813     * @throws FSMsgException if there is an error.
814     */
815    public String toURLEncodedQueryString() throws FSMsgException {
816        if ((providerId == null) || (providerId.length() == 0)) {
817            FSUtils.debug.error("FSLogoutResponse."
818                    + "toURLEncodedQueryString: providerId is null in "
819                    + "the response ");
820            throw new FSMsgException("nullProviderIdInRequest",null);
821        }
822        if ((responseID == null) || (responseID.length() == 0)){
823            responseID = FSUtils.generateID();
824            if (responseID == null) {
825                FSUtils.debug.error("FSNameRegistrationRequest."
826                        + "toURLEncodedQueryString: couldn't generate "
827                        + "responseID.");
828                throw new FSMsgException("errorGenerateID",null);
829            }
830        }
831        
832        StringBuffer urlEncodedAuthnReq = new StringBuffer(300);
833        urlEncodedAuthnReq.append(IFSConstants.RESPONSE_ID)
834        .append(IFSConstants.EQUAL_TO)
835        .append(URLEncDec.encode(responseID))
836        .append(IFSConstants.AMPERSAND);
837        
838        if((inResponseTo != null) && (inResponseTo.length() > 0)) {
839            urlEncodedAuthnReq.append(IFSConstants.IN_RESPONSE_TO)
840            .append(IFSConstants.EQUAL_TO)
841            .append(URLEncDec.encode(inResponseTo))
842            .append(IFSConstants.AMPERSAND);
843        }
844        urlEncodedAuthnReq.append(IFSConstants.MAJOR_VERSION)
845        .append(IFSConstants.EQUAL_TO)
846        .append(majorVersion)
847        .append(IFSConstants.AMPERSAND)
848        .append(IFSConstants.MINOR_VERSION)
849        .append(IFSConstants.EQUAL_TO)
850        .append(minorVersion)
851        .append(IFSConstants.AMPERSAND);
852        
853        if (issueInstant != null) {
854            urlEncodedAuthnReq.append(IFSConstants.ISSUE_INSTANT)
855            .append(IFSConstants.EQUAL_TO)
856            .append(URLEncDec.encode(
857                    DateUtils.toUTCDateFormat(issueInstant)))
858                    .append(IFSConstants.AMPERSAND);
859        } else {
860            FSUtils.debug.error("FSLogoutResponse."
861                    + "toURLEncodedQueryString: issueInstant missing");
862            String[] args = { IFSConstants.ISSUE_INSTANT };
863            throw new FSMsgException("missingAttribute",args);
864            }
865            if(providerId != null && providerId.length() != 0) {
866                urlEncodedAuthnReq.append(IFSConstants.PROVIDER_ID)
867                .append(IFSConstants.EQUAL_TO)
868                .append(URLEncDec.encode(providerId))
869                .append(IFSConstants.AMPERSAND);
870            }
871            
872            if(relayState != null && relayState.length() != 0) {
873                urlEncodedAuthnReq.append(IFSConstants.RELAY_STATE)
874                .append(IFSConstants.EQUAL_TO)
875                .append(URLEncDec.encode(relayState))
876                .append(IFSConstants.AMPERSAND);
877            }
878            
879            if (status != null) {
880                urlEncodedAuthnReq.append(IFSConstants.VALUE)
881                .append(IFSConstants.EQUAL_TO)
882                .append(URLEncDec.encode(
883                        status.getStatusCode().getValue()))
884                        .append(IFSConstants.AMPERSAND);
885            }
886            return urlEncodedAuthnReq.toString();
887        }
888        
889        
890        /**
891         * Returns <code>FSLogoutResponse</code> object. The
892         * object is creating by parsing the <code>HttpServletRequest</code>
893         * object.
894         *
895         * @param request the <code>HttpServletRequest</code> object.
896         * @throws FSMsgException if there is an error creating this object.
897         * @throws SAMLException if there is an error.
898         */
899        public static FSLogoutResponse parseURLEncodedRequest(
900                HttpServletRequest request)
901                throws FSMsgException, SAMLException {
902            FSLogoutResponse retLogoutResponse = new FSLogoutResponse();
903            try {
904                FSUtils.debug.message("checking minor version");
905                retLogoutResponse.majorVersion =
906                        Integer.parseInt(
907                              request.getParameter(IFSConstants.MAJOR_VERSION));
908                retLogoutResponse.minorVersion =
909                        Integer.parseInt(
910                              request.getParameter(IFSConstants.MINOR_VERSION));
911            } catch(NumberFormatException ex){
912                throw new FSMsgException("invalidNumber",null);
913            }
914            
915            String requestID = request.getParameter(IFSConstants.RESPONSE_ID);
916            if (requestID != null) {
917                retLogoutResponse.responseID = requestID ;
918            } else {
919                String[] args = { IFSConstants.RESPONSE_ID };
920                throw new FSMsgException("missingAttribute",args);
921            }
922            retLogoutResponse.inResponseTo =
923                    request.getParameter(IFSConstants.IN_RESPONSE_TO);
924            
925            String instantString =
926                    request.getParameter(IFSConstants.ISSUE_INSTANT);
927            if (instantString == null || instantString.length() == 0) {
928                String[] args = { IFSConstants.ISSUE_INSTANT };
929                throw new FSMsgException("missingAttribute",args);
930            }
931            try{
932                retLogoutResponse.issueInstant =
933                        DateUtils.stringToDate(instantString);
934            } catch (ParseException e){
935                throw new FSMsgException("parseError",null);
936            }
937            FSUtils.debug.message(" get provider Id");
938            String providerID = request.getParameter(IFSConstants.PROVIDER_ID);
939            if (providerID != null) {
940                retLogoutResponse.providerId = providerID;
941                if (FSUtils.debug.messageEnabled()) {
942                    FSUtils.debug.message("ProviderID : "
943                            + retLogoutResponse.providerId);
944                }
945            } else {
946                if (FSUtils.debug.messageEnabled()) {
947                    FSUtils.debug.message("ProviderID : "
948                            + retLogoutResponse.providerId);
949                }
950                throw new FSMsgException("missingElement",null);
951            }
952            
953            String relayState = request.getParameter(IFSConstants.RELAY_STATE);
954            if (relayState != null) {
955                retLogoutResponse.relayState = relayState;
956                if (FSUtils.debug.messageEnabled()) {
957                    FSUtils.debug.message("RelayState:"
958                            + retLogoutResponse.relayState);
959                }
960            }
961            
962            String value = request.getParameter(IFSConstants.VALUE);
963            if (value != null){
964                if (FSUtils.debug.messageEnabled()) {
965                    FSUtils.debug.message("Status : " + value);
966                }
967                StatusCode statusCode = new StatusCode(value);
968                retLogoutResponse.status = new Status(statusCode);
969            } else {
970                if (FSUtils.debug.messageEnabled()) {
971                    FSUtils.debug.message("Status : " + value);
972                }
973                throw new FSMsgException("missingElement",null);
974            }
975            
976            FSUtils.debug.message("Returning Logout response Object");
977            return retLogoutResponse;
978        }
979    }