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