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: RequestAuthnContext.java,v 1.2 2008/06/25 05:46:47 qcheng Exp $
026 *
027 */
028
029package com.sun.identity.federation.message.common;
030
031import com.sun.identity.federation.common.FSUtils;
032import com.sun.identity.federation.common.IFSConstants;
033import com.sun.identity.saml.common.SAMLConstants;
034import com.sun.identity.saml.common.SAMLUtils;
035import com.sun.identity.shared.encode.URLEncDec;
036import com.sun.identity.shared.xml.XMLUtils;
037import java.util.ArrayList;
038import java.util.Collections;
039import java.util.Iterator;
040import java.util.List;
041import java.util.StringTokenizer;
042import javax.servlet.http.HttpServletRequest;
043import org.w3c.dom.Element;
044import org.w3c.dom.Node;
045import org.w3c.dom.NodeList;
046
047
048/**
049 * This class <code>RequestAuthnContext</code> represents the requesting
050 * Authentication Context as part of the <code>FSAuthnRequest</code>.
051 *
052 * @supported.all.api
053 */
054
055public class RequestAuthnContext {
056
057    private List authnContextClassRefList = null;
058    private List authnContextStatementRefList = null;
059    private String authnContextComparison = null;
060    private int minorVersion = 0;
061    
062    /**
063     * Constructor to create <code>RequestAuthnContext</code> object.
064     *
065     * @param authnContextClassRefList 
066     *        Ordered list of AuthnContext Classes Refs.
067     * @param authnContextStatementRefList 
068     *        Ordered list of AuthnContext Statement Refs.
069     *        Note: authnContextClassRefList and authContextStatementRefList
070     *        are mutually exclusive lists.
071     * @param authnContextComparison AuthnContext Comparison Type.
072     *        Possible values are  <code>exact</code>, <code>minimum<code>,
073     *        <code>better</code> and <code>maximum</code>.
074     */
075    public RequestAuthnContext (
076        List authnContextClassRefList,
077        List authnContextStatementRefList,
078        String authnContextComparison) {
079
080        this.authnContextStatementRefList = authnContextStatementRefList;
081        this.authnContextClassRefList = authnContextClassRefList;
082        this.authnContextComparison = authnContextComparison;
083    }
084
085    /**
086     * Default constructor.
087     */
088    public RequestAuthnContext(){}
089    
090    /**
091     * Constructor to create <code>RequestAuthnContext</code> object from
092     * Docuemnt Element.
093     *
094     * @param root the Document Element.
095     * @throws FSMsgException on error.
096     */
097    public RequestAuthnContext(Element root) throws FSMsgException {
098        if(root == null) {
099            FSUtils.debug.message("AuthnContext.parseXML: null input.");
100            throw new FSMsgException("nullInput",null);
101        }
102        
103        String tag = root.getLocalName();
104        
105        if(tag == null) {
106            FSUtils.debug.error("AuthnContext.parseXML: wrong input.");
107            throw new FSMsgException("wrongInput",null);
108        }
109        
110        if(!tag.equals("RequestAuthnContext") && !tag.equals("AuthnContext")) {
111            FSUtils.debug.error("AuthnContext.parseXML: wrong input.");
112            throw new FSMsgException("wrongInput",null);
113        }
114        
115        NodeList nl = root.getChildNodes();
116        int length = nl.getLength();
117        
118        for(int i = 0; i < length; i++) {
119            Node child = nl.item(i);
120            String childName = child.getLocalName();
121            
122            if(childName == null) {
123                continue;
124            }
125            
126            if(childName.equals("AuthnContextClassRef")) {
127                if(authnContextStatementRefList != null) {
128                    FSUtils.debug.error("AuthnContext(Element): Should"
129                            + "contain either <AuthnContextStatementRef> or "
130                            + "<AuthnContextClassRef>");
131                    throw new FSMsgException("wrongInput",null);
132                }
133                
134                if(authnContextClassRefList == null ||
135                        authnContextClassRefList == Collections.EMPTY_LIST) {
136                    authnContextClassRefList = new ArrayList();
137                }
138                
139                authnContextClassRefList.add(
140                        XMLUtils.getElementValue((Element) child));
141                
142            } else if (childName.equals("AuthnContextStatementRef")) {
143                
144                if(authnContextClassRefList != null) {
145                    FSUtils.debug.error("AuthnContext(Element): Should"
146                            + "contain either <AuthnContextStatementRef> or "
147                            + "<AuthnContextClassRef>");
148                    throw new FSMsgException("wrongInput",null);
149                }
150                
151                if(authnContextStatementRefList == null ||
152                       authnContextStatementRefList == Collections.EMPTY_LIST) {
153                    authnContextStatementRefList = new ArrayList();
154                }
155                
156                authnContextStatementRefList.add(
157                        XMLUtils.getElementValue((Element) child));
158                
159            } else if(childName.equals("AuthnContextComparison")) {
160                
161                authnContextComparison = XMLUtils.getElementValue(
162                        (Element)child);
163            }
164        }
165    }
166    
167    /**
168     * Returns <code>List</code> of <code>AuthnContext</code> Class References.
169     *
170     * @return <code>List</code> of <code>AuthnContext</code> Class Reference
171     *         classes.
172     * @see #setAuthnContextClassRefList(List)
173     */
174    public List getAuthnContextClassRefList() {
175        return authnContextClassRefList;
176    }
177    
178    /**
179     * Sets a <code>List</code> of  <code>AuthnContext</code> Class References.
180     *
181     * @param authnContextClassRefList a <code>List</code> of  
182     *        <code>AuthnContext</code> Class References.
183     * @see #getAuthnContextClassRefList
184     */
185    public void setAuthnContextClassRefList(
186        List authnContextClassRefList) {
187
188        this.authnContextClassRefList = authnContextClassRefList;
189    }
190    
191    /**
192     * Returns a <code>List</code> of <code>AuthnContext</code> Statement
193     * References.
194     *
195     * @return a <code>List</code> of <code>AuthnContext</code> Statement
196     *         References.
197     * @see #setAuthnContextStatementRefList(List)
198     */
199    public List getAuthnContextStatementRefList() {
200        return this.authnContextStatementRefList;
201    }
202
203    /**
204     * Sets a <code>List</code> of <code>AuthnContext</code> Statement 
205     * References.
206     *
207     * @param authnContextStatementRefList a <code>List</code> of
208     *        <code>AuthnContext</code> Statement References.
209     * @see #getAuthnContextStatementRefList
210     */
211    public void setAuthnContextStatementRefList(
212                    List authnContextStatementRefList ) {
213        this.authnContextStatementRefList = authnContextStatementRefList;
214    }
215
216    /**
217     * Returns the <code>AuthnContext</code> Comparison type.
218     *
219     * @return authnContextComparison the <code>AuthnContext</code> Comparison 
220     *          type.
221     * @see #setAuthnContextComparison(String)
222     */
223    public String getAuthnContextComparison() {
224        return authnContextComparison;
225    }
226
227    /**
228     * Sets the <code>AuthnContext</code> comparison type.
229     *
230     * @param authnContextComparison the <code>AuthnContext</code> comparison 
231     *        type.
232     * @see #getAuthnContextComparison
233     */
234    public void setAuthnContextComparison(String authnContextComparison) {
235        this.authnContextComparison = authnContextComparison;
236    }
237
238    /**
239     * Returns the <code>MinorVersion</code>.
240     *
241     * @return the <code>MinorVersion</code>.
242     * @see #setMinorVersion(int)
243     */
244    public int getMinorVersion() {
245       return minorVersion;
246    }
247
248    /**
249     * Sets the <code>MinorVersion</code>.
250     *
251     * @param minorVersion the <code>MinorVersion</code>.
252     * @see #getMinorVersion()
253     */
254    public void setMinorVersion(int minorVersion) {
255        this.minorVersion = minorVersion;
256    }
257
258    /**
259     * Returns a String representation of the Logout Response.
260     *
261     * @return a string containing the valid XML for this element
262     * @throws FSMsgException if there is an error converting
263     *         this object ot a string.
264     */
265    public String toXMLString() throws FSMsgException {
266        return this.toXMLString(true, false);
267    }
268    
269    /**
270     * Returns a String representation of this object.
271     *
272     * @param includeNS : Determines whether or not the namespace qualifier
273     *        is prepended to the Element when converted
274     * @param declareNS : Determines whether or not the namespace is declared
275     *        within the Element.
276     * @return a string containing the valid XML for this element
277     * @throws FSMsgException if there is an error converting
278     *         this object to a string.
279     */
280    public String toXMLString(boolean includeNS, boolean declareNS) 
281                              throws FSMsgException {
282      return toXMLString(includeNS, declareNS, false);
283    }
284   
285     /**
286     * Returns a String representation of the Logout Response.
287     *
288     * @param includeNS Determines whether or not the namespace qualifier
289     *        is prepended to the Element when converted
290     * @param declareNS Determines whether or not the namespace is declared
291     *        within the Element.
292     * @param includeHeader Determines whether the output include the xml
293     *        declaration header.
294     * @return a string containing the valid XML for this element
295     * @throws FSMsgException if there is an error converting
296     *         this object ot a string.
297     */
298    public String toXMLString(boolean includeNS, boolean declareNS, 
299                              boolean includeHeader) throws FSMsgException {
300
301        StringBuffer xml = new StringBuffer(300);
302
303        if (includeHeader) {
304            xml.append("<?xml version=\"1.0\" encoding=\"").
305                append(SAMLConstants.DEFAULT_ENCODING).append("\" ?>\n");
306        }
307
308        String prefixAC = "";
309        String prefixLIB = "";
310        String uriAC = "";
311        String uriLIB = "";
312
313        if (includeNS) {
314            prefixLIB = IFSConstants.LIB_PREFIX;
315            prefixAC = IFSConstants.AC_PREFIX;
316        }
317
318        if (declareNS) {
319            if(minorVersion == IFSConstants.FF_12_PROTOCOL_MINOR_VERSION) {
320               uriLIB = IFSConstants.LIB_12_NAMESPACE_STRING;
321               uriAC = IFSConstants.AC_12_NAMESPACE_STRING;
322            } else {
323               uriLIB = IFSConstants.LIB_NAMESPACE_STRING;
324               uriAC = IFSConstants.AC_NAMESPACE_STRING;
325            }
326        }
327        
328        xml.append("<").append(prefixLIB);
329
330        if(minorVersion == IFSConstants.FF_12_PROTOCOL_MINOR_VERSION) {
331           xml.append("RequestAuthnContext");
332        } else {
333           xml.append("AuthnContext");
334        }
335
336        xml.append(uriLIB).append(">\n");
337
338        if ((authnContextClassRefList != null) && 
339            (authnContextClassRefList != Collections.EMPTY_LIST)) {
340
341            if((authnContextStatementRefList != null) && 
342               (authnContextClassRefList != Collections.EMPTY_LIST)) {
343               throw new FSMsgException("ExclusiveEntries",null);
344            }
345
346            Iterator j = authnContextClassRefList.iterator();
347            while (j.hasNext()) {
348                xml.append("<").append(prefixLIB).
349                    append("AuthnContextClassRef").append(">"); 
350                xml.append((String)j.next());
351                xml.append("</").append(prefixLIB).
352                    append("AuthnContextClassRef").append(">\n"); 
353            }
354        }
355        
356        
357        if ((authnContextStatementRefList != null) && 
358            (authnContextStatementRefList != Collections.EMPTY_LIST)) {
359
360            Iterator j = authnContextStatementRefList.iterator();
361            while (j.hasNext()) {
362                xml.append("<").append(prefixLIB).
363                    append("AuthnContextStatementRef").append(">"); 
364                xml.append((String)j.next());
365                xml.append("</").append(prefixLIB).
366                    append("AuthnContextStatementRef").append(">\n"); 
367            }
368        }
369
370        if(minorVersion == IFSConstants.FF_12_PROTOCOL_MINOR_VERSION) {
371           xml.append("<").append(prefixLIB)
372              .append("AuthnContextComparison").append(">")
373              .append(authnContextComparison)
374              .append("</").append(prefixLIB)
375              .append("AuthnContextComparison").append(">\n");
376        }
377        
378        xml.append("</").append(prefixLIB);
379        if(minorVersion == IFSConstants.FF_12_PROTOCOL_MINOR_VERSION) {
380           xml.append("RequestAuthnContext").append(">\n");
381        } else {
382           xml.append("AuthnContext").append(">\n");
383        }
384
385        return xml.toString();    
386    }   
387   
388    /**
389     * Returns <code>RequestAuthnContext</code> object. The
390     * object is creating by parsing the <code>HttpServletRequest</code>
391     * object.
392     *
393     * @param request the <code>HttpServletRequest</code> object.
394     * @return <code><RequestAuthnContext/code> object.
395     * @throws FSMsgException if there is an error
396     *         creating <code>RequestAuthnContext</code> object.
397     */
398    public static RequestAuthnContext parseURLEncodedRequest(
399           HttpServletRequest request, int minorVersion)
400           throws FSMsgException {
401        RequestAuthnContext retAuthnContext = new RequestAuthnContext();
402        String strAuthnContextClassRef = 
403               request.getParameter("AuthnContextClassRef");
404
405        if(strAuthnContextClassRef != null){
406           StringTokenizer st = new StringTokenizer(strAuthnContextClassRef);
407
408            while (st.hasMoreTokens()) {
409               if (retAuthnContext.authnContextClassRefList == null) {
410                   retAuthnContext.authnContextClassRefList = new ArrayList();
411               }                    
412               retAuthnContext.authnContextClassRefList.add(st.nextToken());
413            }
414        }
415        
416        String strAuthnContextStatementRef = 
417               request.getParameter("AuthnContextStatementRef");
418
419        if(strAuthnContextStatementRef != null){
420            StringTokenizer st = 
421                new StringTokenizer(strAuthnContextStatementRef);
422
423            while (st.hasMoreTokens()) {
424               if (retAuthnContext.authnContextStatementRefList == null) {
425                   retAuthnContext.authnContextStatementRefList = 
426                                   new ArrayList();
427               }                    
428               retAuthnContext.authnContextStatementRefList.add(st.nextToken());
429            }
430        }
431
432        String strAuthnContextComparison =
433               request.getParameter("AuthnContextComparison");
434
435        if(strAuthnContextComparison != null) {
436           retAuthnContext.setAuthnContextComparison(strAuthnContextComparison);
437        }
438
439        retAuthnContext.setMinorVersion(minorVersion);
440        
441        return retAuthnContext;        
442    }
443    
444      
445    /**
446     * Returns an URL Encoded String.
447     *
448     * @return a url encoded query string.
449     * @throws FSMsgException if there is an error.
450     */
451    public String toURLEncodedQueryString() throws FSMsgException {
452
453       StringBuffer urlEncodedAuthnReq = new StringBuffer(300);
454
455       if ((authnContextClassRefList != null) && 
456           (!authnContextClassRefList.isEmpty())) {
457
458           if((authnContextStatementRefList != null) &&
459              (!authnContextStatementRefList.isEmpty())) { 
460               throw new FSMsgException("ExclusiveEntries",null);
461           }
462
463           StringBuffer strEncodedString = new StringBuffer(100); 
464           Iterator j = authnContextClassRefList.iterator();
465           strEncodedString.append((String)j.next());
466
467           while(j.hasNext()) {
468               strEncodedString.append(" ").append((String)j.next());
469           }
470
471           urlEncodedAuthnReq.append("AuthnContextClassRef=").
472                    append(URLEncDec.encode(strEncodedString.toString())).
473                    append(IFSConstants.AMPERSAND);
474        }
475       
476       if ((authnContextStatementRefList != null) && 
477            (!authnContextStatementRefList.isEmpty())) {
478
479            StringBuffer strEncodedString = new StringBuffer(100); 
480            Iterator j = authnContextStatementRefList.iterator();
481
482            strEncodedString.append((String)j.next());
483            while (j.hasNext()) {
484                strEncodedString.append(" ").append((String)j.next());
485            }
486
487            urlEncodedAuthnReq.append("AuthnContextClassRef=").
488                   append(URLEncDec.encode(strEncodedString.toString())).
489                   append(IFSConstants.AMPERSAND);
490        }
491
492        if(minorVersion == IFSConstants.FF_12_PROTOCOL_MINOR_VERSION) {
493            if(authnContextComparison != null) {
494                   urlEncodedAuthnReq.append("AuthnContextComparison=").
495                   append(URLEncDec.encode(authnContextComparison)).
496                   append(IFSConstants.AMPERSAND);
497            }
498        }
499        return urlEncodedAuthnReq.toString();         
500    } 
501}