Changeset 5497


Ignore:
Timestamp:
17/07/09 16:41:29 (11 years ago)
Author:
pjkersha
Message:

Work on unit testing for IdP Validator implementation + SSL client authN validator

Location:
TI12-security/trunk/python
Files:
1 added
3 edited

Legend:

Unmodified
Added
Removed
  • TI12-security/trunk/python/ndg.security.common/ndg/security/common/utils/classfactory.py

    r5491 r5497  
    114114 
    115115    # Instantiate class 
    116     log.debug("Instantiating class '%s'" % importedClass.__name__) 
     116    log.debug('Instantiating class "%s"' % importedClass.__name__) 
    117117    try: 
    118118        if classArgs: 
  • TI12-security/trunk/python/ndg.security.server/ndg/security/server/wsgi/openid/relyingparty/validation.py

    r5491 r5497  
    6868       
    6969class XmlConfigReader(object): 
    70  
    71     def getValidators(self, filePath):   
     70    """Parser for IdP and Attribute Provider config 
     71    """ 
     72    @staticmethod 
     73    def getNamedElem(name):  
     74        for e in elem: 
     75            if e == name: 
     76                return e 
     77        return None 
     78     
     79    def getValidators(self, source):   
     80        """Retrieve IdP Validator objects from XML file 
     81        @type source: basestring/file 
     82        @param source: file path to XML file or file object 
     83        """ 
    7284        validators = None 
    7385 
    74         doc = self.__parseConfigFile(filePath) 
    75         if doc is not None: 
    76             validators = extractValidatorConfigs(doc) 
     86        root = self.__parseConfigFile(source) 
     87        if root is not None: 
     88            validators = self.__extractValidatorConfigs(root) 
    7789         
    7890        return validators 
    7991     
    80     def getAttrProviders(self, filePath): 
    81      
     92    def getAttrProviders(self, source): 
     93        """ 
     94        @type source: basestring/file 
     95        @param source: file path to XML file or file object 
     96        """     
    8297        attrProviders = None 
    8398 
    84         doc = self.__parseConfigFile(filePath) 
    85         if doc is not None: 
    86             attrProviders = self.extractAttrProviderConfigs(doc) 
     99        root = self.__parseConfigFile(source) 
     100        if root is not None: 
     101            attrProviders = self.__extractAttrProviderConfigs(root) 
    87102         
    88103        return attrProviders 
    89104     
    90     def __parseConfigFile(self, filePath): 
     105    def __parseConfigFile(self, source): 
    91106        """Read in the XML configuration file 
    92         @type filePath: basestring 
    93         @param filePath: XML file to read 
     107        @type source: basestring/file 
     108        @param source: file path to XML file or file object 
    94109        """ 
    95110        elem = ElementTree.parse(source) 
     
    104119        """ 
    105120        validators = [] 
    106         validatorConfig = None 
    107         parameters = {} 
    108121         
    109122        for elem in root: 
    110123            if getLocalName(elem).lower() == "validator":     
    111                 if validatorConfig is not None: 
    112                     validatorConfig.setParameters(parameters) 
    113                     validators.append(validatorConfig) 
    114                  
    115124                validatorConfig = IdPValidatorConfig() 
    116125                validatorConfig.className = elem.attrib["name"] 
    117                 validatorConfig.setClassName(className) 
    118              
    119             elif getLocalName(elem).lower() == "parameter": 
    120                 if elem.attrib["name"] in parameters: 
    121                     raise XmlConfigReaderError('Duplicate parameter name "%s" ' 
    122                                                'found' % elem.attrib["name"]) 
    123                      
    124                 parameters[elem.attrib["name"]] = elem.attrib["value"] 
    125              
    126         if validatorConfig is not None: 
    127             validatorConfig.parameters = parameters 
    128             validators.append(validatorConfig) 
     126                 
     127                for el in elem: 
     128                    parameters = {} 
     129                    if getLocalName(el).lower() == "parameter": 
     130                        if el.attrib["name"] in parameters: 
     131                            raise XmlConfigReaderError('Duplicate parameter ' 
     132                                                       'name "%s" found' %  
     133                                                       el.attrib["name"]) 
     134                             
     135                        parameters[el.attrib["name"]] = os.path.expandvars( 
     136                                                            el.attrib["value"]) 
     137             
     138                validatorConfig.parameters = parameters 
     139                validators.append(validatorConfig) 
    129140         
    130141        return validators 
     
    175186        raise NotImplementedError() 
    176187 
    177     def initialize(self, parameters): 
     188    def initialize(self, **parameters): 
    178189        '''@raise ConfigException:'''  
    179190        raise NotImplementedError() 
     
    190201     
    191202    def __init__(self): 
    192         self._idpValidators = None 
    193          
    194     def _get_idpValidators(self): 
    195         return self._idpValidators 
    196      
    197     def _set_idpValidators(self, idpValidators): 
    198         self._idpValidators = idpValidators 
    199          
    200     idpValidators = property(fget=_get_idpValidators, 
    201                              fset=_set_idpValidators, 
     203        self._idPValidators = None 
     204         
     205    def _get_idPValidators(self): 
     206        return self._idPValidators 
     207     
     208    def _set_idPValidators(self, idPValidators): 
     209        self._idPValidators = idPValidators 
     210         
     211    idPValidators = property(fget=_get_idPValidators, 
     212                             fset=_set_idPValidators, 
    202213                             doc="list of IdP Validators") 
    203      
    204     def performIdPValidation(self, identifier, discoveries): 
     214 
     215    def readConfig(self, idpConfigFilePath=None): 
    205216        validators = [] 
    206217         
    207         idpConfigFilePath = os.environ("IDP_CONFIG_FILE") 
     218        if idpConfigFilePath is None: 
     219            idpConfigFilePath = os.environ.get("IDP_CONFIG_FILE") 
     220             
    208221        if idpConfigFilePath is None: 
    209222            log.warning("IdPValidationDriver.performIdPValidation: No IdP " 
    210223                        "Configuration file was set") 
    211             return discoveries 
     224            return validators 
    212225         
    213226        configReader = XmlConfigReader() 
     
    223236                                             None,  
    224237                                             objectType=IdPValidator) 
    225                 validator.initialize(parameters) 
     238                validator.initialize(**parameters) 
    226239                validators.append(validator) 
    227240             
    228241            except Exception, e:   
    229                 log.error("Failed to initialise validator: " + e) 
    230              
     242                log.error("Failed to initialise validator: ", e) 
     243                 
     244        return validators 
     245     
     246    def performIdPValidation(self, identifier, discoveries): 
     247        """Perform all IdPValidation for all configured IdPValidators.  if 
     248        the setIdPValidator method was used to initialise a list of 
     249        IdPValidators before this method is called, the configurations 
     250        are still checked each time this method is called and any valid 
     251        IdPValidators found are appended to the initial list and run in 
     252        addition. 
     253        """ 
     254        validators = self.readConfig() 
     255 
    231256        if self.idPValidators is not None: 
    232257            validators += self.idPValidators 
    233258 
    234         log.info(len(validators) + " IdPValidators initialised") 
    235  
    236         # validate the discovered endpoints 
     259        log.info("%d IdPValidators initialised", len(validators)) 
     260 
     261        # validate the discovered end points 
    237262        if len(validators) > 0: 
    238263         
     
    244269                                           identifier.getIdentifier()) 
    245270 
    246                         log.info("Whitelist Validator Accepting endpoint: " +  
     271                        log.info("Whitelist Validator %r accepting endpoint: " 
     272                                 "%s", validator, 
    247273                                 discoveryInfo.getOPEndpoint()) 
    248274 
     
    250276                     
    251277                    except Exception, e:         
    252                         log.info("Whitelist Validator rejecting endpoint: %s: " 
    253                                  "%s", discoveryInfo.getOPEndpoint(), e) 
     278                        log.warning("Whitelist Validator %r rejecting " 
     279                                    "endpoint: %s: %s", validator,  
     280                                    discoveryInfo.getOPEndpoint(), e) 
    254281                         
    255282            if len(newDiscoveries) > 0: 
    256283                discoveries = newDiscoveries 
    257                 log.info("Found %d valid endpoints." % len(discoveries)) 
     284                log.info("Found %d valid endpoint(s)." % len(discoveries)) 
    258285            else:       
    259                 discoveries = None 
    260286                raise IdPInvalidException("No valid endpoints were found " 
    261287                                          "after validation.") 
    262   
     288        else: 
     289            log.warning("No IdP validation executed because no validators " 
     290                        "were set") 
     291             
    263292        return discoveries 
     293 
     294    __call__ = performIdPValidation 
     295     
     296     
     297from openid.fetchers import USER_AGENT, _allowedURL, Urllib2Fetcher 
     298import urllib2 
     299from M2Crypto.m2urllib2 import HTTPSHandler 
     300from M2Crypto import SSL 
     301from M2Crypto.X509 import X509_Store_Context 
     302 
     303class SSLClientAuthNValidator(IdPValidator): 
     304    parameters = { 
     305        'preVerifyOK': int,  
     306        'x509StoreCtx': M2Crypto.X509_Store_Context 
     307    } 
     308    def __init__(self): 
     309        raise NotImplementedError() 
     310 
     311    def initialize(self, **parameters): 
     312        '''@raise ConfigException:'''  
     313        for name, val in parameters.items(): 
     314            if name not in SSLClientAuthNValidator.parameters: 
     315                raise AttributeError('Invalid parameter name "%s".  Valid ' 
     316                                     'names are %r' % (name, 
     317                                    SSLClientAuthNValidator.parameters)) 
     318            if not isinstance(val, parameters[name]): 
     319                raise TypeError() 
     320        
     321    def validate(self, idpEndpoint, idpIdentity): 
     322        '''@raise IdPInvalidException: 
     323        @raise ConfigException:'''  
     324        raise NotImplementedError() 
     325 
     326    def verifyCallback(preVerifyOK, x509StoreCtx): 
     327        '''callback function used to control the behaviour when the  
     328        SSL_VERIFY_PEER flag is set 
     329         
     330        http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html 
     331         
     332        @type preVerifyOK: int 
     333        @param preVerifyOK: If a verification error is found, this parameter  
     334        will be set to 0 
     335        @type x509StoreCtx: M2Crypto.X509_Store_Context 
     336        @param x509StoreCtx: locate the certificate to be verified and perform  
     337        additional verification steps as needed 
     338        @rtype: int 
     339        @return: controls the strategy of the further verification process.  
     340        - If verify_callback returns 0, the verification process is immediately  
     341        stopped with "verification failed" state. If SSL_VERIFY_PEER is set,  
     342        a verification failure alert is sent to the peer and the TLS/SSL  
     343        handshake is terminated.  
     344        - If verify_callback returns 1, the verification process is continued.  
     345        If verify_callback always returns 1, the TLS/SSL handshake will not be  
     346        terminated with respect to verification failures and the connection  
     347        will be established. The calling process can however retrieve the error 
     348        code of the last verification error using SSL_get_verify_result(3) or  
     349        by maintaining its own error storage managed by verify_callback. 
     350        ''' 
     351        if preVerifyOK == 0: 
     352            # Something is wrong with the certificate don't bother proceeding 
     353            # any further 
     354            return preVerifyOK 
     355         
     356        x509Cert = x509StoreCtx.get_current_cert() 
     357        x509Cert.get_subject() 
     358        x509CertChain = x509StoreCtx.get1_chain() 
     359        for cert in x509CertChain: 
     360            subject = cert.get_subject() 
     361            dn = subject.as_text() 
     362            print dn 
     363             
     364        # If all is OK preVerifyOK will be 1.  Return this to the caller to 
     365        # that it's OK to proceed 
     366        return preVerifyOK 
     367         
     368    ctx = SSL.Context() 
     369 
     370    caDirPath = '../ndg.security.test/ndg/security/test/config/ca' 
     371    ctx.set_verify(SSL.verify_peer|SSL.verify_fail_if_no_peer_cert,  
     372                   9,  
     373                   callback=verifyCallback) 
     374#    ctx.set_verify(SSL.verify_peer|SSL.verify_fail_if_no_peer_cert, 1) 
     375 
     376    ctx.load_verify_locations(capath=caDirPath) 
     377#    ctx.load_cert(certFilePath,  
     378#                  keyfile=priKeyFilePath,  
     379#                  callback=lambda *arg, **kw: priKeyPwd) 
     380 
     381    from M2Crypto.m2urllib2 import build_opener 
     382    urllib2.install_opener(build_opener(ssl_context=ctx)) 
     383 
     384 
  • TI12-security/trunk/python/ndg.security.test/ndg/security/test/unit/openid/relyingparty/validation/test_validation.py

    r5491 r5497  
     1"""OpenID IdP Validation unit test package 
     2 
     3NERC DataGrid Project 
     4""" 
     5__author__ = "P J Kershaw" 
     6__date__ = "16/07/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 
     12logging.basicConfig(level=logging.DEBUG) 
     13 
     14import os 
     15import unittest 
     16from ndg.security.test.unit import BaseTestCase 
     17from ndg.security.server.wsgi.openid.relyingparty.validation import \ 
     18    IdPValidator, IdPValidationDriver, IdPInvalidException 
     19     
     20class ProviderWhitelistValidator(IdPValidator): 
     21    def __init__(self): 
     22        pass 
     23     
     24    def initialize(self, parameters): 
     25        '''@raise ConfigException:'''  
     26        pass 
     27        
     28    def validate(self, idpEndpoint, idpIdentity): 
     29        '''@raise IdPInvalidException: 
     30        @raise ConfigException:'''  
     31        pass 
     32 
     33 
     34class ProviderIdentifierTestValidator(IdPValidator): 
     35    def __init__(self): 
     36        pass 
     37 
     38    def initialize(self, parameters): 
     39        '''@raise ConfigException:'''  
     40        pass 
     41        
     42    def validate(self, idpEndpoint, idpIdentity): 
     43        '''@raise IdPInvalidException: 
     44        @raise ConfigException:'''  
     45        raise IdPInvalidException() 
     46 
     47 
     48class DiscoveryInfoPlaceHolder(object): 
     49    def getOPEndpoint(self): 
     50        return 'https://localhost/openid/provider' 
     51 
     52  
     53class IdentifierPlaceHolder(object): 
     54    def getIdentifier(self):  
     55        return 'myid' 
     56 
     57 
     58class IdPValidationTestCase(unittest.TestCase): 
     59    thisDir = os.path.dirname(os.path.abspath(__file__)) 
     60    idpConfigFilePath = os.path.join(thisDir, 'idpvalidator.xml') 
     61    os.environ['NDGSEC_UNITTEST_IDPVALIDATION_DIR'] = thisDir 
     62     
     63    def test01IdPConfigFileEnvVarNotSet(self): 
     64        identifier = IdentifierPlaceHolder() 
     65        discoveries = [DiscoveryInfoPlaceHolder()] 
     66         
     67        idPValidationDriver = IdPValidationDriver() 
     68        validDiscoveries = idPValidationDriver.performIdPValidation(identifier, 
     69                                                                discoveries) 
     70        # Expect no discoveries returned because the IDP_CONFIG_FILE  
     71        # environment variable is not set 
     72        self.assert_(len(validDiscoveries) == 1) 
     73         
     74 
     75    def test02(self): 
     76        identifier = IdentifierPlaceHolder() 
     77        discoveries = [DiscoveryInfoPlaceHolder()] 
     78         
     79        os.environ['IDP_CONFIG_FILE'] = IdPValidationTestCase.idpConfigFilePath 
     80        idPValidationDriver = IdPValidationDriver() 
     81        validDiscoveries = idPValidationDriver.performIdPValidation(identifier, 
     82                                                                discoveries) 
     83        self.assert_(len(validDiscoveries) == 1) 
     84         
     85if __name__ == "__main__": 
     86    unittest.main()         
Note: See TracChangeset for help on using the changeset viewer.