001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/ogcwebservices/wass/saml/SAMLDocument.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001-2008 by:
006     EXSE, Department of Geography, University of Bonn
007     http://www.giub.uni-bonn.de/deegree/
008     lat/lon GmbH
009     http://www.lat-lon.de
010    
011     This library is free software; you can redistribute it and/or
012     modify it under the terms of the GNU Lesser General Public
013     License as published by the Free Software Foundation; either
014     version 2.1 of the License, or (at your option) any later version.
015    
016     This library is distributed in the hope that it will be useful,
017     but WITHOUT ANY WARRANTY; without even the implied warranty of
018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
019     Lesser General Public License for more details.
020    
021     You should have received a copy of the GNU Lesser General Public
022     License along with this library; if not, write to the Free Software
023     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
024    
025     Contact:
026    
027     Andreas Poth
028     lat/lon GmbH
029     Aennchenstr. 19
030     53115 Bonn
031     Germany
032     E-Mail: poth@lat-lon.de
033    
034     Prof. Dr. Klaus Greve
035     Department of Geography
036     University of Bonn
037     Meckenheimer Allee 166
038     53115 Bonn
039     Germany
040     E-Mail: greve@giub.uni-bonn.de
041    
042     
043     ---------------------------------------------------------------------------*/
044    
045    package org.deegree.ogcwebservices.wass.saml;
046    
047    import java.net.URI;
048    import java.util.ArrayList;
049    import java.util.Date;
050    import java.util.List;
051    
052    import javax.xml.datatype.DatatypeConfigurationException;
053    import javax.xml.datatype.DatatypeFactory;
054    
055    import org.deegree.datatypes.QualifiedName;
056    import org.deegree.framework.xml.XMLFragment;
057    import org.deegree.framework.xml.XMLParsingException;
058    import org.deegree.framework.xml.XMLTools;
059    import org.w3c.dom.Element;
060    import org.w3c.dom.Node;
061    
062    /**
063     * Parser class for the SAML elements.
064     * 
065     * Namespace: http://urn:oasis:names:tc.SAML:1.0:assertion
066     * 
067     * The classes in this package are INCOMPLETE and UNTESTED.
068     * 
069     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
070     * @author last edited by: $Author: apoth $
071     * 
072     * @version 2.0, $Revision: 9345 $, $Date: 2007-12-27 17:22:25 +0100 (Do, 27 Dez 2007) $
073     * 
074     * @since 2.0
075     */
076    public class SAMLDocument extends XMLFragment {
077    
078        private DatatypeFactory datatypeFactory;
079    
080        private static final long serialVersionUID = -1020160309145902779L;
081    
082        private static final String PRE = "saml:";
083    
084        /**
085         * @throws DatatypeConfigurationException
086         */
087        public SAMLDocument() throws DatatypeConfigurationException {
088            
089            datatypeFactory = DatatypeFactory.newInstance();
090            
091        }
092    
093        /**
094         * @param val
095         * @return the decision
096         * @throws XMLParsingException
097         */
098        private String parseDecision( String val )
099                                throws XMLParsingException {
100            
101            if ( val.equals( "Permit" ) || val.equals( "Deny" ) || val.equals( "Indeterminate" ) ) {
102                
103                return val;
104            }
105            throw new XMLParsingException( "The value '" + val + "' is not allowed here." );
106        }
107    
108        private URI parseAudience( Element root )
109                                throws XMLParsingException {
110            return XMLTools.getNodeAsURI( root, PRE + "Audience", nsContext, null );
111        }
112    
113        private Conditions parseConditions( Element elem )
114                                throws XMLParsingException {
115            Element root = (Element) XMLTools.getNode( elem, PRE + "Conditions", nsContext );
116    
117            ArrayList<Condition> conditions = new ArrayList<Condition>();
118    
119            List<Node> audiences = XMLTools.getNodes( root, PRE + "AudienceRestrictionCondition", nsContext );
120            for ( Object audience : audiences ) {
121                conditions.add( new Condition( parseAudience( (Element) audience ) ) );
122            }
123    
124            // seems strange that there can be an unlimited number of these conditions, but anyway...
125            List<Node> caches = XMLTools.getNodes( root, PRE + "DoNotCacheCondition", nsContext );
126            if ( caches.size() != 0 )
127                conditions.add( new Condition( true ) );
128    
129            String notBeforeString = XMLTools.getAttrValue( root, null, "NotBefore", null );
130            Date notBefore = null;
131            if ( notBeforeString != null ) {
132                notBefore = datatypeFactory.newXMLGregorianCalendar( notBeforeString ).toGregorianCalendar().getTime();
133            }
134            String notOnOrAfterString = XMLTools.getAttrValue( root, null, "NotOnOrAfter", null );
135            Date notOnOrAfter = null;
136            if ( notOnOrAfterString != null ) {
137                notOnOrAfter = datatypeFactory.newXMLGregorianCalendar( notOnOrAfterString ).toGregorianCalendar().getTime();
138            }
139            
140            return new Conditions( conditions, notBefore, notOnOrAfter );
141        }
142    
143        private Subject parseSubject( Element elem )
144                                throws XMLParsingException {
145            
146            Element root = (Element) XMLTools.getNode( elem, PRE + "Subject", nsContext );
147    
148            // parse name identifier, if any
149            Element nameIdentifier = (Element) XMLTools.getNode( root, PRE + "NameIdentifier", nsContext );
150            String name = null;
151            String nameQualifier = null;
152            URI format = null;
153            if ( nameIdentifier != null ) {
154                name = nameIdentifier.getNodeValue();
155                nameQualifier = XMLTools.getAttrValue( nameIdentifier, null, PRE + "NameQualifier", null );
156                format = XMLTools.getNodeAsURI( nameIdentifier, "@Format", nsContext, null );
157            }
158    
159            URI[] confirmationMethods = null;
160            String subjectConfirmationData = null;
161            // ds:KeyInfo must be parsed as well TODO FIXME LOOKATME
162    
163            Element subjectConfirmation = (Element) XMLTools.getNode( root, PRE + "SubjectConfirmation", nsContext );
164    
165            if ( subjectConfirmation != null ) {
166                confirmationMethods = XMLTools.getNodesAsURIs( subjectConfirmation, PRE + "ConfirmationMethod", nsContext );
167                subjectConfirmationData = XMLTools.getNodeAsString( subjectConfirmation, PRE + "SubjectConfirmation",
168                                                                    nsContext, null );
169            }
170    
171            if ( name == null ) {
172                if ( ( confirmationMethods == null ) || ( confirmationMethods.length == 0 ) )
173                    throw new XMLParsingException( "Invalid content of the saml:Subject element." );
174    
175                return new Subject( confirmationMethods, subjectConfirmationData );
176            }
177            
178            return new Subject( name, nameQualifier, format, confirmationMethods, subjectConfirmationData );
179        }
180    
181        private Statement parseAuthenticationStatement( Element root )
182                                throws XMLParsingException {
183            
184            Subject subject = parseSubject( root );
185    
186            Element locality = (Element) XMLTools.getNode( root, PRE + "SubjectLocality", nsContext );
187            String ip = null;
188            String dns = null;
189            if ( locality != null ) {
190                ip = XMLTools.getNodeAsString( locality, "@IPAddress", nsContext, null );
191                dns = XMLTools.getNodeAsString( locality, "@DNSAddress", nsContext, null );
192            }
193    
194            Element authorityBinding = (Element) XMLTools.getNode( root, PRE + "AuthorityBinding", nsContext );
195            QualifiedName kind = null;
196            URI location = null;
197            URI binding = null;
198            if ( authorityBinding != null ) {
199                kind = XMLTools.getRequiredNodeAsQualifiedName( authorityBinding, "@AuthorityKind", nsContext );
200                location = XMLTools.getRequiredNodeAsURI( authorityBinding, "@Location", nsContext );
201                binding = XMLTools.getRequiredNodeAsURI( authorityBinding, "@Binding", nsContext );
202            }
203    
204            URI authenticationMethod = XMLTools.getRequiredNodeAsURI( root, "@AuthenticationMethod", nsContext );
205            Date authenticationInstant = datatypeFactory.newXMLGregorianCalendar(
206                                                                                  XMLTools.getRequiredNodeAsString(
207                                                                                                                    root,
208                                                                                                                    "@AuthenticationInstant",
209                                                                                                                    nsContext ) ).toGregorianCalendar().getTime();
210    
211            Statement statement = new Statement( subject, authenticationMethod, authenticationInstant );
212            if ( ip != null )
213                statement.setIP( ip );
214            if ( dns != null )
215                statement.setDNS( dns );
216    
217            if ( ( kind != null ) && ( ( location == null ) || ( binding == null ) ) )
218                throw new XMLParsingException( "An saml:AuthorityBinding element requires all of its attributes." );
219            if ( kind != null )
220                statement.setAuthorityBinding( kind, location, binding );
221    
222            
223            return statement;
224        }
225    
226        private Statement parseAuthorizationDecisionStatement( Element root )
227                                throws XMLParsingException {
228            
229            Subject subject = parseSubject( root );
230    
231            List<Node> actionNodes = XMLTools.getRequiredNodes( root, PRE + "Action", nsContext );
232            ArrayList<String> actions = new ArrayList<String>();
233            ArrayList<URI> actionNamespaces = new ArrayList<URI>();
234    
235            for ( Object node : actionNodes ) {
236                actions.add( ( (Element) node ).getNodeValue() );
237                actionNamespaces.add( XMLTools.getNodeAsURI( (Element) node, "@Namespace", nsContext, null ) );
238            }
239    
240            Element evidence = (Element) XMLTools.getNode( root, PRE + "Evidence", nsContext );
241            List<Node> assertionNodes = XMLTools.getNodes( evidence, PRE + "Assertion", nsContext );
242            ArrayList<Assertion> assertions = new ArrayList<Assertion>();
243            for ( Object node : assertionNodes ) {
244                assertions.add( parseAssertion( (Element) node ) );
245            }
246            String[] assertionIDs = XMLTools.getNodesAsStrings( evidence, PRE + "AssertionIDReference", nsContext );
247    
248            URI resource = XMLTools.getRequiredNodeAsURI( root, "@Resource", nsContext );
249            String decision = parseDecision( XMLTools.getRequiredNodeAsString( root, "@Decision", nsContext ) );
250    
251            
252            return new Statement( subject, actions, actionNamespaces, assertions, assertionIDs, resource, decision );
253        }
254    
255        private Statement parseAttributeStatement( Element root )
256                                throws XMLParsingException {
257            
258            Subject subject = parseSubject( root );
259    
260            List<Element> attributes = XMLTools.getRequiredElements( root, PRE + "Attribute", nsContext );
261    
262            ArrayList<String> attributeNames = new ArrayList<String>();
263            ArrayList<URI> attributeNamespaces = new ArrayList<URI>();
264            ArrayList<String[]> attributeValues = new ArrayList<String[]>();
265    
266            for ( Element node : attributes ) {
267                attributeNames.add( XMLTools.getRequiredNodeAsString( node, "@AttributeName", nsContext ) );
268                attributeNamespaces.add( XMLTools.getRequiredNodeAsURI( node, "@AttributeNamespace", nsContext ) );
269                attributeValues.add( XMLTools.getRequiredNodesAsStrings( node, PRE + "AttributeValue", nsContext ) );
270            }
271    
272            
273            return new Statement( subject, attributeNames, attributeNamespaces, attributeValues );
274        }
275    
276        private Assertion parseAssertion( Element root )
277                                throws XMLParsingException {
278            
279            Element node = (Element) XMLTools.getNode( root, PRE + "Conditions", nsContext );
280            Conditions conditions = null;
281            if ( node != null )
282                conditions = parseConditions( node );
283    
284            node = (Element) XMLTools.getNode( root, PRE + "Advice", nsContext );
285    
286            ArrayList<Assertion> advices = new ArrayList<Assertion>();
287            List<Node> assertionNodes = XMLTools.getNodes( node, PRE + "Assertion", nsContext );
288            for ( Object elem : assertionNodes ) {
289                advices.add( parseAssertion( (Element) elem ) );
290            }
291            String[] adviceIDs = XMLTools.getNodesAsStrings( node, PRE + "AssertionIDReference", nsContext );
292    
293            // other stuff is not processed
294    
295            ArrayList<Statement> statements = new ArrayList<Statement>();
296    
297            List<Node> authenticationStatements = XMLTools.getNodes( root, PRE + "AuthenticationStatement", nsContext );
298            for ( Object elem : authenticationStatements ) {
299                statements.add( parseAuthenticationStatement( (Element) elem ) );
300            }
301    
302            List<Node> authorizationDecisionStatements = XMLTools.getNodes( root, PRE + "AuthorizationDecisionStatement",
303                                                                      nsContext );
304            for ( Object elem : authorizationDecisionStatements ) {
305                statements.add( parseAuthorizationDecisionStatement( (Element) elem ) );
306            }
307    
308            List<Node> attributeStatements = XMLTools.getNodes( root, PRE + "AttributeStatement", nsContext );
309            for ( Object elem : attributeStatements ) {
310                statements.add( parseAttributeStatement( (Element) elem ) );
311            }
312    
313            if ( statements.size() == 0 )
314                throw new XMLParsingException( "You must choose at least one Statement element." );
315    
316            // parse signature from ds namespace
317            int majorVersion = Integer.parseInt( XMLTools.getRequiredNodeAsString( root, "@MajorVersion", nsContext ) );
318            int minorVersion = Integer.parseInt( XMLTools.getRequiredNodeAsString( root, "@MinorVersion", nsContext ) );
319            String assertionID = XMLTools.getRequiredNodeAsString( root, "@AssertionID", nsContext );
320            String issuer = XMLTools.getRequiredNodeAsString( root, "@Issuer", nsContext );
321            String issueInstantString = XMLTools.getRequiredNodeAsString( root, "@IssueInstant", nsContext );
322            Date issueInstant = datatypeFactory.newXMLGregorianCalendar( issueInstantString ).toGregorianCalendar().getTime();
323    
324            
325            return new Assertion( conditions, advices, adviceIDs, statements, majorVersion, minorVersion, assertionID,
326                                  issuer, issueInstant );
327        }
328    
329    }