Changeset 5843


Ignore:
Timestamp:
14/10/09 16:41:01 (10 years ago)
Author:
pjkersha
Message:

Completed unit tests for MyProxy? logon web service interface.

Location:
TI12-security/trunk/python
Files:
5 added
4 edited
1 moved

Legend:

Unmodified
Added
Removed
  • TI12-security/trunk/python/ndg_security_server/ndg/security/server/wsgi/__init__.py

    r5774 r5843  
    236236                     
    237237            if propertyDefaults is not None and filtK not in propertyDefaults: 
    238                 badOpt += [k]                 
     238                badOpt += [k] 
    239239            else: 
    240240                opt[filtK] = v 
  • TI12-security/trunk/python/ndg_security_server/ndg/security/server/wsgi/authn.py

    r5838 r5843  
    1616log = logging.getLogger(__name__) 
    1717 
     18import re 
    1819import base64 
    1920import httplib 
    2021import urllib 
    21 from urlparse import urlsplit 
    22 from paste.request import construct_url 
    23 from paste.request import parse_querystring 
    24 from beaker.middleware import SessionMiddleware 
     22from paste.request import construct_url, parse_querystring 
    2523import authkit.authenticate 
    2624 
     
    5250    AUTHN_FUNC_ENV_KEYNAME = ('ndg.security.server.wsgi.authn.' 
    5351                              'HTTPBasicAuthMiddleware.authenticate') 
    54     AUTHN_FUNC_ENV_KEYNAME_PARAMNAME = 'authnFunc'        
    55     PARAM_PREFIX = 'http.auth.basic' 
     52    AUTHN_FUNC_ENV_KEYNAME_OPTNAME = 'authnFuncEnvKeyName'        
     53    PARAM_PREFIX = 'http.auth.basic.' 
    5654    HTTP_HDR_FIELDNAME = 'basic' 
    5755    FIELD_SEP = ':' 
    5856    AUTHZ_ENV_KEYNAME = 'HTTP_AUTHORIZATION' 
    5957     
     58    RE_PATH_MATCH_LIST_OPTNAME = 'rePathMatchList' 
     59     
    6060    def __init__(self, app, app_conf, prefix=PARAM_PREFIX, **local_conf): 
     61        self.__rePathMatchList = None 
    6162        self.__authnFuncEnvironKeyName = None 
    6263         
    6364        super(HTTPBasicAuthMiddleware, self).__init__(app, app_conf,  
    6465                                                      **local_conf) 
     66 
     67        rePathMatchListOptName = prefix + \ 
     68                            HTTPBasicAuthMiddleware.RE_PATH_MATCH_LIST_OPTNAME 
     69        rePathMatchListVal = app_conf.pop(rePathMatchListOptName, '') 
     70         
     71        self.rePathMatchList = [re.compile(i)  
     72                                for i in rePathMatchListVal.split()] 
     73 
    6574        paramName = prefix + \ 
    66                     HTTPBasicAuthMiddleware.AUTHN_FUNC_ENV_KEYNAME_PARAMNAME 
     75                    HTTPBasicAuthMiddleware.AUTHN_FUNC_ENV_KEYNAME_OPTNAME 
    6776                     
    6877        self.authnFuncEnvironKeyName = local_conf.get(paramName, 
     
    8493                                           "custom authentication function " 
    8594                                           "used by this class") 
     95 
     96    def _getRePathMatchList(self): 
     97        return self.__rePathMatchList 
     98 
     99    def _setRePathMatchList(self, value): 
     100        if not isinstance(value, (list, tuple)): 
     101            raise TypeError('Expecting list or tuple type for ' 
     102                            '"rePathMatchList"; got %r' % type(value)) 
     103         
     104        self.__rePathMatchList = value 
     105 
     106    rePathMatchList = property(fget=_getRePathMatchList,  
     107                               fset=_setRePathMatchList,  
     108                               doc="List of regular expressions determine the " 
     109                                   "URI paths intercepted by this middleware") 
     110 
     111    def _pathMatch(self): 
     112        """Apply a list of regular expression matching patterns to the contents 
     113        of environ['PATH_INFO'], if any match, return True.  This method is 
     114        used to determine whether to apply SSL client authentication 
     115        """ 
     116        path = self.pathInfo 
     117        for regEx in self.rePathMatchList: 
     118            if regEx.match(path): 
     119                return True 
     120             
     121        return False    
    86122 
    87123    def _parseCredentials(self): 
     
    116152        log.debug("HTTPBasicAuthNMiddleware.__call__ ...") 
    117153         
     154        if not self._pathMatch(): 
     155            return self._app(environ, start_response) 
     156         
    118157        authenticate = environ.get(self.authnFuncEnvironKeyName) 
    119158        if authenticate is None: 
     
    126165            return self._setErrorResponse(code=httplib.UNAUTHORIZED) 
    127166         
     167        # Call authentication application 
    128168        try: 
    129             authenticate(environ, username, password) 
    130                  
     169            return authenticate(environ, start_response, username, password) 
     170         
    131171        except HTTPBasicAuthUnauthorized, e: 
    132172            log.error(e) 
  • TI12-security/trunk/python/ndg_security_server/ndg/security/server/wsgi/myproxy/__init__.py

    r5839 r5843  
    1313import re 
    1414import httplib 
     15import socket 
    1516 
    16 from myproxy.client import MyProxyClient 
     17from myproxy.client import MyProxyClient, MyProxyClientError 
     18from ndg.security.server.wsgi import NDGSecurityMiddlewareBase, \ 
     19    NDGSecurityMiddlewareConfigError 
     20     
    1721from ndg.security.server.wsgi.authn import HTTPBasicAuthMiddleware 
     22         
     23         
     24class MyProxyClientMiddlewareConfigError(NDGSecurityMiddlewareConfigError): 
     25    """Configuration error with MyProxyClientMiddleware""" 
    1826 
    19 class MyProxyClientMiddleware(object): 
     27 
     28class MyProxyClientMiddleware(NDGSecurityMiddlewareBase): 
    2029    ''' 
    21     HTTPS proxy to a MyProxy server to enable HTTPS type calls to MyProxy 
     30    Create a MyProxy client and make it available to other middleware in the  
     31    WSGI stack 
    2232    ''' 
    2333    # Options for ini file 
    24     RE_PATH_MATCH_LIST_OPTNAME = 'rePathMatchList' 
     34    CLIENT_ENV_KEYNAME_OPTNAME = 'clientEnvKeyName' 
     35    LOGON_FUNC_ENV_KEYNAME_OPTNAME = 'logonFuncEnvKeyName'      
    2536     
     37    # Default environ key names 
     38    CLIENT_ENV_KEYNAME = ('ndg.security.server.wsgi.authn.' 
     39                          'MyProxyClientMiddleware') 
    2640    LOGON_FUNC_ENV_KEYNAME = ('ndg.security.server.wsgi.authn.' 
    2741                              'MyProxyClientMiddleware.logon') 
    28     LOGON_FUNC_ENV_KEYNAME_OPTNAME = 'logonFunc'      
     42     
     43    # Option prefixes 
    2944    PARAM_PREFIX = 'myproxy.' 
    3045    MYPROXY_CLIENT_PARAM_PREFIX = 'client.' 
    3146     
    32     def __init__(self, app, global_conf, prefix=PARAM_PREFIX, **app_conf): 
     47    def __init__(self, app, global_conf, prefix=PARAM_PREFIX,  
     48                 myProxyClientPrefix=MYPROXY_CLIENT_PARAM_PREFIX, **app_conf): 
    3349        '''''' 
    34         super(MyProxyClientMiddleware, self).__init__(app,  
    35                                                       global_conf,  
    36                                                       prefix=prefix, 
    37                                                       *app_conf) 
     50        super(MyProxyClientMiddleware, self).__init__(app, global_conf) 
    3851        self.__myProxyClient = None 
    3952 
    40         rePathMatchListParamName = prefix + \ 
    41                             MyProxyClientMiddleware.RE_PATH_MATCH_LIST_OPTNAME 
    42         rePathMatchListVal = app_conf.get(rePathMatchListParamName, '') 
    43          
    44         self.rePathMatchList = [re.compile(r)  
    45                                 for r in rePathMatchListVal.split()] 
    46  
    4753        # Get MyProxyClient initialisation parameters 
    48         myProxyClientPrefix = prefix + \ 
    49                             MyProxyClientMiddleware.MYPROXY_CLIENT_PARAM_PREFIX 
     54        myProxyClientFullPrefix = prefix + myProxyClientPrefix 
    5055                             
    51         myProxyClientKw = dict([(k.replace(myProxyClientPrefix, ''), v)  
     56        myProxyClientKw = dict([(k.replace(myProxyClientFullPrefix, ''), v)  
    5257                                 for k,v in app_conf.items()  
    53                                  if k.startswith(myProxyClientPrefix)]) 
     58                                 if k.startswith(myProxyClientFullPrefix)]) 
    5459         
    5560        self.myProxyClient = MyProxyClient(**myProxyClientKw) 
    56         paramName = prefix + \ 
     61        clientEnvKeyOptName = prefix + \ 
     62                            MyProxyClientMiddleware.CLIENT_ENV_KEYNAME_OPTNAME 
     63                     
     64        self.clientEnvironKeyName = app_conf.get(clientEnvKeyOptName, 
     65                                MyProxyClientMiddleware.CLIENT_ENV_KEYNAME) 
     66                     
     67        logonFuncEnvKeyOptName = prefix + \ 
    5768                    MyProxyClientMiddleware.LOGON_FUNC_ENV_KEYNAME_OPTNAME 
    58                      
    59         self.logonFuncEnvironKeyName = local_conf.get(paramName, 
     69 
     70        self.logonFuncEnvironKeyName = app_conf.get(logonFuncEnvKeyOptName, 
    6071                                MyProxyClientMiddleware.LOGON_FUNC_ENV_KEYNAME) 
     72 
     73    def _getClientEnvironKeyName(self): 
     74        return self.__clientEnvironKeyName 
     75 
     76    def _setClientEnvironKeyName(self, value): 
     77        if not isinstance(value, basestring): 
     78            raise TypeError('Expecting string type for "clientEnvironKeyName"; ' 
     79                            'got %r type' % type(value)) 
     80        self.__clientEnvironKeyName = value 
     81 
     82    clientEnvironKeyName = property(fget=_getClientEnvironKeyName,  
     83                                    fset=_setClientEnvironKeyName,  
     84                                    doc="key name in environ for the " 
     85                                        "MyProxyClient instance")     
    6186 
    6287    def _getLogonFuncEnvironKeyName(self): 
     
    7398                                       fset=_setLogonFuncEnvironKeyName,  
    7499                                       doc="key name in environ for the " 
    75                                            "custom authentication function " 
    76                                            "used by this class") 
     100                                           "MyProxy logon function") 
     101     
    77102    def _getMyProxyClient(self): 
    78103        return self.__myProxyClient 
     
    91116    @NDGSecurityMiddlewareBase.initCall 
    92117    def __call__(self, environ, start_response): 
    93         '''Intercept MyProxy call and relay to MyProxy server 
     118        '''Set MyProxyClient instance and MyProxy logon method in environ 
    94119         
    95120        @type environ: dict 
     
    99124        ''' 
    100125        log.debug("MyProxyClientMiddleware.__call__ ...") 
    101         environ[self.logonFuncEnvironKeyName] = self.myProxyLogonFunc 
     126        environ[self.clientEnvironKeyName] = self.myProxyClient 
     127        environ[self.logonFuncEnvironKeyName] = self.myProxyLogon 
    102128         
    103 #        if not self._pathMatch(): 
    104 #            return self._app(environ, start_response) 
    105 #        else: 
    106 #            try: 
    107 #                # TODO: get username/passphrase from upstream HTTP Basic Auth 
    108 #                # middleware 
    109 #                credentials = self.myProxyClient.logon(username, passphrase) 
    110 #            except Exception, e: 
    111 #                log.error(e) 
    112 #                 
    113 #                # TODO: fit to appropriate HTTP error code 
    114 #                raise 
    115 #             
    116 #            response = '\n'.join(credentials) 
    117 #            start_response(MyProxyClientMiddleware.getStatusMessage(httplib.OK), 
    118 #                           [('Content-length', str(len(response))), 
    119 #                            ('Content-type', 'plain/text')]) 
    120 #            return [response] 
    121          
    122     def _pathMatch(self): 
    123         """Apply a list of regular expression matching patterns to the contents 
    124         of environ['PATH_INFO'], if any match, return True.  This method is 
    125         used to determine whether to apply SSL client authentication 
    126         """ 
    127         path = self.pathInfo 
    128         for regEx in self.rePathMatchList: 
    129             if regEx.match(path): 
    130                 return True 
    131              
    132         return False    
     129        return self._app(environ, start_response) 
    133130     
    134131    @property 
    135     def myProxyLogonFunc(self): 
     132    def myProxyLogon(self): 
    136133        """Return the MyProxy logon method wrapped as a HTTP Basic Auth  
    137134        authenticate interface function 
    138135        """ 
    139         def HTTPBasicAuthFunc(environ, username, password): 
    140             """Wrap MyProxy logon method as HTTPBasicAuthMiddleware  
    141             authenticate function""" 
    142             return self.myProxyClient.logon(environ, username, password) 
     136        def _myProxylogon(environ, start_response, username, password): 
     137            """Wrap MyProxy logon method as a WSGI app 
     138            """ 
     139            try: 
     140                credentials = self.myProxyClient.logon(username, password) 
     141                status = self.getStatusMessage(httplib.OK) 
     142                response = '\n'.join(credentials) 
     143                 
     144            except MyProxyClientError, e: 
     145                status = self.getStatusMessage(httplib.UNAUTHORIZED) 
     146                response = str(e) 
     147             
     148            except socket.error, e: 
     149                raise MyProxyClientMiddlewareConfigError("Socket error " 
     150                                        "with MyProxy server %r: %s" %  
     151                                        (self.myProxyClient.hostname, e)) 
     152             
     153            start_response(status, 
     154                           [('Content-length', str(len(response))), 
     155                            ('Content-type', 'text/plain')]) 
     156            return [response] 
    143157         
    144         return HTTPBasicAuthFunc 
     158        return _myProxylogon 
    145159         
    146160         
     161class MyProxyLogonMiddlewareConfigError(NDGSecurityMiddlewareConfigError): 
     162    """Configuration error with MyProxyLogonMiddleware""" 
     163     
     164     
    147165class MyProxyLogonMiddleware(NDGSecurityMiddlewareBase): 
    148     """HTTP Basic Auth interface to MyProxy logon""" 
     166    """HTTP Basic Auth interface to MyProxy logon.  This interfaces creates a 
     167    MyProxy client instance and HTTP Basic Auth based web service interface 
     168    for MyProxy logon calls.  This WSGI must be run over HTTPS to ensure 
     169    confidentiality of username/passphrase credentials 
     170    """ 
    149171    PARAM_PREFIX = 'myproxy.logon.' 
    150172     
    151     def __init__(self, app, app_conf, prefix=PARAM_PREFIX, **local_conf): 
    152         app = HTTPBasicAuthMiddleware(app, app_conf, **local_conf) 
    153         app = MyProxyClientMiddleware(app, app_conf, **local_conf) 
     173    def __init__(self, app, global_conf, prefix=PARAM_PREFIX, **app_conf):         
     174         
     175        authnFuncEnvKeyNameOptName = HTTPBasicAuthMiddleware.PARAM_PREFIX + \ 
     176                        HTTPBasicAuthMiddleware.AUTHN_FUNC_ENV_KEYNAME_OPTNAME 
     177                         
     178        if authnFuncEnvKeyNameOptName in app_conf: 
     179            raise MyProxyLogonMiddlewareConfigError("Found %r option name in " 
     180                "application configuration settings.  Use %r instead" % 
     181                (authnFuncEnvKeyNameOptName,  
     182                 MyProxyClientMiddleware.PARAM_PREFIX + \ 
     183                 MyProxyClientMiddleware.LOGON_FUNC_ENV_KEYNAME_OPTNAME)) 
     184         
     185        httpBasicAuthApp = HTTPBasicAuthMiddleware(app, app_conf, **app_conf) 
     186        app = MyProxyClientMiddleware(httpBasicAuthApp, app_conf, **app_conf) 
     187         
     188        # Set HTTP Basic Auth to use the MyProxy client logon for its 
     189        # authentication method 
     190        httpBasicAuthApp.authnFuncEnvironKeyName = app.logonFuncEnvironKeyName 
     191         
     192        super(MyProxyLogonMiddleware, self).__init__(app, global_conf,  
     193                                                     prefix=prefix, **app_conf) 
  • TI12-security/trunk/python/ndg_security_server/setup.py

    r5788 r5843  
    2323    'ndg_security_common', 
    2424    'Pylons <= 0.9.6.2', # TODO: drop Pylons dependency in future release 
    25     'AuthKit' 
     25    'AuthKit', 
     26    'MyProxyClient' 
    2627] 
    2728 
  • TI12-security/trunk/python/ndg_security_test/ndg/security/test/unit/wsgi/authn/test_httpbasicauth.py

    r5838 r5843  
    2525    response = "Test HTTP Basic Authentication application" 
    2626     
    27     loggedIn = lambda self, environ: 'username' in environ.get( 
    28                                                 self.beakerSessionKeyName, {}) 
    29      
    3027    def __init__(self, app_conf, **local_conf): 
    31         self.beakerSessionKeyName = app_conf.get('beakerSessionKeyName') 
     28        pass 
    3229         
    3330    def __call__(self, environ, start_response): 
    3431         
    35         if environ['PATH_INFO'] == '/test_401WithNotLoggedIn': 
    36             status = "401 Unauthorized" 
    37              
    38         elif environ['PATH_INFO'] == '/test_401WithLoggedIn': 
    39             status = "401 Unauthorized" 
    40              
    41         elif environ['PATH_INFO'] == '/test_200WithNotLoggedIn': 
     32        if environ['PATH_INFO'] == '/test_200': 
    4233            status = "200 OK" 
    43              
    44         elif environ['PATH_INFO'] == '/test_200': 
    45             status = "200 OK" 
    46          
    47         elif environ['PATH_INFO'] == '/test_sslClientAuthn': 
    48             if self.loggedIn(environ): 
    49                 status = "200 OK" 
    50             else: 
    51                 status = "401 Unauthorized" 
    5234        else: 
    5335            status = "404 Not found" 
     
    124106        authHeader =  "Basic %s" % base64String 
    125107        req.add_header("Authorization", authHeader) 
    126         try: 
    127             handle = urllib2.urlopen(req) 
    128         except IOError, e: 
    129             print("It looks like the username or password is wrong: %s" % e) 
    130             return 
     108         
     109        handle = urllib2.urlopen(req) 
    131110         
    132111        response = handle.read() 
Note: See TracChangeset for help on using the changeset viewer.