source: TI12-security/trunk/python/ndg.security.common/ndg/security/common/saml/xml/etree.py @ 5554

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.common/ndg/security/common/saml/xml/etree.py@5554
Revision 5554, 24.7 KB checked in by pjkersha, 11 years ago (diff)
  • Started adding ElementTree based parsers for SAML classes in ndg.security.common.saml.xml.etree.
  • ndg.security.common.utils.prettyPrint needs a bug fix for namespace declarations
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__ = "BSD - see LICENSE file in top-level directory"
28__contact__ = "Philip.Kershaw@stfc.ac.uk"
29__revision__ = "$Id$"
30import logging
31log = logging.getLogger(__name__)
32
33try: # python 2.5
34    from xml.etree import cElementTree, ElementTree
35except ImportError:
36    # if you've installed it yourself it comes this way
37    import cElementTree, ElementTree
38
39from ndg.security.common.saml import SAMLObject, Assertion, Attribute, \
40    AttributeStatement, AttributeValue, XSStringAttributeValue, \
41    XSGroupRoleAttributeValue, AttributeQuery, Subject, NameID, Issuer, \
42    SAMLVersion
43   
44from ndg.security.common.saml.xml import XMLObject, IssueInstantXMLObject, \
45    XMLObjectParsingError, SAMLConstants
46
47from ndg.security.common.utils import getLocalName, prettyPrint
48
49class SAMLElementTree(XMLObject):
50    """Implement methods generic to all ElementTree SAML object representations
51    """       
52#    def parse(self, source):
53#        """Read in the XML from source
54#        @type source: basestring/file
55#        @param source: file path to XML file or file object
56#        """
57#        tree = ElementTree.parse(source)
58#        self.__elem = tree.getroot()
59#       
60#        return self.__elem
61#   
62    @staticmethod     
63    def serialize(elem):
64        """Serialise element tree into string"""
65        return cElementTree.tostring(elem)
66   
67    @staticmethod
68    def prettyPrint(elem):
69        """Basic pretty printing separating each element on to a new line"""
70        return prettyPrint(elem)
71#        xml = self.serialize()
72#        xml = ">\n".join(xml.split(">"))
73#        xml = "\n<".join(xml.split("<"))
74#        xml = '\n'.join(xml.split('\n\n'))
75#        return xml
76   
77                         
78class AssertionElementTree(SAMLElementTree, IssueInstantXMLObject):
79    """ElementTree based XML representation of Assertion class
80    """
81    def __init__(self):
82        SAMLElementTree.__init__(self)
83        IssueInstantXMLObject.__init__(self)
84   
85    def create(self, 
86               assertion, 
87               **attributeValueElementTreeFactoryKw):
88        """Make a tree of a XML elements based on the assertion"""
89        if not isinstance(assertion, Assertion):
90            raise TypeError("Expecting %r type got: %r"%(Assertion, assertion))
91       
92        issueInstant = IssueInstantXMLObject.datetime2Str(
93                                                        assertion.issueInstant)
94        attrib = {
95            Assertion.ID_ATTRIB_NAME: assertion.id,
96            Assertion.ISSUE_INSTANT_ATTRIB_NAME: issueInstant,
97           
98            # Nb. Version is a SAMLVersion instance and requires explicit cast
99            Assertion.VERSION_ATTRIB_NAME: str(assertion.version)
100        }
101        self.__elem = ElementTree.Element(str(Assertion.DEFAULT_ELEMENT_NAME), 
102                                         **attrib)
103       
104        ElementTree._namespace_map[ 
105            Assertion.DEFAULT_ELEMENT_NAME.namespaceURI
106        ] = Assertion.DEFAULT_ELEMENT_NAME.prefix
107       
108        attributeStatementElementTree = AttributeStatementElementTree()
109       
110        for attributeStatement in assertion.attributeStatements:
111            attributeStatementElem = attributeStatementElementTree.create(
112                                        attributeStatement,
113                                        **attributeValueElementTreeFactoryKw)
114            self.__elem.append(attributeStatementElem)
115       
116        log.debug("Created Assertion:\n\n%s" % 
117                  ">\n".join(self.serialize().split(">")))
118        return self.__elem
119
120 
121class AttributeStatementElementTree(SAMLElementTree):
122    """ElementTree XML representation of AttributeStatement"""
123   
124    def create(self, 
125               attributeStatement, 
126               **attributeValueElementTreeFactoryKw):
127        if not isinstance(attributeStatement, AttributeStatement):
128            raise TypeError("Expecting %r type got: %r" % (AttributeStatement, 
129                                                           attributeStatement))
130           
131        self.__elem = ElementTree.Element(
132                                str(AttributeStatement.DEFAULT_ELEMENT_NAME))
133        ElementTree._namespace_map[
134            AttributeStatement.DEFAULT_ELEMENT_NAME.namespaceURI
135        ] = AttributeStatement.DEFAULT_ELEMENT_NAME.prefix
136
137        attributeElementTree = AttributeElementTree()
138
139        for attribute in attributeStatement.attributes:
140            # Factory enables support for multiple attribute types
141            attributeElem = attributeElementTree.create(attribute,
142                                        **attributeValueElementTreeFactoryKw)
143            self.__elem.append(attributeElem)
144       
145        return self.__elem
146   
147
148class AttributeElementTree(SAMLElementTree):
149    """ElementTree XML representation of SAML Attribute object.  Extend
150    to make Attribute types""" 
151
152    @classmethod
153    def create(cls, 
154               attribute, 
155               **attributeValueElementTreeFactoryKw):
156        """Make 'Attribute' element"""
157       
158        if not isinstance(attribute, Attribute):
159            raise TypeError("Expecting %r type got: %r"%(Attribute, attribute))
160           
161        elem = ElementTree.Element(str(Attribute.DEFAULT_ELEMENT_NAME))
162        ElementTree._namespace_map[
163            Attribute.DEFAULT_ELEMENT_NAME.namespaceURI
164        ] = Attribute.DEFAULT_ELEMENT_NAME.prefix
165       
166           
167        if attribute.friendlyName:
168            elem.set(Attribute.FRIENDLY_NAME_ATTRIB_NAME,
169                     attribute.friendlyName) 
170             
171        if attribute.name:
172            elem.set(Attribute.NAME_ATTRIB_NAME, attribute.name)
173       
174        if attribute.nameFormat:
175            elem.set(Attribute.NAME_FORMAT_ATTRIB_NAME, attribute.nameFormat)
176
177        for attributeValue in attribute.attributeValues:
178            factory = AttributeValueElementTreeFactory(
179                                        **attributeValueElementTreeFactoryKw)
180           
181            attributeValueElementTree = factory(attributeValue)
182           
183            attributeValueElem=attributeValueElementTree.create(attributeValue)
184            elem.append(attributeValueElem)
185           
186        return elem
187 
188    @classmethod
189    def parse(cls, elem):
190        """Parse ElementTree element into a SAML Attribute object
191       
192        @type elem: ElementTree.Element
193        @param elem: Attribute as ElementTree XML element
194        @rtype: ndg.security.common.saml.Attribute
195        @return: SAML Attribute
196        """
197        if not ElementTree.iselement(elem):
198            raise TypeError("Expecting %r input type for parsing; got %r" %
199                            (ElementTree.Element, elem))
200
201        if getLocalName(elem) != Attribute.DEFAULT_ELEMENT_LOCAL_NAME:
202            raise XMLObjectParsingError("No \"%s\" element found" %
203                                        Attribute.DEFAULT_ELEMENT_LOCAL_NAME)
204           
205        attribute = Attribute()
206           
207        name = elem.attrib.get(Attribute.FORMAT_ATTRIB_NAME)
208        if name is not None:
209            attribute.name = name
210           
211        friendlyName = elem.attrib.get(Attribute.FRIENDLY_NAME_ATTRIB_NAME)
212        if friendlyName is not None:
213            attribute.friendlyName = friendlyName
214           
215        nameFormat = elem.attrib.get(Attribute.NAME_FORMAT_ATTRIB_NAME)   
216        if nameFormat is not None:
217            attribute.nameFormat = nameFormat
218
219        for childElem in elem:
220            localName = getLocalName(childElem)
221            if localName != AttributeValue.DEFAULT_ELEMENT_LOCAL_NAME:
222                raise XMLObjectParsingError('Expecting "%s" element; found '
223                                    '"%s"' %
224                                    (AttributeValue.DEFAULT_ELEMENT_LOCAL_NAME,
225                                     localName))
226           
227            attributeValue = 
228            attribute.attributeValues.append(attributeValue)
229       
230        return attribute
231       
232   
233class AttributeValueElementTreeBase(SAMLElementTree):
234    """Base class ElementTree XML representation of SAML Attribute Value""" 
235   
236    @classmethod
237    def create(cls, attributeValue):
238        """Make 'Attribute' XML element"""
239
240        if not isinstance(attributeValue, AttributeValue):
241            raise TypeError("Expecting %r type got: %r" % (AttributeValue, 
242                                                           attributeValue))
243           
244        elem = ElementTree.Element(str(AttributeValue.DEFAULT_ELEMENT_NAME))
245        ElementTree._namespace_map[
246            AttributeValue.DEFAULT_ELEMENT_NAME.namespaceURI
247        ] = AttributeValue.DEFAULT_ELEMENT_NAME.prefix
248
249        return elem
250
251
252class XSStringAttributeValueElementTree(AttributeValueElementTreeBase):
253    """ElementTree XML representation of SAML String type Attribute Value""" 
254   
255    @classmethod
256    def create(cls, attributeValue):
257        """Create an XML representation of the input SAML Attribute Value"""
258        elem = AttributeValueElementTreeBase.create(attributeValue)
259       
260        if not isinstance(attributeValue, XSStringAttributeValue):
261            raise TypeError("Expecting %r type got: %r" % 
262                            (XSStringAttributeValue, attributeValue)) 
263       
264        # Have to explicitly add namespace declaration here rather use
265        # ElementTree._namespace_map because the prefixes are used for
266        # attributes not element names       
267        elem.set("%s:%s" % (SAMLConstants.XMLNS_PREFIX, 
268                            SAMLConstants.XSD_PREFIX),
269                 SAMLConstants.XSD_NS)
270                                   
271        elem.set("%s:%s" % (SAMLConstants.XMLNS_PREFIX, 
272                            SAMLConstants.XSI_PREFIX),
273                SAMLConstants.XSI_NS)
274       
275        elem.set("%s:%s" % (SAMLConstants.XSI_PREFIX, 'type'), 
276                 "%s:%s" % (SAMLConstants.XSD_PREFIX, 
277                            XSStringAttributeValue.TYPE_LOCAL_NAME))
278
279        elem.text = attributeValue.value
280
281        return elem
282
283
284class XSGroupRoleAttributeValueElementTree(AttributeValueElementTreeBase):
285    """ElementTree XML representation of Earth System Grid custom Group/Role
286    Attribute Value""" 
287
288    def create(self, attributeValue):
289        """Create an XML representation of the input SAML Attribute Value"""
290        super(XSGroupRoleAttributeValueElementTree,self).create(attributeValue)
291       
292        if not isinstance(attributeValue, XSGroupRoleAttributeValue):
293            raise TypeError("Expecting %r type; got: %r" % 
294                            (XSGroupRoleAttributeValue, attributeValue))
295           
296        ElementTree._namespace_map[attributeValue.namespaceURI
297                                   ] = attributeValue.namespacePrefix
298       
299        self.__elem.set(XSGroupRoleAttributeValue.GROUP_ATTRIB_NAME, 
300                       attributeValue.group)
301       
302        self.__elem.set(XSGroupRoleAttributeValue.ROLE_ATTRIB_NAME, 
303                       attributeValue.role)
304
305        return self.__elem
306
307
308class AttributeValueElementTreeFactory(object):
309    """Class factory for AttributeValue ElementTree classes.  These classes are
310    used to represent SAML Attribute value types
311   
312    @type classMap: dict
313    @cvar classMap: mapping between SAML AttributeValue class and its
314    ElementTree handler class
315    """
316    classMap = {
317        XSStringAttributeValue: XSStringAttributeValueElementTree
318    }
319   
320    def __init__(self, customClassMap={}): 
321        """Set-up a SAML class to ElementTree mapping
322        @type customClassMap: dict
323        @param customClassMap: mapping for custom SAML AttributeValue classes
324        to their respective ElementTree based representations.  This appends
325        to AttributeValueElementTreeFactory.classMap
326        """
327        self.__classMap = AttributeValueElementTreeFactory.classMap
328        for samlClass, etreeClass in customClassMap.items(): 
329            if not issubclass(samlClass, AttributeValue):
330                raise TypeError("Input custom class must be derived from %r, "
331                                "got %r instead" % (Attribute, samlClass))
332               
333            self.__classMap[samlClass] = etreeClass
334           
335    def __call__(self, samlObject):
336        """Create an ElementTree object based on the Attribute class type
337        passed in
338        """
339        if not isinstance(samlObject, AttributeValue):
340            raise TypeError("Expecting %r class got %r" % (AttributeValue, 
341                                                           type(samlObject)))
342           
343        xmlObjectClass = self.__classMap.get(samlObject.__class__)
344        if xmlObjectClass is None:
345            raise TypeError("no matching XMLObject class representation for "
346                            "SAML class %r" % samlObject.__class__)
347           
348        return return xmlObjectClass
349
350       
351class IssuerElementTree(SAMLElementTree):
352    """Represent a SAML Issuer element in XML using ElementTree"""
353   
354    @classmethod
355    def create(cls, issuer):
356        """Create an XML representation of the input SAML issuer object"""
357        if not isinstance(issuer, Issuer):
358            raise TypeError("Expecting %r class got %r" % (issuer, 
359                                                           type(Issuer)))
360        attrib = {
361            Issuer.FORMAT_ATTRIB_NAME: issuer.format
362        }
363        elem = ElementTree.Element(str(Issuer.DEFAULT_ELEMENT_NAME), **attrib)
364        ElementTree._namespace_map[issuer.qname.namespaceURI
365                                   ] = issuer.qname.prefix
366                                   
367        elem.text = issuer.value
368
369        return elem
370
371    @classmethod
372    def parse(cls, elem):
373        """Parse ElementTree element into a SAML Issuer instance"""
374        if not ElementTree.iselement(elem):
375            raise TypeError("Expecting %r input type for parsing; got %r" %
376                            (ElementTree.Element, elem))
377
378        if getLocalName(elem) != Issuer.DEFAULT_ELEMENT_LOCAL_NAME:
379            raise XMLObjectParsingError("No \"%s\" element found" %
380                                        Issuer.DEFAULT_ELEMENT_LOCAL_NAME)
381           
382        issuerFormat = elem.attrib.get(Issuer.FORMAT_ATTRIB_NAME)
383        if issuerFormat is None:
384            raise XMLObjectParsingError('No "%s" attribute found in "%s" '
385                                        'element' %
386                                        (issuerFormat,
387                                         Issuer.DEFAULT_ELEMENT_LOCAL_NAME))
388        issuer = Issuer()
389        issuer.format = issuerFormat
390        issuer.value = elem.text.strip() 
391       
392        return issuer
393       
394class NameIdElementTree(SAMLElementTree):
395    """Represent a SAML Name Identifier in XML using ElementTree"""
396   
397    @classmethod
398    def create(cls, nameID):
399        """Create an XML representation of the input SAML Name Identifier
400        object
401        @type nameID: ndg.security.common.saml.Subject
402        @param nameID: SAML subject
403        @rtype: ElementTree.Element
404        @return: Name ID as ElementTree XML element"""
405       
406        if not isinstance(nameID, NameID):
407            raise TypeError("Expecting %r class got %r" % (nameID, 
408                                                           type(NameID)))
409        attrib = {
410            NameID.FORMAT_ATTRIB_NAME: nameID.format
411        }
412        elem = ElementTree.Element(str(NameID.DEFAULT_ELEMENT_NAME),
413                                         **attrib)
414       
415        ElementTree._namespace_map[nameID.qname.namespaceURI
416                                   ] = nameID.qname.prefix
417       
418        elem.text = nameID.value
419
420        return elem
421
422    @classmethod
423    def parse(cls, elem):
424        """Parse ElementTree element into a SAML NameID object
425       
426        @type elem: ElementTree.Element
427        @param elem: Name ID as ElementTree XML element
428        @rtype: ndg.security.common.saml.NameID
429        @return: SAML Name ID
430        """
431        if not ElementTree.iselement(elem):
432            raise TypeError("Expecting %r input type for parsing; got %r" %
433                            (ElementTree.Element, elem))
434
435        if getLocalName(elem) != NameID.DEFAULT_ELEMENT_LOCAL_NAME:
436            raise XMLObjectParsingError("No \"%s\" element found" %
437                                        NameID.DEFAULT_ELEMENT_LOCAL_NAME)
438           
439        format = elem.attrib.get(NameID.FORMAT_ATTRIB_NAME)
440        if format is None:
441            raise XMLObjectParsingError('No "%s" attribute found in "%s" '
442                                        'element' %
443                                        (format,
444                                         NameID.DEFAULT_ELEMENT_LOCAL_NAME))
445        nameID = NameID()
446        nameID.format = format
447        nameID.value = elem.text.strip() 
448       
449        return nameID
450
451
452class SubjectElementTree(SAMLElementTree):
453    """Represent a SAML Subject in XML using ElementTree"""
454   
455    @classmethod
456    def create(cls, subject):
457        """Create an XML representation of the input SAML subject object
458        @type subject: ndg.security.common.saml.Subject
459        @param subject: SAML subject
460        @rtype: ElementTree.Element
461        @return: subject as ElementTree XML element
462        """
463        if not isinstance(subject, Subject):
464            raise TypeError("Expecting %r class got %r" % (subject, 
465                                                           type(Subject)))
466           
467        elem = ElementTree.Element(str(Subject.DEFAULT_ELEMENT_NAME))
468       
469        ElementTree._namespace_map[
470            AttributeQuery.DEFAULT_ELEMENT_NAME.namespaceURI
471        ] = AttributeQuery.DEFAULT_ELEMENT_NAME.prefix
472
473           
474        nameIdElementTree = NameIdElementTree()
475        nameIdElem = nameIdElementTree.create(subject.nameID)
476        elem.append(nameIdElem)
477       
478        return elem
479
480    @classmethod
481    def parse(cls, elem):
482        """Parse ElementTree element into a SAML Subject object
483       
484        @type elem: ElementTree.Element
485        @param elem: subject as ElementTree XML element
486        @rtype: ndg.security.common.saml.Subject
487        @return: SAML subject
488        """
489        if not ElementTree.iselement(elem):
490            raise TypeError("Expecting %r input type for parsing; got %r" %
491                            (ElementTree.Element, elem))
492
493        if getLocalName(elem) != Subject.DEFAULT_ELEMENT_LOCAL_NAME:
494            raise XMLObjectParsingError("No \"%s\" element found" %
495                                        Subject.DEFAULT_ELEMENT_LOCAL_NAME)
496           
497        if len(elem) != 1:
498            raise XMLObjectParseError("Expecting single Name ID child element "
499                                      "for SAML Subject element")
500           
501        subject = Subject()
502        subject.nameID = NameIdElementTree.parse(elem[0])
503       
504        return subject
505   
506   
507class AttributeQueryElementTree(SAMLElementTree, IssueInstantXMLObject):
508    """Represent a SAML Attribute Query in XML using ElementTree"""
509    def __init__(self):
510        SAMLElementTree.__init__(self)
511        IssueInstantXMLObject.__init__(self)
512       
513    @classmethod
514    def create(cls, 
515               attributeQuery, 
516               **attributeValueElementTreeFactoryKw):
517        """Create an XML representation of the input SAML Attribute Query
518        object
519
520        @type attributeQuery: ndg.security.common.saml.AttributeQuery
521        @param attributeQuery: SAML Attribute Query
522        @rtype: ElementTree.Element
523        @return: Attribute Query as ElementTree XML element
524        """
525        if not isinstance(attributeQuery, AttributeQuery):
526            raise TypeError("Expecting %r class got %r" % (AttributeQuery, 
527                                                        type(attributeQuery)))
528           
529       
530        issueInstant = AttributeQueryElementTree.datetime2Str(
531                                                attributeQuery.issueInstant)
532        attrib = {
533            AttributeQuery.ID_ATTRIB_NAME: attributeQuery.id,
534            AttributeQuery.ISSUE_INSTANT_ATTRIB_NAME: issueInstant,
535           
536            # Nb. Version is a SAMLVersion instance and requires explicit cast
537            AttributeQuery.VERSION_ATTRIB_NAME: str(attributeQuery.version)
538        }
539                 
540        elem = ElementTree.Element(str(AttributeQuery.DEFAULT_ELEMENT_NAME),
541                                   **attrib)
542       
543        ElementTree._namespace_map[
544            AttributeQuery.DEFAULT_ELEMENT_NAME.namespaceURI
545        ] = AttributeQuery.DEFAULT_ELEMENT_NAME.prefix
546       
547           
548        issuerElementTree = IssuerElementTree()
549        issuerElem = issuerElementTree.create(attributeQuery.issuer)
550        elem.append(issuerElem)
551
552        subjectElementTree = SubjectElementTree()
553        subjectElem = subjectElementTree.create(attributeQuery.subject)
554       
555        elem.append(subjectElem)
556
557        attributeElementTree = AttributeElementTree()
558
559        for attribute in attributeQuery.attributes:
560            # Factory enables support for multiple attribute types
561            attributeElem = attributeElementTree.create(attribute,
562                                        **attributeValueElementTreeFactoryKw)
563            elem.append(attributeElem)
564       
565        return elem
566
567    @classmethod
568    def parse(cls, elem):
569        """Parse ElementTree element into a SAML AttributeQuery object
570       
571        @type elem: ElementTree.Element
572        @param elem: XML element containing the AttributeQuery
573        @rtype: ndg.security.common.saml.AttributeQuery
574        @return: AttributeQuery object
575        """
576        if not ElementTree.iselement(elem):
577            raise TypeError("Expecting %r input type for parsing; got %r" %
578                            (ElementTree.Element, elem))
579
580        if getLocalName(elem) != AttributeQuery.DEFAULT_ELEMENT_LOCAL_NAME:
581            raise XMLObjectParsingError("No \"%s\" element found" %
582                                    AttributeQuery.DEFAULT_ELEMENT_LOCAL_NAME)
583       
584        # Unpack attributes from top-level element
585        attributeValues = []
586        for attributeName in (AttributeQuery.VERSION_ATTRIB_NAME,
587                              AttributeQuery.ISSUE_INSTANT_ATTRIB_NAME,
588                              AttributeQuery.ID_ATTRIB_NAME):
589            attributeValue = elem.attrib.get(attributeName)
590            if attributeValue is None:
591                raise XMLObjectParsingError('No "%s" attribute found in "%s" '
592                                 'element' %
593                                 (attributeName,
594                                  AttributeQuery.DEFAULT_ELEMENT_LOCAL_NAME))
595               
596            attributeValues.append(attributeValue)
597       
598        attributeQuery = AttributeQuery()
599        attributeQuery.version = SAMLVersion(attributeValues[0])
600        if attributeQuery.version != SAMLVersion.VERSION_20:
601            raise NotImplementedError("Parsing for %r is implemented for "
602                                      "SAML version %s only; version %s is " 
603                                      "not supported" % 
604                                      (cls,
605                                       SAMLVersion(SAMLVersion.VERSION_20),
606                                       SAMLVersion(attributeQuery.version)))
607           
608        attributeQuery.issueInstant = cls.str2Datetime(attributeValues[1])
609        attributeQuery.id = attributeValues[2]
610       
611        for childElem in elem:
612            localName = getLocalName(childElem)
613            if localName == Issuer.DEFAULT_ELEMENT_LOCAL_NAME:
614                # Parse Issuer
615                attributeQuery.issuer = IssuerElementTree.parse(childElem)
616               
617            elif localName == Subject.DEFAULT_ELEMENT_LOCAL_NAME:
618                # Parse Subject
619                attributeQuery.subject = SubjectElementTree.parse(childElem)
620            else:
621                raise XMLObjectParseError("Unrecognised AttributeQuery child "
622                                          "element \"%s\"" % localName)
623       
624        return attributeQuery
Note: See TracBrowser for help on using the repository browser.