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 }