Changeset 4609 for TI12-security


Ignore:
Timestamp:
12/12/08 10:36:33 (11 years ago)
Author:
pjkersha
Message:

#1004 Security Filter:

  • added unit tests for SSLClientAuthNMiddleware. The middleware must <currently> be run under mod_wsgi to enable it to pick up SSL_CLIENT_CERT from environ.
  • TODO: extend to enable client authN with paste running over SSL? - Unit test dir contains an e.g. paste app running over SSL using pyOpenSSL but it's not clear how to pass SSL client cert to the middleware.
  • TODO: current version supports a simple scheme of matching a list of URLs from config to see which ones to secure - will need to change to something more sophisticated as proposed in the NDG3 work plan,
Location:
TI12-security/trunk/python
Files:
14 added
1 edited

Legend:

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

    r4606 r4609  
    1818import logging 
    1919log = logging.getLogger(__name__) 
     20import os 
    2021import httplib 
    21 from ndg.security.common.X509 import X509Cert, X509CertError 
     22from ndg.security.common.X509 import X509Stack, X509Cert, X509CertError 
    2223 
    2324class SSLClientAuthNMiddleware(object): 
     
    3132    } 
    3233 
    33     _isSSLClientCertSet = lambda self: \ 
    34         SSLClientAuthNMiddleware.sslClientCertKeyName in self._environ 
     34    _isSSLClientCertSet = lambda self: bool(self._environ.get( 
     35                                SSLClientAuthNMiddleware.sslClientCertKeyName))  
    3536    isSSLClientCertSet = property(fget=_isSSLClientCertSet, 
    3637                                  doc="Check for client cert. set in environ") 
     
    4546 
    4647        opt = SSLClientAuthNMiddleware.propertyDefaults.copy() 
    47         if app_conf is not None: 
    48             # Update from application config dictionary - filter from using 
    49             # prefix 
     48         
     49        # If no prefix is set, there is no way to distinguish options set for  
     50        # this app and those applying to other applications 
     51        if app_conf is not None and prefix: 
     52            # Update from application config dictionary - filter using prefix 
    5053            SSLClientAuthNMiddleware._filterOpts(opt, app_conf, prefix=prefix) 
    5154                         
    5255        # Similarly, filter keyword input                  
    53         SSLClientAuthNMiddleware._filterOpts(opt, kw, prefix=prefix) 
     56        SSLClientAuthNMiddleware._filterOpts(opt, local_conf, prefix=prefix) 
    5457        
    5558        # Update options from keywords - matching app_conf ones will be  
     
    111114 
    112115        for caCertFilePath in caCertFilePathList: 
    113             self._caCertStack.push(X509.load_cert(caCertFilePath)) 
     116            x509Cert = X509Cert.Read(os.path.expandvars(caCertFilePath)) 
     117            self._caCertStack.push(x509Cert) 
    114118         
    115119    caCertFilePathList = property(fset=_setCACertsFromFileList, 
     
    117121                                      "peer certificate must validate against " 
    118122                                      "one") 
     123    def _getPathMatchList(self): 
     124        return self._pathMatchList 
    119125     
    120126    def _setPathMatchList(self, pathList): 
    121         '''Read CA certificates from file and add them to an X.509 Cert. 
    122         stack 
    123          
     127        ''' 
    124128        @type pathList: list or tuple 
    125129        @param pathList: list of URL paths to apply SSL client authentication  
     
    134138        if isinstance(pathList, basestring): 
    135139            # Try parsing a space separated list of file paths 
    136             pathList = pathList.split() 
     140             self._pathMatchList = pathList.split() 
    137141             
    138142        elif not isinstance(pathList, (list, tuple)): 
    139143            raise TypeError('Expecting a list or tuple for "pathMatchList"') 
     144        else: 
     145            self._pathMatchList = pathList 
     146             
     147    pathMatchList = property(fget=_getPathMatchList, 
     148                             fset=_setPathMatchList, 
     149                             doc='List of URL paths to which to apply SSL ' 
     150                                 'client authentication') 
    140151     
    141152    @classmethod 
     
    174185    def __call__(self, environ, start_response): 
    175186         
    176         self._path = environ.get('PATH_INFO').rstrip('/') 
     187        self._path = environ.get('PATH_INFO') 
     188        if self._path != '/': 
     189            self._path.rstrip('/') 
     190         
     191        self._environ = environ 
    177192         
    178193        if not self.pathMatch: 
    179             return self._setResponse() 
    180          
    181         self._environ = environ 
    182          
    183         if not self.sslClientCertSet: 
    184             return self._setErrorResponse() 
    185              
    186         if self.isValidClientCert():             
    187             return self._setResponse() 
    188         else: 
    189             return self._setErrorResponse() 
    190              
    191     def _setResponse(self): 
     194            log.debug("ignoring path [%s]" % self._path) 
     195            return self._setResponse(environ, start_response) 
     196                     
     197        if not self.isSSLClientCertSet: 
     198            log.error("No SSL Client path set for request to [%s]"%self._path) 
     199            return self._setErrorResponse(environ, start_response, 
     200                                          msg='No client SSL Certificate set') 
     201             
     202        if self.isValidClientCert(environ):             
     203            return self._setResponse(environ, start_response) 
     204        else: 
     205            return self._setErrorResponse(environ, start_response) 
     206             
     207    def _setResponse(self, environ, start_response): 
    192208        if self._app: 
    193209            return self._app(environ, start_response) 
     
    200216            return response 
    201217 
    202     def _setErrorResponse(self, msg='Invalid SSL client certificate'): 
     218    def _setErrorResponse(self, environ, start_response, 
     219                          msg='Invalid SSL client certificate'): 
    203220        response = msg 
    204221        status = '%d %s' % (self.errorResponseCode,  
     
    210227        return response 
    211228 
    212     def isValidClientCert(self): 
    213         x509Cert = X509Cert.Parse(filePath) 
     229    def isValidClientCert(self, environ): 
     230        sslClientCert = environ[SSLClientAuthNMiddleware.sslClientCertKeyName] 
     231        x509Cert = X509Cert.Parse(sslClientCert) 
    214232         
    215233        if len(self._caCertStack) == 0: 
Note: See TracChangeset for help on using the changeset viewer.