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

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

Working authz lite integration tests with integrated SAML Attribute Authority interface to authz middleware: the old NDG Attribute Authority SOAP/WSDL interface is completely removed as a dependency.

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