source: TI12-security/trunk/ndg_saml/ndg/soap/etree.py @ 7662

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/ndg_saml/ndg/soap/etree.py
Revision 7662, 14.7 KB checked in by pjkersha, 9 years ago (diff)

Added SOAPFault handling for ndg.soap package. Needs integration into ndg.saml.saml2.binding.soap.server.wsgi.queryinterface to enable SAML query interface to do better error reporting.

Line 
1"""SOAP client package - XML representation using ElementTree
2
3NERC DataGrid Project
4"""
5__author__ = "P J Kershaw"
6__date__ = "27/07/09"
7__copyright__ = "(C) 2010 Science and Technology Facilities Council"
8__license__ = "http://www.apache.org/licenses/LICENSE-2.0"
9__contact__ = "Philip.Kershaw@stfc.ac.uk"
10__revision__ = '$Id: etree.py 7131 2010-06-30 13:37:48Z pjkersha $'
11import logging
12log = logging.getLogger(__name__)
13   
14try: # python 2.5
15    from xml.etree import cElementTree, ElementTree
16except ImportError:
17    # if you've installed it yourself it comes this way
18    import cElementTree, ElementTree
19
20# ElementTree helper functions
21from ndg.soap.utils.etree import QName
22
23from ndg.soap import (SOAPObject, SOAPEnvelopeBase, SOAPHeaderBase, 
24                      SOAPBodyBase, SOAPFaultBase)
25from ndg.soap import SOAPFaultException as SOAPFaultExceptionBase
26
27
28class ETreeSOAPExtensions(object): 
29    """Utility to enable addition of core ElementTree specific attributes and
30    methods for ElementTree SOAP implementation
31    """
32    def __init__(self):
33        self.__qname = None
34        self.__elem = None
35
36    def _getQname(self):
37        return self.__qname
38
39    def _setQname(self, value):
40        if not isinstance(value, QName):
41            raise TypeError('Expecting %r for "qname" attribute; got %r' %
42                            (QName, type(value)))
43        self.__qname = value
44
45    def _getElem(self):
46        return self.__elem
47
48    def _setElem(self, value):
49        if not ElementTree.iselement(value):
50            raise TypeError('Expecting %r for "elem" attribute; got %r' %
51                            (ElementTree.Element, type(value)))
52        self.__elem = value
53       
54    qname = property(_getQname, _setQname, None, "Qualified name object")
55    elem = property(_getElem, _setElem, None, "Root element")
56
57    @staticmethod
58    def _serialize(elem):
59         """Serialise element tree into string"""
60         return cElementTree.tostring(elem)
61       
62    @classmethod
63    def _prettyPrint(cls, elem):
64        """Basic pretty printing separating each element on to a new line"""
65        xml = cls._serialize(elem)
66        xml = ">\n".join(xml.split(">"))
67        xml = "\n<".join(xml.split("<"))
68        xml = '\n'.join(xml.split('\n\n'))
69        return xml
70
71    @staticmethod
72    def _parse(source):
73        """Read in the XML from source
74        @type source: basestring/file
75        @param source: file path to XML file or file object
76        """
77        tree = ElementTree.parse(source)
78        elem = tree.getroot()
79       
80        return elem       
81
82
83class SOAPHeader(SOAPHeaderBase, ETreeSOAPExtensions):
84    """ElementTree implementation of SOAP Header object"""
85   
86    DEFAULT_ELEMENT_NAME = QName(SOAPHeaderBase.DEFAULT_ELEMENT_NS,
87                               tag=SOAPHeaderBase.DEFAULT_ELEMENT_LOCAL_NAME,
88                               prefix=SOAPHeaderBase.DEFAULT_ELEMENT_NS_PREFIX)
89   
90    def __init__(self):
91        SOAPHeaderBase.__init__(self)
92        ETreeSOAPExtensions.__init__(self)
93       
94        self.qname = QName(SOAPHeaderBase.DEFAULT_ELEMENT_NS, 
95                           tag=SOAPHeaderBase.DEFAULT_ELEMENT_LOCAL_NAME, 
96                           prefix=SOAPHeaderBase.DEFAULT_ELEMENT_NS_PREFIX)
97
98    def create(self):
99        """Create header ElementTree element"""
100       
101        self.elem = ElementTree.Element(str(self.qname))
102        ElementTree._namespace_map[SOAPHeaderBase.DEFAULT_ELEMENT_NS
103                                   ] = SOAPHeaderBase.DEFAULT_ELEMENT_NS_PREFIX
104   
105    def serialize(self):
106        """Serialise element tree into string"""
107        return ETreeSOAPExtensions._serialize(self.elem)
108   
109    def prettyPrint(self):
110        """Basic pretty printing separating each element on to a new line"""
111        return ETreeSOAPExtensions._prettyPrint(self.elem)
112
113
114class SOAPBody(SOAPBodyBase, ETreeSOAPExtensions):
115    """ElementTree based implementation for SOAP Body object"""
116   
117    DEFAULT_ELEMENT_NAME = QName(SOAPBodyBase.DEFAULT_ELEMENT_NS,
118                                 tag=SOAPBodyBase.DEFAULT_ELEMENT_LOCAL_NAME,
119                                 prefix=SOAPBodyBase.DEFAULT_ELEMENT_NS_PREFIX)
120   
121    def __init__(self):
122        SOAPBodyBase.__init__(self)
123        ETreeSOAPExtensions.__init__(self)
124       
125        self.qname = QName(SOAPBodyBase.DEFAULT_ELEMENT_NS, 
126                           tag=SOAPBodyBase.DEFAULT_ELEMENT_LOCAL_NAME, 
127                           prefix=SOAPBodyBase.DEFAULT_ELEMENT_NS_PREFIX)
128        self.__fault = None
129       
130    # Test for SOAPFault present
131    @property
132    def hasSOAPFault(self):
133        """Boolean True if this SOAP BOdy contains a SOAPFault instance"""
134        return self.fault is not None
135   
136    def _getFault(self):
137        return self.__fault
138   
139    def _setFault(self, value):
140        if not isinstance(value, SOAPFault):
141            raise TypeError('Expecting %r type for "fault" attribute; got %r' %
142                            (SOAPFault, type(value)))
143        self.__fault = value
144       
145    fault = property(_getFault, _setFault, doc="SOAP Fault")
146       
147    def create(self):
148        """Create header ElementTree element"""
149        self.elem = ElementTree.Element(str(self.qname))
150        if self.hasSOAPFault:
151            self.fault.create()
152            self.elem.append(self.fault.elem)
153   
154    def serialize(self):
155        """Serialise element tree into string"""
156        return ETreeSOAPExtensions._serialize(self.elem)
157 
158    def parse(self, source):
159        """This method ONLY parses a SOAPFault IF one is found"""
160        if ElementTree.iselement(source):
161            self.elem = source
162        else:
163            self.elem = self._parse(source)
164                 
165        for elem in self.elem:
166            localName = QName.getLocalPart(elem.tag)
167            if localName == SOAPFault.DEFAULT_ELEMENT_LOCAL_NAME:
168                if self.fault is None:
169                    self.fault = SOAPFault()
170                   
171                self.fault.parse(elem)
172               
173                # Only one SOAPFault element is expected
174                break
175           
176    def prettyPrint(self):
177        """Basic pretty printing separating each element on to a new line"""
178        return ETreeSOAPExtensions._prettyPrint(self.elem)
179   
180
181class SOAPFault(SOAPFaultBase, ETreeSOAPExtensions):
182    """Extend SOAP Fault for ElementTree parsing and serialisation"""
183   
184    DEFAULT_ELEMENT_NAME = QName(SOAPFaultBase.DEFAULT_ELEMENT_NS,
185                                 tag=SOAPFaultBase.DEFAULT_ELEMENT_LOCAL_NAME,
186                                 prefix=SOAPFaultBase.DEFAULT_ELEMENT_NS_PREFIX)
187   
188    FAULT_CODE_ELEMENT_NAME = QName(SOAPFaultBase.DEFAULT_ELEMENT_NS,
189                             tag=SOAPFaultBase.FAULT_CODE_ELEMENT_LOCAL_NAME,
190                             prefix=SOAPFaultBase.DEFAULT_ELEMENT_NS_PREFIX)
191   
192    FAULT_STRING_ELEMENT_NAME = QName(SOAPFaultBase.DEFAULT_ELEMENT_NS,
193                             tag=SOAPFaultBase.FAULT_STRING_ELEMENT_LOCAL_NAME,
194                             prefix=SOAPFaultBase.DEFAULT_ELEMENT_NS_PREFIX)
195   
196    FAULT_ACTOR_ELEMENT_NAME = QName(SOAPFaultBase.DEFAULT_ELEMENT_NS,
197                             tag=SOAPFaultBase.FAULT_ACTOR_ELEMENT_LOCAL_NAME,
198                             prefix=SOAPFaultBase.DEFAULT_ELEMENT_NS_PREFIX)
199   
200    DETAIL_ELEMENT_NAME = QName(SOAPFaultBase.DEFAULT_ELEMENT_NS,
201                                tag=SOAPFaultBase.DETAIL_ELEMENT_LOCAL_NAME,
202                                prefix=SOAPFaultBase.DEFAULT_ELEMENT_NS_PREFIX)
203   
204    def __init__(self, *arg, **kw):
205        SOAPFaultBase.__init__(self, *arg, **kw)
206        ETreeSOAPExtensions.__init__(self)
207       
208        self.qname = QName(SOAPFaultBase.DEFAULT_ELEMENT_NS, 
209                           tag=SOAPFaultBase.DEFAULT_ELEMENT_LOCAL_NAME, 
210                           prefix=SOAPFaultBase.DEFAULT_ELEMENT_NS_PREFIX)
211   
212    def _setFaultCode(self, value):
213        """Override to enable ns prefix to be inferred if not added in already
214        """
215        if value.startswith(self.__class__.FAULT_CODE_ELEMENT_NAME.prefix):
216            _value = value
217        else:
218            _value = "%s:%s" % (SOAPFaultBase.DEFAULT_ELEMENT_NS_PREFIX, value)
219       
220        SOAPFaultBase._setFaultCode(self, _value)
221       
222    faultCode = property(SOAPFaultBase._getFaultCode, _setFaultCode,
223                         doc="Fault code")
224   
225    def create(self):
226        """Create Fault ElementTree element"""
227        self.elem = ElementTree.Element(str(self.qname))
228       
229        faultStringElem = ElementTree.Element(
230                                str(self.__class__.FAULT_STRING_ELEMENT_NAME))
231        faultStringElem.text = self.faultString
232        self.elem.append(faultStringElem)
233       
234        faultCodeElem = ElementTree.Element(
235                                str(self.__class__.FAULT_CODE_ELEMENT_NAME))
236        faultCodeElem.text = self.faultCode
237        self.elem.append(faultCodeElem)
238                         
239        if self.faultActor is not None:
240            faultActorElem = ElementTree.Element(
241                                str(self.__class__.FAULT_ACTOR_ELEMENT_NAME))
242            faultActorElem.text = self.faultActor
243            self.elem.append(faultActorElem)
244                             
245        if self.detail is not None:
246            detailElem = ElementTree.Element(
247                                str(self.__class__.DETAIL_ELEMENT_NAME))
248           
249            if ElementTree.iselement(self.detail):
250                detailElem.append(self.detail)
251               
252            elif isinstance(self.detail, basestring): 
253                detailElem.text = self.detail
254            else:
255                raise TypeError('Expecting ElementTree.Element or string '
256                                'type for SOAPFault detail; got %r' %
257                                type(self.detail))
258               
259            self.elem.append(detailElem)
260   
261    def parse(self, source):
262        """Parse SOAPFault element"""
263        if ElementTree.iselement(source):
264            self.elem = source
265        else:
266            self.elem = self._parse(source)
267                 
268        for elem in self.elem:
269            localName = QName.getLocalPart(elem.tag)
270            if localName == SOAPFault.FAULT_CODE_ELEMENT_LOCAL_NAME:
271                self.faultCode = elem.text.strip() 
272               
273            elif localName == SOAPFault.FAULT_STRING_ELEMENT_LOCAL_NAME:
274                self.faultString = elem.text.strip()
275           
276            elif localName == SOAPFault.FAULT_ACTOR_ELEMENT_LOCAL_NAME:
277                self.faultActor = elem.text.strip()   
278           
279            elif localName == SOAPFault.DETAIL_ELEMENT_LOCAL_NAME:
280                # Make no assumptions about the content, simply assing the
281                # element to the detail attribute
282                self.detail = elem   
283                 
284            else:
285                faultCode = str(QName(SOAPFault.DEFAULT_ELEMENT_NS, 
286                                  tag=SOAPFault.CLIENT_FAULT_CODE, 
287                                  prefix=SOAPFault.DEFAULT_ELEMENT_NS_PREFIX))
288               
289                raise SOAPFaultException('Invalid child element in SOAP '
290                                         'Fault "%s" for stream %r' % 
291                                         (localName, source),
292                                         faultCode)
293           
294    def serialize(self):
295        """Serialise element tree into string"""
296        return ETreeSOAPExtensions._serialize(self.elem)
297   
298    def prettyPrint(self):
299        """Basic pretty printing separating each element on to a new line"""
300        return ETreeSOAPExtensions._prettyPrint(self.elem)
301
302
303class SOAPFaultException(SOAPFaultExceptionBase):
304    """Extend SOAP Fault Exception base class to use ElementTree based
305    SOAP Fault implementation for parsing and serialisation"""
306    SOAP_FAULT_CLASS = SOAPFault
307   
308   
309class SOAPEnvelope(SOAPEnvelopeBase, ETreeSOAPExtensions):
310    """ElementTree based SOAP implementation"""
311    DEFAULT_ELEMENT_NAME = QName(SOAPEnvelopeBase.DEFAULT_ELEMENT_NS,
312                             tag=SOAPEnvelopeBase.DEFAULT_ELEMENT_LOCAL_NAME,
313                             prefix=SOAPEnvelopeBase.DEFAULT_ELEMENT_NS_PREFIX)
314
315    def __init__(self):
316        SOAPEnvelopeBase.__init__(self)
317        ETreeSOAPExtensions.__init__(self)
318       
319        self.qname = QName(SOAPEnvelopeBase.DEFAULT_ELEMENT_NS, 
320                           tag=SOAPEnvelopeBase.DEFAULT_ELEMENT_LOCAL_NAME, 
321                           prefix=SOAPEnvelopeBase.DEFAULT_ELEMENT_NS_PREFIX)
322        self.__header = SOAPHeader()
323        self.__body = SOAPBody()
324
325    def _getHeader(self):
326        return self.__header
327
328    def _setHeader(self, value):
329        if not isinstance(value, SOAPHeader):
330            raise TypeError('Expecting %r for "header" attribute; got %r' %
331                            (SOAPHeader, type(value)))
332        self.__header = value
333
334    def _getBody(self):
335        return self.__body
336
337    def _setBody(self, value):
338        if not isinstance(value, SOAPBody):
339            raise TypeError('Expecting %r for "header" attribute; got %r' %
340                            (SOAPBody, type(value)))
341        self.__body = value
342
343    header = property(_getHeader, _setHeader, None, "SOAP header object")
344    body = property(_getBody, _setBody, None, "SOAP body object")
345
346    def create(self):
347        """Create SOAP Envelope with header and body"""
348       
349        self.elem = ElementTree.Element(str(self.qname))
350           
351        self.header.create()
352        self.elem.append(self.header.elem)
353       
354        self.body.create()
355        self.elem.append(self.body.elem)
356   
357    def serialize(self):
358        """Serialise element tree into string"""
359        return ETreeSOAPExtensions._serialize(self.elem)
360   
361    def prettyPrint(self):
362        """Basic pretty printing separating each element onto a new line"""
363        return ETreeSOAPExtensions._prettyPrint(self.elem)
364   
365    def parse(self, source):
366        """Parse SOAP Envelope"""
367        self.elem = self._parse(source) 
368       
369        for elem in self.elem:
370            localName = QName.getLocalPart(elem.tag)
371            if localName == SOAPHeader.DEFAULT_ELEMENT_LOCAL_NAME:
372                self.header.elem = elem
373               
374            elif localName == SOAPBody.DEFAULT_ELEMENT_LOCAL_NAME:
375                self.body.parse(elem)
376            else:
377                faultCode = str(QName(SOAPEnvelopeBase.DEFAULT_ELEMENT_NS, 
378                                  tag=SOAPFault.CLIENT_FAULT_CODE, 
379                                  prefix=SOAPFault.DEFAULT_ELEMENT_NS_PREFIX))
380               
381                raise SOAPFaultException('Invalid child element in SOAP '
382                                         'Envelope "%s" for stream %r' % 
383                                         (localName, source),
384                                         faultCode)
Note: See TracBrowser for help on using the repository browser.