1 | """MyProxy WSGI middleware - places a MyProxy client instance in environ for |
---|
2 | other downstream middleware or apps to access and use |
---|
3 | |
---|
4 | NERC DataGrid Project |
---|
5 | """ |
---|
6 | __author__ = "P J Kershaw" |
---|
7 | __date__ = "24/05/10" |
---|
8 | __copyright__ = "(C) 2010 Science and Technology Facilities Council" |
---|
9 | __license__ = "BSD - see LICENSE file in top-level directory" |
---|
10 | __contact__ = "Philip.Kershaw@stfc.ac.uk" |
---|
11 | __revision__ = "$Id: $" |
---|
12 | import logging |
---|
13 | log = logging.getLogger(__name__) |
---|
14 | import traceback |
---|
15 | import socket |
---|
16 | import httplib |
---|
17 | import base64 |
---|
18 | |
---|
19 | from webob import Request |
---|
20 | from OpenSSL import crypto |
---|
21 | |
---|
22 | from myproxy.client import MyProxyClient, MyProxyClientError |
---|
23 | from myproxy.server.wsgi.httpbasicauth import HttpBasicAuthResponseException |
---|
24 | |
---|
25 | |
---|
26 | class MyProxyClientMiddlewareError(Exception): |
---|
27 | """Runtime error with MyProxyClientMiddleware""" |
---|
28 | |
---|
29 | |
---|
30 | class MyProxyClientMiddlewareConfigError(MyProxyClientMiddlewareError): |
---|
31 | """Configuration error with MyProxyClientMiddleware""" |
---|
32 | |
---|
33 | |
---|
34 | class MyProxyClientMiddlewareBase(object): |
---|
35 | """Base class for common functionality |
---|
36 | |
---|
37 | @cvar CLIENT_ENV_KEYNAME_OPTNAME: ini file option which sets the key name |
---|
38 | in the WSGI environ for referring to the MyProxy client instance shared |
---|
39 | between MyProxy* middleware/apps |
---|
40 | @type CLIENT_ENV_KEYNAME_OPTNAME: string |
---|
41 | |
---|
42 | @cvar DEFAULT_CLIENT_ENV_KEYNAME: default value for key name set in the |
---|
43 | WSGI environ dict which refers to the MyProxy client instance shared |
---|
44 | between MyProxy* middleware/apps |
---|
45 | @type DEFAULT_CLIENT_ENV_KEYNAME: string |
---|
46 | |
---|
47 | @ivar __app: WSGI callable for next middleware or app in the WSGI stack |
---|
48 | @type __app: function |
---|
49 | |
---|
50 | @ivar __clientEnvironKeyName: key name set in the WSGI environ dict which |
---|
51 | refers to the MyProxy client instance shared between MyProxy* middleware/ |
---|
52 | apps |
---|
53 | @type __clientEnvironKeyName: string |
---|
54 | """ |
---|
55 | __slots__ = ( |
---|
56 | '__app', |
---|
57 | '__clientEnvironKeyName', |
---|
58 | ) |
---|
59 | |
---|
60 | CLIENT_ENV_KEYNAME_OPTNAME = 'clientEnvKeyName' |
---|
61 | DEFAULT_CLIENT_ENV_KEYNAME = ('myproxy.server.wsgi.middleware.' |
---|
62 | 'MyProxyClientMiddleware.myProxyClient') |
---|
63 | |
---|
64 | def __init__(self, app): |
---|
65 | """Create WSGI app and MyProxy client attributes |
---|
66 | @type app: function |
---|
67 | @param app: WSGI callable for next middleware or app in the WSGI stack |
---|
68 | """ |
---|
69 | self.__app = app |
---|
70 | self.__clientEnvironKeyName = None |
---|
71 | |
---|
72 | def _getClientEnvironKeyName(self): |
---|
73 | """Get MyProxyClient environ key name |
---|
74 | |
---|
75 | @rtype: basestring |
---|
76 | @return: MyProxyClient environ key name |
---|
77 | """ |
---|
78 | return self.__clientEnvironKeyName |
---|
79 | |
---|
80 | def _setClientEnvironKeyName(self, value): |
---|
81 | """Set MyProxyClient environ key name |
---|
82 | |
---|
83 | @type value: basestring |
---|
84 | @param value: MyProxyClient environ key name |
---|
85 | """ |
---|
86 | if not isinstance(value, basestring): |
---|
87 | raise TypeError('Expecting string type for "clientEnvironKeyName"; ' |
---|
88 | 'got %r type' % type(value)) |
---|
89 | self.__clientEnvironKeyName = value |
---|
90 | |
---|
91 | clientEnvironKeyName = property(fget=_getClientEnvironKeyName, |
---|
92 | fset=_setClientEnvironKeyName, |
---|
93 | doc="key name in environ for the " |
---|
94 | "MyProxyClient instance") |
---|
95 | |
---|
96 | @property |
---|
97 | def app(self): |
---|
98 | """Get Property method for reference to next WSGI application in call |
---|
99 | stack |
---|
100 | @rtype: function |
---|
101 | @return: WSGI application |
---|
102 | """ |
---|
103 | return self.__app |
---|
104 | |
---|
105 | @staticmethod |
---|
106 | def getStatusMessage(statusCode): |
---|
107 | '''Make a standard status message for use with start_response |
---|
108 | @type statusCode: int |
---|
109 | @param statusCode: HTTP status code |
---|
110 | @rtype: string |
---|
111 | @return: status code with standard message |
---|
112 | @raise KeyError: for invalid status code |
---|
113 | ''' |
---|
114 | return '%d %s' % (statusCode, httplib.responses[statusCode]) |
---|
115 | |
---|
116 | |
---|
117 | class MyProxyClientMiddleware(MyProxyClientMiddlewareBase): |
---|
118 | '''Create a MyProxy client and make it available to other middleware in the |
---|
119 | WSGI stack |
---|
120 | |
---|
121 | @cvar LOGON_FUNC_ENV_KEYNAME_OPTNAME: ini file option name to set the key |
---|
122 | name in WSGI environ dict to assign to the Logon function created by this |
---|
123 | middleware |
---|
124 | @type LOGON_FUNC_ENV_KEYNAME_OPTNAME: string |
---|
125 | |
---|
126 | @cvar DEFAULT_LOGON_FUNC_ENV_KEYNAME: default value for the key name in |
---|
127 | WSGI environ dict to assign to the Logon function created by this |
---|
128 | middleware |
---|
129 | @type DEFAULT_LOGON_FUNC_ENV_KEYNAME: string |
---|
130 | |
---|
131 | @cvar CERT_REQ_POST_PARAM_KEYNAME: HTTP POST field name for the |
---|
132 | certificate request posted in logon calls |
---|
133 | @type CERT_REQ_POST_PARAM_KEYNAME: string |
---|
134 | |
---|
135 | @cvar PARAM_PREFIX: prefix for ini file option names |
---|
136 | @type PARAM_PREFIX: string |
---|
137 | |
---|
138 | @cvar MYPROXY_CLIENT_PARAM_PREFIX: default value for ini file sub-prefix |
---|
139 | used for MyProxyClient initialisation settings such as MyProxy server |
---|
140 | hostname, CA cert directory etc. The prefix is such that option names |
---|
141 | will look like this e.g. |
---|
142 | <PARAM_PREFIX><MYPROXY_CLIENT_PARAM_PREFIX>hostname |
---|
143 | ... |
---|
144 | @type MYPROXY_CLIENT_PARAM_PREFIX: string |
---|
145 | |
---|
146 | @ivar __myProxyClient: MyProxy client interface object to enable this |
---|
147 | middleware to communicate with a backend MyProxy server using the MyProxy |
---|
148 | protocol |
---|
149 | @type __myProxyClient: myproxy.client.MyProxyClient |
---|
150 | |
---|
151 | @ivar __logonFuncEnvironKeyName: |
---|
152 | @type __logonFuncEnvironKeyName: string |
---|
153 | ''' |
---|
154 | # Options for ini file |
---|
155 | LOGON_FUNC_ENV_KEYNAME_OPTNAME = 'logonFuncEnvKeyName' |
---|
156 | |
---|
157 | # Default environ key names |
---|
158 | DEFAULT_LOGON_FUNC_ENV_KEYNAME = ('myproxy.server.wsgi.middleware.' |
---|
159 | 'MyProxyClientMiddleware.logon') |
---|
160 | |
---|
161 | CERT_REQ_POST_PARAM_KEYNAME = 'certificate_request' |
---|
162 | |
---|
163 | # Option prefixes |
---|
164 | PARAM_PREFIX = 'myproxy.' |
---|
165 | MYPROXY_CLIENT_PARAM_PREFIX = 'client.' |
---|
166 | |
---|
167 | __slots__ = ( |
---|
168 | '__myProxyClient', |
---|
169 | '__logonFuncEnvironKeyName', |
---|
170 | ) |
---|
171 | |
---|
172 | def __init__(self, app): |
---|
173 | '''Create attributes |
---|
174 | |
---|
175 | @type app: function |
---|
176 | @param app: WSGI callable for next application in stack |
---|
177 | ''' |
---|
178 | super(MyProxyClientMiddleware, self).__init__(app) |
---|
179 | self.__myProxyClient = None |
---|
180 | self.__logonFuncEnvironKeyName = None |
---|
181 | |
---|
182 | @classmethod |
---|
183 | def filter_app_factory(cls, app, global_conf, |
---|
184 | prefix=PARAM_PREFIX, |
---|
185 | myProxyClientPrefix=MYPROXY_CLIENT_PARAM_PREFIX, |
---|
186 | **app_conf): |
---|
187 | """Function following Paste filter app factory signature |
---|
188 | |
---|
189 | @type app: callable following WSGI interface |
---|
190 | @param app: next middleware/application in the chain |
---|
191 | @type global_conf: dict |
---|
192 | @param global_conf: PasteDeploy global configuration dictionary |
---|
193 | @type prefix: basestring |
---|
194 | @param prefix: prefix for configuration items |
---|
195 | @type myProxyClientPrefix: ini file sub-prefix used for MyProxyClient |
---|
196 | initialisation settings such as MyProxy server hostname, CA cert. |
---|
197 | directory etc. |
---|
198 | @param myProxyClientPrefix: basestring |
---|
199 | @type app_conf: dict |
---|
200 | @param app_conf: PasteDeploy application specific configuration |
---|
201 | dictionary |
---|
202 | |
---|
203 | @rtype: myproxy.server.wsgi.middleware.MyProxyClientMiddleware |
---|
204 | @return: an instance of this application |
---|
205 | """ |
---|
206 | app = cls(app) |
---|
207 | app.parseConfig(prefix=prefix, myProxyClientPrefix=myProxyClientPrefix, |
---|
208 | **app_conf) |
---|
209 | return app |
---|
210 | |
---|
211 | def parseConfig(self, |
---|
212 | prefix=PARAM_PREFIX, |
---|
213 | myProxyClientPrefix=MYPROXY_CLIENT_PARAM_PREFIX, |
---|
214 | **app_conf): |
---|
215 | """Parse dictionary of configuration items updating the relevant |
---|
216 | attributes of this instance |
---|
217 | |
---|
218 | @type prefix: basestring |
---|
219 | @param prefix: prefix for configuration items |
---|
220 | @type myProxyClientPrefix: basestring |
---|
221 | @param myProxyClientPrefix: explicit prefix for MyProxyClient class |
---|
222 | specific configuration items |
---|
223 | @type app_conf: dict |
---|
224 | @param app_conf: PasteDeploy application specific configuration |
---|
225 | dictionary |
---|
226 | """ |
---|
227 | |
---|
228 | # Get MyProxyClient initialisation parameters |
---|
229 | myProxyClientFullPrefix = prefix + myProxyClientPrefix |
---|
230 | |
---|
231 | myProxyClientKw = dict([(k.replace(myProxyClientFullPrefix, ''), v) |
---|
232 | for k,v in app_conf.items() |
---|
233 | if k.startswith(myProxyClientFullPrefix)]) |
---|
234 | |
---|
235 | self.myProxyClient = MyProxyClient(**myProxyClientKw) |
---|
236 | clientEnvKeyOptName = prefix + \ |
---|
237 | MyProxyClientMiddleware.CLIENT_ENV_KEYNAME_OPTNAME |
---|
238 | |
---|
239 | self.clientEnvironKeyName = app_conf.get(clientEnvKeyOptName, |
---|
240 | MyProxyClientMiddleware.DEFAULT_CLIENT_ENV_KEYNAME) |
---|
241 | |
---|
242 | logonFuncEnvKeyOptName = prefix + \ |
---|
243 | MyProxyClientMiddleware.LOGON_FUNC_ENV_KEYNAME_OPTNAME |
---|
244 | |
---|
245 | self.logonFuncEnvironKeyName = app_conf.get(logonFuncEnvKeyOptName, |
---|
246 | MyProxyClientMiddleware.DEFAULT_LOGON_FUNC_ENV_KEYNAME) |
---|
247 | |
---|
248 | def _getLogonFuncEnvironKeyName(self): |
---|
249 | """Get MyProxyClient logon function environ key name |
---|
250 | |
---|
251 | @rtype: basestring |
---|
252 | @return: MyProxyClient logon function environ key name |
---|
253 | """ |
---|
254 | return self.__logonFuncEnvironKeyName |
---|
255 | |
---|
256 | def _setLogonFuncEnvironKeyName(self, value): |
---|
257 | """Set MyProxyClient environ key name |
---|
258 | |
---|
259 | @type value: basestring |
---|
260 | @param value: MyProxyClient logon function environ key name |
---|
261 | """ |
---|
262 | if not isinstance(value, basestring): |
---|
263 | raise TypeError('Expecting string type for ' |
---|
264 | '"logonFuncEnvironKeyName"; got %r type' % |
---|
265 | type(value)) |
---|
266 | self.__logonFuncEnvironKeyName = value |
---|
267 | |
---|
268 | logonFuncEnvironKeyName = property(fget=_getLogonFuncEnvironKeyName, |
---|
269 | fset=_setLogonFuncEnvironKeyName, |
---|
270 | doc="key name in environ for the " |
---|
271 | "MyProxy logon function") |
---|
272 | |
---|
273 | def _getMyProxyClient(self): |
---|
274 | """Get MyProxyClient instance |
---|
275 | |
---|
276 | @rtype: myproxy.client.MyProxyClient |
---|
277 | @return: MyProxyClient instance |
---|
278 | """ |
---|
279 | return self.__myProxyClient |
---|
280 | |
---|
281 | def _setMyProxyClient(self, value): |
---|
282 | """Set MyProxyClient instance |
---|
283 | |
---|
284 | @type value: myproxy.client.MyProxyClient |
---|
285 | @param value: MyProxyClient instance |
---|
286 | """ |
---|
287 | if not isinstance(value, MyProxyClient): |
---|
288 | raise TypeError('Expecting %r type for "myProxyClient" attribute ' |
---|
289 | 'got %r' % (MyProxyClient, type(value))) |
---|
290 | self.__myProxyClient = value |
---|
291 | |
---|
292 | myProxyClient = property(fget=_getMyProxyClient, |
---|
293 | fset=_setMyProxyClient, |
---|
294 | doc="MyProxyClient instance used to convert HTTPS" |
---|
295 | " call into a call to a MyProxy server") |
---|
296 | |
---|
297 | def __call__(self, environ, start_response): |
---|
298 | '''Set MyProxyClient instance and MyProxy logon method in environ |
---|
299 | |
---|
300 | @type environ: dict |
---|
301 | @param environ: WSGI environment variables dictionary |
---|
302 | @type start_response: function |
---|
303 | @param start_response: standard WSGI start response function |
---|
304 | ''' |
---|
305 | log.debug("MyProxyClientMiddleware.__call__ ...") |
---|
306 | environ[self.clientEnvironKeyName] = self.myProxyClient |
---|
307 | environ[self.logonFuncEnvironKeyName] = self.myProxyLogon |
---|
308 | |
---|
309 | return self.app(environ, start_response) |
---|
310 | |
---|
311 | @property |
---|
312 | def myProxyLogon(self): |
---|
313 | """Return the MyProxy logon method wrapped as a HTTP Basic Auth |
---|
314 | authenticate interface function |
---|
315 | |
---|
316 | @rtype: function |
---|
317 | @return: MyProxy logon HTTP Basic Auth Callback |
---|
318 | """ |
---|
319 | def _myProxylogon(environ, start_response, username, password): |
---|
320 | """Wrap MyProxy logon method as a WSGI app |
---|
321 | @type environ: dict |
---|
322 | @param environ: WSGI environment variables dictionary |
---|
323 | @type start_response: function |
---|
324 | @param start_response: standard WSGI start response function |
---|
325 | @type username: basestring |
---|
326 | @param username: username credential to MyProxy logon |
---|
327 | @type password: basestring |
---|
328 | @param password: pass-phrase for MyProxy logon call |
---|
329 | @raise HttpBasicAuthResponseException: invalid client request |
---|
330 | @raise MyProxyClientMiddlewareError: socket error for backend |
---|
331 | MyProxy server |
---|
332 | """ |
---|
333 | requestMethod = environ.get('REQUEST_METHOD') |
---|
334 | if requestMethod != 'POST': |
---|
335 | response = "HTTP Request method not recognised" |
---|
336 | log.error("HTTP Request method %r not recognised", |
---|
337 | requestMethod) |
---|
338 | raise HttpBasicAuthResponseException(response, |
---|
339 | httplib.METHOD_NOT_ALLOWED) |
---|
340 | |
---|
341 | request = Request(environ) |
---|
342 | certReqKey = self.__class__.CERT_REQ_POST_PARAM_KEYNAME |
---|
343 | pemCertReq = request.POST.get(certReqKey) |
---|
344 | if pemCertReq is None: |
---|
345 | response = "No %r form variable set" % certReqKey |
---|
346 | log.error(response) |
---|
347 | raise HttpBasicAuthResponseException(response, |
---|
348 | httplib.BAD_REQUEST) |
---|
349 | log.debug("cert req = %r", pemCertReq) |
---|
350 | |
---|
351 | # Expecting PEM encoded request |
---|
352 | try: |
---|
353 | certReq = crypto.load_certificate_request(crypto.FILETYPE_PEM, |
---|
354 | pemCertReq) |
---|
355 | except crypto.Error, e: |
---|
356 | log.error("Error loading input certificate request: %r", |
---|
357 | pemCertReq) |
---|
358 | raise HttpBasicAuthResponseException("Error loading input " |
---|
359 | "certificate request", |
---|
360 | httplib.BAD_REQUEST) |
---|
361 | |
---|
362 | # Convert to ASN1 format expect by logon client call |
---|
363 | asn1CertReq = crypto.dump_certificate_request(crypto.FILETYPE_ASN1, |
---|
364 | certReq) |
---|
365 | |
---|
366 | try: |
---|
367 | credentials = self.myProxyClient.logon(username, |
---|
368 | password, |
---|
369 | certReq=asn1CertReq) |
---|
370 | status = self.getStatusMessage(httplib.OK) |
---|
371 | response = '\n'.join(credentials) |
---|
372 | |
---|
373 | start_response(status, |
---|
374 | [('Content-length', str(len(response))), |
---|
375 | ('Content-type', 'text/plain')]) |
---|
376 | return [response] |
---|
377 | |
---|
378 | except MyProxyClientError, e: |
---|
379 | raise HttpBasicAuthResponseException(str(e), |
---|
380 | httplib.UNAUTHORIZED) |
---|
381 | except socket.error, e: |
---|
382 | raise MyProxyClientMiddlewareError("Socket error " |
---|
383 | "with MyProxy server %r: %s" % |
---|
384 | (self.myProxyClient.hostname, e)) |
---|
385 | except Exception, e: |
---|
386 | log.error("MyProxyClient.logon raised an unknown exception " |
---|
387 | "calling %r: %s", |
---|
388 | self.myProxyClient.hostname, |
---|
389 | traceback.format_exc()) |
---|
390 | raise # Trigger 500 Internal Server Error |
---|
391 | |
---|
392 | return _myProxylogon |
---|
393 | |
---|
394 | |
---|
395 | class MyProxyGetTrustRootsMiddlewareError(Exception): |
---|
396 | """MyProxyGetTrustRootsMiddleware exception class""" |
---|
397 | |
---|
398 | |
---|
399 | class MyProxyGetTrustRootsMiddleware(MyProxyClientMiddlewareBase): |
---|
400 | """HTTP client interface for MyProxy server Get Trust Roots method |
---|
401 | |
---|
402 | It relies on a myproxy.server.wsgi.MyProxyClientMiddleware instance called |
---|
403 | upstream in the WSGI stack to set up a MyProxyClient instance and make it |
---|
404 | available in the environ to call its getTrustRoots method. |
---|
405 | |
---|
406 | @cvar PATH_OPTNAME: ini file option to set the URI path for this service |
---|
407 | @type PATH_OPTNAME: string |
---|
408 | |
---|
409 | @cvar DEFAULT_PATH: default URI path setting |
---|
410 | @type DEFAULT_PATH: string |
---|
411 | |
---|
412 | @cvar PARAM_PREFIX: prefix for ini file option names |
---|
413 | @type PARAM_PREFIX: string |
---|
414 | |
---|
415 | @ivar __path: URI path setting for this service |
---|
416 | @type __path: basestring |
---|
417 | """ |
---|
418 | |
---|
419 | PATH_OPTNAME = 'path' |
---|
420 | DEFAULT_PATH = '/myproxy/get-trustroots' |
---|
421 | |
---|
422 | # Option prefixes |
---|
423 | PARAM_PREFIX = 'myproxy.getTrustRoots.' |
---|
424 | |
---|
425 | __slots__ = ( |
---|
426 | '__path', |
---|
427 | ) |
---|
428 | |
---|
429 | def __init__(self, app): |
---|
430 | '''Create attributes |
---|
431 | |
---|
432 | @type app: function |
---|
433 | @param app: WSGI callable for next application in stack |
---|
434 | ''' |
---|
435 | super(MyProxyGetTrustRootsMiddleware, self).__init__(app) |
---|
436 | self.__path = None |
---|
437 | |
---|
438 | @classmethod |
---|
439 | def filter_app_factory(cls, app, global_conf, prefix=PARAM_PREFIX, |
---|
440 | **app_conf): |
---|
441 | """Function following Paste filter app factory signature |
---|
442 | |
---|
443 | @type app: callable following WSGI interface |
---|
444 | @param app: next middleware/application in the chain |
---|
445 | @type global_conf: dict |
---|
446 | @param global_conf: PasteDeploy global configuration dictionary |
---|
447 | @type prefix: basestring |
---|
448 | @param prefix: prefix for configuration items |
---|
449 | @type app_conf: dict |
---|
450 | @param app_conf: PasteDeploy application specific configuration |
---|
451 | dictionary |
---|
452 | |
---|
453 | @rtype: myproxy.server.wsgi.middleware.MyProxyGetTrustRootsMiddleware |
---|
454 | @return: an instance of this middleware |
---|
455 | """ |
---|
456 | app = cls(app) |
---|
457 | app.parseConfig(prefix=prefix, **app_conf) |
---|
458 | return app |
---|
459 | |
---|
460 | def parseConfig(self, prefix=PARAM_PREFIX, **app_conf): |
---|
461 | """Parse dictionary of configuration items updating the relevant |
---|
462 | attributes of this instance |
---|
463 | |
---|
464 | @type prefix: basestring |
---|
465 | @param prefix: prefix for configuration items |
---|
466 | @type app_conf: dict |
---|
467 | @param app_conf: PasteDeploy application specific configuration |
---|
468 | dictionary |
---|
469 | """ |
---|
470 | clientEnvKeyOptName = prefix + self.__class__.CLIENT_ENV_KEYNAME_OPTNAME |
---|
471 | |
---|
472 | self.clientEnvironKeyName = app_conf.get(clientEnvKeyOptName, |
---|
473 | self.__class__.DEFAULT_CLIENT_ENV_KEYNAME) |
---|
474 | |
---|
475 | pathOptName = prefix + self.__class__.PATH_OPTNAME |
---|
476 | self.path = app_conf.get(pathOptName, self.__class__.DEFAULT_PATH) |
---|
477 | |
---|
478 | def _getPath(self): |
---|
479 | """Get URI path for get trust roots method |
---|
480 | @rtype: basestring |
---|
481 | @return: path for get trust roots method |
---|
482 | """ |
---|
483 | return self.__path |
---|
484 | |
---|
485 | def _setPath(self, value): |
---|
486 | """Set URI path for get trust roots method |
---|
487 | @type value: basestring |
---|
488 | @param value: path for get trust roots method |
---|
489 | """ |
---|
490 | if not isinstance(value, basestring): |
---|
491 | raise TypeError('Expecting string type for "path"; got %r' % |
---|
492 | type(value)) |
---|
493 | |
---|
494 | self.__path = value |
---|
495 | |
---|
496 | path = property(fget=_getPath, fset=_setPath, |
---|
497 | doc="environ SCRIPT_NAME path which invokes the " |
---|
498 | "getTrustRoots method on this middleware") |
---|
499 | |
---|
500 | def __call__(self, environ, start_response): |
---|
501 | '''Get MyProxyClient instance from environ and call MyProxy |
---|
502 | getTrustRoots method returning the response. |
---|
503 | |
---|
504 | MyProxyClientMiddleware must be in place upstream in the WSGI stack |
---|
505 | |
---|
506 | @type environ: dict |
---|
507 | @param environ: WSGI environment variables dictionary |
---|
508 | @type start_response: function |
---|
509 | @param start_response: standard WSGI start response function |
---|
510 | |
---|
511 | @rtype: list |
---|
512 | @return: get trust roots response |
---|
513 | ''' |
---|
514 | # Skip if path doesn't match |
---|
515 | if environ['PATH_INFO'] != self.path: |
---|
516 | return self.app(environ, start_response) |
---|
517 | |
---|
518 | log.debug("MyProxyGetTrustRootsMiddleware.__call__ ...") |
---|
519 | |
---|
520 | # Check method |
---|
521 | requestMethod = environ.get('REQUEST_METHOD') |
---|
522 | if requestMethod != 'GET': |
---|
523 | response = "HTTP Request method not recognised" |
---|
524 | log.error("HTTP Request method %r not recognised", requestMethod) |
---|
525 | status = self.__class__.getStatusMessage(httplib.BAD_REQUEST) |
---|
526 | start_response(status, |
---|
527 | [('Content-type', 'text/plain'), |
---|
528 | ('Content-length', str(len(response)))]) |
---|
529 | return [response] |
---|
530 | |
---|
531 | myProxyClient = environ[self.clientEnvironKeyName] |
---|
532 | if not isinstance(myProxyClient, MyProxyClient): |
---|
533 | raise TypeError('Expecting %r type for "myProxyClient" environ[%r] ' |
---|
534 | 'attribute got %r' % (MyProxyClient, |
---|
535 | self.clientEnvironKeyName, |
---|
536 | type(myProxyClient))) |
---|
537 | |
---|
538 | response = self._getTrustRoots(myProxyClient) |
---|
539 | start_response(self.getStatusMessage(httplib.OK), |
---|
540 | [('Content-length', str(len(response))), |
---|
541 | ('Content-type', 'text/plain')]) |
---|
542 | |
---|
543 | return [response] |
---|
544 | |
---|
545 | @classmethod |
---|
546 | def _getTrustRoots(cls, myProxyClient): |
---|
547 | """Call getTrustRoots method on MyProxyClient instance retrieved from |
---|
548 | environ and format and return a HTTP response |
---|
549 | |
---|
550 | @type myProxyClient: myproxy.client.MyProxyClient |
---|
551 | @param myProxyClient: MyProxyClient instance on which to call |
---|
552 | getTrustRoots method |
---|
553 | |
---|
554 | @rtype: basestring |
---|
555 | @return: trust roots base64 encoded and concatenated together |
---|
556 | @raise MyProxyGetTrustRootsMiddlewareError: socket error with backend |
---|
557 | MyProxy server |
---|
558 | @raise MyProxyClientError: error response received by MyProxyClient |
---|
559 | instance |
---|
560 | """ |
---|
561 | try: |
---|
562 | trustRoots = myProxyClient.getTrustRoots() |
---|
563 | |
---|
564 | # Serialise dict response |
---|
565 | response = "\n".join(["%s=%s" % (k, base64.b64encode(v)) |
---|
566 | for k,v in trustRoots.items()]) |
---|
567 | |
---|
568 | return response |
---|
569 | |
---|
570 | except MyProxyClientError, e: |
---|
571 | log.error("MyProxyClient.getTrustRoots raised an " |
---|
572 | "MyProxyClientError exception calling %r: %s", |
---|
573 | myProxyClient.hostname, |
---|
574 | traceback.format_exc()) |
---|
575 | |
---|
576 | except socket.error, e: |
---|
577 | raise MyProxyGetTrustRootsMiddlewareError("Socket error with " |
---|
578 | "MyProxy server %r: %s" % |
---|
579 | (myProxyClient.hostname, |
---|
580 | e)) |
---|
581 | except Exception, e: |
---|
582 | log.error("MyProxyClient.getTrustRoots raised an unknown exception " |
---|
583 | "calling %r: %s", |
---|
584 | myProxyClient.hostname, |
---|
585 | traceback.format_exc()) |
---|
586 | raise # Trigger 500 Internal Server Error |
---|
587 | |
---|