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 }