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: AuthenticationStatement.java,v 1.3 2008/06/25 05:47:31 qcheng Exp $
026 *
027 */
028
029/**
030 * Portions Copyrighted 2014 ForgeRock AS
031 */
032package com.sun.identity.saml.assertion;
033
034import java.text.*; 
035import java.util.*;
036import org.w3c.dom.*;
037import com.sun.identity.shared.DateUtils;
038import com.sun.identity.saml.common.SAMLUtilsCommon;
039import com.sun.identity.saml.common.SAMLConstants;
040import com.sun.identity.saml.common.SAMLException;
041import com.sun.identity.saml.common.SAMLRequesterException;
042
043/**
044 * The <code>AuthenticationStatement</code> element supplies a 
045 * statement by the issuer that its subject was authenticated by a
046 * particular means at a particular time. The
047 * <code>AuthenticationStatement</code> element is of type 
048 * <code>AuthenticationStatementType</code>, which extends the
049 * <code>SubjectStatementAbstractType</code> with the additional element and
050 * attributes.
051 * @supported.all.api
052 */
053public class AuthenticationStatement extends SubjectStatement {
054    //The AuthenticationMethod attribute specifies the type of Authentication
055    //that took place. 
056    protected String  _authenticationMethod = null;
057    
058    //The AuthenticationInstant attribute specifies the time at which the 
059    //authentication took place.
060    protected Date  _authenticationInstant = null;
061   
062    //The SubjectLocality specifies the DNS domain name and IP address 
063    //for the system entity from which the Subject was apparently authenticated.
064    protected SubjectLocality  _subjectLocality = null ; 
065    
066    //The authority binding specifies the type of authority that performed 
067    //the authentication. 
068    protected List  _authorityBinding = null;  
069    
070    /**
071     *Default constructor
072     */
073    protected AuthenticationStatement() {
074    }
075  
076    /**
077     * Constructs an authentication statement element from an
078     * existing XML block.
079     *
080     * @param element representing a DOM tree element.
081     * @exception SAMLException if there is an error in the sender or in the
082     *            element definition.
083     */
084    public AuthenticationStatement(Element element) throws SAMLException {
085        // make sure input is not null
086        if (element == null) {
087            SAMLUtilsCommon.debug.message(
088                "AuthenticationStatement: null input.");
089            throw new SAMLRequesterException(
090                SAMLUtilsCommon.bundle.getString("nullInput"));
091        }
092        // check if it's an AuthenticationStatement
093        boolean valid = SAMLUtilsCommon.checkStatement(element,
094                            "AuthenticationStatement");
095        if (!valid) {
096            SAMLUtilsCommon.debug.message(
097                "AuthenticationStatement: Wrong input.");
098            throw new SAMLRequesterException(
099                SAMLUtilsCommon.bundle.getString("wrongInput"));
100        }
101
102        int i = 0; 
103        //handle the attributes of AuthenticationStatement 
104        NamedNodeMap atts = ((Node)element).getAttributes(); 
105        int attCount = atts.getLength(); 
106        for (i = 0; i < attCount; i++) {
107            Node att = atts.item(i);
108            if (att.getNodeType() == Node.ATTRIBUTE_NODE) {
109                String attName = att.getLocalName();
110                if (attName == null || attName.length() == 0) {
111                    if (SAMLUtilsCommon.debug.messageEnabled()) {
112                        SAMLUtilsCommon.debug.message(
113                            "AuthenticationStatement:" +
114                            "Attribute name is either null or empty.");
115                    }
116                    continue;
117                    //throw new SAMLRequesterException(
118                      //  SAMLUtilsCommon.bundle.getString("nullInput"));
119                }
120                if (attName.equals("AuthenticationMethod")) {
121                    _authenticationMethod = ((Attr)att).getValue().trim();
122                } else if (attName.equals("AuthenticationInstant")) {
123                    try {
124                        _authenticationInstant =
125                            DateUtils.stringToDate(((Attr)att).getValue());   
126                    } catch (ParseException pe ) {
127                        SAMLUtilsCommon.debug.error(
128                            "AuthenticationStatement:StringToDate", pe);
129                        throw new SAMLRequesterException(
130                            SAMLUtilsCommon.bundle.getString(
131                            "wrongDateFormat"));
132                    } // end of try...catch
133                }
134            }
135        } // end of for loop 
136        //Handle the children elements of AuthenticationStatement  
137        NodeList nodes = element.getChildNodes();
138        int nodeCount = nodes.getLength();
139        if (nodeCount > 0) {
140            for (i = 0; i < nodeCount; i++) {
141                Node currentNode = nodes.item(i);
142                if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
143                    String tagName = currentNode.getLocalName();
144                    String tagNS = currentNode.getNamespaceURI(); 
145                    if ((tagName == null) || tagName.length() == 0 ||
146                        tagNS == null || tagNS.length() == 0) {
147                        if (SAMLUtilsCommon.debug.messageEnabled()) {
148                            SAMLUtilsCommon.debug.message(
149                                "AuthenticationStatement: The" +
150                                " tag name or tag namespace of child" +
151                                " element is either null or empty.");
152                        }
153                        throw new SAMLRequesterException(
154                            SAMLUtilsCommon.bundle.getString("nullInput"));
155                    }
156                    if (tagName.equals("Subject") &&
157                        tagNS.equals(SAMLConstants.assertionSAMLNameSpaceURI)) {
158                        if (this._subject != null) {
159                            if (SAMLUtilsCommon.debug.messageEnabled()) {
160                                SAMLUtilsCommon.debug.message("Authentication" +
161                                   "Statement:should only contain one subject");
162                            }
163                            throw new SAMLRequesterException(
164                                SAMLUtilsCommon.bundle.getString("oneElement"));
165                        } else { 
166                            this._subject =
167                                createSubject((Element)currentNode); 
168                        }
169                    } else if (tagName.equals("SubjectLocality") &&
170                        tagNS.equals(SAMLConstants.assertionSAMLNameSpaceURI)) {
171                        if (_subjectLocality != null) {
172                            if (SAMLUtilsCommon.debug.messageEnabled()) {
173                                SAMLUtilsCommon.debug.message("Authentication"+
174                                            "Statement: should at most " +
175                                            "contain one SubjectLocality.");
176                             }
177                             throw new SAMLRequesterException(
178                                 SAMLUtilsCommon.bundle.getString(
179                                     "oneElement"));        
180                        } else {
181                             _subjectLocality = 
182                                    createSubjectLocality((Element)currentNode);
183                        }
184                    } else if (tagName.equals("AuthorityBinding") &&
185                        tagNS.equals(SAMLConstants.assertionSAMLNameSpaceURI)) {
186                         if (_authorityBinding == null) {
187                             _authorityBinding = new ArrayList(); 
188                         }
189                         if ((_authorityBinding.add(createAuthorityBinding(
190                             (Element)currentNode))) == false) {
191                             if (SAMLUtilsCommon.debug.messageEnabled()) {
192                                 SAMLUtilsCommon.debug.message(
193                                     "Authentication Statment: failed to" +
194                                     " add to the AuthorityBinding list.");
195                             }
196                             throw new SAMLRequesterException(
197                                 SAMLUtilsCommon.bundle.getString(
198                                     "addListError"));   
199                         }
200                    } else {
201                         if (SAMLUtilsCommon.debug.messageEnabled()) {
202                             SAMLUtilsCommon.debug.message(
203                                 "AuthenticationStatement:"+
204                                 "Wrong element " + tagName + "included."); 
205                         }  
206                         throw new SAMLRequesterException(
207                             SAMLUtilsCommon.bundle.getString("wrongInput"));
208                    }
209                } // end of if (currentNode.getNodeType() == Node.ELEMENT_NODE) 
210            } // end of for loop 
211        }  // end of if (nodeCount > 0)
212        
213        // check if the subject is null 
214        if (this._subject == null) {
215            if (SAMLUtilsCommon.debug.messageEnabled()) {
216                SAMLUtilsCommon.debug.message(
217                    "AuthenticationStatement should " +
218                    "contain one subject.");
219            }
220            throw new SAMLRequesterException(
221                        SAMLUtilsCommon.bundle.getString("missingElement"));
222        }
223    }
224    
225    /**
226     * Constructs <code>Authentication statement</code>
227     *
228     * @param authMethod (optional) A String specifies the type of
229     *        authentication that took place. 
230     * @param authInstant (optional) A String specifies the time at which
231     *        the authentication took place.
232     * @param subject (required) A Subject object 
233     * @exception SAMLException if there is an error in the sender.
234     */   
235    public AuthenticationStatement(String authMethod, Date authInstant,
236        Subject subject) throws SAMLException {
237        _authenticationMethod = authMethod;    
238        _authenticationInstant = authInstant; 
239        
240        // check if the subject is null 
241        if (subject == null) {
242            if (SAMLUtilsCommon.debug.messageEnabled()) {
243                SAMLUtilsCommon.debug.message(
244                    "AuthenticationStatement:missing the subject.");
245            }
246            throw new SAMLRequesterException(
247                SAMLUtilsCommon.bundle.getString("missingElement"));
248        } else 
249            this._subject = subject; 
250    }
251          
252    /**
253     * Constructs <code>AuthenticationStatement</code>
254     *
255     * @param authMethod (optional) A String specifies the type of
256     *        authentication that took place. 
257     * @param authInstant (optional) A String specifies the time at which the 
258     *        authentication that took place.
259     * @param subject (required) A Subject object 
260     * @param subjectLocality (optional) A <code>SubjectLocality</code> object.
261     * @param authorityBinding (optional) A List of
262     *        <code>AuthorityBinding</code> objects.
263     * @exception SAMLException if there is an error in the sender.
264     */   
265    public AuthenticationStatement(String authMethod, Date authInstant, 
266        Subject subject, SubjectLocality subjectLocality,
267        List authorityBinding) throws SAMLException {
268        _authenticationMethod = authMethod;    
269        _authenticationInstant = authInstant; 
270        // check if the subject is null 
271        if (subject == null) {
272            if (SAMLUtilsCommon.debug.messageEnabled()) {
273                SAMLUtilsCommon.debug.message(
274                    "AuthenticationStatement: should" +
275                    " contain one subject.");
276            }
277            throw new SAMLRequesterException(
278                SAMLUtilsCommon.bundle.getString("oneElement"));
279        } else {
280            this._subject = subject; 
281        }
282        _subjectLocality = subjectLocality;  
283
284        if (authorityBinding != null && !authorityBinding.isEmpty()) {
285            if (_authorityBinding == null) {
286                _authorityBinding = new ArrayList(); 
287            }
288            _authorityBinding = authorityBinding; 
289        }
290    }      
291  
292    /**
293     * Returns the <code>SubjectLocality</code> from
294     * <code>AuthenticationStatement</code>
295     *
296     * @return The <code>SubjectLocality</code> object within the authentication
297     *         statement.
298     */
299    public SubjectLocality getSubjectLocality() {
300        return _subjectLocality ; 
301    }
302    
303    /**
304     * Sets the <code>SubjectLocality</code> for
305     * <code>AuthenticationStatement</code>.
306     *
307     * @param subjectlocality The <code>SubjectLocality</code> object within
308     *        the <code>AuthenticationStatement</code>.
309     * @return true if the operation is successful.
310     */
311    public boolean setSubjectLocality(SubjectLocality subjectlocality) {
312        if (subjectlocality == null) {
313            if (SAMLUtilsCommon.debug.messageEnabled()) {
314                SAMLUtilsCommon.debug.message("AuthenticationStatement: " +
315                                        "setSubjectLocality:Input is null.");
316            }
317            return false; 
318        }
319        _subjectLocality = subjectlocality ; 
320        return true; 
321    }
322    
323    /**
324     * Returns <code>AuthenticationMethod</code> from authentication statement
325     * @return A String representing the authentication method of the
326     *         authentication statement.
327     */
328    public String getAuthenticaionMethod() {
329        return _authenticationMethod; 
330    }
331    
332    /**
333     * Sets <code>AuthenticationMethod</code> for
334     * <code>AuthenticationStatement</code>.
335     *
336     * @param authenticationmethod input authentication method 
337     * @return true if the operation is successful. Otherwise return false.
338     */
339    public boolean setAuthenticaionMethod(String authenticationmethod) {
340        if (authenticationmethod == null || 
341            authenticationmethod.length() == 0) {
342            if (SAMLUtilsCommon.debug.messageEnabled()) {
343                SAMLUtilsCommon.debug.message("AuthenticationStatement: " +
344                                      "setAuthenticationMethod:Input is null.");
345            }
346            return false; 
347        }
348        _authenticationMethod = authenticationmethod; 
349        return true;  
350    }
351    
352    /**
353     * Returns <code>AuthenticationInstant</code> from authentication statement.
354     * @return The date/time when the authentication statement is created.
355     */
356    public Date getAuthenticationInstant() {
357        return _authenticationInstant;
358    }
359    
360    /**
361     * Sets <code>AuthenticationInstant</code> for
362     * <code>AuthenticationStatement</code>.
363     *
364     * @param authenticationinstant The date/time when the authentication
365     *        statement is created.
366     * @return true if the operation is successful.
367     */
368    public boolean setAuthenticationInstant(Date authenticationinstant) {
369        if (authenticationinstant == null) {
370            if (SAMLUtilsCommon.debug.messageEnabled()) {
371                SAMLUtilsCommon.debug.message("AuthenticationStatement: " +
372                                     "setAuthenticationInstant:Input is null.");
373            }
374            return false; 
375        }
376        _authenticationInstant = authenticationinstant;
377        return true;
378    }
379    
380    /**
381     * Returns the <code>AuthorityBinding</code> from
382     * <code>AuthenticationStatement</code>.
383     *
384     * @return A list of the <code>AuthorityBinding</code> objects
385     */
386    public List getAuthorityBinding() { 
387        return  _authorityBinding; 
388    } 
389    
390    /**
391     * Sets the <code>AuthorityBinding</code> for
392     * <code>AuthenticationStatement</code>.
393     *
394     * @param authoritybinding A list of the <code>AuthorityBinding</code>
395     *        objects.
396     * @return true if the operation is successful.
397     */
398    public boolean setAuthorityBinding(List authoritybinding) { 
399        if (authoritybinding == null || authoritybinding.isEmpty()) {
400            if (SAMLUtilsCommon.debug.messageEnabled()) {
401                SAMLUtilsCommon.debug.message("AuthenticationStatement: " +
402                                        "setAuthorityBinding:Input is null.");
403            }
404            return false; 
405        }
406        _authorityBinding = authoritybinding; 
407        return true;   
408    }
409    
410    /**
411     *Gets the real type of the Statement. 
412     *This method returns Statement.AUTHENTICATION_STATEMENT. 
413     *@return an integer which is Statement.AUTHENTICATION_STATEMENT. 
414     */
415    public int getStatementType() {
416        return Statement.AUTHENTICATION_STATEMENT; 
417    }
418    
419    /** 
420     * Returns a String representation of the Authentication Statement.
421     * 
422     * @return A String representation of the
423     *         <code>&lt;saml:AuthenticationStatement&gt;</code>
424     *         element.
425     */
426    public String toString()  {
427        return (toString(true, false)); 
428    }
429   
430    /** 
431     * Returns a String representation of the
432     * <code>AuthenticationStatement</code>
433     * @param includeNS Determines whether or not the namespace qualifier is 
434     *        prepended  to the Element when converted
435     * @param declareNS Determines whether or not the namespace is declared 
436     *        within the Element.
437     * @return A String representation of the 
438     *         <code>&lt;saml:AuthenticationStatement&gt;</code> element.
439     */                             
440    public  String  toString(boolean includeNS, boolean declareNS)  {
441        StringBuffer result = new StringBuffer(1000);
442        String prefix = "";
443        String uri = "";
444        if (includeNS) {
445            prefix = SAMLConstants.ASSERTION_PREFIX;
446        }
447        if (declareNS) {
448            uri = SAMLConstants.assertionDeclareStr;
449        }
450        result.append("<").append(prefix).append("AuthenticationStatement").
451               append(uri); 
452
453        if (_authenticationMethod != null &&
454            _authenticationMethod.length() != 0){
455            result.append(" AuthenticationMethod=\"").
456                   append(_authenticationMethod.replace("|", "%7C")).append("\"");
457        }
458
459        if (_authenticationInstant != null &&
460            _authenticationInstant.toString().length() != 0
461        ) {
462            result.append(" AuthenticationInstant=\"")
463                .append(DateUtils.toUTCDateFormat(_authenticationInstant))
464                .append("\""); 
465        }
466
467        result.append(">\n").append(this._subject.toString(includeNS, false));
468        if (_subjectLocality != null) {
469            result.append(_subjectLocality.toString(includeNS, false));
470        }
471        if (_authorityBinding != null && !_authorityBinding.isEmpty()) {
472                Iterator iter = this.getAuthorityBinding().iterator(); 
473                while (iter.hasNext()) {
474                    AuthorityBinding authBinding= 
475                        (AuthorityBinding)iter.next(); 
476                    result.append(authBinding.toString(includeNS, false)); 
477                }
478        }
479        result.append("</").append(prefix).append("AuthenticationStatement>\n");
480        return(result.toString());
481    }
482
483    protected Subject createSubject(Element subjectElement)
484        throws SAMLException {
485        return new Subject(subjectElement);
486    }
487    
488    protected SubjectLocality createSubjectLocality(
489        Element subjectLocalityElement)
490        throws SAMLException {
491        return new SubjectLocality(subjectLocalityElement);
492    }
493   
494    protected AuthorityBinding createAuthorityBinding(
495        Element authorityBindingElement) throws SAMLException {
496        return new AuthorityBinding(authorityBindingElement);
497    }
498}