source: TI12-security/branches/ndg-security-1.5.x/ndg_security_server/ndg/security/server/wsgi/myproxy/__init__.py @ 6633

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/branches/ndg-security-1.5.x/ndg_security_server/ndg/security/server/wsgi/myproxy/__init__.py@6633
Revision 6633, 9.6 KB checked in by pjkersha, 9 years ago (diff)

Merging in changes from 6557

Line 
1"""Functionality for WSGI HTTPS proxy to MyProxy server.
2 
3NERC DataGrid Project
4"""
5__author__ = "P J Kershaw"
6__date__ = "13/01/09"
7__copyright__ = "(C) 2009 Science and Technology Facilities Council"
8__license__ = "BSD - see LICENSE file in top-level directory"
9__contact__ = "Philip.Kershaw@stfc.ac.uk"
10__revision__ = "$Id: $"
11import logging
12log = logging.getLogger(__name__)
13import traceback
14import re
15import httplib
16import socket
17
18from M2Crypto import X509
19
20from myproxy.client import MyProxyClient, MyProxyClientError
21from ndg.security.server.wsgi import (NDGSecurityMiddlewareBase, 
22                                      NDGSecurityMiddlewareConfigError)
23   
24from ndg.security.server.wsgi.authn import HTTPBasicAuthMiddleware
25       
26       
27class MyProxyClientMiddlewareConfigError(NDGSecurityMiddlewareConfigError):
28    """Configuration error with MyProxyClientMiddleware"""
29
30
31class MyProxyClientMiddleware(NDGSecurityMiddlewareBase):
32    '''
33    Create a MyProxy client and make it available to other middleware in the
34    WSGI stack
35    '''
36    # Options for ini file
37    CLIENT_ENV_KEYNAME_OPTNAME = 'clientEnvKeyName'
38    LOGON_FUNC_ENV_KEYNAME_OPTNAME = 'logonFuncEnvKeyName'     
39   
40    # Default environ key names
41    CLIENT_ENV_KEYNAME = ('ndg.security.server.wsgi.authn.'
42                          'MyProxyClientMiddleware')
43    LOGON_FUNC_ENV_KEYNAME = ('ndg.security.server.wsgi.authn.'
44                              'MyProxyClientMiddleware.logon')
45   
46    WSGI_INPUT_ENV_KEYNAME = 'wsgi.input'
47   
48    # Option prefixes
49    PARAM_PREFIX = 'myproxy.'
50    MYPROXY_CLIENT_PARAM_PREFIX = 'client.'
51   
52    def __init__(self, app, global_conf, prefix=PARAM_PREFIX, 
53                 myProxyClientPrefix=MYPROXY_CLIENT_PARAM_PREFIX, **app_conf):
54        ''''''
55        super(MyProxyClientMiddleware, self).__init__(app, global_conf)
56        self.__myProxyClient = None
57
58        # Get MyProxyClient initialisation parameters
59        myProxyClientFullPrefix = prefix + myProxyClientPrefix
60                           
61        myProxyClientKw = dict([(k.replace(myProxyClientFullPrefix, ''), v) 
62                                 for k,v in app_conf.items() 
63                                 if k.startswith(myProxyClientFullPrefix)])
64       
65        self.myProxyClient = MyProxyClient(**myProxyClientKw)
66        clientEnvKeyOptName = prefix + \
67                            MyProxyClientMiddleware.CLIENT_ENV_KEYNAME_OPTNAME
68                   
69        self.clientEnvironKeyName = app_conf.get(clientEnvKeyOptName,
70                                MyProxyClientMiddleware.CLIENT_ENV_KEYNAME)
71                   
72        logonFuncEnvKeyOptName = prefix + \
73                    MyProxyClientMiddleware.LOGON_FUNC_ENV_KEYNAME_OPTNAME
74
75        self.logonFuncEnvironKeyName = app_conf.get(logonFuncEnvKeyOptName,
76                                MyProxyClientMiddleware.LOGON_FUNC_ENV_KEYNAME)
77
78    def _getClientEnvironKeyName(self):
79        return self.__clientEnvironKeyName
80
81    def _setClientEnvironKeyName(self, value):
82        if not isinstance(value, basestring):
83            raise TypeError('Expecting string type for "clientEnvironKeyName"; '
84                            'got %r type' % type(value))
85        self.__clientEnvironKeyName = value
86
87    clientEnvironKeyName = property(fget=_getClientEnvironKeyName, 
88                                    fset=_setClientEnvironKeyName, 
89                                    doc="key name in environ for the "
90                                        "MyProxyClient instance")   
91
92    def _getLogonFuncEnvironKeyName(self):
93        return self.__logonFuncEnvironKeyName
94
95    def _setLogonFuncEnvironKeyName(self, value):
96        if not isinstance(value, basestring):
97            raise TypeError('Expecting string type for '
98                            '"logonFuncEnvironKeyName"; got %r type' % 
99                            type(value))
100        self.__logonFuncEnvironKeyName = value
101
102    logonFuncEnvironKeyName = property(fget=_getLogonFuncEnvironKeyName, 
103                                       fset=_setLogonFuncEnvironKeyName, 
104                                       doc="key name in environ for the "
105                                           "MyProxy logon function")
106   
107    def _getMyProxyClient(self):
108        return self.__myProxyClient
109
110    def _setMyProxyClient(self, value):
111        if not isinstance(value, MyProxyClient):
112            raise TypeError('Expecting %r type for "myProxyClient" attribute '
113                            'got %r' % (MyProxyClient, type(value)))
114        self.__myProxyClient = value
115       
116    myProxyClient = property(fget=_getMyProxyClient,
117                             fset=_setMyProxyClient, 
118                             doc="MyProxyClient instance used to convert HTTPS"
119                                 " call into a call to a MyProxy server")
120
121    @NDGSecurityMiddlewareBase.initCall
122    def __call__(self, environ, start_response):
123        '''Set MyProxyClient instance and MyProxy logon method in environ
124       
125        @type environ: dict
126        @param environ: WSGI environment variables dictionary
127        @type start_response: function
128        @param start_response: standard WSGI start response function
129        '''
130        log.debug("MyProxyClientMiddleware.__call__ ...")
131        environ[self.clientEnvironKeyName] = self.myProxyClient
132        environ[self.logonFuncEnvironKeyName] = self.myProxyLogon
133       
134        return self._app(environ, start_response)
135   
136    @property
137    def myProxyLogon(self):
138        """Return the MyProxy logon method wrapped as a HTTP Basic Auth
139        authenticate interface function
140        """
141        def _myProxylogon(environ, start_response, username, password):
142            """Wrap MyProxy logon method as a WSGI app
143            """
144            if environ.get('REQUEST_METHOD') == 'GET':
145                # No certificate request passed with GET call
146                # TODO: retire this method? - keys are generated here instead of
147                # the client
148                certReq = None
149                   
150            elif environ.get('REQUEST_METHOD') == 'POST':
151               
152                pemCertReq = environ[
153                        MyProxyClientMiddleware.WSGI_INPUT_ENV_KEYNAME].read()
154               
155                # TODO: restore WSGI file object
156               
157                # Expecting PEM encoded request
158                certReq = X509.load_request_string(pemCertReq) 
159            else:
160                status = self.getStatusMessage(httplib.UNAUTHORIZED)
161                response = ("HTTP request method %r not recognised for this "
162                            "request " % environ.get('REQUEST_METHOD', 
163                                                     '<Not set>'))
164               
165            try:
166                credentials = self.myProxyClient.logon(username, 
167                                                       password,
168                                                       certReq=certReq)
169                status = self.getStatusMessage(httplib.OK)
170                response = '\n'.join(credentials)
171               
172            except MyProxyClientError, e:
173                status = self.getStatusMessage(httplib.UNAUTHORIZED)
174                response = str(e)
175           
176            except socket.error, e:
177                raise MyProxyClientMiddlewareConfigError("Socket error "
178                                        "with MyProxy server %r: %s" % 
179                                        (self.myProxyClient.hostname, e))
180            except Exception, e:
181                log.error("MyProxyClient.logon raised an unknown exception "
182                          "calling %r: %s", 
183                          self.myProxyClient.hostname,
184                          traceback.format_exc())
185                raise
186           
187            start_response(status,
188                           [('Content-length', str(len(response))),
189                            ('Content-type', 'text/plain')])
190            return [response]
191       
192        return _myProxylogon
193       
194       
195class MyProxyLogonMiddlewareConfigError(NDGSecurityMiddlewareConfigError):
196    """Configuration error with MyProxyLogonMiddleware"""
197   
198   
199class MyProxyLogonMiddleware(NDGSecurityMiddlewareBase):
200    """HTTP Basic Auth interface to MyProxy logon.  This interfaces creates a
201    MyProxy client instance and HTTP Basic Auth based web service interface
202    for MyProxy logon calls.  This WSGI must be run over HTTPS to ensure
203    confidentiality of username/passphrase credentials
204    """
205    PARAM_PREFIX = 'myproxy.logon.'
206   
207    def __init__(self, app, global_conf, prefix=PARAM_PREFIX, **app_conf):       
208       
209        authnFuncEnvKeyNameOptName = HTTPBasicAuthMiddleware.PARAM_PREFIX + \
210                        HTTPBasicAuthMiddleware.AUTHN_FUNC_ENV_KEYNAME_OPTNAME
211                       
212        if authnFuncEnvKeyNameOptName in app_conf:
213            raise MyProxyLogonMiddlewareConfigError("Found %r option name in "
214                "application configuration settings.  Use %r instead" %
215                (authnFuncEnvKeyNameOptName, 
216                 MyProxyClientMiddleware.PARAM_PREFIX + \
217                 MyProxyClientMiddleware.LOGON_FUNC_ENV_KEYNAME_OPTNAME))
218       
219        httpBasicAuthApp = HTTPBasicAuthMiddleware(app, app_conf, **app_conf)
220        app = MyProxyClientMiddleware(httpBasicAuthApp, app_conf, **app_conf)
221       
222        # Set HTTP Basic Auth to use the MyProxy client logon for its
223        # authentication method
224        httpBasicAuthApp.authnFuncEnvironKeyName = app.logonFuncEnvironKeyName
225       
226        super(MyProxyLogonMiddleware, self).__init__(app, global_conf, 
227                                                     prefix=prefix, **app_conf)
Note: See TracBrowser for help on using the repository browser.