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