Package ndg :: Package saml :: Package xml :: Module etree
[hide private]

Source Code for Module ndg.saml.xml.etree

   1  """Implementation of SAML 2.0 for NDG Security - ElementTree module for 
   2  ElementTree representation of SAML objects 
   3   
   4  NERC DataGrid Project 
   5   
   6  This implementation is adapted from the Java OpenSAML implementation.  The  
   7  copyright and licence information are included here: 
   8   
   9  Copyright [2005] [University Corporation for Advanced Internet Development, Inc.] 
  10   
  11  Licensed under the Apache License, Version 2.0 (the "License"); 
  12  you may not use this file except in compliance with the License. 
  13  You may obtain a copy of the License at 
  14   
  15  http://www.apache.org/licenses/LICENSE-2.0 
  16   
  17  Unless required by applicable law or agreed to in writing, software 
  18  distributed under the License is distributed on an "AS IS" BASIS, 
  19  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  20  See the License for the specific language governing permissions and 
  21  limitations under the License. 
  22  """ 
  23  __author__ = "P J Kershaw" 
  24  __date__ = "23/07/09" 
  25  __copyright__ = "(C) 2009 Science and Technology Facilities Council" 
  26  __contact__ = "Philip.Kershaw@stfc.ac.uk" 
  27  __license__ = "http://www.apache.org/licenses/LICENSE-2.0" 
  28  __contact__ = "Philip.Kershaw@stfc.ac.uk" 
  29  __revision__ = "$Id: etree.py 8009 2012-01-30 16:19:43Z rwilkinson $" 
  30  import logging 
  31  log = logging.getLogger(__name__) 
  32  import re 
  33   
  34  from ndg.saml import Config, importElementTree 
  35  ElementTree = importElementTree() 
  36   
  37  from ndg.saml.saml2.core import (SAMLObject, Attribute, AttributeStatement,  
  38                                   AuthnStatement, AuthzDecisionStatement,  
  39                                   Assertion, Conditions, AttributeValue,  
  40                                   AttributeQuery, AuthzDecisionQuery, Subject,  
  41                                   NameID, Issuer, Response, Status, StatusCode,  
  42                                   StatusMessage, StatusDetail, Advice, Action,  
  43                                   Evidence, DecisionType, XSStringAttributeValue)  
  44                                
  45  from ndg.saml.common import SAMLVersion 
  46  from ndg.saml.common.xml import SAMLConstants 
  47  from ndg.saml.common.xml import QName as GenericQName 
  48  from ndg.saml.xml import XMLTypeParseError, UnknownAttrProfile 
  49  from ndg.saml.utils import SAMLDateTime 
  50   
  51  # Map of QName to ElementTree parsing class to be used in addition to those 
  52  # defined in this module. 
  53  _extensionElementTreeMap = {} 
  54   
  55  if Config.use_lxml: 
56 - def makeEtreeElement(tag, ns_prefix, ns_uri, attrib={}, **extra):
57 """Makes an ElementTree element handling namespaces in the way 58 appropriate for the ElementTree implementation in use. 59 """ 60 elem = ElementTree.Element(tag, {ns_prefix: ns_uri}, attrib, **extra) 61 return elem
62 else:
63 - def makeEtreeElement(tag, ns_prefix, ns_uri, attrib={}, **extra):
64 """Makes an ElementTree element handling namespaces in the way 65 appropriate for the ElementTree implementation in use. 66 """ 67 elem = ElementTree.Element(tag, attrib, **extra) 68 ElementTree._namespace_map[ns_uri] = ns_prefix 69 return elem
70
71 # Generic ElementTree Helper classes 72 -class QName(ElementTree.QName):
73 """Extend ElementTree implementation for improved attribute access support 74 """ 75 76 # ElementTree tag is of the form {namespace}localPart. getNs extracts the 77 # namespace from within the brackets but if not found returns '' 78 getNs = staticmethod(lambda tag: getattr(re.search('(?<=\{).+(?=\})', tag), 79 'group', 80 str)()) 81 82 getLocalPart = staticmethod(lambda tag: tag.rsplit('}', 1)[-1]) 83
84 - def __init__(self, input, tag=None, prefix=None):
85 """ 86 @type input: basestring 87 @param input: ElementTree style namespace URI + tag name - 88 {namespace URI}tag - OR if tag keyword is set, the namespace URI alone 89 @type tag: basestring / None 90 @param tag: element tag name. If None, input must contain the 91 namespace URI and tag name in the ElementTree form {namespace URI}tag. 92 @type prefix: basestring / None 93 @param prefix: namespace prefix 94 """ 95 96 ElementTree.QName.__init__(self, input, tag=tag) 97 98 if tag: 99 self.namespaceURI = input 100 self.localPart = tag 101 else: 102 # No tag provided namespace and localPart of QN must be parsed from 103 # the namespace 104 self.namespaceURI = QName.getNs(input) 105 self.localPart = QName.getLocalPart(input) 106 107 self.prefix = prefix
108
109 - def _getPrefix(self):
110 """@return: NS prefix 111 @rtype: basestring 112 """ 113 return self.__prefix
114
115 - def _setPrefix(self, value):
116 """@param value: NS prefix 117 @type value: basestring 118 """ 119 self.__prefix = value
120 121 prefix = property(_getPrefix, _setPrefix, None, "Prefix") 122
123 - def _getLocalPart(self):
124 """@return: NS local name 125 @rtype: basestring 126 """ 127 return self.__localPart
128
129 - def _setLocalPart(self, value):
130 """@param value: NS local name 131 @type value: basestring 132 """ 133 self.__localPart = value
134 135 localPart = property(_getLocalPart, _setLocalPart, None, "LocalPart") 136
137 - def _getNamespaceURI(self):
138 """@return: NS URI 139 @rtype: basestring 140 """ 141 return self.__namespaceURI
142
143 - def _setNamespaceURI(self, value):
144 """@param value: NS URI 145 @type value: basestring 146 """ 147 self.__namespaceURI = value
148 149 namespaceURI = property(_getNamespaceURI, _setNamespaceURI, None, 150 "Namespace URI'") 151
152 - def __eq__(self, qname):
153 """Enable equality check for QName. Note that prefixes don't need to 154 match 155 156 @type qname: ndg.security.common.utils.etree.QName 157 @param qname: Qualified Name to compare with self 158 159 @return: True if the qualified names match, False otherwise 160 @rtype: bool 161 """ 162 if not isinstance(qname, QName): 163 raise TypeError('Expecting %r; got %r' % (QName, type(qname))) 164 165 # Nb. prefixes don't need to agree! 166 return (self.namespaceURI, self.localPart) == \ 167 (qname.namespaceURI, qname.localPart)
168
169 - def __ne__(self, qname):
170 """Enable equality check for QName. Note that prefixes don't need to 171 match 172 173 @type qname: ndg.security.common.utils.etree.QName 174 @param qname: Qualified Name to compare with self 175 @return: True if the qualified names don't match, False otherwise 176 @rtype: bool 177 """ 178 return not self.__eq__(qname)
179 180 @classmethod
181 - def fromGeneric(cls, genericQName):
182 '''Cast the generic QName type in ndg.saml.common.xml to this 183 ElementTree specific implementation 184 185 @param genericQName: SAML core qualified name type 186 @type genericQName: ndg.saml.common.xml.QName 187 @return: ElementTree specific qualified name type 188 @rtype: ndg.saml.xml.etree.QName 189 ''' 190 if not isinstance(genericQName, GenericQName): 191 raise TypeError("Expecting %r for QName, got %r" % (GenericQName, 192 type(genericQName))) 193 194 qname = cls(genericQName.namespaceURI, 195 tag=genericQName.localPart, 196 prefix=genericQName.prefix) 197 return qname
198
199 200 -def prettyPrint(*arg, **kw):
201 '''Lightweight pretty printing of ElementTree elements. This function 202 wraps the PrettyPrint class 203 204 @param arg: arguments to pretty print function 205 @type arg: tuple 206 @param kw: keyword arguments to pretty print function 207 @type kw: dict 208 ''' 209 210 # Keep track of namespace declarations made so they're not repeated 211 declaredNss = [] 212 if not Config.use_lxml: 213 mappedPrefixes = dict.fromkeys(ElementTree._namespace_map.values(), True) 214 namespace_map_backup = ElementTree._namespace_map.copy() 215 else: 216 mappedPrefixes = {} 217 218 _prettyPrint = _PrettyPrint(declaredNss, mappedPrefixes) 219 result = _prettyPrint(*arg, **kw) 220 221 if not Config.use_lxml: 222 ElementTree._namespace_map = namespace_map_backup 223 224 return result
225
226 227 -class _PrettyPrint(object):
228 '''Class for lightweight pretty printing of ElementTree elements''' 229 MAX_NS_TRIES = 256
230 - def __init__(self, declaredNss, mappedPrefixes):
231 """ 232 @param declaredNss: declared namespaces 233 @type declaredNss: iterable of string elements 234 @param mappedPrefixes: map of namespace URIs to prefixes 235 @type mappedPrefixes: map of string to string 236 """ 237 self.declaredNss = declaredNss 238 self.mappedPrefixes = mappedPrefixes
239 240 @staticmethod
241 - def estrip(elem):
242 '''Utility to remove unwanted leading and trailing whitespace 243 244 @param elem: ElementTree element 245 @type elem: ElementTree.Element 246 @return: element content with whitespace removed 247 @rtype: basestring''' 248 if elem is None: 249 return '' 250 else: 251 # just in case the elem is another simple type - e.g. int - 252 # wrapper it as a string 253 return str(elem).strip()
254
255 - def __call__(self, elem, indent='', html=0, space=' '*4):
256 '''Most of the work done in this wrapped function - wrapped so that 257 state can be maintained for declared namespace declarations during 258 recursive calls using "declaredNss" above 259 260 @param elem: ElementTree element 261 @type elem: ElementTree.Element 262 @param indent: set indent for output 263 @type indent: basestring 264 @param space: set output spacing 265 @type space: basestring 266 @return: pretty print format for doc 267 @rtype: basestring 268 ''' 269 strAttribs = [] 270 for attr, attrVal in elem.attrib.items(): 271 nsDeclaration = '' 272 273 attrNamespace = QName.getNs(attr) 274 if attrNamespace: 275 nsPrefix = self._getNamespacePrefix(elem, attrNamespace) 276 277 attr = "%s:%s" % (nsPrefix, QName.getLocalPart(attr)) 278 279 if attrNamespace not in self.declaredNss: 280 nsDeclaration = ' xmlns:%s="%s"' % (nsPrefix,attrNamespace) 281 self.declaredNss.append(attrNamespace) 282 283 strAttribs.append('%s %s="%s"' % (nsDeclaration, attr, attrVal)) 284 285 strAttrib = ''.join(strAttribs) 286 287 namespace = QName.getNs(elem.tag) 288 nsPrefix = self._getNamespacePrefix(elem, namespace) 289 290 tag = "%s:%s" % (nsPrefix, QName.getLocalPart(elem.tag)) 291 292 # Put in namespace declaration if one doesn't already exist 293 # FIXME: namespace declaration handling is wrong for handling child 294 # element scope 295 if namespace in self.declaredNss: 296 nsDeclaration = '' 297 else: 298 nsDeclaration = ' xmlns:%s="%s"' % (nsPrefix, namespace) 299 self.declaredNss.append(namespace) 300 301 result = '%s<%s%s%s>%s' % (indent, tag, nsDeclaration, strAttrib, 302 _PrettyPrint.estrip(elem.text)) 303 304 children = len(elem) 305 if children: 306 for child in elem: 307 declaredNss = self.declaredNss[:] 308 _prettyPrint = _PrettyPrint(declaredNss, self.mappedPrefixes) 309 result += '\n'+ _prettyPrint(child, indent=indent+space) 310 311 result += '\n%s%s</%s>' % (indent, 312 _PrettyPrint.estrip(child.tail), 313 tag) 314 else: 315 result += '</%s>' % tag 316 317 return result
318 319 if Config.use_lxml:
320 - def _getNamespacePrefix(self, elem, namespace):
321 for nsPrefix, ns in elem.nsmap.iteritems(): 322 if ns == namespace: 323 return nsPrefix 324 raise KeyError('prettyPrint: missing namespace "%s" for ' 325 'elem.nsmap' % namespace)
326 else:
327 - def _getNamespacePrefix(self, elem, namespace):
328 nsPrefix = self._allocNsPrefix(namespace) 329 if nsPrefix is None: 330 raise KeyError('prettyPrint: missing namespace "%s" for ' 331 'ElementTree._namespace_map' % namespace) 332 return nsPrefix
333
334 - def _allocNsPrefix(self, nsURI):
335 """Allocate a namespace prefix if one is not already set for the given 336 Namespace URI 337 """ 338 nsPrefix = ElementTree._namespace_map.get(nsURI) 339 if nsPrefix is not None: 340 return nsPrefix 341 342 for i in range(self.__class__.MAX_NS_TRIES): 343 nsPrefix = "ns%d" % i 344 if nsPrefix not in self.mappedPrefixes: 345 ElementTree._namespace_map[nsURI] = nsPrefix 346 self.mappedPrefixes[nsPrefix] = True 347 break 348 349 if nsURI not in ElementTree._namespace_map: 350 raise KeyError('prettyPrint: error adding namespace ' 351 '"%s" to ElementTree._namespace_map' % 352 nsURI) 353 354 return nsPrefix
355
356 # ElementTree SAML wrapper classes 357 -class ConditionsElementTree(Conditions):
358 """ElementTree based XML representation of Conditions class 359 """ 360 361 @classmethod
362 - def toXML(cls, conditions):
363 """Make a tree of a XML elements based on the assertion conditions 364 365 @type conditions: saml.saml2.core.Conditions 366 @param conditions: Assertion conditions to be represented as an 367 ElementTree Element 368 @rtype: ElementTree.Element 369 @return: ElementTree Element 370 """ 371 if not isinstance(conditions, Conditions): 372 raise TypeError("Expecting %r type got: %r" % (Conditions, 373 conditions)) 374 375 notBeforeStr = SAMLDateTime.toString(conditions.notBefore) 376 notOnOrAfterStr = SAMLDateTime.toString(conditions.notOnOrAfter) 377 attrib = { 378 cls.NOT_BEFORE_ATTRIB_NAME: notBeforeStr, 379 cls.NOT_ON_OR_AFTER_ATTRIB_NAME: notOnOrAfterStr, 380 } 381 382 tag = str(QName.fromGeneric(cls.DEFAULT_ELEMENT_NAME)) 383 elem = makeEtreeElement(tag, cls.DEFAULT_ELEMENT_NAME.prefix, 384 cls.DEFAULT_ELEMENT_NAME.namespaceURI, 385 **attrib) 386 387 for condition in conditions.conditions: 388 raise NotImplementedError("Conditions list creation is not " 389 "implemented") 390 391 return elem
392 393 @classmethod
394 - def fromXML(cls, elem):
395 """Parse an ElementTree SAML Conditions element into an 396 Conditions object 397 398 @type elem: ElementTree.Element 399 @param elem: ElementTree element containing the conditions 400 @rtype: saml.saml2.core.Conditions 401 @return: Conditions object 402 """ 403 404 if not ElementTree.iselement(elem): 405 raise TypeError("Expecting %r input type for parsing; got %r" % 406 (ElementTree.Element, elem)) 407 408 localName = QName.getLocalPart(elem.tag) 409 if localName != cls.DEFAULT_ELEMENT_LOCAL_NAME: 410 raise XMLTypeParseError("No \"%s\" element found" % 411 cls.DEFAULT_ELEMENT_LOCAL_NAME) 412 413 414 if len(elem) > 0: 415 raise NotImplementedError("Conditions list parsing is not " 416 "implemented") 417 418 conditions = Conditions() 419 notBefore = elem.attrib.get(Conditions.NOT_BEFORE_ATTRIB_NAME) 420 if notBefore is not None: 421 conditions.notBefore = SAMLDateTime.fromString(notBefore) 422 423 notOnOrAfter = elem.attrib.get(Conditions.NOT_ON_OR_AFTER_ATTRIB_NAME) 424 if notBefore is not None: 425 conditions.notOnOrAfter = SAMLDateTime.fromString(notOnOrAfter) 426 427 return conditions
428
429 430 -class AssertionElementTree(Assertion):
431 """ElementTree based XML representation of Assertion class 432 """ 433 434 @classmethod
435 - def toXML(cls, assertion, **attributeValueElementTreeFactoryKw):
436 """Make a tree of a XML elements based on the assertion 437 438 @type assertion: saml.saml2.core.Assertion 439 @param assertion: Assertion to be represented as an ElementTree Element 440 @type attributeValueElementTreeFactoryKw: dict 441 @param attributeValueElementTreeFactoryKw: keywords for AttributeValue 442 factory 443 @rtype: ElementTree.Element 444 @return: ElementTree Element 445 """ 446 447 if not isinstance(assertion, Assertion): 448 raise TypeError("Expecting %r type got: %r"%(Assertion, assertion)) 449 450 issueInstant = SAMLDateTime.toString(assertion.issueInstant) 451 attrib = { 452 cls.ID_ATTRIB_NAME: assertion.id, 453 cls.ISSUE_INSTANT_ATTRIB_NAME: issueInstant, 454 455 # Nb. Version is a SAMLVersion instance and requires explicit cast 456 cls.VERSION_ATTRIB_NAME: str(assertion.version) 457 } 458 tag = str(QName.fromGeneric(cls.DEFAULT_ELEMENT_NAME)) 459 elem = makeEtreeElement(tag, cls.DEFAULT_ELEMENT_NAME.prefix, 460 cls.DEFAULT_ELEMENT_NAME.namespaceURI, 461 **attrib) 462 463 if assertion.issuer is not None: 464 issuerElem = IssuerElementTree.toXML(assertion.issuer) 465 elem.append(issuerElem) 466 467 if assertion.subject is not None: 468 subjectElem = SubjectElementTree.toXML(assertion.subject) 469 elem.append(subjectElem) 470 471 if assertion.advice: 472 raise NotImplementedError("Assertion Advice creation is not " 473 "implemented") 474 475 if assertion.conditions is not None: 476 conditionsElem = ConditionsElementTree.toXML(assertion.conditions) 477 elem.append(conditionsElem) 478 479 for statement in assertion.statements: 480 qname = statement.qname 481 etreeImpl = _getElementTreeImplementationForQName(qname) 482 if etreeImpl is None: 483 raise NotImplementedError("No ElementTree implementation for " 484 "QName {%s}%s" % 485 (qname.namespaceURI, qname.localPart)) 486 statementElem = etreeImpl.toXML(statement) 487 elem.append(statementElem) 488 489 for authnStatement in assertion.authnStatements: 490 raise NotImplementedError("Assertion Authentication Statement " 491 "creation is not implemented") 492 493 for authzDecisionStatement in assertion.authzDecisionStatements: 494 authzDecisionStatementElem = \ 495 AuthzDecisionStatementElementTree.toXML(authzDecisionStatement) 496 elem.append(authzDecisionStatementElem) 497 498 for attributeStatement in assertion.attributeStatements: 499 attributeStatementElem = AttributeStatementElementTree.toXML( 500 attributeStatement, 501 **attributeValueElementTreeFactoryKw) 502 elem.append(attributeStatementElem) 503 504 return elem
505 506 @classmethod
507 - def fromXML(cls, elem, **attributeValueElementTreeFactoryKw):
508 """Parse an ElementTree representation of an Assertion into an 509 Assertion object 510 511 @type elem: ElementTree.Element 512 @param elem: ElementTree element containing the assertion 513 @type attributeValueElementTreeFactoryKw: dict 514 @param attributeValueElementTreeFactoryKw: keywords for AttributeValue 515 @rtype: saml.saml2.core.Assertion 516 @return: Assertion object 517 """ 518 if not ElementTree.iselement(elem): 519 raise TypeError("Expecting %r input type for parsing; got %r" % 520 (ElementTree.Element, elem)) 521 522 localName = QName.getLocalPart(elem.tag) 523 if localName != cls.DEFAULT_ELEMENT_LOCAL_NAME: 524 raise XMLTypeParseError("No \"%s\" element found" % 525 cls.DEFAULT_ELEMENT_LOCAL_NAME) 526 527 528 # Unpack attributes from top-level element 529 attributeValues = [] 530 for attributeName in (cls.VERSION_ATTRIB_NAME, 531 cls.ISSUE_INSTANT_ATTRIB_NAME, 532 cls.ID_ATTRIB_NAME): 533 attributeValue = elem.attrib.get(attributeName) 534 if attributeValue is None: 535 raise XMLTypeParseError('No "%s" attribute found in "%s" ' 536 'element' % 537 (attributeName, 538 cls.DEFAULT_ELEMENT_LOCAL_NAME)) 539 540 attributeValues.append(attributeValue) 541 542 assertion = cls() 543 assertion.version = SAMLVersion(attributeValues[0]) 544 if assertion.version != SAMLVersion.VERSION_20: 545 raise NotImplementedError("Parsing for %r is implemented for " 546 "SAML version %s only; version %s is " 547 "not supported" % 548 (cls, 549 SAMLVersion(SAMLVersion.VERSION_20), 550 SAMLVersion(assertion.version))) 551 552 assertion.issueInstant = SAMLDateTime.fromString(attributeValues[1]) 553 assertion.id = attributeValues[2] 554 555 for childElem in elem: 556 localName = QName.getLocalPart(childElem.tag) 557 558 statementElementTree = _getElementTreeImplementationForQName(QName(childElem.tag)) 559 560 if localName == Issuer.DEFAULT_ELEMENT_LOCAL_NAME: 561 # Parse Issuer 562 assertion.issuer = IssuerElementTree.fromXML(childElem) 563 564 elif localName == Subject.DEFAULT_ELEMENT_LOCAL_NAME: 565 # Parse subject 566 assertion.subject = SubjectElementTree.fromXML(childElem) 567 568 elif localName == Advice.DEFAULT_ELEMENT_LOCAL_NAME: 569 raise NotImplementedError("Assertion Advice parsing is not " 570 "implemented") 571 572 elif localName == Conditions.DEFAULT_ELEMENT_LOCAL_NAME: 573 assertion.conditions = ConditionsElementTree.fromXML(childElem) 574 575 elif statementElementTree is not None: 576 statement = statementElementTree.fromXML(childElem) 577 assertion.statements.append(statement) 578 579 elif localName == AuthnStatement.DEFAULT_ELEMENT_LOCAL_NAME: 580 raise NotImplementedError("Assertion Authentication Statement " 581 "parsing is not implemented") 582 583 elif localName == AuthzDecisionStatement.DEFAULT_ELEMENT_LOCAL_NAME: 584 authzDecisionStatement = \ 585 AuthzDecisionStatementElementTree.fromXML(childElem) 586 assertion.authzDecisionStatements.append(authzDecisionStatement) 587 588 elif localName == AttributeStatement.DEFAULT_ELEMENT_LOCAL_NAME: 589 attributeStatement = AttributeStatementElementTree.fromXML( 590 childElem, 591 **attributeValueElementTreeFactoryKw) 592 assertion.attributeStatements.append(attributeStatement) 593 else: 594 raise XMLTypeParseError('Assertion child element name "%s" ' 595 'not recognised' % localName) 596 597 return assertion
598
599 600 -class AttributeStatementElementTree(AttributeStatement):
601 """ElementTree XML representation of AttributeStatement""" 602 603 @classmethod
604 - def toXML(cls, attributeStatement, **attributeValueElementTreeFactoryKw):
605 """Make a tree of a XML elements based on the attribute statement 606 607 @type attributeStatement: saml.saml2.core.AttributeStatement 608 @param attributeStatement: Attribute Statement to be represented as an 609 ElementTree Element 610 @type attributeValueElementTreeFactoryKw: dict 611 @param attributeValueElementTreeFactoryKw: keywords for AttributeValue 612 factory 613 @rtype: ElementTree.Element 614 @return: ElementTree Element 615 """ 616 if not isinstance(attributeStatement, AttributeStatement): 617 raise TypeError("Expecting %r type got: %r" % (AttributeStatement, 618 attributeStatement)) 619 620 tag = str(QName.fromGeneric(cls.DEFAULT_ELEMENT_NAME)) 621 elem = makeEtreeElement(tag, cls.DEFAULT_ELEMENT_NAME.prefix, 622 cls.DEFAULT_ELEMENT_NAME.namespaceURI) 623 624 for attribute in attributeStatement.attributes: 625 # Factory enables support for multiple attribute types 626 attributeElem = AttributeElementTree.toXML(attribute, 627 **attributeValueElementTreeFactoryKw) 628 elem.append(attributeElem) 629 630 return elem
631 632 @classmethod
633 - def fromXML(cls, elem, **attributeValueElementTreeFactoryKw):
634 """Parse an ElementTree SAML AttributeStatement element into an 635 AttributeStatement object 636 637 @type elem: ElementTree.Element 638 @param elem: ElementTree element containing the AttributeStatement 639 @type attributeValueElementTreeFactoryKw: dict 640 @param attributeValueElementTreeFactoryKw: keywords for AttributeValue 641 factory 642 @rtype: saml.saml2.core.AttributeStatement 643 @return: Attribute Statement 644 """ 645 646 if not ElementTree.iselement(elem): 647 raise TypeError("Expecting %r input type for parsing; got %r" % 648 (ElementTree.Element, elem)) 649 650 localName = QName.getLocalPart(elem.tag) 651 if localName != cls.DEFAULT_ELEMENT_LOCAL_NAME: 652 raise XMLTypeParseError("No \"%s\" element found" % 653 cls.DEFAULT_ELEMENT_LOCAL_NAME) 654 655 656 attributeStatement = AttributeStatement() 657 658 for childElem in elem: 659 # Factory enables support for multiple attribute types 660 attribute = AttributeElementTree.fromXML(childElem, 661 **attributeValueElementTreeFactoryKw) 662 attributeStatement.attributes.append(attribute) 663 664 return attributeStatement
665
666 667 -class AuthzDecisionStatementElementTree(AuthzDecisionStatement):
668 """ElementTree XML representation of AuthzDecisionStatement""" 669 670 @classmethod
671 - def toXML(cls, authzDecisionStatement):
672 """Make a tree of a XML elements based on the authzDecision statement 673 674 @type authzDecisionStatement: saml.saml2.core.AuthzDecisionStatement 675 @param authzDecisionStatement: AuthzDecision Statement to be represented 676 as an ElementTree Element 677 @rtype: ElementTree.Element 678 @return: ElementTree Element 679 """ 680 if not isinstance(authzDecisionStatement, AuthzDecisionStatement): 681 raise TypeError("Expecting %r type got: %r" % 682 (AuthzDecisionStatement, authzDecisionStatement)) 683 684 if not authzDecisionStatement.resource: 685 raise AttributeError("Resource for AuthzDecisionStatement is not " 686 "set") 687 688 attrib = { 689 cls.DECISION_ATTRIB_NAME: str(authzDecisionStatement.decision), 690 cls.RESOURCE_ATTRIB_NAME: authzDecisionStatement.resource 691 } 692 693 tag = str(QName.fromGeneric(cls.DEFAULT_ELEMENT_NAME)) 694 elem = makeEtreeElement(tag, cls.DEFAULT_ELEMENT_NAME.prefix, 695 cls.DEFAULT_ELEMENT_NAME.namespaceURI, 696 **attrib) 697 698 for action in authzDecisionStatement.actions: 699 # Factory enables support for multiple authzDecision types 700 actionElem = ActionElementTree.toXML(action) 701 elem.append(actionElem) 702 703 if (authzDecisionStatement.evidence and 704 len(authzDecisionStatement.evidence.values) > 0): 705 raise NotImplementedError("authzDecisionStatementElementTree does " 706 "not currently support the Evidence type") 707 708 return elem
709 710 @classmethod
711 - def fromXML(cls, elem):
712 """Parse an ElementTree SAML AuthzDecisionStatement element into an 713 AuthzDecisionStatement object 714 715 @type elem: ElementTree.Element 716 @param elem: ElementTree element containing the AuthzDecisionStatement 717 @rtype: saml.saml2.core.AuthzDecisionStatement 718 @return: AuthzDecision Statement""" 719 720 if not ElementTree.iselement(elem): 721 raise TypeError("Expecting %r input type for parsing; got %r" % 722 (ElementTree.Element, elem)) 723 724 localName = QName.getLocalPart(elem.tag) 725 if localName != cls.DEFAULT_ELEMENT_LOCAL_NAME: 726 raise XMLTypeParseError("No \"%s\" element found" % 727 cls.DEFAULT_ELEMENT_LOCAL_NAME) 728 729 # Unpack attributes from top-level element 730 attributeValues = [] 731 for attributeName in (cls.DECISION_ATTRIB_NAME, 732 cls.RESOURCE_ATTRIB_NAME): 733 attributeValue = elem.attrib.get(attributeName) 734 if attributeValue is None: 735 raise XMLTypeParseError('No "%s" attribute found in "%s" ' 736 'element' % 737 (attributeName, 738 cls.DEFAULT_ELEMENT_LOCAL_NAME)) 739 740 attributeValues.append(attributeValue) 741 742 authzDecisionStatement = AuthzDecisionStatement() 743 authzDecisionStatement.decision = DecisionType(attributeValues[0]) 744 authzDecisionStatement.resource = attributeValues[1] 745 746 for childElem in elem: 747 localName = QName.getLocalPart(childElem.tag) 748 749 if localName == Action.DEFAULT_ELEMENT_LOCAL_NAME: 750 action = ActionElementTree.fromXML(childElem) 751 authzDecisionStatement.actions.append(action) 752 753 elif localName == Evidence.DEFAULT_ELEMENT_LOCAL_NAME: 754 raise NotImplementedError("XML parse of %s element is not " 755 "implemented" % 756 Evidence.DEFAULT_ELEMENT_LOCAL_NAME) 757 else: 758 raise XMLTypeParseError("AuthzDecisionStatement child element " 759 "name %r not recognised" % localName) 760 761 return authzDecisionStatement
762
763 764 -class AttributeElementTree(Attribute):
765 """ElementTree XML representation of SAML Attribute object. Extend 766 to make Attribute types""" 767 768 @classmethod
769 - def toXML(cls, attribute, **attributeValueElementTreeFactoryKw):
770 """Make a tree of a XML elements based on the Attribute 771 772 @type attribute: saml.saml2.core.Attribute 773 @param attribute: Attribute to be represented as an ElementTree Element 774 @type attributeValueElementTreeFactoryKw: dict 775 @param attributeValueElementTreeFactoryKw: keywords for AttributeValue 776 factory 777 @rtype: ElementTree.Element 778 @return: ElementTree Element 779 """ 780 if not isinstance(attribute, Attribute): 781 raise TypeError("Expecting %r type got: %r"%(Attribute, attribute)) 782 783 tag = str(QName.fromGeneric(cls.DEFAULT_ELEMENT_NAME)) 784 elem = makeEtreeElement(tag, cls.DEFAULT_ELEMENT_NAME.prefix, 785 cls.DEFAULT_ELEMENT_NAME.namespaceURI) 786 787 if attribute.friendlyName: 788 elem.set(cls.FRIENDLY_NAME_ATTRIB_NAME, attribute.friendlyName) 789 790 if attribute.name: 791 elem.set(cls.NAME_ATTRIB_NAME, attribute.name) 792 793 if attribute.nameFormat: 794 elem.set(cls.NAME_FORMAT_ATTRIB_NAME, attribute.nameFormat) 795 796 for attributeValue in attribute.attributeValues: 797 factory = AttributeValueElementTreeFactory( 798 **attributeValueElementTreeFactoryKw) 799 800 attributeValueElementTree = factory(attributeValue) 801 802 attributeValueElem = attributeValueElementTree.toXML(attributeValue) 803 elem.append(attributeValueElem) 804 805 return elem
806 807 @classmethod
808 - def fromXML(cls, elem, **attributeValueElementTreeFactoryKw):
809 """Parse ElementTree element into a SAML Attribute object 810 811 @type elem: ElementTree.Element 812 @param elem: Attribute as ElementTree XML element 813 @type attributeValueElementTreeFactoryKw: dict 814 @param attributeValueElementTreeFactoryKw: keywords for AttributeValue 815 factory 816 @rtype: saml.saml2.core.Attribute 817 @return: SAML Attribute 818 """ 819 if not ElementTree.iselement(elem): 820 raise TypeError("Expecting %r input type for parsing; got %r" % 821 (ElementTree.Element, elem)) 822 823 if QName.getLocalPart(elem.tag) != cls.DEFAULT_ELEMENT_LOCAL_NAME: 824 raise XMLTypeParseError("No \"%s\" element found" % 825 cls.DEFAULT_ELEMENT_LOCAL_NAME) 826 827 attribute = Attribute() 828 829 # Name is mandatory in the schema 830 name = elem.attrib.get(cls.NAME_ATTRIB_NAME) 831 if name is None: 832 raise XMLTypeParseError('No "%s" attribute found in the "%s" ' 833 'element' % 834 (cls.NAME_ATTRIB_NAME, 835 cls.DEFAULT_ELEMENT_LOCAL_NAME)) 836 attribute.name = name 837 838 friendlyName = elem.attrib.get(cls.FRIENDLY_NAME_ATTRIB_NAME) 839 if friendlyName is not None: 840 attribute.friendlyName = friendlyName 841 842 nameFormat = elem.attrib.get(cls.NAME_FORMAT_ATTRIB_NAME) 843 if nameFormat is not None: 844 attribute.nameFormat = nameFormat 845 846 # Factory to handle the different Attribute Value types 847 factory = AttributeValueElementTreeFactory( 848 **attributeValueElementTreeFactoryKw) 849 850 for childElem in elem: 851 localName = QName.getLocalPart(childElem.tag) 852 if localName != AttributeValue.DEFAULT_ELEMENT_LOCAL_NAME: 853 raise XMLTypeParseError('Expecting "%s" element; found "%s"'% 854 (AttributeValue.DEFAULT_ELEMENT_LOCAL_NAME, 855 localName)) 856 857 attributeValueElementTreeClass = factory(childElem) 858 attributeValue = attributeValueElementTreeClass.fromXML(childElem) 859 attribute.attributeValues.append(attributeValue) 860 861 return attribute
862
863 864 -class AttributeValueElementTreeBase(AttributeValue):
865 """Base class ElementTree XML representation of SAML Attribute Value""" 866 867 @classmethod
868 - def toXML(cls, attributeValue):
869 """Make a tree of a XML elements based on the Attribute value 870 871 @type attributeValue: saml.saml2.core.AttributeValue 872 @param attributeValue: Assertion to be represented as an ElementTree 873 Element 874 @rtype: ElementTree.Element 875 @return: ElementTree Element 876 """ 877 if not isinstance(attributeValue, AttributeValue): 878 raise TypeError("Expecting %r type got: %r" % (AttributeValue, 879 attributeValue)) 880 881 tag = str(QName.fromGeneric(cls.DEFAULT_ELEMENT_NAME)) 882 elem = makeEtreeElement(tag, cls.DEFAULT_ELEMENT_NAME.prefix, 883 cls.DEFAULT_ELEMENT_NAME.namespaceURI) 884 885 return elem
886
887 888 -class XSStringAttributeValueElementTree(AttributeValueElementTreeBase, 889 XSStringAttributeValue):
890 """ElementTree XML representation of SAML String type Attribute Value""" 891 892 @classmethod
893 - def toXML(cls, attributeValue):
894 """Create an XML representation of the input SAML Attribute Value 895 896 @type attributeValue: saml.saml2.core.XSStringAttributeValue 897 @param attributeValue: xs:string to be represented as an ElementTree 898 Element 899 @rtype: ElementTree.Element 900 @return: ElementTree Element 901 """ 902 elem = AttributeValueElementTreeBase.toXML(attributeValue) 903 904 if not isinstance(attributeValue, XSStringAttributeValue): 905 raise TypeError("Expecting %r type got: %r" % 906 (XSStringAttributeValue, attributeValue)) 907 908 if Config.use_lxml: 909 elem.set(("{%s}%s" % (SAMLConstants.XSI_NS, 'type')), 910 "%s:%s" % (SAMLConstants.XSD_PREFIX, 911 cls.TYPE_LOCAL_NAME)) 912 else: 913 # Have to explicitly add namespace declaration here rather use 914 # ElementTree._namespace_map because the prefixes are used for 915 # attributes not element names 916 elem.set("%s:%s" % (SAMLConstants.XMLNS_PREFIX, 917 SAMLConstants.XSD_PREFIX), 918 SAMLConstants.XSD_NS) 919 920 elem.set("%s:%s" % (SAMLConstants.XMLNS_PREFIX, 921 SAMLConstants.XSI_PREFIX), 922 SAMLConstants.XSI_NS) 923 924 elem.set("%s:%s" % (SAMLConstants.XSI_PREFIX, 'type'), 925 "%s:%s" % (SAMLConstants.XSD_PREFIX, 926 cls.TYPE_LOCAL_NAME)) 927 928 elem.text = attributeValue.value 929 930 return elem
931 932 @classmethod
933 - def fromXML(cls, elem):
934 """Parse ElementTree xs:string element into a SAML 935 XSStringAttributeValue object 936 937 @type elem: ElementTree.Element 938 @param elem: Attribute value as ElementTree XML element 939 @rtype: saml.saml2.core.AttributeValue 940 @return: SAML Attribute value 941 """ 942 if not ElementTree.iselement(elem): 943 raise TypeError("Expecting %r input type for parsing; got %r" % 944 (ElementTree.Element, elem)) 945 946 localName = QName.getLocalPart(elem.tag) 947 if localName != cls.DEFAULT_ELEMENT_LOCAL_NAME: 948 raise XMLTypeParseError("No \"%s\" element found" % 949 cls.DEFAULT_ELEMENT_LOCAL_NAME) 950 951 # Parse the attribute type checking that it is set to the expected 952 # string type 953 typeQName = QName(SAMLConstants.XSI_NS, tag='type') 954 955 typeValue = elem.attrib.get(str(typeQName), '') 956 typeValueLocalName = typeValue.split(':')[-1] 957 if typeValueLocalName != cls.TYPE_LOCAL_NAME: 958 raise XMLTypeParseError('Expecting "%s" type; got "%s"' % 959 (cls.TYPE_LOCAL_NAME, 960 typeValueLocalName)) 961 962 # Update namespace map as an XSI type has been referenced. This will 963 # ensure the correct prefix is applied if it is re-serialised. 964 if not Config.use_lxml: 965 ElementTree._namespace_map[SAMLConstants.XSI_NS 966 ] = SAMLConstants.XSI_PREFIX 967 968 attributeValue = XSStringAttributeValue() 969 if elem.text is not None: 970 attributeValue.value = elem.text.strip() 971 972 return attributeValue
973
974 975 -class AttributeValueElementTreeFactory(object):
976 """Class factory for AttributeValue ElementTree classes. These classes are 977 used to represent SAML Attribute value types 978 979 @type toXMLTypeMap: dict 980 @cvar toXMLTypeMap: mapping between SAML AttributeValue class and its 981 ElementTree handler class 982 @type toSAMLTypeMap: dict 983 @cvar toSAMLTypeMap: mapping between SAML AttributeValue string identifier 984 and its ElementTree handler class 985 """ 986 toXMLTypeMap = { 987 XSStringAttributeValue: XSStringAttributeValueElementTree 988 } 989
990 - def xsstringMatch(elem):
991 """Match function for xs:string type attribute. 992 993 @type elem: ElementTree.Element 994 @param elem: Attribute Value element to be checked 995 @rtype: XSStringAttributeValueElementTree/None 996 @return: Parsing class if this element is an xs:string Attribute Value, 997 None otherwise. 998 """ 999 # Iterate through the attributes searching for a type attribute set to 1000 # xs:string 1001 for attribName, attribVal in elem.attrib.items(): 1002 qname = QName(attribName) 1003 if qname.localPart == "type": 1004 typeLocalName = attribVal.split(':')[-1] 1005 1006 if typeLocalName == XSStringAttributeValue.TYPE_LOCAL_NAME: 1007 return XSStringAttributeValueElementTree 1008 else: 1009 return None 1010 1011 # No type attribute was found for this Attribute element 1012 return None
1013 1014 toSAMLTypeMap = [xsstringMatch] 1015 xsstringMatch = staticmethod(toSAMLTypeMap[0]) 1016
1017 - def __init__(self, customToXMLTypeMap=None, customToSAMLTypeMap=None):
1018 """Set-up a SAML class to ElementTree mapping 1019 1020 @type customToXMLTypeMap: dict 1021 @param customToXMLTypeMap: mapping for custom SAML AttributeValue 1022 classes to their respective ElementTree based representations. This 1023 appends to self.__toXMLTypeMap 1024 @type customToSAMLTypeMap: dict 1025 @param customToSAMLTypeMap: string ID based mapping for custom SAML 1026 AttributeValue classes to their respective ElementTree based 1027 representations. As with customToXMLTypeMap, this appends to 1028 to the respective self.__toSAMLTypeMap 1029 """ 1030 if customToXMLTypeMap is None: 1031 customToXMLTypeMap = {} 1032 1033 if customToSAMLTypeMap is None: 1034 customToSAMLTypeMap = [] 1035 1036 self.__toXMLTypeMap = AttributeValueElementTreeFactory.toXMLTypeMap 1037 if not isinstance(customToXMLTypeMap, dict): 1038 raise TypeError('Expecting dict type for "customToXMLTypeMap"') 1039 1040 for samlClass, etreeClass in customToXMLTypeMap.items(): 1041 if not issubclass(samlClass, AttributeValue): 1042 raise TypeError("Input custom class must be derived from %r, " 1043 "got %r instead" % (Attribute, samlClass)) 1044 1045 self.__toXMLTypeMap[samlClass] = etreeClass 1046 1047 if not isinstance(customToSAMLTypeMap, (list, tuple)): 1048 raise TypeError('Expecting list or tuple type for ' 1049 '"customToSAMLTypeMap"') 1050 1051 self.__toSAMLTypeMap = AttributeValueElementTreeFactory.toSAMLTypeMap[:] 1052 for func in customToSAMLTypeMap: 1053 if not callable(func): 1054 raise TypeError('"customToSAMLTypeMap" items must be callable') 1055 1056 self.__toSAMLTypeMap += customToSAMLTypeMap
1057
1058 - def __call__(self, input):
1059 """Create an ElementTree object based on the Attribute class type 1060 passed in 1061 1062 @type input: saml.saml2.core.AttributeValue or basestring 1063 @param input: pass an AttributeValue derived type or a string. If 1064 an AttributeValue type, then self.__toXMLTypeMap is checked for a 1065 matching AttributeValue class entry, if a string is passed, 1066 self.__toSAMLTypeMap is checked for a matching string ID. In both 1067 cases, if a match is found an ElementTree class is returned which can 1068 render or parse the relevant AttributeValue class 1069 """ 1070 if isinstance(input, AttributeValue): 1071 XMLTypeClass = self.__toXMLTypeMap.get(input.__class__) 1072 if XMLTypeClass is None: 1073 raise UnknownAttrProfile("no matching XMLType class " 1074 "representation for class %r" % 1075 input.__class__) 1076 1077 elif ElementTree.iselement(input): 1078 XMLTypeClasses = [] 1079 for matchFunc in self.__toSAMLTypeMap: 1080 cls = matchFunc(input) 1081 if cls is None: 1082 continue 1083 elif issubclass(cls, AttributeValue): 1084 XMLTypeClasses.append(cls) 1085 else: 1086 raise TypeError("Expecting AttributeValue derived type " 1087 "for XML class; got %r" % cls) 1088 1089 nXMLTypeClasses = len(XMLTypeClasses) 1090 if nXMLTypeClasses == 0: 1091 raise UnknownAttrProfile("no matching XMLType class " 1092 "representation for SAML " 1093 "AttributeValue type %r" % input) 1094 elif nXMLTypeClasses > 1: 1095 raise TypeError("Multiple XMLType classes %r matched for " 1096 "for SAML AttributeValue type %r" % 1097 (XMLTypeClasses, input)) 1098 1099 XMLTypeClass = XMLTypeClasses[0] 1100 else: 1101 raise TypeError("Expecting %r class got %r" % (AttributeValue, 1102 type(input))) 1103 return XMLTypeClass
1104
1105 1106 -class IssuerElementTree(Issuer):
1107 """Represent a SAML Issuer element in XML using ElementTree""" 1108 1109 @classmethod
1110 - def toXML(cls, issuer):
1111 """Create an XML representation of the input SAML issuer object 1112 1113 @type issuer: saml.saml2.core.Issuer 1114 @param issuer: Assertion object 1115 @rtype: ElementTree.Element 1116 @return: ElementTree element containing the assertion 1117 """ 1118 if not isinstance(issuer, Issuer): 1119 raise TypeError("Expecting %r class got %r" % (Issuer, 1120 type(issuer))) 1121 1122 # Issuer format may be omitted from a response: saml-profiles-2.0-os, 1123 # Section 4.1.4.2 1124 attrib = {} 1125 if issuer.format is not None: 1126 attrib[cls.FORMAT_ATTRIB_NAME] = issuer.format 1127 1128 tag = str(QName.fromGeneric(cls.DEFAULT_ELEMENT_NAME)) 1129 elem = makeEtreeElement(tag, issuer.qname.prefix, 1130 issuer.qname.namespaceURI, 1131 **attrib) 1132 1133 elem.text = issuer.value 1134 1135 return elem
1136 1137 @classmethod
1138 - def fromXML(cls, elem):
1139 """Parse ElementTree element into a SAML Issuer instance 1140 1141 @type elem: ElementTree.Element 1142 @param elem: ElementTree element containing the assertion 1143 @rtype: saml.saml2.core.Issuer 1144 @return: Assertion object""" 1145 if not ElementTree.iselement(elem): 1146 raise TypeError("Expecting %r input type for parsing; got %r" % 1147 (ElementTree.Element, elem)) 1148 1149 if QName.getLocalPart(elem.tag) != cls.DEFAULT_ELEMENT_LOCAL_NAME: 1150 raise XMLTypeParseError('No "%s" element found' % 1151 cls.DEFAULT_ELEMENT_LOCAL_NAME) 1152 1153 issuerFormat = elem.attrib.get(cls.FORMAT_ATTRIB_NAME) 1154 issuer = Issuer() 1155 1156 # Issuer format may be omitted from a response: saml-profiles-2.0-os, 1157 # Section 4.1.4.2 1158 if issuerFormat is not None: 1159 issuer.format = issuerFormat 1160 1161 if elem.text is None: 1162 raise XMLTypeParseError('No SAML issuer value set') 1163 1164 issuer.value = elem.text.strip() 1165 1166 return issuer
1167
1168 1169 -class NameIdElementTree(NameID):
1170 """Represent a SAML Name Identifier in XML using ElementTree""" 1171 1172 @classmethod
1173 - def toXML(cls, nameID):
1174 """Create an XML representation of the input SAML Name Identifier 1175 object 1176 @type nameID: saml.saml2.core.NameID 1177 @param nameID: SAML name ID 1178 @rtype: ElementTree.Element 1179 @return: Name ID as ElementTree XML element 1180 """ 1181 1182 if not isinstance(nameID, NameID): 1183 raise TypeError("Expecting %r class got %r" % (NameID, 1184 type(nameID))) 1185 attrib = { 1186 cls.FORMAT_ATTRIB_NAME: nameID.format 1187 } 1188 tag = str(QName.fromGeneric(cls.DEFAULT_ELEMENT_NAME)) 1189 elem = makeEtreeElement(tag, nameID.qname.prefix, 1190 nameID.qname.namespaceURI, 1191 **attrib) 1192 1193 elem.text = nameID.value 1194 1195 return elem
1196 1197 @classmethod
1198 - def fromXML(cls, elem):
1199 """Parse ElementTree element into a SAML NameID object 1200 1201 @type elem: ElementTree.Element 1202 @param elem: Name ID as ElementTree XML element 1203 @rtype: saml.saml2.core.NameID 1204 @return: SAML Name ID 1205 """ 1206 if not ElementTree.iselement(elem): 1207 raise TypeError("Expecting %r input type for parsing; got %r" % 1208 (ElementTree.Element, elem)) 1209 1210 if QName.getLocalPart(elem.tag) != cls.DEFAULT_ELEMENT_LOCAL_NAME: 1211 raise XMLTypeParseError("No \"%s\" element found" % 1212 cls.DEFAULT_ELEMENT_LOCAL_NAME) 1213 1214 format = elem.attrib.get(cls.FORMAT_ATTRIB_NAME) 1215 if format is None: 1216 raise XMLTypeParseError('No "%s" attribute found in "%s" ' 1217 'element' % 1218 (cls.FORMAT_ATTRIB_NAME, 1219 cls.DEFAULT_ELEMENT_LOCAL_NAME)) 1220 nameID = NameID() 1221 nameID.format = format 1222 if elem.text is None: 1223 nameID.value = '' 1224 else: 1225 nameID.value = elem.text.strip() 1226 1227 return nameID
1228
1229 1230 -class SubjectElementTree(Subject):
1231 """Represent a SAML Subject in XML using ElementTree""" 1232 1233 @classmethod
1234 - def toXML(cls, subject):
1235 """Create an XML representation of the input SAML subject object 1236 @type subject: saml.saml2.core.Subject 1237 @param subject: SAML subject 1238 @rtype: ElementTree.Element 1239 @return: subject as ElementTree XML element 1240 """ 1241 if not isinstance(subject, Subject): 1242 raise TypeError("Expecting %r class got %r" % (Subject, 1243 type(subject))) 1244 1245 tag = str(QName.fromGeneric(cls.DEFAULT_ELEMENT_NAME)) 1246 elem = makeEtreeElement(tag, cls.DEFAULT_ELEMENT_NAME.prefix, 1247 cls.DEFAULT_ELEMENT_NAME.namespaceURI) 1248 1249 nameIdElem = NameIdElementTree.toXML(subject.nameID) 1250 elem.append(nameIdElem) 1251 1252 return elem
1253 1254 @classmethod
1255 - def fromXML(cls, elem):
1256 """Parse ElementTree element into a SAML Subject object 1257 1258 @type elem: ElementTree.Element 1259 @param elem: subject as ElementTree XML element 1260 @rtype: saml.saml2.core.Subject 1261 @return: SAML subject 1262 """ 1263 if not ElementTree.iselement(elem): 1264 raise TypeError("Expecting %r input type for parsing; got %r" % 1265 (ElementTree.Element, elem)) 1266 1267 if QName.getLocalPart(elem.tag) != cls.DEFAULT_ELEMENT_LOCAL_NAME: 1268 raise XMLTypeParseError("No \"%s\" element found" % 1269 cls.DEFAULT_ELEMENT_LOCAL_NAME) 1270 1271 if len(elem) != 1: 1272 raise XMLTypeParseError("Expecting single Name ID child element " 1273 "for SAML Subject element") 1274 1275 subject = Subject() 1276 subject.nameID = NameIdElementTree.fromXML(elem[0]) 1277 1278 return subject
1279
1280 1281 -class StatusCodeElementTree(StatusCode):
1282 """Represent a SAML Status Code in XML using ElementTree""" 1283 1284 @classmethod
1285 - def toXML(cls, statusCode):
1286 """Create an XML representation of the input SAML Name Status Code 1287 1288 @type statusCode: saml.saml2.core.StatusCode 1289 @param statusCode: SAML Status Code 1290 @rtype: ElementTree.Element 1291 @return: Status Code as ElementTree XML element 1292 """ 1293 1294 if not isinstance(statusCode, StatusCode): 1295 raise TypeError("Expecting %r class got %r" % (StatusCode, 1296 type(statusCode))) 1297 1298 attrib = { 1299 cls.VALUE_ATTRIB_NAME: statusCode.value 1300 } 1301 tag = str(QName.fromGeneric(cls.DEFAULT_ELEMENT_NAME)) 1302 elem = makeEtreeElement(tag, statusCode.qname.prefix, 1303 statusCode.qname.namespaceURI, 1304 **attrib) 1305 1306 return elem
1307 1308 @classmethod
1309 - def fromXML(cls, elem):
1310 """Parse ElementTree element into a SAML StatusCode object 1311 1312 @type elem: ElementTree.Element 1313 @param elem: Status Code as ElementTree XML element 1314 @rtype: saml.saml2.core.StatusCode 1315 @return: SAML Status Code 1316 """ 1317 if not ElementTree.iselement(elem): 1318 raise TypeError("Expecting %r input type for parsing; got %r" % 1319 (ElementTree.Element, elem)) 1320 1321 if QName.getLocalPart(elem.tag) != cls.DEFAULT_ELEMENT_LOCAL_NAME: 1322 raise XMLTypeParseError('No "%s" element found' % 1323 cls.DEFAULT_ELEMENT_LOCAL_NAME) 1324 1325 statusCode = StatusCode() 1326 1327 value = elem.attrib.get(cls.VALUE_ATTRIB_NAME) 1328 if value is None: 1329 raise XMLTypeParseError('No "%s" attribute found in "%s" element' % 1330 (cls.VALUE_ATTRIB_NAME, 1331 cls.DEFAULT_ELEMENT_LOCAL_NAME)) 1332 statusCode.value = value 1333 1334 return statusCode
1335
1336 1337 -class StatusMessageElementTree(StatusMessage):
1338 """Represent a SAML Status Message in XML using ElementTree""" 1339 1340 @classmethod
1341 - def toXML(cls, statusMessage):
1342 """Create an XML representation of the input SAML Name Status Message 1343 1344 @type statusMessage: saml.saml2.core.StatusMessage 1345 @param statusMessage: SAML Status Message 1346 @rtype: ElementTree.Element 1347 @return: Status Code as ElementTree XML element 1348 """ 1349 1350 if not isinstance(statusMessage, StatusMessage): 1351 raise TypeError("Expecting %r class got %r" % (StatusMessage, 1352 type(statusMessage))) 1353 1354 tag = str(QName.fromGeneric(cls.DEFAULT_ELEMENT_NAME)) 1355 elem = makeEtreeElement(tag, statusMessage.qname.prefix, 1356 statusMessage.qname.namespaceURI) 1357 1358 elem.text = statusMessage.value 1359 1360 return elem
1361 1362 @classmethod
1363 - def fromXML(cls, elem):
1364 """Parse ElementTree element into a SAML StatusMessage object 1365 1366 @type elem: ElementTree.Element 1367 @param elem: Status Code as ElementTree XML element 1368 @rtype: saml.saml2.core.StatusMessage 1369 @return: SAML Status Message 1370 """ 1371 if not ElementTree.iselement(elem): 1372 raise TypeError("Expecting %r input type for parsing; got %r" % 1373 (ElementTree.Element, elem)) 1374 1375 if QName.getLocalPart(elem.tag) != cls.DEFAULT_ELEMENT_LOCAL_NAME: 1376 raise XMLTypeParseError('No "%s" element found' % 1377 cls.DEFAULT_ELEMENT_LOCAL_NAME) 1378 1379 statusMessage = StatusMessage() 1380 if elem.text is not None: 1381 statusMessage.value = elem.text.strip() 1382 1383 return statusMessage
1384
1385 1386 -class StatusElementTree(Status):
1387 """Represent a SAML Status in XML using ElementTree""" 1388 1389 @classmethod
1390 - def toXML(cls, status):
1391 """Create an XML representation of the input SAML subject object 1392 @type status: saml.saml2.core.Status 1393 @param status: SAML subject 1394 @rtype: ElementTree.Element 1395 @return: subject as ElementTree XML element 1396 """ 1397 if not isinstance(status, Status): 1398 raise TypeError("Expecting %r class got %r" % (status, 1399 type(Status))) 1400 1401 tag = str(QName.fromGeneric(Status.DEFAULT_ELEMENT_NAME)) 1402 elem = makeEtreeElement(tag, cls.DEFAULT_ELEMENT_NAME.prefix, 1403 cls.DEFAULT_ELEMENT_NAME.namespaceURI) 1404 1405 statusCodeElem = StatusCodeElementTree.toXML(status.statusCode) 1406 elem.append(statusCodeElem) 1407 1408 # Status message is optional 1409 if status.statusMessage is not None and \ 1410 status.statusMessage.value is not None: 1411 statusMessageElem = StatusMessageElementTree.toXML( 1412 status.statusMessage) 1413 elem.append(statusMessageElem) 1414 1415 if status.statusDetail is not None: 1416 raise NotImplementedError("StatusDetail XML serialisation is not " 1417 "implemented") 1418 1419 return elem
1420 1421 @classmethod
1422 - def fromXML(cls, elem):
1423 """Parse ElementTree element into a SAML Status object 1424 1425 @type elem: ElementTree.Element 1426 @param elem: subject as ElementTree XML element 1427 @rtype: saml.saml2.core.Status 1428 @return: SAML subject 1429 """ 1430 if not ElementTree.iselement(elem): 1431 raise TypeError("Expecting %r input type for parsing; got %r" % 1432 (ElementTree.Element, elem)) 1433 1434 if QName.getLocalPart(elem.tag) != Status.DEFAULT_ELEMENT_LOCAL_NAME: 1435 raise XMLTypeParseError('No "%s" element found' % 1436 Status.DEFAULT_ELEMENT_LOCAL_NAME) 1437 1438 if len(elem) < 1: 1439 raise XMLTypeParseError("Expecting a StatusCode child element for " 1440 "SAML Status element") 1441 1442 status = Status() 1443 for childElem in elem: 1444 localName = QName.getLocalPart(childElem.tag) 1445 if localName == StatusCode.DEFAULT_ELEMENT_LOCAL_NAME: 1446 status.statusCode = StatusCodeElementTree.fromXML(childElem) 1447 1448 elif localName == StatusMessage.DEFAULT_ELEMENT_LOCAL_NAME: 1449 status.statusMessage = StatusMessageElementTree.fromXML( 1450 childElem) 1451 elif localName == StatusDetail.DEFAULT_ELEMENT_LOCAL_NAME: 1452 raise NotImplementedError("XML parse of %s element is not " 1453 "implemented" % 1454 StatusDetail.DEFAULT_ELEMENT_LOCAL_NAME) 1455 else: 1456 raise XMLTypeParseError("Status child element name %r not " 1457 "recognised" % localName) 1458 1459 return status
1460
1461 1462 -class AttributeQueryElementTree(AttributeQuery):
1463 """Represent a SAML Attribute Query in XML using ElementTree""" 1464 1465 @classmethod
1466 - def toXML(cls, attributeQuery, **attributeValueElementTreeFactoryKw):
1467 """Create an XML representation of the input SAML Attribute Query 1468 object 1469 1470 @type attributeQuery: saml.saml2.core.AttributeQuery 1471 @param attributeQuery: SAML Attribute Query 1472 @type attributeValueElementTreeFactoryKw: dict 1473 @param attributeValueElementTreeFactoryKw: keywords for AttributeValue 1474 factory 1475 @rtype: ElementTree.Element 1476 @return: Attribute Query as ElementTree XML element 1477 """ 1478 if not isinstance(attributeQuery, AttributeQuery): 1479 raise TypeError("Expecting %r class got %r" % (AttributeQuery, 1480 type(attributeQuery))) 1481 1482 1483 issueInstant = SAMLDateTime.toString(attributeQuery.issueInstant) 1484 attrib = { 1485 cls.ID_ATTRIB_NAME: attributeQuery.id, 1486 cls.ISSUE_INSTANT_ATTRIB_NAME: issueInstant, 1487 1488 # Nb. Version is a SAMLVersion instance and requires explicit cast 1489 cls.VERSION_ATTRIB_NAME: str(attributeQuery.version) 1490 } 1491 1492 tag = str(QName.fromGeneric(cls.DEFAULT_ELEMENT_NAME)) 1493 elem = makeEtreeElement(tag, cls.DEFAULT_ELEMENT_NAME.prefix, 1494 cls.DEFAULT_ELEMENT_NAME.namespaceURI, 1495 **attrib) 1496 1497 issuerElem = IssuerElementTree.toXML(attributeQuery.issuer) 1498 elem.append(issuerElem) 1499 1500 subjectElem = SubjectElementTree.toXML(attributeQuery.subject) 1501 elem.append(subjectElem) 1502 1503 for attribute in attributeQuery.attributes: 1504 # Factory enables support for multiple attribute types 1505 attributeElem = AttributeElementTree.toXML(attribute, 1506 **attributeValueElementTreeFactoryKw) 1507 elem.append(attributeElem) 1508 1509 return elem
1510 1511 @classmethod
1512 - def fromXML(cls, elem):
1513 """Parse ElementTree element into a SAML AttributeQuery object 1514 1515 @type elem: ElementTree.Element 1516 @param elem: XML element containing the AttributeQuery 1517 @rtype: saml.saml2.core.AttributeQuery 1518 @return: AttributeQuery object 1519 """ 1520 if not ElementTree.iselement(elem): 1521 raise TypeError("Expecting %r input type for parsing; got %r" % 1522 (ElementTree.Element, elem)) 1523 1524 if QName.getLocalPart(elem.tag) != cls.DEFAULT_ELEMENT_LOCAL_NAME: 1525 raise XMLTypeParseError("No \"%s\" element found" % 1526 cls.DEFAULT_ELEMENT_LOCAL_NAME) 1527 1528 # Unpack attributes from top-level element 1529 attributeValues = [] 1530 for attributeName in (cls.VERSION_ATTRIB_NAME, 1531 cls.ISSUE_INSTANT_ATTRIB_NAME, 1532 cls.ID_ATTRIB_NAME): 1533 attributeValue = elem.attrib.get(attributeName) 1534 if attributeValue is None: 1535 raise XMLTypeParseError('No "%s" attribute found in "%s" ' 1536 'element' % 1537 (attributeName, 1538 cls.DEFAULT_ELEMENT_LOCAL_NAME)) 1539 1540 attributeValues.append(attributeValue) 1541 1542 attributeQuery = AttributeQuery() 1543 attributeQuery.version = SAMLVersion(attributeValues[0]) 1544 if attributeQuery.version != SAMLVersion.VERSION_20: 1545 raise NotImplementedError("Parsing for %r is implemented for " 1546 "SAML version %s only; version %s is " 1547 "not supported" % 1548 (cls, 1549 SAMLVersion(SAMLVersion.VERSION_20), 1550 SAMLVersion(attributeQuery.version))) 1551 1552 attributeQuery.issueInstant = SAMLDateTime.fromString( 1553 attributeValues[1]) 1554 attributeQuery.id = attributeValues[2] 1555 1556 for childElem in elem: 1557 localName = QName.getLocalPart(childElem.tag) 1558 if localName == Issuer.DEFAULT_ELEMENT_LOCAL_NAME: 1559 # Parse Issuer 1560 attributeQuery.issuer = IssuerElementTree.fromXML(childElem) 1561 1562 elif localName == Subject.DEFAULT_ELEMENT_LOCAL_NAME: 1563 # Parse Subject 1564 attributeQuery.subject = SubjectElementTree.fromXML(childElem) 1565 1566 elif localName == Attribute.DEFAULT_ELEMENT_LOCAL_NAME: 1567 attribute = AttributeElementTree.fromXML(childElem) 1568 attributeQuery.attributes.append(attribute) 1569 else: 1570 raise XMLTypeParseError("Unrecognised AttributeQuery child " 1571 "element \"%s\"" % localName) 1572 1573 return attributeQuery
1574
1575 1576 -class ResponseElementTree(Response):
1577 """Represent a SAML Response in XML using ElementTree""" 1578 1579 @classmethod
1580 - def toXML(cls, response, **attributeValueElementTreeFactoryKw):
1581 """Create an XML representation of the input SAML Response 1582 object 1583 1584 @type response: saml.saml2.core.Response 1585 @param response: SAML Response 1586 @type attributeValueElementTreeFactoryKw: dict 1587 @param attributeValueElementTreeFactoryKw: keywords for AttributeValue 1588 factory 1589 @rtype: ElementTree.Element 1590 @return: Response as ElementTree XML element 1591 """ 1592 if not isinstance(response, Response): 1593 raise TypeError("Expecting %r class, got %r" % (Response, 1594 type(response))) 1595 1596 if response.id is None: 1597 raise TypeError("SAML Response id is not set") 1598 1599 if response.issueInstant is None: 1600 raise TypeError("SAML Response issueInstant is not set") 1601 1602 # TODO: Does inResponseTo have to be set? This implementation 1603 # currently enforces this ... 1604 if response.inResponseTo is None: 1605 raise TypeError("SAML Response inResponseTo identifier is not set") 1606 1607 issueInstant = SAMLDateTime.toString(response.issueInstant) 1608 attrib = { 1609 cls.ID_ATTRIB_NAME: response.id, 1610 cls.ISSUE_INSTANT_ATTRIB_NAME: issueInstant, 1611 cls.IN_RESPONSE_TO_ATTRIB_NAME: response.inResponseTo, 1612 1613 # Nb. Version is a SAMLVersion instance and requires explicit cast 1614 cls.VERSION_ATTRIB_NAME: str(response.version) 1615 } 1616 1617 tag = str(QName.fromGeneric(cls.DEFAULT_ELEMENT_NAME)) 1618 elem = makeEtreeElement(tag, cls.DEFAULT_ELEMENT_NAME.prefix, 1619 cls.DEFAULT_ELEMENT_NAME.namespaceURI, 1620 **attrib) 1621 1622 # Issuer may be omitted: saml-profiles-2.0-os Section 4.1.4.2 1623 if response.issuer is not None: 1624 issuerElem = IssuerElementTree.toXML(response.issuer) 1625 elem.append(issuerElem) 1626 1627 statusElem = StatusElementTree.toXML(response.status) 1628 elem.append(statusElem) 1629 1630 for assertion in response.assertions: 1631 # Factory enables support for multiple attribute types 1632 assertionElem = AssertionElementTree.toXML(assertion, 1633 **attributeValueElementTreeFactoryKw) 1634 elem.append(assertionElem) 1635 1636 return elem
1637 1638 @classmethod
1639 - def fromXML(cls, elem, **attributeValueElementTreeFactoryKw):
1640 """Parse ElementTree element into a SAML Response object 1641 1642 @type elem: ElementTree.Element 1643 @param elem: XML element containing the Response 1644 @type attributeValueElementTreeFactoryKw: dict 1645 @param attributeValueElementTreeFactoryKw: keywords for AttributeValue 1646 @rtype: saml.saml2.core.Response 1647 @return: Response object 1648 """ 1649 if not ElementTree.iselement(elem): 1650 raise TypeError("Expecting %r input type for parsing; got %r" % 1651 (ElementTree.Element, elem)) 1652 1653 if QName.getLocalPart(elem.tag) != Response.DEFAULT_ELEMENT_LOCAL_NAME: 1654 raise XMLTypeParseError("No \"%s\" element found" % 1655 Response.DEFAULT_ELEMENT_LOCAL_NAME) 1656 1657 # Unpack attributes from top-level element 1658 attributeValues = [] 1659 for attributeName in (Response.VERSION_ATTRIB_NAME, 1660 Response.ISSUE_INSTANT_ATTRIB_NAME, 1661 Response.ID_ATTRIB_NAME, 1662 Response.IN_RESPONSE_TO_ATTRIB_NAME): 1663 attributeValue = elem.attrib.get(attributeName) 1664 if attributeValue is None: 1665 raise XMLTypeParseError('No "%s" attribute found in "%s" ' 1666 'element' % 1667 (attributeName, 1668 Response.DEFAULT_ELEMENT_LOCAL_NAME)) 1669 1670 attributeValues.append(attributeValue) 1671 1672 response = Response() 1673 response.version = SAMLVersion(attributeValues[0]) 1674 if response.version != SAMLVersion.VERSION_20: 1675 raise NotImplementedError("Parsing for %r is implemented for " 1676 "SAML version %s only; version %s is " 1677 "not supported" % 1678 (cls, 1679 SAMLVersion(SAMLVersion.VERSION_20), 1680 SAMLVersion(response.version))) 1681 1682 response.issueInstant = SAMLDateTime.fromString(attributeValues[1]) 1683 response.id = attributeValues[2] 1684 response.inResponseTo = attributeValues[3] 1685 1686 for childElem in elem: 1687 localName = QName.getLocalPart(childElem.tag) 1688 if localName == Issuer.DEFAULT_ELEMENT_LOCAL_NAME: 1689 # Parse Issuer 1690 response.issuer = IssuerElementTree.fromXML(childElem) 1691 1692 elif localName == Status.DEFAULT_ELEMENT_LOCAL_NAME: 1693 # Get status of response 1694 response.status = StatusElementTree.fromXML(childElem) 1695 1696 elif localName == Subject.DEFAULT_ELEMENT_LOCAL_NAME: 1697 # Parse Subject 1698 response.subject = SubjectElementTree.fromXML(childElem) 1699 1700 elif localName == Assertion.DEFAULT_ELEMENT_LOCAL_NAME: 1701 assertion = AssertionElementTree.fromXML(childElem, 1702 **attributeValueElementTreeFactoryKw) 1703 response.assertions.append(assertion) 1704 else: 1705 raise XMLTypeParseError('Unrecognised Response child ' 1706 'element "%s"' % localName) 1707 1708 return response
1709
1710 1711 -class ActionElementTree(Action):
1712 """Represent a SAML authorization action in XML using ElementTree""" 1713 1714 @classmethod
1715 - def toXML(cls, action):
1716 """Create an XML representation of the input SAML Name Identifier 1717 object 1718 @type action: saml.saml2.core.Action 1719 @param action: SAML subject 1720 @rtype: ElementTree.Element 1721 @return: Name ID as ElementTree XML element 1722 """ 1723 1724 if not isinstance(action, Action): 1725 raise TypeError("Expecting %r class got %r" % (Action, 1726 type(action))) 1727 1728 if not action.namespace: 1729 raise AttributeError("No action namespace set") 1730 1731 attrib = { 1732 cls.NAMESPACE_ATTRIB_NAME: action.namespace 1733 } 1734 tag = str(QName.fromGeneric(cls.DEFAULT_ELEMENT_NAME)) 1735 elem = makeEtreeElement(tag, action.qname.prefix, 1736 action.qname.namespaceURI, 1737 **attrib) 1738 elem = ElementTree.Element(tag, **attrib) 1739 1740 if not action.value: 1741 raise AttributeError("No action name set") 1742 1743 elem.text = action.value 1744 1745 return elem
1746 1747 @classmethod
1748 - def fromXML(cls, elem):
1749 """Parse ElementTree element into a SAML Action object 1750 1751 @type elem: ElementTree.Element 1752 @param elem: Name ID as ElementTree XML element 1753 @rtype: saml.saml2.core.Action 1754 @return: SAML Name ID 1755 """ 1756 if not ElementTree.iselement(elem): 1757 raise TypeError("Expecting %r input type for parsing; got %r" % 1758 (ElementTree.Element, elem)) 1759 1760 if QName.getLocalPart(elem.tag) != cls.DEFAULT_ELEMENT_LOCAL_NAME: 1761 raise XMLTypeParseError("No \"%s\" element found" % 1762 cls.DEFAULT_ELEMENT_LOCAL_NAME) 1763 1764 action = Action() 1765 namespace = elem.attrib.get(cls.NAMESPACE_ATTRIB_NAME) 1766 if namespace is None: 1767 log.warning('No "%s" attribute found in "%s" element assuming ' 1768 '%r action namespace' % 1769 (cls.NAMESPACE_ATTRIB_NAME, 1770 cls.DEFAULT_ELEMENT_LOCAL_NAME, 1771 action.namespace)) 1772 else: 1773 action.namespace = namespace 1774 1775 action.value = elem.text.strip() 1776 1777 return action
1778
1779 1780 -class AuthzDecisionQueryElementTree(AuthzDecisionQuery):
1781 """Represent a SAML Attribute Query in XML using ElementTree""" 1782 1783 @classmethod
1784 - def toXML(cls, authzDecisionQuery):
1785 """Create an XML representation of the input SAML Authorization 1786 Decision Query object 1787 1788 @type authzDecisionQuery: saml.saml2.core.AuthzDecisionQuery 1789 @param authzDecisionQuery: SAML Authorization Decision Query 1790 @rtype: ElementTree.Element 1791 @return: Attribute Query as ElementTree XML element 1792 """ 1793 if not isinstance(authzDecisionQuery, AuthzDecisionQuery): 1794 raise TypeError("Expecting %r class got %r" % (AuthzDecisionQuery, 1795 type(authzDecisionQuery))) 1796 1797 if not authzDecisionQuery.resource: 1798 raise AttributeError("No resource has been set for the " 1799 "AuthzDecisionQuery") 1800 1801 issueInstant = SAMLDateTime.toString(authzDecisionQuery.issueInstant) 1802 attrib = { 1803 cls.ID_ATTRIB_NAME: authzDecisionQuery.id, 1804 cls.ISSUE_INSTANT_ATTRIB_NAME: issueInstant, 1805 1806 # Nb. Version is a SAMLVersion instance and requires explicit cast 1807 cls.VERSION_ATTRIB_NAME: str(authzDecisionQuery.version), 1808 1809 cls.RESOURCE_ATTRIB_NAME: authzDecisionQuery.resource 1810 } 1811 1812 tag = str(QName.fromGeneric(cls.DEFAULT_ELEMENT_NAME)) 1813 elem = makeEtreeElement(tag, cls.DEFAULT_ELEMENT_NAME.prefix, 1814 cls.DEFAULT_ELEMENT_NAME.namespaceURI, 1815 **attrib) 1816 1817 issuerElem = IssuerElementTree.toXML(authzDecisionQuery.issuer) 1818 elem.append(issuerElem) 1819 1820 subjectElem = SubjectElementTree.toXML(authzDecisionQuery.subject) 1821 elem.append(subjectElem) 1822 1823 for action in authzDecisionQuery.actions: 1824 # Factory enables support for multiple attribute types 1825 actionElem = ActionElementTree.toXML(action) 1826 elem.append(actionElem) 1827 1828 if (authzDecisionQuery.evidence and 1829 len(authzDecisionQuery.evidence.evidence) > 0): 1830 raise NotImplementedError("Conversion of AuthzDecisionQuery " 1831 "Evidence type to ElementTree Element is " 1832 "not currently supported") 1833 1834 return elem
1835 1836 @classmethod
1837 - def fromXML(cls, elem):
1838 """Parse ElementTree element into a SAML AuthzDecisionQuery object 1839 1840 @type elem: ElementTree.Element 1841 @param elem: XML element containing the AuthzDecisionQuery 1842 @rtype: saml.saml2.core.AuthzDecisionQuery 1843 @return: AuthzDecisionQuery object 1844 """ 1845 if not ElementTree.iselement(elem): 1846 raise TypeError("Expecting %r input type for parsing; got %r" % 1847 (ElementTree.Element, elem)) 1848 1849 if QName.getLocalPart(elem.tag) != cls.DEFAULT_ELEMENT_LOCAL_NAME: 1850 raise XMLTypeParseError("No \"%s\" element found" % 1851 cls.DEFAULT_ELEMENT_LOCAL_NAME) 1852 1853 # Unpack attributes from top-level element 1854 attributeValues = [] 1855 for attributeName in (cls.VERSION_ATTRIB_NAME, 1856 cls.ISSUE_INSTANT_ATTRIB_NAME, 1857 cls.ID_ATTRIB_NAME, 1858 cls.RESOURCE_ATTRIB_NAME): 1859 attributeValue = elem.attrib.get(attributeName) 1860 if attributeValue is None: 1861 raise XMLTypeParseError('No "%s" attribute found in "%s" ' 1862 'element' % 1863 (attributeName, 1864 cls.DEFAULT_ELEMENT_LOCAL_NAME)) 1865 1866 attributeValues.append(attributeValue) 1867 1868 authzDecisionQuery = AuthzDecisionQuery() 1869 authzDecisionQuery.version = SAMLVersion(attributeValues[0]) 1870 if authzDecisionQuery.version != SAMLVersion.VERSION_20: 1871 raise NotImplementedError("Parsing for %r is implemented for " 1872 "SAML version %s only; version %s is " 1873 "not supported" % 1874 (cls, 1875 SAMLVersion(SAMLVersion.VERSION_20), 1876 SAMLVersion(authzDecisionQuery.version))) 1877 1878 authzDecisionQuery.issueInstant = SAMLDateTime.fromString( 1879 attributeValues[1]) 1880 authzDecisionQuery.id = attributeValues[2] 1881 authzDecisionQuery.resource = attributeValues[3] 1882 1883 for childElem in elem: 1884 localName = QName.getLocalPart(childElem.tag) 1885 if localName == Issuer.DEFAULT_ELEMENT_LOCAL_NAME: 1886 # Parse Issuer 1887 authzDecisionQuery.issuer = IssuerElementTree.fromXML(childElem) 1888 1889 elif localName == Subject.DEFAULT_ELEMENT_LOCAL_NAME: 1890 # Parse Subject 1891 authzDecisionQuery.subject = SubjectElementTree.fromXML(childElem) 1892 1893 elif localName == Action.DEFAULT_ELEMENT_LOCAL_NAME: 1894 action = ActionElementTree.fromXML(childElem) 1895 authzDecisionQuery.actions.append(action) 1896 else: 1897 raise XMLTypeParseError("Unrecognised AuthzDecisionQuery child " 1898 "element \"%s\"" % localName) 1899 1900 return authzDecisionQuery
1901
1902 -def _getElementTreeImplementationForQName(qname):
1903 key = ("{%s}%s" % (qname.namespaceURI, qname.localPart)) 1904 return _extensionElementTreeMap.get(key)
1905
1906 -def setElementTreeImplementationForQName(qname, impl):
1907 key = ("{%s}%s" % (qname.namespaceURI, qname.localPart)) 1908 _extensionElementTreeMap[key] = impl
1909