Ignore:
Timestamp:
11/12/08 17:08:31 (12 years ago)
Author:
pjkersha
Message:

#1004 Security Filter:

  • SSLClientAuthNMiddleware initial version near completion
File:
1 edited

Legend:

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

    r4603 r4606  
     1"""SSL Client Authentication Middleware 
     2 
     3Apply to SSL client authentication to configured URL paths. 
     4 
     5SSL Client certificate is expected to be present in environ as SSL_CLIENT_CERT 
     6key as set by standard Apache SSL. 
     7 
     8NERC Data Grid Project 
     9 
     10This software may be distributed under the terms of the Q Public License, 
     11version 1.0 or later. 
     12""" 
     13__author__ = "P J Kershaw" 
     14__date__ = "11/12/08" 
     15__copyright__ = "(C) 2008 STFC & NERC" 
     16__contact__ = "Philip.Kershaw@stfc.ac.uk" 
     17__revision__ = "$Id$" 
    118import logging 
    219log = logging.getLogger(__name__) 
    3 from ndg.security.common.X509 import X509Cert 
     20import httplib 
     21from ndg.security.common.X509 import X509Cert, X509CertError 
    422 
    523class SSLClientAuthNMiddleware(object): 
     
    1735    isSSLClientCertSet = property(fget=_isSSLClientCertSet, 
    1836                                  doc="Check for client cert. set in environ") 
     37     
     38    _pathMatch = lambda self: self._path in self.pathMatchList 
     39    pathMatch = property(fget=_pathMatch, 
     40                         doc="Check for input path match to list of paths" 
     41                             "to which SSL client AuthN is to be applied") 
    1942     
    2043    def __init__(self, app, app_conf, prefix='', **local_conf): 
     
    5881            raise TypeError('Expecting int or string type for ' 
    5982                            '"errorResponseCode" attribute') 
     83             
     84        if self._errorResponseCode not in httplib.responses:  
     85            raise ValueError("Error response code [%d] is not recognised " 
     86                             "standard HTTP response code" %  
     87                             self._errorResponseCode)   
    6088             
    6189    errorResponseCode = property(fget=_getErrorResponseCode, 
     
    145173                
    146174    def __call__(self, environ, start_response): 
     175         
     176        self._path = environ.get('PATH_INFO').rstrip('/') 
     177         
     178        if not self.pathMatch: 
     179            return self._setResponse() 
     180         
    147181        self._environ = environ 
    148         self._path = environ.get('PATH_INFO').rstrip('/') 
    149          
    150         if self.sslClientCertSet: 
    151             self.verifyClientCert() 
    152              
     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): 
    153192        if self._app: 
    154193            return self._app(environ, start_response) 
    155194        else: 
    156             response = 'No app set for SSLClientAuthNMiddleware' 
    157             start_response('200 OK', 
     195            response = 'No application set for SSLClientAuthNMiddleware' 
     196            status = '%d %s' % (404, httplib.responses[404]) 
     197            start_response(status, 
    158198                           [('Content-type', 'text/plain'), 
    159199                            ('Content-Length', str(len(response)))]) 
    160200            return response 
    161                    
    162     def verifyClientCert(self): 
     201 
     202    def _setErrorResponse(self, msg='Invalid SSL client certificate'): 
     203        response = msg 
     204        status = '%d %s' % (self.errorResponseCode,  
     205                            httplib.responses[self.errorResponseCode]) 
     206         
     207        start_response(status, 
     208                       [('Content-type', 'text/plain'), 
     209                        ('Content-Length', str(len(response)))]) 
     210        return response 
     211 
     212    def isValidClientCert(self): 
    163213        x509Cert = X509Cert.Parse(filePath) 
    164214         
    165          
     215        if len(self._caCertStack) == 0: 
     216            log.warning("No CA certificates set for Client certificate " 
     217                        "signature verification") 
     218        else: 
     219            try: 
     220                self._caCertStack.verifyCertChain(x509Cert2Verify=x509Cert) 
     221 
     222            except X509CertError, e: 
     223                log.info("Client certificate verification failed: %s" % e) 
     224                return False 
     225             
     226            except Exception, e: 
     227                log.error("Client certificate verification failed with " 
     228                          "unexpected error: %s" % e) 
     229                return False 
     230             
     231        return True 
     232         
     233 
     234# Utility functions to support Paste Deploy application and filter function 
     235# signatures         
    166236def filter_app_factory(app, app_conf, **local_conf): 
     237    '''Wrapper to SSLClientAuthNMiddleware for Paste Deploy filter''' 
    167238    return SSLClientAuthNMiddleware(app, app_conf, **local_conf) 
    168239    
    169240def app_factory(app_conf, **local_conf): 
     241    '''Wrapper to SSLClientAuthNMiddleware for Paste Deploy app''' 
    170242    return SSLClientAuthNMiddleware(None, app_conf, **local_conf) 
Note: See TracChangeset for help on using the changeset viewer.