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

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

saml.xml.etree: important fixes to ElementTree based Status element serialisation and de-serialisation
ndg.security.server.attributeauthority: added clockSkew parameter to provide some leeway in SAML attribute query clock checks. Also added StatusMessage? element for additional error info in responses.
ndg.security.common.soap.client: added check of HTTP Content-type in SOAP responses.

Line 
1"""SOAP client module for NDG Security - initially for use with SAML SOAP
2binding based Attribute Authority interface
3
4NERC DataGrid Project
5"""
6__author__ = "P J Kershaw"
7__date__ = "27/07/09"
8__copyright__ = ""
9__license__ = "BSD - see LICENSE file in top-level directory"
10__contact__ = "Philip.Kershaw@stfc.ac.uk"
11__revision__ = '$Id:$'
12
13from ndg.security.common.soap import SOAPEnvelopeBase
14
15class SOAPClientError(Exception):
16    """Base class for SOAP Client exceptions"""
17
18class SOAPParseError(SOAPClientError):
19    """Error parsing SOAP response"""
20   
21           
22class SOAPClientBase(object):
23    """Handle client request to a SOAP Service
24    @cvar RESPONSE_CONTENT_TYPES: expected content type to be returned in a response
25    from a service
26    @type RESPONSE_CONTENT_TYPES: string
27    """
28    RESPONSE_CONTENT_TYPES = ('text/xml', )
29   
30    def __init__(self):
31        self.__responseEnvelopeClass = None
32
33    def _getResponseEnvelopeClass(self):
34        return self.__responseEnvelopeClass
35
36    def _setResponseEnvelopeClass(self, value):
37        if not issubclass(value, SOAPEnvelopeBase):
38            raise TypeError("Setting SOAP envelope class: expecting %r, got "
39                            "%r" % (SOAPEnvelopeBase, type(value)))
40        self.__responseEnvelopeClass = value
41
42    responseEnvelopeClass = property(fget=_getResponseEnvelopeClass, 
43                                     fset=_setResponseEnvelopeClass, 
44                                     doc="Set the class for handling "
45                                         "the SOAP envelope responses")
46     
47    def send(self, soapRequest):
48        raise NotImplementedError()
49
50
51class _SoapIOBase(object):
52    """Base class for request and response classes"""
53   
54    def __init__(self):
55        self.__envelope = None
56
57    def _getEnvelope(self):
58        return self.__envelope
59
60    def _setEnvelope(self, value):
61        if not isinstance(value, SOAPEnvelopeBase):
62            raise TypeError('Setting SOAP envelope object: expecting %r; got '
63                            '%r' % (SOAPEnvelopeBase, type(value)))
64                           
65        self.__envelope = value
66
67    envelope = property(fget=_getEnvelope, 
68                        fset=_setEnvelope, 
69                        doc="SOAP Envelope object used in request/response")
70
71       
72class SOAPRequestBase(object):
73    """Interface for SOAP requests"""
74    def __init__(self):
75        self.__url = None
76        self.__envelope = None
77
78    def _getUrl(self):
79        return self.__url
80
81    def _setUrl(self, value):
82        if not isinstance(value, basestring):
83            raise TypeError('Setting request URL: expecting %r; got '
84                            '%r' % (basestring, type(value)))
85        self.__url = value
86
87    url = property(fget=_getUrl, fset=_setUrl, doc="URL of SOAP endpoint")
88
89   
90class SOAPResponseBase(_SoapIOBase):
91    """Interface for SOAP responses"""
92
93import httplib
94import urllib2
95from urllib import addinfourl
96 
97class UrlLib2SOAPClientError(SOAPClientError):
98    """Specialisation to enable the urllib2 response to be included in the
99    exception instance as context information for the caller
100    """
101    URLLIB2RESPONSE_TYPE = addinfourl
102   
103    def __init__(self, *arg, **kw):
104        Exception.__init__(self, *arg, **kw)
105        self.__urllib2Response = None
106
107    def _getUrllib2Response(self):
108        return self.__urllib2Response
109
110    def _setUrllib2Response(self, value):
111        if not isinstance(value, UrlLib2SOAPClientError.URLLIB2RESPONSE_TYPE):
112            raise TypeError('Expecting %r type for "urllib2Response"; got %r' %
113                            (UrlLib2SOAPClientError.URLLIB2RESPONSE_TYPE, 
114                             type(value)))
115        self.__urllib2Response = value
116
117    urllib2Response = property(_getUrllib2Response, 
118                               _setUrllib2Response, 
119                               doc="Urllib2Response")
120
121
122class SOAPResponseError(UrlLib2SOAPClientError):
123    """Raise for invalid SOAP response from server"""
124       
125class HTTPException(UrlLib2SOAPClientError):
126    """Server returned HTTP code error code"""
127
128class UrlLib2SOAPRequest(SOAPRequestBase): 
129    """Interface for UrlLib2 based SOAP Requests"""
130   
131   
132class UrlLib2SOAPResponse(SOAPResponseBase):
133    """Interface for UrlLib2 based SOAP Responses"""
134    def __init__(self):
135        self.__fileobject = None
136
137    def _getFileobject(self):
138        return self.__fileobject
139
140    fileobject = property(fget=_getFileobject,
141                          doc="urllib2 file object returned from request")
142
143   
144class UrlLib2SOAPClient(SOAPClientBase):
145    """urllib2 based SOAP Client"""
146   
147    def __init__(self):
148        super(UrlLib2SOAPClient, self).__init__()
149        self.__openerDirector = urllib2.OpenerDirector()
150        self.__openerDirector.add_handler(urllib2.UnknownHandler())
151        self.__openerDirector.add_handler(urllib2.HTTPHandler())
152        self.__timeout = None
153
154    def _getTimeout(self):
155        return self.__timeout
156
157    def _setTimeout(self, value):
158        if not isinstance(value, (int, float)):
159            raise TypeError("Setting request timeout: got %r, expecting int "
160                            "float type" % type(value))
161        self.__timeout = value
162
163    timeout = property(fget=_getTimeout, 
164                       fset=_setTimeout, 
165                       doc="Timeout (seconds) for requests")
166
167    def _getOpenerDirector(self):
168        return self.__openerDirector
169
170    def _setOpenerDirector(self, value):
171        """This shouldn't need to be used much in practice because __init__
172        creates one"""
173        if not isinstance(value, urllib2.OpenerDirector):
174            raise TypeError("Setting opener: expecting %r; got %r" % 
175                            (urllib2.OpenerDirector, type(value)))
176        self.__openerDirector = value
177
178    openerDirector = property(fget=_getOpenerDirector, 
179                              fset=_setOpenerDirector, 
180                              doc="urllib2.OpenerDirector defines the "
181                                  "opener(s) for handling requests")
182   
183    def send(self, soapRequest):
184        """Make a request to the given URL with a SOAP Request object"""
185       
186        if not isinstance(soapRequest, UrlLib2SOAPRequest):
187            raise TypeError('UrlLib2SOAPClient.send: expecting %r '
188                            'derived type for SOAP request, got %r' % 
189                            (self.responseEnvelopeClass, type(soapRequest)))
190           
191        if not isinstance(soapRequest.envelope, self.responseEnvelopeClass):
192            raise TypeError('UrlLib2SOAPClient.send: expecting %r '
193                            'derived type for SOAP envelope, got %r' % 
194                            (self.responseEnvelopeClass, type(soapRequest)))
195                           
196        if self.timeout is not None:
197            arg = (self.timeout,)
198        else:
199            arg = ()
200           
201        soapRequestStr = soapRequest.envelope.serialize()
202        soapResponse = UrlLib2SOAPResponse()
203        response = self.openerDirector.open(soapRequest.url, 
204                                            soapRequestStr, 
205                                            *arg)
206        if response.code != httplib.OK:
207            output = response.read()
208            excep = HTTPException("Response is: %d %s" % (response.code, 
209                                                          response.msg))
210            excep.urllib2Response = response
211            raise excep
212       
213        if response.headers.typeheader not in \
214           UrlLib2SOAPClient.RESPONSE_CONTENT_TYPES:
215            responseType = ', '.join(UrlLib2SOAPClient.RESPONSE_CONTENT_TYPES)
216            excep = SOAPResponseError("Expecting %r response type; got %r for "
217                                      "request to [%s]" % 
218                                      (responseType, 
219                                       response.headers.typeheader,
220                                       soapRequest.url))
221            excep.urllib2Response = response
222            raise excep
223           
224        soapResponse.fileObject = response
225        soapResponse.envelope = self.responseEnvelopeClass() 
226       
227        try:
228            soapResponse.envelope.parse(soapResponse.fileObject)
229        except Exception, e:
230            raise SOAPParseError("%r type error raised parsing response for "
231                                 "request to [%s]: %s"
232                                 % (type(e), soapRequest.url, e))
233           
234        return soapResponse
Note: See TracBrowser for help on using the repository browser.