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

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.common/ndg/security/common/saml/xml/etree.py@5538
Revision 5538, 16.3 KB checked in by pjkersha, 11 years ago (diff)

Working SAML Attribute Query with SOAP binding

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   
43from ndg.security.common.saml.xml import XMLObject, IssueInstantXMLObject, \
44    SAMLConstants
45
46
47class ETreeObject(XMLObject):
48    """Implement methods generic to all ElementTree SAML object representations
49    """
50    def __init__(self):
51        self._elem = None
52       
53    def parse(self, source):
54        """Read in the XML from source
55        @type source: basestring/file
56        @param source: file path to XML file or file object
57        """
58        tree = ElementTree.parse(source)
59        self._elem = tree.getroot()
60       
61        return self._elem
62#   
63#    def serialize(self):
64#        """Serialise element tree into string"""
65#        return canonicalize(self._elem)
66   
67    def serialize(self):
68        """Serialise element tree into string"""
69        return cElementTree.tostring(self._elem)
70   
71    def prettyPrint(self):
72        """Basic pretty printing separating each element on to a new line"""
73        xml = self.serialize()
74        xml = ">\n".join(xml.split(">"))
75        xml = "\n<".join(xml.split("<"))
76        xml = '\n'.join(xml.split('\n\n'))
77        return xml
78   
79                         
80class AssertionETreeObject(ETreeObject, IssueInstantXMLObject):
81    """ElementTree based XML representation of Assertion class
82    """
83    def __init__(self):
84        ETreeObject.__init__(self)
85        IssueInstantXMLObject.__init__(self)
86   
87    def create(self, 
88               assertion, 
89               **attributeValueETreeObjectFactoryKw):
90        """Make a tree of a XML elements based on the assertion"""
91        if not isinstance(assertion, Assertion):
92            raise TypeError("Expecting %r type got: %r"%(Assertion, assertion))
93       
94        issueInstant = IssueInstantXMLObject.datetime2Str(
95                                                        assertion.issueInstant)
96        attrib = {
97            Assertion.ID_ATTRIB_NAME: assertion.id,
98            Assertion.ISSUE_INSTANT_ATTRIB_NAME: issueInstant,
99           
100            # Nb. Version is a SAMLVersion instance and requires explicit cast
101            Assertion.VERSION_ATTRIB_NAME: str(assertion.version)
102        }
103        self._elem = ElementTree.Element(str(Assertion.DEFAULT_ELEMENT_NAME), 
104                                         **attrib)
105       
106        ElementTree._namespace_map[ 
107            Assertion.DEFAULT_ELEMENT_NAME.namespaceURI
108        ] = Assertion.DEFAULT_ELEMENT_NAME.prefix
109       
110        attributeStatementETreeObject = AttributeStatementETreeObject()
111       
112        for attributeStatement in assertion.attributeStatements:
113            attributeStatementElem = attributeStatementETreeObject.create(
114                                        attributeStatement,
115                                        **attributeValueETreeObjectFactoryKw)
116            self._elem.append(attributeStatementElem)
117       
118        log.debug("Created Assertion:\n\n%s" % 
119                  ">\n".join(self.serialize().split(">")))
120        return self._elem
121
122 
123class AttributeStatementETreeObject(ETreeObject):
124    """ElementTree XML representation of AttributeStatement"""
125   
126    def create(self, 
127               attributeStatement, 
128               **attributeValueETreeObjectFactoryKw):
129        if not isinstance(attributeStatement, AttributeStatement):
130            raise TypeError("Expecting %r type got: %r" % (AttributeStatement, 
131                                                           attributeStatement))
132           
133        self._elem = ElementTree.Element(
134                                str(AttributeStatement.DEFAULT_ELEMENT_NAME))
135        ElementTree._namespace_map[
136            AttributeStatement.DEFAULT_ELEMENT_NAME.namespaceURI
137        ] = AttributeStatement.DEFAULT_ELEMENT_NAME.prefix
138
139        attributeETreeObject = AttributeETreeObject()
140
141        for attribute in attributeStatement.attributes:
142            # Factory enables support for multiple attribute types
143            attributeElem = attributeETreeObject.create(attribute,
144                                        **attributeValueETreeObjectFactoryKw)
145            self._elem.append(attributeElem)
146       
147        return self._elem
148   
149
150class AttributeETreeObject(ETreeObject):
151    """ElementTree XML representation of SAML Attribute object.  Extend
152    to make Attribute types""" 
153
154    def create(self, 
155               attribute, 
156               **attributeValueETreeObjectFactoryKw):
157        """Make 'Attribute' element"""
158       
159        if not isinstance(attribute, Attribute):
160            raise TypeError("Expecting %r type got: %r"%(Attribute, attribute))
161           
162        self._elem = ElementTree.Element(str(Attribute.DEFAULT_ELEMENT_NAME))
163        ElementTree._namespace_map[
164            Attribute.DEFAULT_ELEMENT_NAME.namespaceURI
165        ] = Attribute.DEFAULT_ELEMENT_NAME.prefix
166       
167           
168        if attribute.friendlyName:
169            self._elem.set(Attribute.FRIENDLY_NAME_ATTRIB_NAME,
170                           attribute.friendlyName) 
171        if attribute.name:
172            self._elem.set(Attribute.NAME_ATTRIB_NAME, attribute.name)
173       
174        if attribute.nameFormat:
175            self._elem.set(Attribute.NAME_FORMAT_ATTRIB_NAME,
176                           attribute.nameFormat)
177
178        for attributeValue in attribute.attributeValues:
179            factory = AttributeValueETreeObjectFactory(
180                                        **attributeValueETreeObjectFactoryKw)
181           
182            attributeValueETreeObject = factory(attributeValue)
183           
184            attributeValueElem=attributeValueETreeObject.create(attributeValue)
185            self._elem.append(attributeValueElem)
186           
187        return self._elem
188 
189   
190class AttributeValueETreeObjectBase(ETreeObject):
191    """Base class ElementTree XML representation of SAML Attribute Value""" 
192   
193    def create(self, attributeValue):
194        """Make 'Attribute' XML element"""
195
196        if not isinstance(attributeValue, AttributeValue):
197            raise TypeError("Expecting %r type got: %r" % (AttributeValue, 
198                                                           attributeValue))
199           
200        self._elem = ElementTree.Element(
201                                    str(AttributeValue.DEFAULT_ELEMENT_NAME))
202        ElementTree._namespace_map[
203            AttributeValue.DEFAULT_ELEMENT_NAME.namespaceURI
204        ] = AttributeValue.DEFAULT_ELEMENT_NAME.prefix
205
206        return self._elem
207
208
209class XSStringAttributeValueETreeObject(AttributeValueETreeObjectBase):
210    """ElementTree XML representation of SAML String type Attribute Value""" 
211   
212    def create(self, attributeValue):
213        """Create an XML representation of the input SAML Attribute Value"""
214        super(XSStringAttributeValueETreeObject, self).create(attributeValue)
215       
216        if not isinstance(attributeValue, XSStringAttributeValue):
217            raise TypeError("Expecting %r type got: %r" % 
218                            (XSStringAttributeValue, attributeValue)) 
219       
220        # Have to explicitly add namespace declaration here rather use
221        # ElementTree._namespace_map because the prefixes are used for
222        # attributes not element names       
223        self._elem.set("%s:%s" % (SAMLConstants.XMLNS_PREFIX, 
224                                  SAMLConstants.XSD_PREFIX),
225                       SAMLConstants.XSD_NS)
226                                   
227        self._elem.set("%s:%s" % (SAMLConstants.XMLNS_PREFIX, 
228                                  SAMLConstants.XSI_PREFIX),
229                       SAMLConstants.XSI_NS)
230       
231        self._elem.set("%s:%s" % (SAMLConstants.XSI_PREFIX, 'type'), 
232                       "%s:%s" % (SAMLConstants.XSD_PREFIX, 
233                                  XSStringAttributeValue.TYPE_LOCAL_NAME))
234
235        self._elem.text = attributeValue.value
236
237        return self._elem
238
239
240class XSGroupRoleAttributeValueETreeObject(AttributeValueETreeObjectBase):
241    """ElementTree XML representation of Earth System Grid custom Group/Role
242    Attribute Value""" 
243
244    def create(self, attributeValue):
245        """Create an XML representation of the input SAML Attribute Value"""
246        super(XSGroupRoleAttributeValueETreeObject,self).create(attributeValue)
247       
248        if not isinstance(attributeValue, XSGroupRoleAttributeValue):
249            raise TypeError("Expecting %r type; got: %r" % 
250                            (XSGroupRoleAttributeValue, attributeValue))
251           
252        ElementTree._namespace_map[attributeValue.namespaceURI
253                                   ] = attributeValue.namespacePrefix
254       
255        self._elem.set(XSGroupRoleAttributeValue.GROUP_ATTRIB_NAME, 
256                       attributeValue.group)
257       
258        self._elem.set(XSGroupRoleAttributeValue.ROLE_ATTRIB_NAME, 
259                       attributeValue.role)
260
261        return self._elem
262
263
264class AttributeValueETreeObjectFactory(object):
265    """Factory for creating ElementTree representations of SAML Attribute
266    value types
267    """
268    classMap = {
269        XSStringAttributeValue: XSStringAttributeValueETreeObject
270    }
271   
272    def __init__(self, customClassMap={}): 
273        """Set-up a SAML class to ElementTree mapping
274        """
275        self.__classMap = AttributeValueETreeObjectFactory.classMap
276        for samlClass, etreeClass in customClassMap.items(): 
277            if not issubclass(samlClass, AttributeValue):
278                raise TypeError("Input custom class must be derived from %r, "
279                                "got %r instead" % (Attribute, samlClass))
280               
281            self.__classMap[samlClass] = etreeClass
282           
283    def __call__(self, samlObject):
284        """Create an ElementTree object based on the Attribute class type
285        passed in
286        """
287        if not isinstance(samlObject, AttributeValue):
288            raise TypeError("Expecting %r class got %r" % (AttributeValue, 
289                                                           type(samlObject)))
290           
291        xmlObjectClass = self.__classMap.get(samlObject.__class__)
292        if xmlObjectClass is None:
293            raise TypeError("no matching XMLObject class representation for "
294                            "SAML class %r" % samlObject.__class__)
295           
296        return xmlObjectClass()
297
298       
299class IssuerETreeObject(ETreeObject):
300    """Represent a SAML Issuer element in XML using ElementTree"""
301   
302    def create(self, issuer):
303        """Create an XML representation of the input SAML issuer object"""
304        if not isinstance(issuer, Issuer):
305            raise TypeError("Expecting %r class got %r" % (issuer, 
306                                                           type(Issuer)))
307        attrib = {
308            Issuer.FORMAT_ATTRIB_NAME: issuer.format
309        }
310        self._elem = ElementTree.Element(str(Issuer.DEFAULT_ELEMENT_NAME),
311                                         **attrib)
312        ElementTree._namespace_map[issuer.qname.namespaceURI
313                                   ] = issuer.qname.prefix
314                                   
315        self._elem.text = issuer.value
316
317        return self._elem
318
319       
320class NameIdETreeObject(ETreeObject):
321    """Represent a SAML Name Identifier in XML using ElementTree"""
322   
323    def create(self, nameID):
324        """Create an XML representation of the input SAML Name Identifier
325        object"""
326        if not isinstance(nameID, NameID):
327            raise TypeError("Expecting %r class got %r" % (nameID, 
328                                                           type(NameID)))
329        attrib = {
330            NameID.FORMAT_ATTRIB_NAME: nameID.format
331        }
332        self._elem = ElementTree.Element(str(NameID.DEFAULT_ELEMENT_NAME),
333                                         **attrib)
334       
335        ElementTree._namespace_map[nameID.qname.namespaceURI
336                                   ] = nameID.qname.prefix
337       
338        self._elem.text = nameID.value
339
340        return self._elem
341
342
343class SubjectETreeObject(ETreeObject):
344    """Represent a SAML Subject in XML using ElementTree"""
345   
346    def create(self, subject):
347        """Create an XML representation of the input SAML subject object"""
348        if not isinstance(subject, Subject):
349            raise TypeError("Expecting %r class got %r" % (subject, 
350                                                           type(Subject)))
351           
352        self._elem = ElementTree.Element(str(Subject.DEFAULT_ELEMENT_NAME))
353       
354        ElementTree._namespace_map[
355            AttributeQuery.DEFAULT_ELEMENT_NAME.namespaceURI
356        ] = AttributeQuery.DEFAULT_ELEMENT_NAME.prefix
357
358           
359        nameIdETreeObject = NameIdETreeObject()
360        nameIdElem = nameIdETreeObject.create(subject.nameID)
361        self._elem.append(nameIdElem)
362       
363        return self._elem
364
365     
366class AttributeQueryETreeObject(ETreeObject, IssueInstantXMLObject):
367    """Represent a SAML Attribute Query in XML using ElementTree"""
368    def __init__(self):
369        ETreeObject.__init__(self)
370        IssueInstantXMLObject.__init__(self)
371       
372    def create(self, 
373               attributeQuery, 
374               **attributeValueETreeObjectFactoryKw):
375        """Create an XML representation of the input SAML Attribute Query
376        object
377        """
378        if not isinstance(attributeQuery, AttributeQuery):
379            raise TypeError("Expecting %r class got %r" % (AttributeQuery, 
380                                                        type(attributeQuery)))
381           
382       
383        issueInstant = AttributeQueryETreeObject.datetime2Str(
384                                                attributeQuery.issueInstant)
385        attrib = {
386            AttributeQuery.ID_ATTRIB_NAME: attributeQuery.id,
387            AttributeQuery.ISSUE_INSTANT_ATTRIB_NAME: issueInstant,
388           
389            # Nb. Version is a SAMLVersion instance and requires explicit cast
390            AttributeQuery.VERSION_ATTRIB_NAME: str(attributeQuery.version)
391        }
392                 
393        self._elem = ElementTree.Element(
394                                    str(AttributeQuery.DEFAULT_ELEMENT_NAME),
395                                    **attrib)
396       
397        ElementTree._namespace_map[
398            AttributeQuery.DEFAULT_ELEMENT_NAME.namespaceURI
399        ] = AttributeQuery.DEFAULT_ELEMENT_NAME.prefix
400       
401           
402        issuerETreeObject = IssuerETreeObject()
403        issuerElem = issuerETreeObject.create(attributeQuery.issuer)
404        self._elem.append(issuerElem)
405
406        subjectETreeObject = SubjectETreeObject()
407        subjectElem = subjectETreeObject.create(attributeQuery.subject)
408       
409        self._elem.append(subjectElem)
410
411        attributeETreeObject = AttributeETreeObject()
412
413        for attribute in attributeQuery.attributes:
414            # Factory enables support for multiple attribute types
415            attributeElem = attributeETreeObject.create(attribute,
416                                        **attributeValueETreeObjectFactoryKw)
417            self._elem.append(attributeElem)
418       
419        return self._elem
Note: See TracBrowser for help on using the repository browser.