Changeset 6938


Ignore:
Timestamp:
07/06/10 11:09:11 (9 years ago)
Author:
pjkersha
Message:

Incomplete - task 5: MyProxy? Logon HTTPS Interface

  • Added middleware for get trust roots interface
Location:
TI12-security/trunk/MyProxyLogonWebService
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • TI12-security/trunk/MyProxyLogonWebService/myproxy/server/test/myproxywsgi.ini

    r6937 r6938  
    2323myproxy.logonFuncEnvKeyName = MYPROXY_LOGON_FUNC 
    2424myproxy.rePathMatchList = /logon 
     25myproxy.getTrustRoots.path = /get-trustroots 
    2526#myproxy.client.hostname = localhost 
    2627myproxy.client.hostname = gabriel.badc.rl.ac.uk 
  • TI12-security/trunk/MyProxyLogonWebService/myproxy/server/test/test_myproxywsgi.py

    r6897 r6938  
    5555        self.assert_(response) 
    5656         
    57          
    58 class MyProxyLogonAppTestCase(unittest.TestCase): 
     57 
     58class MyProxyPasteDeployTestCaseBase(unittest.TestCase):   
     59    """Base class for common Paste Deploy related set-up""" 
    5960    INI_FILENAME = 'myproxywsgi.ini' 
    6061    THIS_DIR = os.path.dirname(__file__) 
     
    6465    def __init__(self, *args, **kwargs): 
    6566        here_dir = os.path.dirname(os.path.abspath(__file__)) 
    66         wsgiapp = loadapp('config:' + MyProxyLogonAppTestCase.INI_FILENAME,  
     67        wsgiapp = loadapp('config:' + self.__class__.INI_FILENAME,  
    6768                          relative_to=here_dir) 
    6869        self.app = paste.fixture.TestApp(wsgiapp) 
    6970         
    70         self.cfg = SafeConfigParser({'here': MyProxyLogonAppTestCase.THIS_DIR}) 
     71        self.cfg = SafeConfigParser({'here': self.__class__.THIS_DIR}) 
    7172        self.cfg.optionxform = str 
    72         self.cfg.read(MyProxyLogonAppTestCase.CONFIG_FILEPATH) 
     73        self.cfg.read(self.__class__.CONFIG_FILEPATH) 
    7374         
    74         unittest.TestCase.__init__(self, *args, **kwargs) 
     75        unittest.TestCase.__init__(self, *args, **kwargs)   
     76                 
     77                 
     78class MyProxyLogonAppTestCase(MyProxyPasteDeployTestCaseBase): 
     79    """Test HTTP MyProxy logon interface""" 
    7580         
    7681    def _createRequestCreds(self): 
     
    153158        self.assert_(response)                
    154159 
     160 
     161class MyProxyGetTrustRootsMiddlewareTestCase(MyProxyPasteDeployTestCaseBase): 
     162    """Test HTTP MyProxy get trust roots interface""" 
     163     
     164    def test01(self): 
     165        response = self.app.get('/get-trustroots', status=200) 
     166        self.assert_(response)          
     167        print response  
     168 
    155169if __name__ == "__main__": 
    156170    unittest.main()         
  • TI12-security/trunk/MyProxyLogonWebService/myproxy/server/wsgi/app.py

    r6897 r6938  
    1010__revision__ = "$Id: $" 
    1111from myproxy.server.wsgi.httpbasicauth import HttpBasicAuthMiddleware 
    12 from myproxy.server.wsgi.middleware import MyProxyClientMiddleware 
     12from myproxy.server.wsgi.middleware import (MyProxyClientMiddleware, 
     13                                            MyProxyGetTrustRootsMiddleware) 
    1314      
    1415         
    15 class MyProxyLogonMiddlewareConfigError(Exception): 
    16     """Configuration error with MyProxyLogonMiddleware""" 
    17      
    18      
    19 class MyProxyLogonApp(object): 
    20     """HTTP interface to MyProxy logon.  This interfaces creates a MyProxy  
    21     client instance with a HTTP Basic Auth based web service interface 
    22     to pass username/passphrase for MyProxy logon calls.   
     16class MyProxyApp(object): 
     17    """HTTP interface to MyProxy logon and get trsut roots.  This interfaces  
     18    creates a MyProxy client instance with a HTTP Basic Auth based web service  
     19    interface to pass username/passphrase for MyProxy logon calls.   
    2320     
    2421    This WSGI must be run over HTTPS to ensure confidentiality of  
     
    2724    Apache SSL configuration. 
    2825    """ 
    29     PARAM_PREFIX = 'myproxy.logon.' 
     26    PARAM_PREFIX = 'myproxy.' 
     27    GET_TRUSTROOTS_PARAM_PREFIX = 'getTrustRoots.' 
    3028    HTTPBASICAUTH_REALM_OPTNAME = 'httpbasicauth.realm' 
    3129     
     
    4240        dictionary 
    4341        """ 
    44         logonFuncEnvKeyNameOptName = prefix + \ 
    45                         MyProxyClientMiddleware.LOGON_FUNC_ENV_KEYNAME_OPTNAME 
     42        # This app               
     43        app = cls() 
    4644         
    47         logonFuncEnvironKeyName = app_conf.get(logonFuncEnvKeyNameOptName, 
    48                                 MyProxyClientMiddleware.LOGON_FUNC_ENV_KEYNAME) 
    49                         
    50         app = MyProxyLogonApp() 
     45        # HTTP Basic auth middleware - a container for MyProxy logon 
    5146        httpBasicAuthMWare = HttpBasicAuthMiddleware.filter_app_factory(app,  
    5247                                                                global_conf,  
     
    5449                                                                **app_conf) 
    5550         
    56         app = MyProxyClientMiddleware.filter_app_factory(httpBasicAuthMWare,  
     51        # MyProxy get trust roots middleware 
     52        getTrustRootsPrefix = prefix + cls.GET_TRUSTROOTS_PARAM_PREFIX 
     53        getTrustRootsMWare = MyProxyGetTrustRootsMiddleware.filter_app_factory( 
     54                                                    httpBasicAuthMWare,  
     55                                                    global_conf,  
     56                                                    prefix=getTrustRootsPrefix) 
     57         
     58        # Middleware to hold a MyProxy client and expose interface in environ 
     59        app = MyProxyClientMiddleware.filter_app_factory(getTrustRootsMWare,  
    5760                                                         global_conf,  
    5861                                                         prefix=prefix, 
  • TI12-security/trunk/MyProxyLogonWebService/myproxy/server/wsgi/middleware.py

    r6937 r6938  
    3232 
    3333 
    34 class MyProxyClientMiddleware(object): 
     34class MyProxyClientMiddlewareBase(object): 
     35    """Base class for common functionality""" 
     36    __slots__ = ('__app', '__clientEnvironKeyName',) 
     37     
     38    CLIENT_ENV_KEYNAME_OPTNAME = 'clientEnvKeyName' 
     39    DEFAULT_CLIENT_ENV_KEYNAME = ('myproxy.server.wsgi.middleware.' 
     40                                  'MyProxyClientMiddleware.myProxyClient') 
     41         
     42    def __init__(self, app): 
     43        self.__app = app 
     44        self.__clientEnvironKeyName = None 
     45     
     46    def _getClientEnvironKeyName(self): 
     47        """Get MyProxyClient environ key name 
     48         
     49        @rtype: basestring 
     50        @return: MyProxyClient environ key name 
     51        """ 
     52        return self.__clientEnvironKeyName 
     53 
     54    def _setClientEnvironKeyName(self, value): 
     55        """Set MyProxyClient environ key name 
     56         
     57        @type value: basestring 
     58        @param value: MyProxyClient environ key name 
     59        """ 
     60        if not isinstance(value, basestring): 
     61            raise TypeError('Expecting string type for "clientEnvironKeyName"; ' 
     62                            'got %r type' % type(value)) 
     63        self.__clientEnvironKeyName = value 
     64 
     65    clientEnvironKeyName = property(fget=_getClientEnvironKeyName,  
     66                                    fset=_setClientEnvironKeyName,  
     67                                    doc="key name in environ for the " 
     68                                        "MyProxyClient instance")   
     69     
     70    @property 
     71    def app(self): 
     72        """Get Property method for reference to next WSGI application in call 
     73        stack 
     74        @rtype: function 
     75        @return: WSGI application 
     76        """ 
     77        return self.__app 
     78     
     79    @staticmethod 
     80    def getStatusMessage(statusCode): 
     81        '''Make a standard status message for use with start_response 
     82        @type statusCode: int 
     83        @param statusCode: HTTP status code 
     84        @rtype: str 
     85        @return: status code with standard message 
     86        @raise KeyError: for invalid status code 
     87        ''' 
     88        return '%d %s' % (statusCode, httplib.responses[statusCode]) 
     89         
     90     
     91class MyProxyClientMiddleware(MyProxyClientMiddlewareBase): 
    3592    ''' 
    3693    Create a MyProxy client and make it available to other middleware in the  
     
    3895    ''' 
    3996    # Options for ini file 
    40     CLIENT_ENV_KEYNAME_OPTNAME = 'clientEnvKeyName' 
    4197    LOGON_FUNC_ENV_KEYNAME_OPTNAME = 'logonFuncEnvKeyName'      
    4298     
    4399    # Default environ key names 
    44     CLIENT_ENV_KEYNAME = ('myproxy.server.wsgi.middleware.' 
    45                           'MyProxyClientMiddleware.myProxyClient') 
    46100    LOGON_FUNC_ENV_KEYNAME = ('myproxy.server.wsgi.middleware.' 
    47101                              'MyProxyClientMiddleware.logon') 
    48102     
    49     CERTIFICATE_REQUST_POST_PARAM_KEYNAME = 'certificate_request' 
    50     GET_TRUSTROOTS_PARAM_KEYNAME = 'get_trustroots' 
    51     GET_TRUSTROOTS_TRUE_STR = '1' 
     103    CERT_REQ_POST_PARAM_KEYNAME = 'certificate_request' 
    52104     
    53105    # Option prefixes 
     
    59111        '__myProxyClient',  
    60112        '__logonFuncEnvironKeyName', 
    61         '__clientEnvironKeyName' 
    62113    ) 
    63114     
    64115    def __init__(self, app): 
    65         '''''' 
    66         self.__app = app 
     116        '''Create attributes 
     117         
     118        @type app: function 
     119        @param app: WSGI callable for next application in stack 
     120        ''' 
     121        super(MyProxyClientMiddleware, self).__init__(app) 
    67122        self.__myProxyClient = None 
    68123        self.__logonFuncEnvironKeyName = None 
    69         self.__clientEnvironKeyName = None 
    70124 
    71125    @classmethod 
     
    118172                     
    119173        self.clientEnvironKeyName = app_conf.get(clientEnvKeyOptName, 
    120                                 MyProxyClientMiddleware.CLIENT_ENV_KEYNAME) 
     174                            MyProxyClientMiddleware.DEFAULT_CLIENT_ENV_KEYNAME) 
    121175                     
    122176        logonFuncEnvKeyOptName = prefix + \ 
     
    124178 
    125179        self.logonFuncEnvironKeyName = app_conf.get(logonFuncEnvKeyOptName, 
    126                                 MyProxyClientMiddleware.LOGON_FUNC_ENV_KEYNAME)  
    127                 
    128     def _getClientEnvironKeyName(self): 
    129         return self.__clientEnvironKeyName 
    130  
    131     def _setClientEnvironKeyName(self, value): 
    132         if not isinstance(value, basestring): 
    133             raise TypeError('Expecting string type for "clientEnvironKeyName"; ' 
    134                             'got %r type' % type(value)) 
    135         self.__clientEnvironKeyName = value 
    136  
    137     clientEnvironKeyName = property(fget=_getClientEnvironKeyName,  
    138                                     fset=_setClientEnvironKeyName,  
    139                                     doc="key name in environ for the " 
    140                                         "MyProxyClient instance")     
     180                                MyProxyClientMiddleware.LOGON_FUNC_ENV_KEYNAME) 
    141181 
    142182    def _getLogonFuncEnvironKeyName(self): 
     183        """Get MyProxyClient logon function environ key name 
     184         
     185        @rtype: basestring 
     186        @return: MyProxyClient logon function environ key name 
     187        """ 
    143188        return self.__logonFuncEnvironKeyName 
    144189 
    145190    def _setLogonFuncEnvironKeyName(self, value): 
     191        """Set MyProxyClient environ key name 
     192         
     193        @type value: basestring 
     194        @param value: MyProxyClient logon function environ key name 
     195        """ 
    146196        if not isinstance(value, basestring): 
    147197            raise TypeError('Expecting string type for ' 
     
    156206     
    157207    def _getMyProxyClient(self): 
     208        """Get MyProxyClient instance 
     209         
     210        @rtype: myproxy.client.MyProxyClient 
     211        @return: MyProxyClient instance 
     212        """ 
    158213        return self.__myProxyClient 
    159214 
    160215    def _setMyProxyClient(self, value): 
     216        """Set MyProxyClient instance 
     217         
     218        @type value: myproxy.client.MyProxyClient 
     219        @param value: MyProxyClient instance 
     220        """ 
    161221        if not isinstance(value, MyProxyClient): 
    162222            raise TypeError('Expecting %r type for "myProxyClient" attribute ' 
     
    181241        environ[self.logonFuncEnvironKeyName] = self.myProxyLogon 
    182242         
    183         return self.__app(environ, start_response) 
     243        return self.app(environ, start_response) 
    184244     
    185245    @property 
     
    200260             
    201261            request = Request(environ) 
    202             certReqKey = self.__class__.CERTIFICATE_REQUST_POST_PARAM_KEYNAME 
     262            certReqKey = self.__class__.CERT_REQ_POST_PARAM_KEYNAME 
    203263            pemCertReq = request.POST.get(certReqKey) 
    204264            if pemCertReq is None: 
     
    208268                                                     httplib.BAD_REQUEST) 
    209269            log.debug("cert req = %r", pemCertReq) 
    210             getTrustRootsKey = self.__class__.GET_TRUSTROOTS_PARAM_KEYNAME 
    211             getTrustRoots = (request.postvars.get(getTrustRootsKey) ==  
    212                              self.__class__.GET_TRUSTROOTS_TRUE_STR) 
    213270             
    214271            # Expecting PEM encoded request 
     
    228285             
    229286            try: 
    230                 if getTrustRoots: 
    231                     trustRootsDict = self.myProxyClient.getTrustRoots() 
    232                      
    233                     trustRoots = '\n'.join([ 
    234                         "FILEDATA_%s=%s" % (fileName, fileContents)  
    235                         for fileName, fileContents in trustRootsDict.items() 
    236                     ]) 
    237                 else: 
    238                     trustRoots = '' 
    239                  
    240287                credentials = self.myProxyClient.logon(username,  
    241288                                                       password, 
     
    243290                status = self.getStatusMessage(httplib.OK) 
    244291                response = '\n'.join(credentials) 
    245                 response += '\n'+trustRoots 
    246292                 
    247293                start_response(status, 
     
    264310                raise # Trigger 500 Internal Server Error 
    265311             
    266  
    267          
    268312        return _myProxylogon 
    269  
    270     @staticmethod 
    271     def getStatusMessage(statusCode): 
    272         '''Make a standard status message for use with start_response 
    273         @type statusCode: int 
    274         @param statusCode: HTTP status code 
    275         @rtype: str 
    276         @return: status code with standard message 
    277         @raise KeyError: for invalid status code 
     313     
     314     
     315class MyProxyGetTrustRootsMiddlewareError(Exception): 
     316    """MyProxyGetTrustRootsMiddleware exception class""" 
     317     
     318     
     319class MyProxyGetTrustRootsMiddleware(MyProxyClientMiddlewareBase): 
     320    """HTTP client interface for MyProxy server Get Trust Roots method 
     321     
     322    It relies on a myproxy.server.wsgi.MyProxyClientMiddleware instance called  
     323    upstream in the WSGI stack to set up a MyProxyClient instance and make it  
     324    available in the environ to call its getTrustRoots method. 
     325    """ 
     326     
     327    # Options for ini file 
     328    CLIENT_ENV_KEYNAME_OPTNAME = \ 
     329        MyProxyClientMiddleware.CLIENT_ENV_KEYNAME_OPTNAME 
     330         
     331    PATH_OPTNAME = 'path'      
     332     
     333    DEFAULT_CLIENT_ENV_KEYNAME = \ 
     334        MyProxyClientMiddleware.DEFAULT_CLIENT_ENV_KEYNAME 
     335     
     336    DEFAULT_PATH = '/myproxy/get-trustroots' 
     337     
     338    # Option prefixes 
     339    PARAM_PREFIX = 'myproxy.getTrustRoots.' 
     340     
     341    __slots__ = ( 
     342        '__path', 
     343    ) 
     344     
     345    def __init__(self, app): 
     346        '''Create attributes 
     347         
     348        @type app: function 
     349        @param app: WSGI callable for next application in stack 
    278350        ''' 
    279         return '%d %s' % (statusCode, httplib.responses[statusCode]) 
    280      
     351        super(MyProxyGetTrustRootsMiddleware, self).__init__(app) 
     352        self.__path = None 
     353         
     354    @classmethod 
     355    def filter_app_factory(cls, app, global_conf, prefix=PARAM_PREFIX,  
     356                           **app_conf): 
     357        """Function following Paste filter app factory signature 
     358         
     359        @type app: callable following WSGI interface 
     360        @param app: next middleware/application in the chain       
     361        @type global_conf: dict         
     362        @param global_conf: PasteDeploy global configuration dictionary 
     363        @type prefix: basestring 
     364        @param prefix: prefix for configuration items 
     365        @type app_conf: dict         
     366        @param app_conf: PasteDeploy application specific configuration  
     367        dictionary 
     368        """ 
     369        app = cls(app) 
     370        app.parseConfig(prefix=prefix, **app_conf) 
     371        return app 
     372     
     373    def parseConfig(self, prefix=PARAM_PREFIX, **app_conf): 
     374        """Parse dictionary of configuration items updating the relevant  
     375        attributes of this instance 
     376         
     377        @type prefix: basestring 
     378        @param prefix: prefix for configuration items 
     379        @type app_conf: dict         
     380        @param app_conf: PasteDeploy application specific configuration  
     381        dictionary 
     382        """ 
     383        clientEnvKeyOptName = prefix + self.__class__.CLIENT_ENV_KEYNAME_OPTNAME 
     384                     
     385        self.clientEnvironKeyName = app_conf.get(clientEnvKeyOptName, 
     386                                    self.__class__.DEFAULT_CLIENT_ENV_KEYNAME) 
     387         
     388        pathOptName = prefix + self.__class__.PATH_OPTNAME 
     389        self.path = app_conf.get(pathOptName, self.__class__.DEFAULT_PATH) 
     390 
     391    def _getPath(self): 
     392        """Get URI path for get trust roots method 
     393        @rtype: basestring 
     394        @return: path for get trust roots method 
     395        """ 
     396        return self.__path 
     397 
     398    def _setPath(self, value): 
     399        """Set URI path for get trust roots method 
     400        @type value: basestring 
     401        @param value: path for get trust roots method 
     402        """ 
     403        if not isinstance(value, basestring): 
     404            raise TypeError('Expecting string type for "path"; got %r' %  
     405                            type(value)) 
     406         
     407        self.__path = value 
     408 
     409    path = property(fget=_getPath, fset=_setPath,  
     410                    doc="environ SCRIPT_NAME path which invokes the " 
     411                        "getTrustRoots method on this middleware") 
     412     
     413    def __call__(self, environ, start_response): 
     414        '''Get MyProxyClient instance from environ and call MyProxy  
     415        getTrustRoots method returning the response. 
     416         
     417        MyProxyClientMiddleware must be in place upstream in the WSGI stack 
     418         
     419        @type environ: dict 
     420        @param environ: WSGI environment variables dictionary 
     421        @type start_response: function 
     422        @param start_response: standard WSGI start response function 
     423        ''' 
     424        # Skip if path doesn't match 
     425        if environ['PATH_INFO'] != self.path: 
     426            return self.app(environ, start_response) 
     427         
     428        log.debug("MyProxyGetTrustRootsMiddleware.__call__ ...") 
     429         
     430        # Check method 
     431        requestMethod = environ.get('REQUEST_METHOD')              
     432        if requestMethod != 'GET': 
     433            response = "HTTP Request method not recognised" 
     434            log.error("HTTP Request method %r not recognised", requestMethod) 
     435            status = self.__class__.getStatusMessage(httplib.BAD_REQUEST) 
     436            start_response(status, 
     437                           [('Content-type', 'text/plain'), 
     438                            ('Content-length', str(len(response)))]) 
     439            return [response] 
     440         
     441        myProxyClient = environ[self.clientEnvironKeyName] 
     442        if not isinstance(myProxyClient, MyProxyClient): 
     443            raise TypeError('Expecting %r type for "myProxyClient" environ[%r] ' 
     444                            'attribute got %r' % (MyProxyClient,  
     445                                                  self.clientEnvironKeyName, 
     446                                                  type(myProxyClient))) 
     447         
     448        response = self._getTrustRoots(myProxyClient) 
     449        start_response(self.getStatusMessage(httplib.OK), 
     450                       [('Content-length', str(len(response))), 
     451                        ('Content-type', 'text/plain')]) 
     452 
     453        return [response] 
     454     
     455    @classmethod 
     456    def _getTrustRoots(cls, myProxyClient): 
     457        """Call getTrustRoots method on MyProxyClient instance retrieved from 
     458        environ and format and return a HTTP response 
     459         
     460        @type myProxyClient: myproxy.client.MyProxyClient 
     461        @param myProxyClient: MyProxyClient instance on which to call  
     462        getTrustRoots method 
     463         
     464        @rtype: basestring 
     465        @return: trust roots formatted as a HTTP response 
     466        """ 
     467        try: 
     468            trustRoots = myProxyClient.getTrustRoots() 
     469             
     470            # Serialise dict response 
     471            response = "\n".join(["%s=%s" % i for i in trustRoots.items()]) 
     472             
     473            return response 
     474                    
     475        except MyProxyClientError, e: 
     476            log.error("MyProxyClient.getTrustRoots raised an " 
     477                      "MyProxyClientError exception calling %r: %s",  
     478                      myProxyClient.hostname, 
     479                      traceback.format_exc()) 
     480             
     481        except socket.error, e: 
     482            raise MyProxyGetTrustRootsMiddlewareError("Socket error with " 
     483                                                      "MyProxy server %r: %s" %  
     484                                                      (myProxyClient.hostname,  
     485                                                       e)) 
     486        except Exception, e: 
     487            log.error("MyProxyClient.getTrustRoots raised an unknown exception " 
     488                      "calling %r: %s",  
     489                      myProxyClient.hostname, 
     490                      traceback.format_exc()) 
     491            raise # Trigger 500 Internal Server Error 
     492        
  • TI12-security/trunk/MyProxyLogonWebService/setup.py

    r6886 r6938  
    3535    url =               'http://proj.badc.rl.ac.uk/ndg/wiki/Security/MyProxyClient', 
    3636    platforms =         ['POSIX', 'Linux', 'Windows'], 
    37     install_requires =  ['MyProxyClient'], 
     37    install_requires =  ['PasteDeploy',  
     38                         'PasteSecript', 
     39                         'WebOb',  
     40                         'MyProxyClient'], 
    3841    license =           __license__, 
    3942    test_suite =        'myproxy.server.test', 
Note: See TracChangeset for help on using the changeset viewer.