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: FSAuthnResponseEnvelope.java,v 1.2 2008/06/25 05:46:43 qcheng Exp $
026 *
027 */
028
029package com.sun.identity.federation.message;
030
031import com.sun.identity.federation.common.FSUtils;
032import com.sun.identity.federation.common.IFSConstants;
033import com.sun.identity.federation.message.common.FSMsgException;
034import com.sun.identity.saml.common.SAMLUtils;
035import com.sun.identity.saml.common.SAMLException;
036import com.sun.identity.shared.encode.Base64;
037import com.sun.identity.shared.xml.XMLUtils;
038import java.io.ByteArrayInputStream;
039import java.io.IOException;
040import java.util.List;
041import org.w3c.dom.Element;
042import org.w3c.dom.Node;
043import org.w3c.dom.NodeList;
044import org.w3c.dom.Document;
045
046/**
047 * This  class defines methods for setting and retrieving attributes and
048 * elements associated with a Liberty Response . 
049 *
050 * @supported.all.api
051 */
052
053public class FSAuthnResponseEnvelope {
054   private List otherElements;
055   private FSAuthnResponse authnResponse;
056   private String assertionConsumerServiceURL = null;
057   private int minorVersion = IFSConstants.FF_11_PROTOCOL_MINOR_VERSION;
058   
059   /**
060    * Default Constructor.
061    */
062    public FSAuthnResponseEnvelope() {    
063    }
064   
065  /**
066   * Constructor create <code>FSAuthnResponseEnvelope</code> object.
067   *
068   * @param authnResponse the <code>FSAuthnResponse</code> object.
069   */
070   public FSAuthnResponseEnvelope(FSAuthnResponse authnResponse) {    
071       this.authnResponse = authnResponse;
072       this.otherElements = null;
073   }
074   
075  /**
076   * Constructor create <code>FSAuthnResponseEnvelope</code> object.
077   *
078   * @param root the Document element .
079   * @throws FSMsgException if there is an error creating the object.
080   * @throws SAMLException if there is an error creating the object.
081   */
082   public FSAuthnResponseEnvelope(Element root) 
083                    throws FSMsgException, SAMLException {    
084        if (root == null) {
085            FSUtils.debug.message("FSAuthnResponseEnvelope.parseXML: "
086                                  + "null input.");
087            throw new FSMsgException("nullInput",null);
088        }
089        String tag = null;
090        if (((tag = root.getLocalName()) == null) ||
091                        (!tag.equals(IFSConstants.AUTHN_RESPONSE_ENVELOPE))) {
092            FSUtils.debug.message("FSAuthnResponseEnvelope.parseXML: "
093                                  + "wrong input.");
094            throw new FSMsgException("wrongInput",null);
095        }
096        String ns = root.getNamespaceURI();
097        if (ns == null) {
098           FSUtils.debug.error("FSAuthnResponseEnvelope(Element):"
099                               + "No namespace");
100           throw new FSMsgException("wrongInput",null);
101        }
102
103        if (ns.equals(IFSConstants.FF_12_XML_NS)) {
104           minorVersion = IFSConstants.FF_12_PROTOCOL_MINOR_VERSION;
105        }
106        NodeList nl = root.getChildNodes();
107        Node child;
108        String childName;
109        int length = nl.getLength();
110        for (int i = 0; i < length; i++) {
111            child = nl.item(i);
112            if ((childName = child.getLocalName()) != null) {
113                if (childName.equals(IFSConstants.AUTHN_RESPONSE)) {
114                    if (authnResponse != null) {
115                        if (FSUtils.debug.messageEnabled()) {
116                            FSUtils.debug.message("FSAuthnResponseEnvelope: "
117                                + "included more than one <AuthnResponse>");
118                        }
119                        throw new FSMsgException("moreElement",null);
120                    }
121                    authnResponse = new FSAuthnResponse((Element) child);
122                } else if (childName.equals(
123                              IFSConstants.ASSERTION_CONSUMER_SERVICE_URL)) {
124                    assertionConsumerServiceURL = 
125                        XMLUtils.getElementValue((Element) child);                    
126                } 
127            } 
128       }
129   }
130
131  /** 
132   * Returns the value of <code>MinorVersion</code> attribute.
133   *
134   * @return the value of <code>MinorVersion</code> attribute.
135   * @see #setMinorVersion(int)
136   */
137   public int getMinorVersion() {
138       return minorVersion;
139   }
140
141  /**
142   * Sets the value of <code>MinorVersion<code> attribute.
143   *
144   * @param minorVersion the <code>MinorVersion</code> attribute.
145   */
146   public void setMinorVersion(int minorVersion) {
147       this.minorVersion = minorVersion;
148   }
149
150   /**
151    * Returns a list of elements.
152    *
153    * @return list of elements.
154    * @see #setOtherElements(List)
155    */
156    public List getOtherElements() {
157        return otherElements;
158    }
159  /**
160   * Sets a list of elements.
161   *
162   * @param otherElement a list of elements.
163   * @see #getOtherElements
164   */
165   public void setOtherElements(List otherElement) {
166       this.otherElements = otherElement;
167   }
168   
169   /**
170    * Returns the <code>FSAuthnResponse</code> object.
171    *
172    * @return the <code>FSAuthnResponse</code> object.
173    * @see #setAuthnResponse(FSAuthnResponse)
174    */
175
176   public FSAuthnResponse getAuthnResponse() {
177       return authnResponse;
178   }
179   
180   /**
181    * Sets the <code>FSAuthnResponse</code> object.
182    *
183    * @param authnResponse the <code>FSAuthnResponse</code> object.
184    * @see #getAuthnResponse
185    */
186
187   public void setAuthnResponse(FSAuthnResponse authnResponse) {
188       this.authnResponse = authnResponse;
189   }
190    
191   /**
192    * Returns the Assertion Consumer Service URL.
193    *
194    * @return the  Assertion Consumer Service URL.
195    * @see #setAssertionConsumerServiceURL(String)
196    */
197    public String getAssertionConsumerServiceURL() {
198       return assertionConsumerServiceURL;
199   }
200   /**
201    * Sets the Assertion Consumer Service URL.
202    *
203    * @param assertionConsumerUrl the Assertion Consumer Service Identifier.
204    * @see #getAssertionConsumerServiceURL
205    */
206    public void setAssertionConsumerServiceURL(String assertionConsumerUrl) {
207        this.assertionConsumerServiceURL = assertionConsumerUrl;
208   }
209    
210   /**
211    * Returns the <code>FSAuthnResponseEnvelope</code> object.
212    *
213    * @param xml the XML string to create this object from
214    * @return <code>FSAuthnResponseEnvelope</code> object.
215    * @throws FSMsgException if there is error creating the object.
216    */
217   public static FSAuthnResponseEnvelope parseXML(String xml)
218                                         throws FSMsgException {
219      try {
220           Document doc = XMLUtils.toDOMDocument(xml, FSUtils.debug);
221           Element root = doc.getDocumentElement();
222           return new FSAuthnResponseEnvelope(root);           
223       } catch(SAMLException ex){
224           if (FSUtils.debug.messageEnabled()) {
225                    FSUtils.debug.message("FSAuthnResponseEnvelope.parseXML: "
226                                + "Error while parsing input xml string");
227                }
228           throw new FSMsgException("parseError",null);
229        }          
230   }
231   /**
232    * Returns XML document String for this object based on the Response Schema.
233    *
234    * @return XML String representing the <code>Response</code>
235    * @throws FSMsgException if there is an error.
236    */
237    public String toXMLString() throws FSMsgException {
238        return toXMLString(true, true);
239    }
240    
241   /**
242    * Creates a String representation of the <code>Response<code> object.
243    * @param includeNS : Determines whether or not the namespace qualifier
244    *        is prepended to the Element when converted
245    * @param declareNS : Determines whether or not the namespace is declared
246    *        within the Element.
247    * @return A string containing the valid XML for this element.
248    * @throws FSMsgException if there is an error.
249    */
250    public String toXMLString(boolean includeNS, boolean declareNS)
251                              throws FSMsgException {
252        return toXMLString(includeNS, declareNS, false);
253    }
254    
255    /**
256     * Creates a String representation of the  <code>Response</code> object.
257     *
258     * @param includeNS  Determines whether or not the namespace qualifier
259     *        is prepended to the Element when converted
260     * @param declareNS  Determines whether or not the namespace is declared
261     *        within the Element.
262     * @param includeHeader Determines whether the output include the xml
263     *        declaration header.
264     * @return a string containing the valid XML for this object.
265     * @throws FSMsgException if there is an error.
266     */
267    public String toXMLString(boolean includeNS,
268                              boolean declareNS,
269                              boolean includeHeader) throws FSMsgException {        
270                        
271        StringBuffer xml = new StringBuffer(300);
272        if (includeHeader) {
273            xml.append(IFSConstants.XML_PREFIX)
274               .append(IFSConstants.DEFAULT_ENCODING)
275               .append(IFSConstants.QUOTE)
276               .append(IFSConstants.SPACE)
277               .append(IFSConstants.QUESTION_MARK)
278               .append(IFSConstants.RIGHT_ANGLE);
279        }
280        String prefix = "";
281        String uri = "";
282        if (includeNS) {
283            prefix = IFSConstants.LIB_PREFIX;
284        }
285        if (declareNS) {
286            if (minorVersion == IFSConstants.FF_12_PROTOCOL_MINOR_VERSION) {
287                uri = IFSConstants.LIB_12_NAMESPACE_STRING;
288            } else {
289               uri = IFSConstants.LIB_NAMESPACE_STRING;
290            }
291        }       
292        xml.append(IFSConstants.LEFT_ANGLE)
293           .append(prefix)
294           .append(IFSConstants.AUTHN_RESPONSE_ENVELOPE)
295           .append(uri)
296           .append(IFSConstants.RIGHT_ANGLE);            
297
298        if (authnResponse != null) {
299            xml.append(authnResponse.toXMLString());
300        }
301
302        if(assertionConsumerServiceURL != null && 
303            assertionConsumerServiceURL.length() != 0) {
304            xml.append(IFSConstants.LEFT_ANGLE)
305               .append(prefix)
306               .append(IFSConstants.ASSERTION_CONSUMER_SERVICE_URL)
307               .append(uri)
308               .append(IFSConstants.RIGHT_ANGLE)
309               .append(assertionConsumerServiceURL)
310               .append(IFSConstants.START_END_ELEMENT)
311               .append(prefix)
312               .append(IFSConstants.ASSERTION_CONSUMER_SERVICE_URL)
313               .append(IFSConstants.RIGHT_ANGLE);            
314        } else {
315            throw new FSMsgException("nullInput",null);
316        }
317        xml.append(IFSConstants.START_END_ELEMENT)
318           .append(prefix)
319           .append(IFSConstants.AUTHN_RESPONSE_ENVELOPE);
320
321        return xml.toString();
322    }
323      
324   /**
325    * Returns a <code>Base64</code> Encoded String.
326    *
327    * @return a <code>Base64</code> Encoded String.
328    * @throws FSMsgException if there is an error encoding     
329    *         the string.
330    */
331    public String toBASE64EncodedString() throws FSMsgException  {        
332        return Base64.encode(this.toXMLString().getBytes());
333    }
334    
335   /**
336    * Returns <code>FSAuthnResponseEnvelope</code> object. The
337    * object is creating by parsing the <code>Base64</code>
338    * encoded <code>XML</code> string.
339    *
340    * @param encodedReq the <code>Based64</code> encoded <code>XML</code> 
341    *        string.
342    * @throws FSMsgException if there is an error
343    *         creating <code>FSAuthnResponseEnvelope</code> object.
344    */
345    public static FSAuthnResponseEnvelope parseBASE64EncodedString(
346                                   String encodedReq) throws FSMsgException {        
347        if (encodedReq != null) {
348            String decodedAuthnReq = new String(Base64.decode(encodedReq));
349            if (FSUtils.debug.messageEnabled()) {
350                FSUtils.debug.message("FSAuthnResponseEnvelope."
351                    + "parseBASE64EncodedString: decoded input string: \n"
352                    + decodedAuthnReq);
353            }
354            return parseXML(decodedAuthnReq);
355        } else {
356            if (FSUtils.debug.messageEnabled()) {
357                FSUtils.debug.message("FSAuthnResponseEnvelope."
358                    + "parseBASE64EncodedString: null String passed"
359                    + "in as argument.");
360            }
361            throw new FSMsgException("nullInput",null);            
362        }
363    }
364}