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: Assertion.java,v 1.3 2008/06/25 05:47:31 qcheng Exp $
026 *
027 */
028
029  
030package com.sun.identity.saml.assertion;
031
032import com.sun.identity.shared.DateUtils;
033import com.sun.identity.saml.common.*;
034import com.sun.identity.saml.xmlsig.*;
035import com.sun.identity.common.SystemConfigurationUtil;
036import com.sun.identity.shared.xml.XMLUtils;
037import java.util.Set;
038import java.util.List;
039import java.util.Date;
040import java.text.ParseException;
041import org.w3c.dom.Node;
042import org.w3c.dom.NodeList;
043import org.w3c.dom.Element;
044import org.w3c.dom.Document;
045
046/**
047 *This object stands for <code>Assertion</code> element. An Assertion is a
048 *package of information that supplies one or more <code>Statement</code> made 
049 *by an issuer. There are three kinds of assertionsL Authentication, 
050 *AuthorizationDecision and Attribute assertion.
051 *@supported.all.api
052 */
053public class Assertion extends AssertionBase {          
054
055    /**
056     * Returns whether the signature on the object is valid or not.
057     * @return true if the signature on the object is valid; false otherwise.
058     */
059    public boolean isSignatureValid() {
060        if (signed & ! validationDone) {
061            valid = SAMLUtils.checkSignatureValid(
062                xmlString, ASSERTION_ID_ATTRIBUTE, _issuer); 
063                
064            validationDone = true;
065        }
066        return valid;
067    }
068
069    /**
070     * Signs the Assertion.
071     * @exception SAMLException If it could not sign the Assertion.
072     */
073    public void signXML() throws SAMLException {
074        String certAlias =
075            SystemConfigurationUtil.getProperty(
076            "com.sun.identity.saml.xmlsig.certalias");
077        signXML(certAlias);
078    }
079
080    /**
081     * Signs the Assertion.
082     *
083     * @param certAlias certification Alias used to sign Assertion.
084     * @exception SAMLException if it could not sign the Assertion.
085     */
086    public void signXML(String certAlias) throws SAMLException {
087        if (signed) {
088            if (SAMLUtils.debug.messageEnabled()) {
089                SAMLUtils.debug.message("Assertion.signXML: the assertion is "
090                    + "already signed.");
091            }
092            throw new SAMLException(
093                SAMLUtils.bundle.getString("alreadySigned"));
094        }
095
096        if (certAlias == null) {
097            if (SAMLUtils.debug.messageEnabled()) {
098                SAMLUtils.debug.message("Assetion.signXML: couldn't obtain "
099                    + "this site's cert alias.");
100            }
101            throw new SAMLResponderException(
102                SAMLUtils.bundle.getString("cannotFindCertAlias"));
103        }
104
105        XMLSignatureManager manager = XMLSignatureManager.getInstance();
106        if ((_majorVersion == 1) && (_minorVersion == 0)) { 
107            SAMLUtils.debug.message("Assetion.signXML: sign with version 1.0");
108            signatureString = manager.signXML(this.toString(true, true), 
109                                              certAlias);
110            // this block is used for later return of signature element by
111            // getSignature() method
112            signature = 
113                XMLUtils.toDOMDocument(signatureString, SAMLUtils.debug)
114                        .getDocumentElement();
115
116        } else {
117            SAMLUtils.debug.message("Assetion.signXML: sign with version 1.1");
118            Document doc = XMLUtils.toDOMDocument(this.toString(true, true),
119                                                  SAMLUtils.debug);
120            // sign with SAML 1.1 spec & include cert in KeyInfo
121            signature = manager.signXML(doc, certAlias, null, 
122                ASSERTION_ID_ATTRIBUTE, getAssertionID(), true, null);
123            signatureString = XMLUtils.print(signature);
124        }
125        signed = true;
126        xmlString = this.toString(true, true);
127    }
128
129
130    /** 
131     *Default constructor
132     *Declaring protected to enable extensibility
133     */
134    protected Assertion() {
135        super();
136    }
137   
138    /**
139     * Contructs <code>Assertion</code> object from a
140     * block of existing XML that has already been built into a DOM.
141     *
142     * @param assertionElement A <code>org.w3c.dom.Element</code> representing 
143     *        DOM tree for <code>Assertion</code> object
144     * @exception SAMLException if it could not process the Element properly, 
145     *            implying that there is an error in the sender or in the
146     *            element definition.
147     */
148    public Assertion(org.w3c.dom.Element assertionElement) 
149        throws SAMLException 
150    {
151        parseAssertionElement(assertionElement);
152    }
153
154    protected void parseAssertionElement(Element assertionElement)
155        throws SAMLException
156    {
157        if (SAMLUtils.debug.messageEnabled()) {
158            SAMLUtils.debug.message("Assertion.parseAssertionElement:");
159        }
160
161        Element elt = (Element) assertionElement;
162        String eltName = elt.getLocalName();
163        if (eltName == null)  {
164            if (SAMLUtils.debug.messageEnabled()) {
165                SAMLUtils.debug.message("Assertion: local name missing");
166            }
167            throw new SAMLRequesterException(SAMLUtils.bundle.getString
168                                        ("nullInput")) ;
169        }
170        if (!(eltName.equals("Assertion")))  {
171            if (SAMLUtils.debug.messageEnabled()) {
172                SAMLUtils.debug.message("Assertion: invalid root element");
173            }
174            throw new SAMLRequesterException(SAMLUtils.bundle.getString
175                ("invalidElement")+ ":"+eltName) ;   
176        }
177
178        String read = elt.getAttribute("Issuer");
179        if ((read == null) || (read.length() == 0)) {
180            if (SAMLUtils.debug.messageEnabled()) {
181                SAMLUtils.debug.message("Assertion: Issuer missing");
182            }
183            throw new SAMLRequesterException(
184                SAMLUtils.bundle.getString("missingAttribute") +":"+"Issuer");
185        } else  {
186            _issuer = read;
187        }
188
189        List signs = XMLUtils.getElementsByTagNameNS1(assertionElement,
190                                        SAMLConstants.XMLSIG_NAMESPACE_URI,
191                                        SAMLConstants.XMLSIG_ELEMENT_NAME);
192        int signsSize = signs.size();
193        if (signsSize == 1) {
194            // delay the signature validation till user call isSignatureValid()
195            xmlString = XMLUtils.print(assertionElement);
196            signed = true;
197            validationDone = false;
198        } else if (signsSize != 0) {
199            if (SAMLUtils.debug.messageEnabled()) {
200                SAMLUtils.debug.message("Assertion(Element): included more than"
201                    + " one Signature element.");
202            }
203            throw new SAMLRequesterException(
204                SAMLUtils.bundle.getString("moreElement"));
205        }
206
207        read = elt.getAttribute("MajorVersion");
208        if ((read == null) || (read.length() == 0)) {
209            if (SAMLUtils.debug.messageEnabled())  {
210                SAMLUtils.debug.message("Assertion: MajorVersion missing");
211            }
212            throw new SAMLRequesterException(
213                    SAMLUtils.bundle.getString("missingAttribute")+":"+
214                        "MajorVersion");
215        }
216        else  {
217            int ver = 0;
218            try {
219                ver = Integer.parseInt(read);
220            } catch ( NumberFormatException ne ) {
221                SAMLUtils.debug.error(
222                        "Assertion: invalid integer in MajorVersion", ne);
223                throw new SAMLRequesterException(
224                        SAMLUtils.bundle.getString("invalidNumber")+":"+
225                        "MajorVersion");
226            }
227            if (ver != sc.ASSERTION_MAJOR_VERSION) {
228                if (ver < sc.ASSERTION_MAJOR_VERSION) {
229                    if (SAMLUtils.debug.messageEnabled())  {
230                        SAMLUtils.debug.message(
231                            "Assertion: MajorVersion too low");
232                    }
233                    throw new SAMLVersionMismatchException(
234                        SAMLUtils.bundle.getString("assertionVersionTooLow")
235                        + ":"+"MajorVersion");
236                } else if (ver > sc.ASSERTION_MAJOR_VERSION) {
237                    if (SAMLUtils.debug.messageEnabled())  {
238                        SAMLUtils.debug.message(
239                            "Assertion: MajorVersion too high");
240                    }
241                    throw new SAMLVersionMismatchException(
242                        SAMLUtils.bundle.getString("assertionVersionTooHigh")
243                            +":"+"MajorVersion");
244                }
245            }
246        }
247        read = elt.getAttribute("MinorVersion");
248        if ((read == null) || (read.length() == 0)) {
249            if (SAMLUtils.debug.messageEnabled()) 
250                SAMLUtils.debug.message("Assertion: MinorVersion missing");
251            throw new SAMLRequesterException(
252                                SAMLUtils.bundle.getString("missingAttribute")
253                                    +":"+"MinorVersion");
254        }
255        else  {
256            int ver = 0;
257            try {
258                ver = Integer.parseInt(read);
259            } catch ( NumberFormatException ne ) {
260                SAMLUtils.debug.error(
261                        "Assertion: invalid integer in MinorVersion", ne);
262                throw new SAMLRequesterException(
263                        SAMLUtils.bundle.getString("invalidNumber")
264                                    +":"+"MinorVersion");
265            }
266
267            if (SAMLUtils.debug.messageEnabled()) {
268                SAMLUtils.debug.message("Assertion.parseAssertionElement: " +
269                       "minMinorVersion = " + getMinAssertionMinorVersion() +
270                       ", maxMinorVersion = " + getMaxAssertionMinorVersion());
271            }
272
273            if (ver < getMinAssertionMinorVersion()) {
274                if (SAMLUtils.debug.messageEnabled())  {
275                    SAMLUtils.debug.message("Assertion: MinorVersion too low");
276                }
277                throw new SAMLVersionMismatchException(
278                        SAMLUtils.bundle.getString("assertionVersionTooLow"));
279            } else if (ver > getMaxAssertionMinorVersion()) {
280                if (SAMLUtils.debug.messageEnabled())  {
281                    SAMLUtils.debug.message("Assertion: MinorVersion too high");
282                }
283                throw new SAMLVersionMismatchException(
284                        SAMLUtils.bundle.getString("assertionVersionTooHigh")
285                                    +":"+"MinorVersion");
286            } else {
287                _minorVersion=ver;
288            }
289        }
290        read = elt.getAttribute("AssertionID");
291        if ((read == null) || (read.length() == 0)) {
292            if (SAMLUtils.debug.messageEnabled()) 
293                SAMLUtils.debug.message("Assertion: AssertionID missing");
294            throw new SAMLRequesterException(
295                                SAMLUtils.bundle.getString("missingAttribute")
296                                    +":"+"AssertionID");
297        }
298        else  {
299            _assertionID = new AssertionIDReference(read);
300        }
301
302        read = elt.getAttribute("IssueInstant");
303        if ((read == null) || (read.length() == 0)) {
304            if (SAMLUtils.debug.messageEnabled())  {
305                SAMLUtils.debug.message("Assertion: IssueInstant missing");
306            }
307            throw new SAMLRequesterException(
308                                SAMLUtils.bundle.getString("missingAttribute")
309                                    +":"+"IssueInstant");
310        } else  {
311            try {
312                _issueInstant = DateUtils.stringToDate(read);
313            } catch (ParseException pe) {
314                if (SAMLUtils.debug.messageEnabled()) 
315                    SAMLUtils.debug.message(
316                    "Assertion: could not parse IssueInstant", pe);
317               throw new SAMLRequesterException(SAMLUtils.bundle.getString(
318                        "wrongInput") + " " + pe.getMessage());
319            }
320        }
321
322        NodeList nl = assertionElement.getChildNodes();
323        int length = nl.getLength();
324        for (int n=0; n<length; n++) {
325            Node child = (Node)nl.item(n);
326            if (child.getNodeType() != Node.ELEMENT_NODE) continue;
327            String childName = child.getLocalName();
328            if (childName.equals("Conditions"))
329                _conditions = new Conditions((Element)child);
330            else if (childName.equals("Advice"))
331                _advice = new Advice((Element)child);
332            else if (childName.equals("AuthenticationStatement")) {
333                _statements.add(new AuthenticationStatement((Element)child));
334            }
335            else if (childName.equals("AuthorizationDecisionStatement")) {
336                _statements.add(new AuthorizationDecisionStatement(
337                        (Element)child));
338            }
339            else if (childName.equals("AttributeStatement")) {
340                _statements.add(new AttributeStatement((Element)child));
341            }
342            else if (childName.equals("Signature")) {
343                signature = (Element) child;
344            }
345            else if (!processUnknownElement((Element)child)) {
346                if (SAMLUtils.debug.messageEnabled()) {
347                    SAMLUtils.debug.message(
348                        "Assertion: invalid element in Assertion");
349                }
350                throw new SAMLRequesterException("invalidElement");
351            }
352        }
353        if (_statements.isEmpty()) {
354            if (SAMLUtils.debug.messageEnabled()) {
355                SAMLUtils.debug.message(
356                    "Assertion: mandatory statement missing");
357            }
358            throw new SAMLRequesterException("missingStatement");
359        }
360    } 
361   
362 
363    /**
364     *  Contructs <code>Assertion</code> object and populate the data members:
365     * <code>assertionID</code>, the issuer, time when assertion issued and a
366     * set of <code>Statement</code>(s) in the assertion.
367     *
368     * @param assertionID <code>assertionID</code> attribute contained within
369     *        this <code>Assertion</code> if null, an <code>assertionID</code>
370     *        is generated internally.
371     * @param issuer The issuer of this assertion.
372     * @param issueInstant time instant of the issue. It has type
373     *        <code>dateTime</code> which is built in to the W3C XML Schema
374     *        Types specification.if null, current time is used.
375     * @param statements set of <code>Statement</code> objects within this 
376     *        <code>Assertion</code>. It could be of type
377     *        <code>AuthenticationStatement</code>, 
378     *        <code>AuthorizationDecisionStatement</code> and
379     *        <code>AttributeStatement</code>. Each Assertion can have multiple
380     *        type of statements in it.
381     * @exception SAMLException if there is an error in processing input.
382     */
383    public Assertion(String assertionID,java.lang.String issuer, 
384        Date issueInstant,  Set statements) throws SAMLException
385    {
386        super(assertionID, issuer, issueInstant, statements);
387    }
388
389    /**
390     * Contructs <code>Assertion</code> object and  populate the data members: 
391     * the <code>assertionID</code>, the issuer, time when assertion issued,  
392     * the conditions when creating a new assertion and a set of
393     * <code>Statement</code>(s) in the assertion.
394     *
395     * @param assertionID <code>AssertionID</code> contained within this 
396     *        <code>Assertion</code> if null its generated internally.
397     * @param issuer The issuer of this assertion.
398     * @param issueInstant time instant of the issue. It has type
399     *        <code>dateTime</code> which is built in to the W3C XML Schema
400     *        Types specification. if null, current time is used.
401     * @param conditions <code>Conditions</code> under which the this 
402     *        <code>Assertion</code> is valid.
403     * @param statements Set of <code>Statement</code> objects within this 
404     *        <code>Assertion</code>. It could be of type
405     *        <code>AuthenticationStatement</code>,
406     *        <code>AuthorizationDecisionStatement</code> and 
407     *        <code>AttributeStatement</code>. Each Assertion can have multiple
408     *        type of statements in it.
409     * @exception SAMLException if there is an error in processing input.
410     */
411    public Assertion(String assertionID,java.lang.String issuer, 
412        Date issueInstant,  Conditions conditions, Set statements) 
413        throws SAMLException
414    {
415        super(assertionID, issuer, issueInstant, conditions, statements);
416    }
417   
418    /**
419     * Contructs <code>Assertion</code> object and populate the data members: 
420     * the <code>ssertionID</code>, the issuer, time when assertion issued,
421     * the conditions when creating a new assertion , <code>Advice</code>
422     * applicable to this <code>Assertion</code> and a set of
423     * <code>Statement</code>(s) in the assertion.
424     *
425     * @param assertionID <code>AssertionID</code> object contained within this
426     *        <code>Assertion</code> if null its generated internally.
427     * @param issuer The issuer of this assertion.
428     * @param issueInstant Time instant of the issue. It has type
429     *        <code>dateTime</code> which is built in to the W3C XML Schema
430     *        Types specification. if null, current time is used.
431     * @param conditions <code>Conditions</code> under which the this 
432     *        <code>Assertion</code> is valid.
433     * @param advice <code>Advice</code> applicable for this
434     *        <code>Assertion</code>.
435     * @param statements Set of <code>Statement</code> objects within this 
436     *         <code>Assertion</code>. It could be of type
437     *         <code>AuthenticationStatement</code>,
438     *         <code>AuthorizationDecisionStatement</code> and 
439     *         <code>AttributeStatement</code>. Each Assertion can have
440     *         multiple type of statements in it.
441     * @exception SAMLException if there is an error in processing input.
442     */
443    public Assertion(String assertionID,java.lang.String issuer, 
444        Date issueInstant,  Conditions conditions, Advice advice, 
445        Set statements) throws SAMLException
446    {
447        super(assertionID, issuer, issueInstant, conditions, advice,statements);
448    }
449
450    /**
451     * Returns the advice of an assertion.
452     *
453     * @return <code>Advice</code> object containing advice information of the
454     *         assertion.
455     */
456    public Advice getAdvice() {
457        return (Advice)_advice; 
458    }
459
460    protected AdviceBase createAdvice(Element adviceElement) 
461        throws SAMLException {
462        return new Advice(adviceElement);
463    }
464  
465    protected  AuthorizationDecisionStatementBase
466        createAuthorizationDecisionStatement(Element authDecisionElement)
467            throws SAMLException {
468        return new AuthorizationDecisionStatement(authDecisionElement);
469    }
470  
471    protected  AuthenticationStatement
472        createAuthenticationStatement(Element authenticationElement)
473            throws SAMLException {
474        return new AuthenticationStatement(authenticationElement);
475    }
476  
477    protected  AttributeStatement
478        createAttributeStatement(Element attributeElement)
479        throws SAMLException {
480        return new AttributeStatement(attributeElement);
481    }
482  
483    protected  AssertionIDReference
484        createAssertionIDReference(Element assertionIDRefElement)
485            throws SAMLException {
486        return  new AssertionIDReference(assertionIDRefElement);
487    }
488  
489    protected  AssertionIDReference
490        createAssertionIDReference(String assertionID) throws SAMLException {
491        return  new AssertionIDReference(assertionID);
492    }
493  
494    protected  Conditions
495        createConditions(Element conditionsElement) throws SAMLException {
496        return new Conditions(conditionsElement);
497    }
498
499    protected boolean processUnknownElement(Element element)
500        throws SAMLException
501    {
502        if (SAMLUtils.debug.messageEnabled()) {
503            SAMLUtils.debug.message("Assertion.processUnknownElement:");
504        }
505        return false;
506    }
507
508    protected int getMinAssertionMinorVersion() {
509        return sc.ASSERTION_MINOR_VERSION_ZERO;
510    }
511
512    protected int getMaxAssertionMinorVersion() {
513        return sc.ASSERTION_MINOR_VERSION_ONE;
514    }
515}