source: TI12-security/trunk/ndg_saml/ndg/saml/xml/etree.py @ 6913

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/ndg_saml/ndg/saml/xml/etree.py@6913
Revision 6913, 74.6 KB checked in by pjkersha, 10 years ago (diff)

Incomplete - task 6: Put NDG SAML package on PyPI

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