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