source: TI12-security/trunk/python/ndg_security_common/ndg/security/common/soap.py @ 6069

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg_security_common/ndg/security/common/soap.py@6069
Revision 6069, 9.8 KB checked in by pjkersha, 10 years ago (diff)

Re-release as rc1

Line 
1"""SOAP implementation
2
3Initially for use with SAML SOAP Binding to Attribute Authority.  This itself
4uses ElementTree.  This SOAP interface provides an ElementTree interface to
5support it
6
7NERC DataGrid Project"""
8__author__ = "P J Kershaw"
9__date__ = "24/07/09"
10__copyright__ = "(C) 2009 Science and Technology Facilities Council"
11__license__ = "BSD - see LICENSE file in top-level directory"
12__contact__ = "Philip.Kershaw@stfc.ac.uk"
13__revision__ = '$Id: $'
14import logging
15log = logging.getLogger(__name__)
16
17from ndg.security.common.utils.etree import QName
18
19class SOAPException(Exception):
20    """Base SAOP Exception class"""
21   
22class SOAPFault(SOAPException):
23    """SOAP Fault"""
24   
25class SOAPObject(object):
26    """Base class for SOAP envelope, header and body elements"""
27   
28    ELEMENT_PREFIX = "SOAP-ENV"
29    SOAP11_NS = "http://schemas.xmlsoap.org/soap/envelope/"
30    SOAP12_NS = "http://www.w3.org/2003/05/soap-envelope"
31    DEFAULT_NS = SOAP11_NS
32   
33    def create(self):
34        raise NotImplementedError()
35   
36    def parse(self):
37        raise NotImplementedError()
38   
39    def serialize(self):
40        raise NotImplementedError()
41   
42    def prettyPrint(self):
43        raise NotImplementedError()
44 
45   
46class SOAPEnvelopeBase(SOAPObject):
47    """SOAP Envelope"""
48   
49    DEFAULT_ELEMENT_LOCAL_NAME = "Envelope"
50    DEFAULT_ELEMENT_NS = SOAPObject.DEFAULT_NS
51    DEFAULT_ELEMENT_NS_PREFIX = SOAPObject.ELEMENT_PREFIX
52    DEFAULT_ELEMENT_NAME = QName(DEFAULT_ELEMENT_NS,
53                                 tag=DEFAULT_ELEMENT_LOCAL_NAME,
54                                 prefix=DEFAULT_ELEMENT_NS_PREFIX)
55   
56    soapHeader = property()
57    soapBody = property()
58   
59   
60class SOAPHeaderBase(SOAPObject):
61    """SOAP Header base class"""
62   
63    DEFAULT_ELEMENT_LOCAL_NAME = "Header"
64    DEFAULT_ELEMENT_NS = SOAPObject.DEFAULT_NS
65    DEFAULT_ELEMENT_NS_PREFIX = SOAPObject.ELEMENT_PREFIX
66    DEFAULT_ELEMENT_NAME = QName(DEFAULT_ELEMENT_NS,
67                                 tag=DEFAULT_ELEMENT_LOCAL_NAME,
68                                 prefix=DEFAULT_ELEMENT_NS_PREFIX)
69   
70       
71class SOAPBodyBase(SOAPObject):
72    """SOAP Body base class"""
73   
74    DEFAULT_ELEMENT_LOCAL_NAME = "Body"
75    DEFAULT_ELEMENT_NS = SOAPObject.DEFAULT_NS
76    DEFAULT_ELEMENT_NS_PREFIX = SOAPObject.ELEMENT_PREFIX
77    DEFAULT_ELEMENT_NAME = QName(DEFAULT_ELEMENT_NS,
78                                 tag=DEFAULT_ELEMENT_LOCAL_NAME,
79                                 prefix=DEFAULT_ELEMENT_NS_PREFIX)
80   
81   
82# ElementTree Specific implementations start here
83# TODO: refactor into a separate module
84try: # python 2.5
85    from xml.etree import cElementTree as ElementTree
86except ImportError:
87    # if you've installed it yourself it comes this way
88    import cElementTree as ElementTree
89
90from ndg.security.common.utils import canonicalize, getLocalName
91
92
93class ETreeSOAPExtensions(object): 
94    """Utility to enable addition of core ElementTree specific attributes and
95    methods for ElementTree SOAP implementation
96    """
97    def __init__(self):
98        self.__qname = None
99        self.__elem = None
100
101    def _getQname(self):
102        return self.__qname
103
104    def _setQname(self, value):
105        if not isinstance(value, QName):
106            raise TypeError('Expecting %r for "qname" attribute; got %r' %
107                            (QName, type(value)))
108        self.__qname = value
109
110    def _getElem(self):
111        return self.__elem
112
113    def _setElem(self, value):
114        if not ElementTree.iselement(value):
115            raise TypeError('Expecting %r for "elem" attribute; got %r' %
116                            (ElementTree.Element, type(value)))
117        self.__elem = value
118       
119    qname = property(_getQname, _setQname, None, "Qualified name object")
120    elem = property(_getElem, _setElem, None, "Root element")
121   
122    @staticmethod
123    def _serialize(elem):
124        """Serialise element tree into string"""
125       
126        # Make a basic check for the SOAP name space declaration, if the
127        # element is constructed from a call to ElementTree.parse it may not
128        # be present
129        namespaceDeclarationFound = False
130        soapElemNsDeclaration = (
131            'xmlns:%s' % SOAPObject.ELEMENT_PREFIX, 
132            SOAPObject.DEFAULT_NS
133        )
134        if soapElemNsDeclaration[0] not in elem.attrib:
135            log.warning("No SOAP namespace declaration found - adding one in")
136            elem.set(*soapElemNsDeclaration)
137       
138        return canonicalize(elem)
139   
140    @classmethod
141    def _prettyPrint(cls, elem):
142        """Basic pretty printing separating each element on to a new line"""
143        xml = cls._serialize(elem)
144        xml = ">\n".join(xml.split(">"))
145        xml = "\n<".join(xml.split("<"))
146        xml = '\n'.join(xml.split('\n\n'))
147        return xml
148
149    def _parse(self, source):
150        """Read in the XML from source
151        @type source: basestring/file
152        @param source: file path to XML file or file object
153        """
154        tree = ElementTree.parse(source)
155        elem = tree.getroot()
156       
157        return elem       
158
159
160class SOAPHeader(SOAPHeaderBase, ETreeSOAPExtensions):
161    """ElementTree implementation of SOAP Header object"""
162   
163    def __init__(self):
164        SOAPHeaderBase.__init__(self)
165        ETreeSOAPExtensions.__init__(self)
166       
167        self.qname = QName(SOAPHeaderBase.DEFAULT_ELEMENT_NS, 
168                           tag=SOAPHeaderBase.DEFAULT_ELEMENT_LOCAL_NAME, 
169                           prefix=SOAPHeaderBase.DEFAULT_ELEMENT_NS_PREFIX)
170
171    def create(self, makeNsDeclaration=True):
172        """Create header ElementTree element"""
173       
174        self.elem = ElementTree.Element(str(self.qname))
175        if makeNsDeclaration:
176            self.elem.set(
177                    "xmlns:%s" % SOAPHeaderBase.DEFAULT_ELEMENT_NS_PREFIX,
178                    SOAPHeaderBase.DEFAULT_ELEMENT_NS)
179   
180    def serialize(self):
181        """Serialise element tree into string"""
182        return ETreeSOAPExtensions._serialize(self.elem)
183   
184    def prettyPrint(self):
185        """Basic pretty printing separating each element on to a new line"""
186        return ETreeSOAPExtensions._prettyPrint(self.elem)
187
188
189class SOAPBody(SOAPBodyBase, ETreeSOAPExtensions):
190    """ElementTree based implementation for SOAP Body object"""
191   
192    def __init__(self):
193        SOAPBodyBase.__init__(self)
194        ETreeSOAPExtensions.__init__(self)
195       
196        self.qname = QName(SOAPBodyBase.DEFAULT_ELEMENT_NS, 
197                           tag=SOAPBodyBase.DEFAULT_ELEMENT_LOCAL_NAME, 
198                           prefix=SOAPBodyBase.DEFAULT_ELEMENT_NS_PREFIX)
199       
200    def create(self, makeNsDeclaration=True):
201        """Create header ElementTree element"""
202       
203        self.elem = ElementTree.Element(str(self.qname))
204        if makeNsDeclaration:
205            self.elem.set("xmlns:%s" % SOAPBodyBase.DEFAULT_ELEMENT_NS_PREFIX,
206                          SOAPBodyBase.DEFAULT_ELEMENT_NS)
207   
208    def serialize(self):
209        """Serialise element tree into string"""
210        return ETreeSOAPExtensions._serialize(self.elem)
211   
212    def prettyPrint(self):
213        """Basic pretty printing separating each element on to a new line"""
214        return ETreeSOAPExtensions._prettyPrint(self.elem)
215   
216
217class SOAPEnvelope(SOAPEnvelopeBase, ETreeSOAPExtensions):
218    """ElementTree based SOAP implementation"""
219
220    def __init__(self):
221        SOAPEnvelopeBase.__init__(self)
222        ETreeSOAPExtensions.__init__(self)
223       
224        self.qname = QName(SOAPEnvelopeBase.DEFAULT_ELEMENT_NS, 
225                             tag=SOAPEnvelopeBase.DEFAULT_ELEMENT_LOCAL_NAME, 
226                             prefix=SOAPEnvelopeBase.DEFAULT_ELEMENT_NS_PREFIX)
227        self.__header = SOAPHeader()
228        self.__body = SOAPBody()
229
230    def _getHeader(self):
231        return self.__header
232
233    def _setHeader(self, value):
234        if not isinstance(value, SOAPHeader):
235            raise TypeError('Expecting %r for "header" attribute; got %r' %
236                            (SOAPHeader, type(value)))
237        self.__header = value
238
239    def _getBody(self):
240        return self.__body
241
242    def _setBody(self, value):
243        if not isinstance(value, SOAPBody):
244            raise TypeError('Expecting %r for "header" attribute; got %r' %
245                            (SOAPBody, type(value)))
246        self.__body = value
247
248    header = property(_getHeader, _setHeader, None, "SOAP header object")
249    body = property(_getBody, _setBody, None, "SOAP body object")
250
251    def create(self, makeNsDeclaration=True):
252        """Create SOAP Envelope with header and body"""
253       
254        self.elem = ElementTree.Element(str(self.qname))
255        if makeNsDeclaration:
256            self.elem.set("xmlns:%s" % SOAPBodyBase.DEFAULT_ELEMENT_NS_PREFIX,
257                          SOAPBodyBase.DEFAULT_ELEMENT_NS)
258           
259        self.header.create(makeNsDeclaration=False)
260        self.elem.append(self.header.elem)
261       
262        self.body.create(makeNsDeclaration=False)
263        self.elem.append(self.body.elem)
264   
265    def serialize(self):
266        """Serialise element tree into string"""
267        return ETreeSOAPExtensions._serialize(self.elem)
268   
269    def prettyPrint(self):
270        """Basic pretty printing separating each element onto a new line"""
271        return ETreeSOAPExtensions._prettyPrint(self.elem)
272   
273    def parse(self, source):
274        self.elem = ETreeSOAPExtensions._parse(self, source) 
275       
276        for elem in self.elem:
277            localName = getLocalName(elem)
278            if localName == SOAPHeader.DEFAULT_ELEMENT_LOCAL_NAME:
279                self.header.elem = elem
280               
281            elif localName == SOAPBody.DEFAULT_ELEMENT_LOCAL_NAME:
282                self.body.elem = elem
283               
284            else:
285                raise SOAPFault('Invalid child element in SOAP Envelope "%s" '
286                                'for source %r' % (localName, source))
Note: See TracBrowser for help on using the repository browser.