Changeset 6050


Ignore:
Timestamp:
25/11/09 16:18:17 (10 years ago)
Author:
pjkersha
Message:
  • Enhanced ndg.security.common.saml_utils.bindings for better AttributeQuery? client interface
  • Refactored AttributeQuery? interface out of SamlCredentialWallet? - it belongs in the above only.
  • deleted ndg.security.common.credentialrepository package - relic from NDG1
Location:
TI12-security/trunk/python
Files:
1 deleted
11 edited

Legend:

Unmodified
Added
Removed
  • TI12-security/trunk/python/ndg_security_common/ndg/security/common/credentialwallet.py

    r6044 r6050  
    1212import logging 
    1313log = logging.getLogger(__name__) 
    14  
    15 import traceback 
     14logging.basicConfig(level=logging.DEBUG) 
     15 
     16import os 
     17import warnings 
    1618 
    1719# Check Attribute Certificate validity times 
    1820from datetime import datetime, timedelta 
     21 
     22from ConfigParser import ConfigParser 
     23 
     24from saml.utils import SAMLDateTime 
     25from saml.saml2.core import Assertion 
    1926 
    2027# Access Attribute Authority's web service using ZSI - allow pass if not  
     
    4855 
    4956# Authentication X.509 Certificate 
    50 from ndg.security.common.X509 import * 
     57from ndg.security.common.X509 import X509Cert 
    5158from M2Crypto import X509, BIO, RSA 
    5259 
    5360# Authorisation - attribute certificate  
    54 from ndg.security.common.AttCert import * 
     61from ndg.security.common.AttCert import AttCert, AttCertError 
    5562from ndg.security.common.wssecurity.signaturehandler.dom import SignatureHandler 
    5663 
     
    5865from ndg.security.common.utils.configfileparsers import \ 
    5966                                                INIPropertyFileWithValidation 
     67 
     68from ndg.security.common.utils import TypedList 
     69from ndg.security.common.utils.configfileparsers import (      
     70                                                    CaseSensitiveConfigParser,) 
    6071 
    6172 
     
    252263        for attr, val in attrDict.items(): 
    253264            setattr(self, attr, val) 
    254  
    255265        
    256266 
    257267class CredentialWalletBase(object): 
    258268    """Abstract base class for NDG and SAML Credential Wallet implementations 
    259     """ 
    260      
    261     CONFIG_FILE_OPTNAMES = ("userId", "attributeAuthorityURI") 
     269    """  
     270    CONFIG_FILE_OPTNAMES = ("userId", ) 
    262271         
    263272    __slots__ = ( 
     
    272281    def __init__(self): 
    273282        self.__userId = None 
    274         self.__attributeAuthorityURI = None 
    275283        self.__credentials = {} 
    276284        self.__credentialsKeyedByURI = {} 
     285 
     286    @classmethod 
     287    def fromConfig(cls, cfg, **kw): 
     288        '''Alternative constructor makes object from config file settings 
     289        @type cfg: basestring /ConfigParser derived type 
     290        @param cfg: configuration file path or ConfigParser type object 
     291        @rtype: ndg.security.common.credentialWallet.SamlCredentialWallet 
     292        @return: new instance of this class 
     293        ''' 
     294        credentialWallet = cls() 
     295        credentialWallet.parseConfig(cfg, **kw) 
    277296 
    278297    def parseConfig(self, cfg, prefix='', section='DEFAULT'): 
     
    310329        existing certificate from the same issuer has a later expiry it will 
    311330        take precedence and the new input certificate is ignored.""" 
    312         raise NotImplementedError(BaseCredentialWallet.addCredential.__doc__) 
     331        raise NotImplementedError(CredentialWalletBase.addCredential.__doc__) 
    313332             
    314333    def audit(self): 
    315334        """Check the credentials held in the wallet removing any that have 
    316335        expired or are otherwise invalid.""" 
    317         raise NotImplementedError(BaseCredentialWallet.audit.__doc__) 
     336        raise NotImplementedError(CredentialWalletBase.audit.__doc__) 
    318337 
    319338    def updateCredentialRepository(self, auditCred=True): 
     
    325344        removing invalid ones""" 
    326345        raise NotImplementedError( 
    327                     BaseCredentialWallet.updateCredentialRepository.__doc__) 
     346                    CredentialWalletBase.updateCredentialRepository.__doc__) 
    328347         
    329348    def _getCredentials(self): 
     
    365384                      doc="User Identity for this wallet") 
    366385 
    367     def _getAttributeAuthorityURI(self): 
    368         return self.__attributeAuthorityURI 
    369  
    370     def _setAttributeAuthorityURI(self, value): 
    371         """String or None type are allowed - The URI may be set to None to  
    372         flag that a local Attribute Authority instance is being invoked rather 
    373         one hosted via a remote URI 
    374         """ 
    375         if not isinstance(value, (basestring, type(None))): 
    376             raise TypeError('Expecting string or None type for ' 
    377                             '"attributeAuthorityURI"; got %r instead' %  
    378                             type(value)) 
    379         self.__attributeAuthorityURI = value 
    380  
    381     attributeAuthorityURI = property(_getAttributeAuthorityURI, 
    382                                      _setAttributeAuthorityURI,  
    383                                      doc="Attribute Authority Service URI") 
    384  
    385386    def __getstate__(self): 
    386387        '''Enable pickling for use with beaker.session''' 
     
    392393        for attr, val in attrDict.items(): 
    393394            setattr(self, attr, val) 
     395 
     396 
     397class SamlCredentialWallet(CredentialWalletBase): 
     398    """CredentialWallet for Earth System Grid supporting caching of SAML  
     399    Attribute Assertions 
     400    """ 
     401    __slots__ = () 
     402     
     403    CREDENTIAL_REPOSITORY_NOT_SUPPORTED_MSG = ("SamlCredentialWallet doesn't " 
     404                                               "support the " 
     405                                               "CredentialRepository " 
     406                                               "interface") 
     407 
     408    @classmethod 
     409    def fromConfig(cls, cfg, **kw): 
     410        '''Alternative constructor makes object from config file settings 
     411        @type cfg: basestring /ConfigParser derived type 
     412        @param cfg: configuration file path or ConfigParser type object 
     413        @rtype: ndg.security.common.credentialWallet.SamlCredentialWallet 
     414        @return: new instance of this class 
     415        ''' 
     416        credentialWallet = cls() 
     417        credentialWallet.parseConfig(cfg, **kw) 
     418         
     419        return credentialWallet 
     420 
     421    def parseConfig(self, cfg, prefix='', section='DEFAULT'): 
     422        '''Read config file settings 
     423        @type cfg: basestring /ConfigParser derived type 
     424        @param cfg: configuration file path or ConfigParser type object 
     425        @type prefix: basestring 
     426        @param prefix: prefix for option names e.g. "certExtApp." 
     427        @type section: baestring 
     428        @param section: configuration file section from which to extract 
     429        parameters. 
     430        '''   
     431        if isinstance(cfg, basestring): 
     432            cfgFilePath = os.path.expandvars(cfg) 
     433            _cfg = CaseSensitiveConfigParser() 
     434            _cfg.read(cfgFilePath) 
     435             
     436        elif isinstance(cfg, ConfigParser): 
     437            _cfg = cfg    
     438        else: 
     439            raise AttributeError('Expecting basestring or ConfigParser type ' 
     440                                 'for "cfg" attribute; got %r type' % type(cfg)) 
     441         
     442        prefixLen = len(prefix) 
     443        for optName, val in _cfg.items(section): 
     444            if prefix and optName.startswith(prefix): 
     445                optName = optName[prefixLen:] 
     446                 
     447            setattr(self, optName, val) 
     448 
     449    def addCredential(self,  
     450                      credential,  
     451                      attributeAuthorityURI=None, 
     452                      bUpdateCredentialRepository=False): 
     453        """Add a new assertion to the list of assertion credentials held. 
     454 
     455        @type credential: SAML assertion 
     456        @param credential: new assertion to be added 
     457        @type attributeAuthorityURI: basestring 
     458        @param attributeAuthorityURI: input the Attribute Authority URI from 
     459        which credential was retrieved.  This is added to a dict to enable  
     460        access to a given Attribute Certificate keyed by Attribute Authority  
     461        URI. See the getCredential method. 
     462        @type bUpdateCredentialRepository: bool 
     463        @param bUpdateCredentialRepository: if set to True, and a repository  
     464        exists it will be updated with the new credentials also 
     465         
     466        @rtype: bool 
     467        @return: True if certificate was added otherwise False.  - If an 
     468        existing certificate from the same issuer has a later expiry it will 
     469        take precedence and the new input certificate is ignored.""" 
     470         
     471        # Check input 
     472        if not isinstance(credential, Assertion): 
     473            raise CredentialWalletError("Input credential must be an " 
     474                                        "%r type object" % Assertion)         
     475 
     476        if not self.isValidCredential(credential): 
     477            raise CredentialWalletError("Validity time error with assertion %r" 
     478                                        % assertion) 
     479         
     480        # Check to see if there is an existing Attribute Certificate held 
     481        # that was issued by the same host.  If so, compare the expiry time. 
     482        # The one with the latest expiry will be retained and the other 
     483        # ingored 
     484        bUpdateCred = True 
     485        issuerName = credential.issuer.value 
     486         
     487        if issuerName in self.credentials: 
     488            # There is an existing certificate held with the same issuing 
     489            # host name as the new certificate 
     490            credentialOld = self.credentials[issuerName].credential 
     491 
     492            # If the new certificate has an earlier expiry time then ignore it 
     493            bUpdateCred = (credential.conditions.notOnOrAfter >  
     494                           credentialOld.conditions.notOnOrAfter) 
     495 
     496        if bUpdateCred: 
     497            thisCredential = CredentialContainer(Assertion) 
     498            thisCredential.credential = credential 
     499            thisCredential.issuerName = issuerName 
     500            thisCredential.attributeAuthorityURI = attributeAuthorityURI 
     501             
     502            self.credentials[issuerName] = thisCredential 
     503             
     504            if attributeAuthorityURI: 
     505                self.credentialsKeyedByURI[ 
     506                    attributeAuthorityURI] = thisCredential  
     507 
     508            # Update the Credentials Repository - the permanent store of user 
     509            # authorisation credentials.  This allows credentials for previous 
     510            # sessions to be re-instated 
     511            if bUpdateCredentialRepository: 
     512                self.updateCredentialRepository() 
     513 
     514        # Flag to caller to indicate whether the input certificate was added 
     515        # to the credentials or an exsiting certificate from the same issuer 
     516        # took precedence 
     517        return bUpdateCred 
     518                         
     519    def audit(self): 
     520        """Check the credentials held in the wallet removing any that have 
     521        expired or are otherwise invalid.""" 
     522 
     523        log.debug("SamlCredentialWallet.audit ...") 
     524         
     525        for issuerName, issuerEntry in self.credentials.items(): 
     526            if not self.isValidCredential(issuerEntry.credential): 
     527                self.credentialsKeyedByURI.pop( 
     528                    issuerEntry.attributeAuthorityURI, 
     529                    None) 
     530                     
     531                del self.credentials[issuerName] 
     532 
     533    def updateCredentialRepository(self, auditCred=True): 
     534        """No Credential Repository support is required""" 
     535        msg = SamlCredentialWallet.CREDENTIAL_REPOSITORY_NOT_SUPPORTED_MSG 
     536        log.warning(msg) 
     537        warnings.warn(msg) 
     538 
     539    def isValidCredential(self, assertion): 
     540        """Validate SAML assertion time validity""" 
     541        utcNow = datetime.utcnow() 
     542        if utcNow < assertion.conditions.notBefore: 
     543            msg = ('The current clock time [%s] is before the SAML Attribute ' 
     544                   'Response assertion conditions not before time [%s]' %  
     545                   (SAMLDateTime.toString(utcNow), 
     546                    assertion.conditions.notBefore)) 
     547            log.warning(msg) 
     548            return False 
     549             
     550        if utcNow >= assertion.conditions.notOnOrAfter: 
     551            msg = ('The current clock time [%s] is on or after the SAML ' 
     552                   'Attribute Response assertion conditions not on or after ' 
     553                   'time [%s]' %  
     554                   (SAMLDateTime.toString(utcNow), 
     555                    assertion.conditions.notOnOrAfter)) 
     556            log.warning(msg) 
     557            return False 
     558             
     559        return True 
    394560             
    395561    
    396 class CredentialWallet(CredentialWalletBase): 
     562class NdgCredentialWallet(CredentialWalletBase): 
    397563    """Volatile store of user credentials associated with a user session 
    398564     
     
    533699        '_attributeAuthorityClnt', 
    534700        '_attributeAuthority', 
     701        '_attributeAuthorityURI', 
    535702        '_caCertFilePathList', 
    536703        '_mapFromTrustedHosts', 
     
    559726        from 
    560727        @type cfgPrefix: basestring 
    561         @param cfgPrefix: apply a prefix to all CredentialWallet config params  
     728        @param cfgPrefix: apply a prefix to all NdgCredentialWallet config params  
    562729        so that if placed in a file with other parameters they can be  
    563730        distinguished 
     
    565732        @param cfgKw: set parameters as key value pairs.""" 
    566733 
    567         log.debug("Calling CredentialWallet.__init__ ...") 
    568  
    569         super(CredentialWallet, self).__init__() 
     734        log.debug("Calling NdgCredentialWallet.__init__ ...") 
     735 
     736        super(NdgCredentialWallet, self).__init__() 
    570737         
    571738        # Initialise attributes - 1st protected ones 
    572         attr = {}.fromkeys(CredentialWallet._protectedAttrs) 
     739        attr = {}.fromkeys(NdgCredentialWallet._protectedAttrs) 
    573740         
    574741        # ... then properties 
    575         attr.update(CredentialWallet.propertyDefaults) 
     742        attr.update(NdgCredentialWallet.propertyDefaults) 
    576743        for k, v in attr.items(): 
    577744            setattr(self, k, v) 
     
    585752        # the private key 
    586753        self.userPriKeyPwd = kw.pop('userPriKeyPwd', None) 
    587         for k,v in kw.items(): 
     754        for k, v in kw.items(): 
    588755            setattr(self, k, v) 
    589756 
     
    624791            try: 
    625792                for cred in userCred:  
    626                     attCert = AttCertParse(cred.attCert) 
     793                    attCert = AttCert.Parse(cred.attCert) 
    627794                    issuerName = attCert['issuerName'] 
    628795                     
     
    656823        prop = readAndValidate(cfgFilePath, 
    657824                               cfg=self._cfg, 
    658                                validKeys=CredentialWallet.propertyDefaults, 
     825                               validKeys=NdgCredentialWallet.propertyDefaults, 
    659826                               prefix=prefix, 
    660827                               sections=(section,)) 
     
    721888             
    722889        elif isinstance(cert, basestring): 
    723             return X509CertParse(cert) 
     890            return X509Cert.Parse(cert) 
    724891         
    725892        else: 
     
    746913        if isinstance(filePath, basestring): 
    747914            filePath = os.path.expandvars(filePath) 
    748             self._userX509Cert = X509CertRead(filePath) 
     915            self._userX509Cert = X509Cert.Read(filePath) 
    749916             
    750917        elif filePath is not None: 
     
    774941        if isinstance(filePath, basestring): 
    775942            filePath = os.path.expandvars(filePath) 
    776             self._issuerX509Cert = X509CertRead(filePath) 
     943            self._issuerX509Cert = X509Cert.Read(filePath) 
    777944             
    778945        elif filePath is not None: 
     
    8941061                                      "verification of AC and SOAP message " 
    8951062                                      "signatures") 
    896              
    897     def _setAttributeAuthorityURI(self, attributeAuthorityURI): 
    898         """Set property method for Attribute Authority Web Service URI to 
    899         connect to.  This method ALSO RESETS attributeAuthority - a local 
    900         Attribute Authority instance - to None 
    901          
    902         @type attributeAuthorityURI: basestring/None 
    903         @param attributeAuthorityURI: Attribute Authority Web Service URI.  Set 
    904         to None to initialise.""" 
    905         super(CredentialWallet, self)._setAttributeAuthorityURI( 
    906                                                         attributeAuthorityURI) 
     1063 
     1064    def _getAttributeAuthorityURI(self): 
     1065        return self._attributeAuthorityURI 
     1066 
     1067    def _setAttributeAuthorityURI(self, value): 
     1068        """String or None type are allowed - The URI may be set to None to  
     1069        flag that a local Attribute Authority instance is being invoked rather 
     1070        one hosted via a remote URI 
     1071        """ 
     1072        if not isinstance(value, (basestring, type(None))): 
     1073            raise TypeError('Expecting string or None type for ' 
     1074                            '"attributeAuthorityURI"; got %r instead' %  
     1075                            type(value)) 
     1076             
     1077        self._attributeAuthorityURI = value 
    9071078          
    908         if attributeAuthorityURI is not None:      
     1079        if value is not None:      
    9091080            # Re-initialize local instance 
    910             self._attributeAuthority = CredentialWallet.propertyDefaults[ 
     1081            self._attributeAuthority = NdgCredentialWallet.propertyDefaults[ 
    9111082                                                        'attributeAuthority'] 
    912              
    913     attributeAuthorityURI = property( 
    914                             fget=CredentialWalletBase._getAttributeAuthorityURI, 
    915                             fset=_setAttributeAuthorityURI, 
    916                             doc="Attribute Authority address - setting also " 
    917                             "sets up AttributeAuthorityClient instance!") 
     1083 
     1084    attributeAuthorityURI = property(fget=_getAttributeAuthorityURI, 
     1085                                     fset=_setAttributeAuthorityURI, 
     1086                                     doc="Attribute Authority address - " 
     1087                                         "setting also sets up " 
     1088                                         "AttributeAuthorityClient instance!") 
    9181089 
    9191090    def _getAttributeAuthority(self): 
     
    10131184            return self._userX509Cert.isValidTime(**x509CertKeys) 
    10141185        else: 
    1015             log.warning("CredentialWallet.isValid: no user certificate set in " 
     1186            log.warning("NdgCredentialWallet.isValid: no user certificate set in " 
    10161187                        "wallet") 
    10171188            return True 
     
    11011272        expired or are otherwise invalid.""" 
    11021273 
    1103         log.debug("CredentialWallet.audit ...") 
     1274        log.debug("NdgCredentialWallet.audit ...") 
    11041275         
    11051276        # Nb. No signature check is carried out.  To do a check, access is 
     
    11201291        removing invalid ones""" 
    11211292 
    1122         log.debug("CredentialWallet.updateCredentialRepository ...") 
     1293        log.debug("NdgCredentialWallet.updateCredentialRepository ...") 
    11231294         
    11241295        if not self.credentialRepository: 
     
    11451316        @return: new Attribute Authority client instance""" 
    11461317 
    1147         log.debug('CredentialWallet._createAttributeAuthorityClnt for ' 
     1318        log.debug('NdgCredentialWallet._createAttributeAuthorityClnt for ' 
    11481319                  'service: "%s"' % attributeAuthorityURI) 
    11491320 
     
    12111382        Attribute Authority""" 
    12121383       
    1213         log.debug("CredentialWallet._getAttCert ...") 
     1384        log.debug("NdgCredentialWallet._getAttCert ...") 
    12141385         
    12151386         
     
    12391410            aaInterface = self._createAttributeAuthorityClnt( 
    12401411                                                        attributeAuthorityURI)                             
    1241             log.debug('CredentialWallet._getAttCert for remote Attribute ' 
     1412            log.debug('NdgCredentialWallet._getAttCert for remote Attribute ' 
    12421413                      'Authority service: "%s" ...' % attributeAuthorityURI) 
    12431414                 
     
    12461417            # configuration file attributeAuthority 
    12471418            aaInterface = attributeAuthority 
    1248             log.debug('CredentialWallet._getAttCert for local Attribute ' 
     1419            log.debug('NdgCredentialWallet._getAttCert for local Attribute ' 
    12491420                      'Authority: "%r" ...' % attributeAuthority) 
    12501421        else: 
     
    13151486            attributeAuthorityURI = self.attributeAuthorityURI 
    13161487         
    1317         log.debug('CredentialWallet._getAAHostInfo for service: "%s" ...' %  
     1488        log.debug('NdgCredentialWallet._getAAHostInfo for service: "%s" ...' %  
    13181489                  attributeAuthorityURI or attributeAuthority) 
    13191490             
     
    13721543            attributeAuthorityURI = self.attributeAuthorityURI 
    13731544         
    1374         log.debug('CredentialWallet._getAATrustedHostInfo for role "%s" and ' 
     1545        log.debug('NdgCredentialWallet._getAATrustedHostInfo for role "%s" and ' 
    13751546                  'service: "%s" ...' % (userRole,  
    13761547                                attributeAuthorityURI or attributeAuthority)) 
     
    15141685        @return: Attribute Certificate retrieved from Attribute Authority""" 
    15151686         
    1516         log.debug("CredentialWallet.getAttCert ...") 
     1687        log.debug("NdgCredentialWallet.getAttCert ...") 
    15171688         
    15181689        # Both these assignments are calling set property methods implicitly! 
     
    15301701            # Find out the site ID for the target AA by calling AA's host 
    15311702            # info WS method 
    1532             log.debug("CredentialWallet.getAttCert - check AA site ID ...") 
     1703            log.debug("NdgCredentialWallet.getAttCert - check AA site ID ...") 
    15331704            try: 
    15341705                hostInfo = self._getAAHostInfo() 
     
    17251896                    # Exit here returning the list of candidate certificates 
    17261897                    # that could be used to make a mapped certificate 
    1727                     msg = "User is not registered with Attribute " + \ 
    1728                           "Authority - retry using one of the returned " + \ 
    1729                           "Attribute Certificates obtained from other " + \ 
    1730                           "trusted hosts" 
     1898                    msg = ("User is not registered with Attribute " 
     1899                           "Authority - retry using one of the returned " 
     1900                           "Attribute Certificates obtained from other "  
     1901                           "trusted hosts") 
    17311902                           
    17321903                    raise CredentialWalletAttributeRequestDenied(msg, 
    17331904                                            extAttCertList=extAttCertList, 
    1734                                             trustedHostInfo=trustedHostInfo)             
    1735  
    1736 """Alias to CredentialWallet to make distinction between this and the SAML 
    1737 implementation""" 
    1738 NDGCredentialWallet = CredentialWallet 
    1739  
    1740  
    1741 class SamlCredentialWalletAttributeQueryResponseError(CredentialWalletError): 
    1742     """Attribute Authority returned a SAML Response error code""" 
    1743     def __init__(self, *arg, **kw): 
    1744         CredentialWalletError.__init__(self, *arg, **kw) 
    1745         self.__status = Status() 
    1746         self.__status.statusCode = StatusCode() 
    1747         self.__status.statusMessage = StatusMessage() 
    1748      
    1749     def _getStatus(self): 
    1750         '''Gets the Status of this response. 
    1751          
    1752         @return the Status of this response 
    1753         ''' 
    1754         return self.__status 
    1755  
    1756     def _setStatus(self, value): 
    1757         '''Sets the Status of this response. 
    1758          
    1759         @param value: the Status of this response 
    1760         ''' 
    1761         if not isinstance(value, Status): 
    1762             raise TypeError('"status" must be a %r, got %r' % (Status, 
    1763                                                                type(value))) 
    1764         self.__status = value 
    1765          
    1766     status = property(fget=_getStatus, fset=_setStatus,  
    1767                       doc="Attribute Authority SAML Response error status") 
    1768      
    1769     def __str__(self): 
    1770         if self.status is not None: 
    1771             return self.status.statusMessage.value or '' 
    1772         else: 
    1773             return '' 
    1774  
    1775 import re 
    1776 from uuid import uuid4 
    1777 try: # >= python 2.5 
    1778     from xml.etree import ElementTree 
    1779 except ImportError: 
    1780     import ElementTree 
    1781  
    1782 from M2Crypto.m2urllib2 import HTTPSHandler 
    1783  
    1784 from saml.utils import SAMLDateTime 
    1785 from saml.common.xml import SAMLConstants 
    1786 from saml.saml2.core import (Attribute, 
    1787                              Assertion,  
    1788                              SAMLVersion,  
    1789                              Subject,  
    1790                              NameID,  
    1791                              Issuer,  
    1792                              AttributeQuery,  
    1793                              XSStringAttributeValue,  
    1794                              Status, 
    1795                              StatusCode, 
    1796                              StatusMessage) 
    1797 from saml.xml.etree import ResponseElementTree 
    1798     
    1799 from ndg.security.common.saml_utils.bindings import SOAPBinding as \ 
    1800                                                                 SamlSoapBinding 
    1801 from ndg.security.common.saml_utils.esg import EsgSamlNamespaces 
    1802 from ndg.security.common.X509 import X500DN 
    1803 from ndg.security.common.utils import TypedList 
    1804 from ndg.security.common.utils.m2crypto import SSLContextProxy 
    1805 from ndg.security.common.utils.etree import prettyPrint 
    1806 from ndg.security.common.utils.configfileparsers import (      
    1807                                                     CaseSensitiveConfigParser,) 
    1808  
    1809  
    1810 class SamlCredentialWallet(CredentialWalletBase): 
    1811     """CredentialWallet for Earth System Grid supporting SAML based Attribute  
    1812     Queries 
    1813      
    1814     @cvar DEFAULT_QUERY_ATTRIBUTES: default attribute to query 
    1815     @type DEFAULT_QUERY_ATTRIBUTES: basestring 
    1816     @cvar QUERY_ATTRIBUTES_PAT: regular expression for parsing a query  
    1817     attribute definition from a single string as set in an ini file e.g. 
    1818     queryAttribute.0 = urn:esg:firstName, FirstName,  
    1819     sets the name, friendly name and format respectivelty for a SAML Attribute 
    1820     to be used in a query. 
    1821      
    1822     @type QUERY_ATTRIBUTES_PAT: basestring""" 
    1823      
    1824     XSSTRING_NS = "%s#%s" % ( 
    1825         SAMLConstants.XSD_NS, 
    1826         XSStringAttributeValue.TYPE_LOCAL_NAME 
    1827     ) 
    1828      
    1829     DEFAULT_QUERY_ATTRIBUTES = TypedList(Attribute) 
    1830     DEFAULT_QUERY_ATTRIBUTES += [Attribute(), Attribute(), Attribute()] 
    1831      
    1832     DEFAULT_QUERY_ATTRIBUTES[0].name = EsgSamlNamespaces.FIRSTNAME_ATTRNAME  
    1833     DEFAULT_QUERY_ATTRIBUTES[0].friendlyName = \ 
    1834                                     EsgSamlNamespaces.FIRSTNAME_FRIENDLYNAME 
    1835     DEFAULT_QUERY_ATTRIBUTES[0].format = XSSTRING_NS 
    1836  
    1837     DEFAULT_QUERY_ATTRIBUTES[1].name = EsgSamlNamespaces.LASTNAME_ATTRNAME  
    1838     DEFAULT_QUERY_ATTRIBUTES[1].friendlyName = \ 
    1839                                     EsgSamlNamespaces.LASTNAME_FRIENDLYNAME 
    1840     DEFAULT_QUERY_ATTRIBUTES[1].format = XSSTRING_NS 
    1841      
    1842     DEFAULT_QUERY_ATTRIBUTES[2].name = EsgSamlNamespaces.EMAILADDRESS_ATTRNAME 
    1843     DEFAULT_QUERY_ATTRIBUTES[2].friendlyName = \ 
    1844                                     EsgSamlNamespaces.EMAILADDRESS_FRIENDLYNAME 
    1845     DEFAULT_QUERY_ATTRIBUTES[2].format =  XSSTRING_NS 
    1846  
    1847     QUERY_ATTRIBUTES_ATTRNAME = 'queryAttributes' 
    1848     LEN_QUERY_ATTRIBUTES_ATTRNAME = len(QUERY_ATTRIBUTES_ATTRNAME) 
    1849  
    1850     QUERY_ATTRIBUTES_PAT = re.compile(',\s*') 
    1851      
    1852     ESG_NAME_ID_FORMAT = EsgSamlNamespaces.NAMEID_FORMAT 
    1853      
    1854     ISSUER_DN_OPTNAME = 'issuerDN' 
    1855     CLOCK_SKEW_OPTNAME = 'clockSkew' 
    1856      
    1857     CONFIG_FILE_OPTNAMES = ( 
    1858         ISSUER_DN_OPTNAME,                  
    1859         CLOCK_SKEW_OPTNAME             
    1860     ) + CredentialWalletBase.CONFIG_FILE_OPTNAMES 
    1861      
    1862     __slots__ = ( 
    1863        'sslCtxProxy', 
    1864        QUERY_ATTRIBUTES_ATTRNAME, 
    1865     ) 
    1866     __slots__ += CONFIG_FILE_OPTNAMES 
    1867     __PRIVATE_ATTR_PREFIX = '_SamlCredentialWallet__' 
    1868     __slots__ += tuple([__PRIVATE_ATTR_PREFIX + i for i in __slots__]) 
    1869     del i 
    1870      
    1871     CREDENTIAL_REPOSITORY_NOT_SUPPORTED_MSG = ("SamlCredentialWallet doesn't " 
    1872                                                "support the " 
    1873                                                "CredentialRepository " 
    1874                                                "interface") 
    1875  
    1876     def __init__(self): 
    1877         super(SamlCredentialWallet, self).__init__() 
    1878          
    1879         self.__issuerDN = None 
    1880         self.__clockSkew = timedelta(seconds=0.) 
    1881         self.__queryAttributes = TypedList(Attribute) 
    1882         self.__sslCtxProxy = SSLContextProxy() 
    1883  
    1884     @classmethod 
    1885     def fromConfig(cls, cfg, **kw): 
    1886         '''Alternative constructor makes object from config file settings 
    1887         @type cfg: basestring /ConfigParser derived type 
    1888         @param cfg: configuration file path or ConfigParser type object 
    1889         @rtype: ndg.security.common.credentialWallet.SamlCredentialWallet 
    1890         @return: new instance of this class 
    1891         ''' 
    1892         credentialWallet = cls() 
    1893         credentialWallet.parseConfig(cfg, **kw) 
    1894          
    1895         return credentialWallet 
    1896  
    1897     def parseConfig(self, cfg, prefix='', section='DEFAULT'): 
    1898         '''Read config file settings 
    1899         @type cfg: basestring /ConfigParser derived type 
    1900         @param cfg: configuration file path or ConfigParser type object 
    1901         @type prefix: basestring 
    1902         @param prefix: prefix for option names e.g. "certExtApp." 
    1903         @type section: baestring 
    1904         @param section: configuration file section from which to extract 
    1905         parameters. 
    1906         '''   
    1907         if isinstance(cfg, basestring): 
    1908             cfgFilePath = os.path.expandvars(cfg) 
    1909             _cfg = CaseSensitiveConfigParser() 
    1910             _cfg.read(cfgFilePath) 
    1911              
    1912         elif isinstance(cfg, ConfigParser): 
    1913             _cfg = cfg    
    1914         else: 
    1915             raise AttributeError('Expecting basestring or ConfigParser type ' 
    1916                                  'for "cfg" attribute; got %r type' % type(cfg))  
    1917          
    1918         prefixLen = len(prefix) 
    1919         for optName, val in _cfg.items(section): 
    1920             if prefix and optName.startswith(prefix): 
    1921                 optName = optName[prefixLen:] 
    1922                  
    1923             setattr(self, optName, val) 
    1924  
    1925     def __setattr__(self, name, value): 
    1926         """Enable setting of SAML query attribute objects via a comma separated 
    1927         string suitable for use reading from an ini file.  Also enable direct 
    1928         setting of SSLContextProxy attributes as if they are attributes of 
    1929         this class again for convenience with ini file parsing. 
    1930         """ 
    1931         try: 
    1932             super(SamlCredentialWallet, self).__setattr__(name, value) 
    1933              
    1934         except AttributeError, e: 
    1935             if name.startswith(SamlCredentialWallet.QUERY_ATTRIBUTES_ATTRNAME): 
    1936                 # Special handler for parsing string format settings 
    1937                 if not isinstance(value, basestring): 
    1938                     raise TypeError('Expecting string format for special ' 
    1939                                     '%r attribute; got %r instead' % 
    1940                                     (name, type(value))) 
    1941                      
    1942                 pat = SamlCredentialWallet.QUERY_ATTRIBUTES_PAT 
    1943                 attribute = Attribute() 
    1944                  
    1945                 (attribute.name,  
    1946                  attribute.friendlyName,  
    1947                  attribute.format) = pat.split(value) 
    1948                   
    1949                 self.queryAttributes.append(attribute) 
    1950             else: 
    1951                 # Coerce into setting SSL Context Proxy attributes 
    1952                 try: 
    1953                     setattr(self.sslCtxProxy, name, value) 
    1954                 except: 
    1955                     raise e 
    1956       
    1957     @property 
    1958     def sslCtxProxy(self): 
    1959         """SSL Context Proxy object used for setting up an SSL Context for 
    1960         queries to the Attribute Authority 
    1961         """ 
    1962         return self.__sslCtxProxy 
    1963             
    1964     def _getQueryAttributes(self): 
    1965         """Returns a *COPY* of the attributes to avoid overwriting the  
    1966         member variable content 
    1967         """ 
    1968         return self.__queryAttributes 
    1969  
    1970     def _setQueryAttributes(self, value): 
    1971         if not isinstance(value, TypedList) and value.elementType != Attribute: 
    1972             raise TypeError('Expecting TypedList(Attribute) type for ' 
    1973                             '"queryAttributes"; got %r instead' % type(value))  
    1974          
    1975         self.__queryAttributes = value 
    1976      
    1977     queryAttributes = property(_getQueryAttributes,  
    1978                                _setQueryAttributes,  
    1979                                doc="List of attributes to query from the " 
    1980                                    "Attribute Authority") 
    1981  
    1982     def _getIssuerDN(self): 
    1983         return self.__issuerDN 
    1984  
    1985     def _setIssuerDN(self, value): 
    1986         if isinstance(value, basestring): 
    1987             self.__issuerDN = X500DN.fromString(value) 
    1988              
    1989         elif isinstance(value, X500DN): 
    1990             self.__issuerDN = value 
    1991         else: 
    1992             raise TypeError('Expecting string or X500DN type for "issuerDN"; ' 
    1993                             'got %r instead' % type(value)) 
    1994         self.__issuerDN = value 
    1995  
    1996     issuerDN = property(_getIssuerDN, _setIssuerDN,  
    1997                         doc="Distinguished Name of issuer of SAML Attribute " 
    1998                             "Query to Attribute Authority") 
    1999  
    2000     def _getClockSkew(self): 
    2001         return self.__clockSkew 
    2002  
    2003     def _setClockSkew(self, value): 
    2004         if isinstance(value, (float, int, long)): 
    2005             self.__clockSkew = timedelta(seconds=value) 
    2006              
    2007         elif isinstance(value, basestring): 
    2008             self.__clockSkew = timedelta(seconds=float(value)) 
    2009         else: 
    2010             raise TypeError('Expecting float, int, long or string type for ' 
    2011                             '"clockSkew"; got %r' % type(value)) 
    2012  
    2013     clockSkew = property(fget=_getClockSkew,  
    2014                          fset=_setClockSkew,  
    2015                          doc="Allow a clock skew in seconds for SAML Attribute" 
    2016                              " Query issueInstant parameter check") 
    2017          
    2018     def __getstate__(self): 
    2019         '''Specific implementation needed with __slots__''' 
    2020         return dict([(attrName, getattr(self, attrName))  
    2021                      for attrName in SamlCredentialWallet.__slots__]) 
    2022          
    2023     def __setstate__(self, attrDict): 
    2024         '''Specific implementation needed with __slots__''' 
    2025         for attr, val in attrDict.items(): 
    2026             setattr(self, attr, val) 
    2027          
    2028     def attributeQuery(self): 
    2029         """Query an Attribute Authority to retrieve an assertion for the  
    2030         given user""" 
    2031                  
    2032         # Create a SAML attribute query 
    2033         attributeQuery = AttributeQuery() 
    2034         attributeQuery.version = SAMLVersion(SAMLVersion.VERSION_20) 
    2035         attributeQuery.id = str(uuid4()) 
    2036         attributeQuery.issueInstant = datetime.utcnow() 
    2037          
    2038         attributeQuery.issuer = Issuer() 
    2039         attributeQuery.issuer.format = Issuer.X509_SUBJECT 
    2040         attributeQuery.issuer.value = self.issuerDN 
    2041                          
    2042         attributeQuery.subject = Subject()   
    2043         attributeQuery.subject.nameID = NameID() 
    2044         attributeQuery.subject.nameID.format = \ 
    2045                                         SamlCredentialWallet.ESG_NAME_ID_FORMAT 
    2046         attributeQuery.subject.nameID.value = self.userId 
    2047                    
    2048         # Add list of attributes to query                       
    2049         for attribute in self.queryAttributes: 
    2050             attributeQuery.attributes.append(attribute) 
    2051  
    2052         # Make query over SOAP interface to remote service 
    2053         binding = SamlSoapBinding(handlers=()) 
    2054         httpsHandler = HTTPSHandler(ssl_context=self.sslCtxProxy.createCtx()) 
    2055         binding.client.openerDirector.add_handler(httpsHandler) 
    2056          
    2057         response = binding.attributeQuery(attributeQuery,  
    2058                                           self.attributeAuthorityURI) 
    2059          
    2060         if log.level <= logging.DEBUG:             
    2061             log.debug("Attribute Authority [%s] SAML Response:",  
    2062                       self.attributeAuthorityURI) 
    2063             log.debug("_"*80) 
    2064             responseElem = ResponseElementTree.toXML(response) 
    2065             log.debug(prettyPrint(responseElem)) 
    2066          
    2067         if response.status.statusCode.value != StatusCode.SUCCESS_URI: 
    2068             samlRespError = SamlCredentialWalletAttributeQueryResponseError() 
    2069             samlRespError.status = response.status 
    2070             raise samlRespError 
    2071          
    2072         # Check Query ID matches the query ID the service received 
    2073         if response.inResponseTo != attributeQuery.id: 
    2074             samlRespError = SamlCredentialWalletAttributeQueryResponseError() 
    2075             samlRespError.status = response.status 
    2076             raise samlRespError 
    2077          
    2078         utcNow = datetime.utcnow() + self.clockSkew 
    2079         if response.issueInstant > utcNow: 
    2080             samlRespError = SamlCredentialWalletAttributeQueryResponseError() 
    2081              
    2082             msg = ('SAML Attribute Response issueInstant [%s] is after ' 
    2083                    'the current clock time [%s]' %  
    2084                    (attributeQuery.issueInstant, SAMLDateTime.toString(utcNow))) 
    2085                        
    2086             samlRespError.status.statusCode.value = StatusCode.RESPONDER_URI 
    2087             samlRespError.status.statusMessage.value = msg 
    2088             raise samlRespError 
    2089          
    2090         if utcNow < response.assertions[-1].conditions.notBefore: 
    2091             samlRespError = SamlCredentialWalletAttributeQueryResponseError() 
    2092              
    2093             msg = ('The current clock time [%s] is before the SAML Attribute ' 
    2094                    'Response assertion conditions not before time [%s]' %  
    2095                    (SAMLDateTime.toString(utcNow), 
    2096                     response.assertions[-1].conditions.notBefore)) 
    2097                        
    2098             samlRespError.status.statusCode.value = StatusCode.RESPONDER_URI 
    2099             samlRespError.status.statusMessage.value = msg 
    2100             raise samlRespError 
    2101           
    2102         if utcNow >= response.assertions[-1].conditions.notOnOrAfter: 
    2103             samlRespError = SamlCredentialWalletAttributeQueryResponseError()         
    2104              
    2105             msg = ('The current clock time [%s] is on or after the SAML ' 
    2106                    'Attribute Response assertion conditions not on or after ' 
    2107                    'time [%s]' %  
    2108                    (SAMLDateTime.toString(utcNow), 
    2109                     response.assertions[-1].conditions.notOnOrAfter)) 
    2110                        
    2111             samlRespError.status.statusCode.value = StatusCode.RESPONDER_URI 
    2112             samlRespError.status.statusMessage.value = msg 
    2113             raise samlRespError 
    2114  
    2115         # Add credential into wallet 
    2116         # 
    2117         # Nb. if the certificates signature is invalid, it will be rejected 
    2118         log.debug("Adding credentials into wallet...") 
    2119         for assertion in response.assertions: 
    2120             self.addCredential(assertion,  
    2121                                attributeAuthorityURI=self.attributeAuthorityURI) 
    2122              
    2123         return response 
    2124  
    2125     def addCredential(self,  
    2126                       credential,  
    2127                       attributeAuthorityURI=None, 
    2128                       bUpdateCredentialRepository=False): 
    2129         """Add a new assertion to the list of assertion credentials held. 
    2130  
    2131         @type credential: SAML assertion 
    2132         @param credential: new assertion to be added 
    2133         @type attributeAuthorityURI: basestring 
    2134         @param attributeAuthorityURI: input the Attribute Authority URI from 
    2135         which credential was retrieved.  This is added to a dict to enable  
    2136         access to a given Attribute Certificate keyed by Attribute Authority  
    2137         URI. See the getCredential method. 
    2138         @type bUpdateCredentialRepository: bool 
    2139         @param bUpdateCredentialRepository: if set to True, and a repository  
    2140         exists it will be updated with the new credentials also 
    2141          
    2142         @rtype: bool 
    2143         @return: True if certificate was added otherwise False.  - If an 
    2144         existing certificate from the same issuer has a later expiry it will 
    2145         take precedence and the new input certificate is ignored.""" 
    2146          
    2147         # Check input 
    2148         if not isinstance(credential, Assertion): 
    2149             raise CredentialWalletError("Input credential must be an " 
    2150                                         "%r type object" % Assertion)         
    2151  
    2152         # Check to see if there is an existing Attribute Certificate held 
    2153         # that was issued by the same host.  If so, compare the expiry time. 
    2154         # The one with the latest expiry will be retained and the other 
    2155         # ingored 
    2156         bUpdateCred = True 
    2157         issuerName = credential.issuer.value 
    2158          
    2159         if issuerName in self.credentials: 
    2160             # There is an existing certificate held with the same issuing 
    2161             # host name as the new certificate 
    2162             credentialOld = self.credentials[issuerName]['assertion'] 
    2163  
    2164             # If the new certificate has an earlier expiry time then ignore it 
    2165             bUpdateCred = (credential.conditions.notOnOrAfter >  
    2166                            credentialOld.conditions.notOnOrAfter) 
    2167  
    2168         if bUpdateCred: 
    2169             thisCredential = CredentialContainer(Assertion) 
    2170             thisCredential.credential = credential 
    2171             thisCredential.issuerName = issuerName 
    2172             thisCredential.attributeAuthorityURI = attributeAuthorityURI 
    2173              
    2174             self.credentials[issuerName] = thisCredential 
    2175              
    2176             if attributeAuthorityURI: 
    2177                 self.credentialsKeyedByURI[ 
    2178                     attributeAuthorityURI] = thisCredential  
    2179  
    2180             # Update the Credentials Repository - the permanent store of user 
    2181             # authorisation credentials.  This allows credentials for previous 
    2182             # sessions to be re-instated 
    2183             if bUpdateCredentialRepository: 
    2184                 self.updateCredentialRepository() 
    2185  
    2186         # Flag to caller to indicate whether the input certificate was added 
    2187         # to the credentials or an exsiting certificate from the same issuer 
    2188         # took precedence 
    2189         return bUpdateCred 
    2190                          
    2191     def audit(self): 
    2192         """Check the credentials held in the wallet removing any that have 
    2193         expired or are otherwise invalid.""" 
    2194  
    2195         log.debug("CredentialWallet.audit ...") 
    2196          
    2197         for key, val in self.credentials.items(): 
    2198             if not self.isValidCredential(val['attCert']): 
    2199                 del self.credentials[key] 
    2200  
    2201     def updateCredentialRepository(self, auditCred=True): 
    2202         msg = SamlCredentialWallet.CREDENTIAL_REPOSITORY_NOT_SUPPORTED_MSG 
    2203         log.warning(msg) 
    2204         warnings.warn(msg) 
    2205  
    2206     def isValidCredential(self, assertion): 
    2207         if utcNow < assertion.conditions.notBefore: 
    2208             msg = ('The current clock time [%s] is before the SAML Attribute ' 
    2209                    'Response assertion conditions not before time [%s]' %  
    2210                    (SAMLDateTime.toString(utcNow), 
    2211                     assertion.conditions.notBefore)) 
    2212             log.warning(msg) 
    2213             return False 
    2214              
    2215         if utcNow >= assertion.conditions.notOnOrAfter: 
    2216             msg = ('The current clock time [%s] is on or after the SAML ' 
    2217                    'Attribute Response assertion conditions not on or after ' 
    2218                    'time [%s]' %  
    2219                    (SAMLDateTime.toString(utcNow), 
    2220                     assertion.conditions.notOnOrAfter)) 
    2221             log.warning(msg) 
    2222             return False 
    2223              
    2224         return True 
     1905                                            trustedHostInfo=trustedHostInfo) 
    22251906         
    22261907         
  • TI12-security/trunk/python/ndg_security_common/ndg/security/common/saml_utils/bindings.py

    r6044 r6050  
    1313 
    1414import re 
     15from os import path 
    1516from datetime import datetime, timedelta 
     17from uuid import uuid4 
     18from ConfigParser import ConfigParser 
    1619 
    1720from M2Crypto.m2urllib2 import HTTPSHandler 
     
    1922from saml.common import SAMLObject 
    2023from saml.utils import SAMLDateTime 
    21 from saml.saml2.core import Attribute, AttributeQuery, StatusCode, Response 
     24from saml.saml2.core import (Attribute, AttributeQuery, StatusCode, Response, 
     25                             Issuer, Subject, SAMLVersion, NameID) 
    2226from saml.xml.etree import AttributeQueryElementTree, ResponseElementTree 
    2327 
    24 # Prevent whole module breaking if this is not available - it's only needed for 
    25 # AttributeQuerySslSOAPBinding 
    26 try: 
    27     from ndg.security.common.utils.m2crypto import SSLContextProxy 
    28     _sslContextProxySupport = True 
    29      
    30 except ImportError: 
    31     _sslContextProxySupport = False 
    32  
     28from ndg.security.common.saml_utils.esg import EsgSamlNamespaces 
    3329from ndg.security.common.utils import TypedList 
     30from ndg.security.common.utils.configfileparsers import ( 
     31                                                    CaseSensitiveConfigParser) 
    3432from ndg.security.common.utils.etree import QName    
    3533from ndg.security.common.X509 import X500DN  
     
    3836from ndg.security.common.soap.client import (UrlLib2SOAPClient,  
    3937                                             UrlLib2SOAPRequest) 
     38 
     39# Prevent whole module breaking if this is not available - it's only needed for 
     40# AttributeQuerySslSOAPBinding 
     41try: 
     42    from ndg.security.common.utils.m2crypto import SSLContextProxy 
     43    _sslContextProxySupport = True 
     44     
     45except ImportError: 
     46    _sslContextProxySupport = False 
     47 
    4048 
    4149 
     
    6876                 requestEnvelopeClass=SOAPEnvelope, 
    6977                 responseEnvelopeClass=SOAPEnvelope, 
     78                 serialise=AttributeQueryElementTree.toXML, 
     79                 deserialise=ResponseElementTree.fromXML, 
    7080                 handlers=(HTTPSHandler,)): 
    71         '''Create SAML SOAP Client''' 
     81        '''Create SAML SOAP Client - Nb. serialisation functions assume  
     82        AttributeQuery/Response''' 
    7283        self.__client = None 
    73            
     84        self.serialise = serialise 
     85        self.deserialise = deserialise 
     86         
    7487        self.client = UrlLib2SOAPClient() 
    7588         
     
    219232class AttributeQuerySOAPBinding(SOAPBinding):  
    220233    """SAML Attribute Query SOAP Binding""" 
     234    SUBJECT_ID_OPTNAME = 'subjectID' 
    221235    ISSUER_DN_OPTNAME = 'issuerDN' 
    222236    CLOCK_SKEW_OPTNAME = 'clockSkew' 
    223237     
    224238    CONFIG_FILE_OPTNAMES = ( 
     239        SUBJECT_ID_OPTNAME, 
    225240        ISSUER_DN_OPTNAME,                  
    226241        CLOCK_SKEW_OPTNAME             
     
    246261                 
    247262        super(AttributeQuerySOAPBinding, self).__init__(**kw) 
    248          
    249         self.serialise = AttributeQueryElementTree.toXML 
    250         self.deserialise = ResponseElementTree.fromXML 
    251263 
    252264    @classmethod 
     
    282294        else: 
    283295            raise AttributeError('Expecting basestring or ConfigParser type ' 
    284                                  'for "cfg" attribute; got %r type' % type(cfg))  
     296                                 'for "cfg" attribute; got %r type' % type(cfg)) 
    285297         
    286298        prefixLen = len(prefix) 
    287299        for optName, val in _cfg.items(section): 
    288             if prefix and optName.startswith(prefix): 
    289                 optName = optName[prefixLen:] 
    290                  
    291             setattr(self, optName, val) 
     300            if prefix: 
     301                # Filter attributes based on prefix 
     302                if optName.startswith(prefix): 
     303                    setattr(self, optName[prefixLen:], val) 
     304            else: 
     305                # No prefix set - attempt to set all attributes    
     306                setattr(self, optName, val) 
    292307             
    293308    def __setattr__(self, name, value): 
     
    317332            else: 
    318333                raise 
    319             
     334 
     335    def _getSubjectID(self): 
     336        return self.__subjectID 
     337 
     338    def _setSubjectID(self, value): 
     339        if not isinstance(value, basestring): 
     340            raise TypeError('Expecting string type for "subjectID"; got %r ' 
     341                            'instead' % type(value)) 
     342        self.__subjectID = value 
     343 
     344    subjectID = property(_getSubjectID, _setSubjectID,  
     345                         doc="ID to be sent as query subject")   
     346              
    320347    def _getQueryAttributes(self): 
    321348        """Returns a *COPY* of the attributes to avoid overwriting the  
     
    371398                         doc="Allow a clock skew in seconds for SAML Attribute" 
    372399                             " Query issueInstant parameter check")   
    373                
    374     def send(self, attributeQuery, **kw): 
     400 
     401    def _createQuery(self): 
     402        """ Create a SAML attribute query""" 
     403        attributeQuery = AttributeQuery() 
     404        attributeQuery.version = SAMLVersion(SAMLVersion.VERSION_20) 
     405        attributeQuery.id = str(uuid4()) 
     406        attributeQuery.issueInstant = datetime.utcnow() 
     407         
     408        attributeQuery.issuer = Issuer() 
     409        attributeQuery.issuer.format = Issuer.X509_SUBJECT 
     410        attributeQuery.issuer.value = self.issuerDN 
     411                         
     412        attributeQuery.subject = Subject()   
     413        attributeQuery.subject.nameID = NameID() 
     414        attributeQuery.subject.nameID.format = EsgSamlNamespaces.NAMEID_FORMAT 
     415        attributeQuery.subject.nameID.value = self.subjectID 
     416                   
     417        # Add list of attributes to query                       
     418        for attribute in self.queryAttributes: 
     419            attributeQuery.attributes.append(attribute) 
     420             
     421        return attributeQuery 
     422 
     423    def send(self, **kw): 
    375424        '''Make an attribute query to a remote SAML service 
    376425         
    377         @type attributeQuery: saml.saml2.core.AttributeQuery 
    378         @param attributeQuery: attribute query 
    379426        @type uri: basestring  
    380427        @param uri: uri of service.  May be omitted if set from request.url 
     
    383430        defaults to ndg.security.common.soap.client.UrlLib2SOAPRequest 
    384431        ''' 
    385         if not isinstance(attributeQuery, AttributeQuery): 
    386             raise TypeError('Expecting %r for input attribute query; got %r' 
    387                             % (AttributeQuery, type(attributeQuery))) 
     432        attributeQuery = self._createQuery() 
    388433             
    389434        response = super(AttributeQuerySOAPBinding, self).send(attributeQuery,  
     
    438483                samlRespError = AttributeQueryResponseError(msg)  
    439484                samlRespError.response = response 
    440                 raise samlRespError     
     485                raise samlRespError    
     486             
     487        return response  
    441488 
    442489     
     
    446493    """ 
    447494    SSL_CONTEXT_PROXY_SUPPORT = _sslContextProxySupport 
     495    __slots__ = ('sslCtxProxy', '_AttributeQuerySslSOAPBinding__sslCtxProxy') 
    448496     
    449497    def __init__(self, **kw): 
     
    480528        """ 
    481529        try: 
    482             super(AttributeQuerySOAPBinding, self).__setattr__(name, value) 
     530            super(AttributeQuerySslSOAPBinding, self).__setattr__(name, value) 
    483531             
    484532        except AttributeError: 
     
    487535                setattr(self.sslCtxProxy, name, value) 
    488536            except: 
    489                 raise e 
     537                raise 
  • TI12-security/trunk/python/ndg_security_common/ndg/security/common/saml_utils/esg/__init__.py

    r6034 r6050  
    99__contact__ = "Philip.Kershaw@stfc.ac.uk" 
    1010__revision__ = '$Id$' 
    11 from saml.saml2.core import AttributeValue 
    12 from saml.common.xml import QName 
     11from saml.saml2.core import XSStringAttributeValue, AttributeValue, Attribute 
     12from saml.common.xml import QName, SAMLConstants 
     13 
     14from ndg.security.common.utils import TypedList 
     15 
    1316 
    1417class  _MetaEsgSamlNamespaces(type): 
     
    137140        # no children 
    138141        return None 
     142 
     143 
     144class EsgDefaultQueryAttributes(object):     
     145    XSSTRING_NS = "%s#%s" % ( 
     146        SAMLConstants.XSD_NS, 
     147        XSStringAttributeValue.TYPE_LOCAL_NAME 
     148    ) 
     149     
     150    ATTRIBUTES = TypedList(Attribute) 
     151    N_ATTRIBUTES = 3 
     152    ATTRIBUTES += [Attribute(),]*N_ATTRIBUTES 
     153     
     154    ATTRIBUTES[0].name = EsgSamlNamespaces.FIRSTNAME_ATTRNAME  
     155    ATTRIBUTES[0].friendlyName = EsgSamlNamespaces.FIRSTNAME_FRIENDLYNAME 
     156    ATTRIBUTES[0].format = XSSTRING_NS 
     157 
     158    ATTRIBUTES[1].name = EsgSamlNamespaces.LASTNAME_ATTRNAME  
     159    ATTRIBUTES[1].friendlyName = EsgSamlNamespaces.LASTNAME_FRIENDLYNAME 
     160    ATTRIBUTES[1].format = XSSTRING_NS 
     161     
     162    ATTRIBUTES[2].name = EsgSamlNamespaces.EMAILADDRESS_ATTRNAME 
     163    ATTRIBUTES[2].friendlyName = EsgSamlNamespaces.EMAILADDRESS_FRIENDLYNAME 
     164    ATTRIBUTES[2].format =  XSSTRING_NS 
  • TI12-security/trunk/python/ndg_security_common/ndg/security/common/utils/m2crypto.py

    r6043 r6050  
    214214         
    215215        # load up certificate stuff 
    216         if self._clntCertFilePath is not None and \ 
    217            self._clntPriKeyFilePath is not None: 
     216        if (self._clntCertFilePath is not None and  
     217            self._clntPriKeyFilePath is not None): 
    218218            self.ssl_ctx.load_cert(self._clntCertFilePath,  
    219219                                   self._clntPriKeyFilePath) 
     
    300300         
    301301        for name in SSLContextProxy.OPTNAMES: 
    302             setattr(self, name, getattr(self, sslCtxProxy)) 
     302            setattr(self, name, getattr(sslCtxProxy, name)) 
    303303             
    304304    def createVerifySSLPeerCertCallback(self): 
     
    397397        @param sslCACertFilePath: file path to CA certificate file.  If None 
    398398        then the input is quietly ignored.""" 
    399         if not isinstance(value, (basestring, type(None))): 
     399        if isinstance(value, basestring): 
     400            self.__sslCACertFilePath = os.path.expandvars(value) 
     401             
     402        elif value is None: 
     403            self.__sslCACertFilePath = value 
     404             
     405        else: 
    400406            raise TypeError("Input CA Certificate file path must be " 
    401407                            "a valid string or None type: %r" % type(value))  
    402408         
    403         self.__sslCACertFilePath = os.path.expandvars(value) 
    404409         
    405410    sslCACertFilePath = property(fget=_getSSLCACertFilePath, 
     
    427432        @param sslCACertDir: directory containing CA certificate files. 
    428433        """ 
    429         if not isinstance(value, (basestring, type(None))): 
     434        if isinstance(value, basestring): 
     435            self.__sslCACertDir = os.path.expandvars(value) 
     436        elif value is None: 
     437            self.__sslCACertDir = value 
     438        else: 
    430439            raise TypeError("Input CA Certificate directroy must be " 
    431440                            "a valid string or None type: %r" % type(value))       
    432          
    433         self.__sslCACertDir = os.path.expandvars(value) 
    434441         
    435442    sslCACertDir = property(fget=_getSSLCACertDir, 
  • TI12-security/trunk/python/ndg_security_server/ndg/security/server/sessionmanager.py

    r5441 r6050  
    2323 
    2424# Credential Wallet 
    25 from ndg.security.common.credentialwallet import CredentialWallet, \ 
    26     CredentialRepository, CredentialWalletError, \ 
    27     CredentialWalletAttributeRequestDenied, NullCredentialRepository 
     25from ndg.security.common.credentialwallet import (NdgCredentialWallet,  
     26    CredentialRepository, CredentialWalletError,  
     27    CredentialWalletAttributeRequestDenied, NullCredentialRepository) 
    2828     
    2929from ndg.security.common.wssecurity import WSSecurityConfig 
    30 from ndg.security.common.X509 import X500DN, X509Cert, X509CertParse, \ 
    31     X509CertExpired, X509CertInvalidNotBeforeTime  
     30from ndg.security.common.X509 import (X500DN, X509Cert, X509CertParse,  
     31    X509CertExpired, X509CertInvalidNotBeforeTime)  
    3232 
    3333# generic parser to read INI/XML properties file 
    34 from ndg.security.common.utils.configfileparsers import \ 
    35                                                 INIPropertyFileWithValidation 
     34from ndg.security.common.utils.configfileparsers import ( 
     35                                                INIPropertyFileWithValidation) 
    3636 
    3737# utility to instantiate classes dynamically 
     
    6767     
    6868    def __init__(self, lifetime=28800, **credentialWalletKeys): 
    69         """Initialise UserSession with keywords to CredentialWallet 
     69        """Initialise UserSession with keywords to NdgCredentialWallet 
    7070         
    7171        @type lifetime: int / float 
     
    7878        # Set time stamp to enable auditing to remove stale sessions.  The 
    7979        # user's credential wallet may contain a user certificate which may 
    80         # also be checked for expiry using CredentialWallet.isValid() but there 
     80        # also be checked for expiry using NdgCredentialWallet.isValid() but there 
    8181        # may be no user certificate set.  This code is an extra provision to 
    8282        # to allow for this 
     
    8888        self.__sessIdList = [] 
    8989        self.addNewSessID() 
    90         self.__credentialWallet = CredentialWallet(**credentialWalletKeys) 
     90        self.__credentialWallet = NdgCredentialWallet(**credentialWalletKeys) 
    9191 
    9292        log.info("Created a session with ID = %s" % self.__sessIdList[-1]) 
     
    154154        @raise UserSessionExpired: session has expired 
    155155        @raise X509CertInvalidNotBeforeTime: X.509 certificate held by the 
    156         CredentialWallet is set after the current time 
     156        NdgCredentialWallet is set after the current time 
    157157        @raise X509CertExpired: X.509 certificate held by the 
    158         CredentialWallet has expired 
     158        NdgCredentialWallet has expired 
    159159        """ 
    160160        if not self.isValidTime(raiseExcep=raiseExcep): 
     
    172172     
    173173    credentialWallet = property(fget=__getCredentialWallet, 
    174                           doc="Read-only access to CredentialWallet instance") 
     174                                doc="Read-only access to NdgCredentialWallet " 
     175                                    "instance") 
    175176 
    176177    def __getSessIdList(self): 
     
    537538                                    sections=(section,)) 
    538539         
    539         # Keep a copy of the config file for the CredentialWallet to reference  
     540        # Keep a copy of the config file for the NdgCredentialWallet to reference  
    540541        # so that it can retrieve WS-Security settings 
    541542        self._cfg = readPropertiesFile.cfg 
     
    10641065         
    10651066        @type credentialWalletKw: dict 
    1066         @param **credentialWalletKw: keywords to CredentialWallet.getAttCert 
     1067        @param **credentialWalletKw: keywords to NdgCredentialWallet.getAttCert 
    10671068        """ 
    10681069         
  • TI12-security/trunk/python/ndg_security_server/ndg/security/server/wsgi/authz.py

    r6043 r6050  
    1717from ndg.security.common.utils.classfactory import importClass 
    1818from ndg.security.common.X509 import X500DN 
    19 from ndg.security.common.utils.m2crypto import SSLClientProxy 
     19from ndg.security.common.utils.m2crypto import SSLContextProxy 
    2020 
    2121from ndg.security.common.credentialwallet import (CredentialWallet, 
     
    2727                                      NDGSecurityMiddlewareConfigError) 
    2828from ndg.security.server.wsgi.authn import (SessionMiddlewareBase,  
    29                                             SessionHandlerMiddlewareBase) 
     29                                            SessionHandlerMiddleware) 
    3030 
    3131from ndg.security.common.authz.msi import (Policy, PIP, PIPBase,  
     
    322322 
    323323    
    324 class PIPMiddlewareError(Exception): 
     324class NdgPIPMiddlewareError(Exception): 
    325325    """Base class for Policy Information Point WSGI middleware exception types 
    326326    """ 
    327327     
    328 class PIPMiddlewareConfigError(PIPMiddlewareError): 
     328class NdgPIPMiddlewareConfigError(NdgPIPMiddlewareError): 
    329329    """Configuration related error for Policy Information Point WSGI middleware 
    330330    """ 
     331     
    331332     
    332333class NdgPIPMiddleware(PIP, NDGSecurityMiddlewareBase): 
     
    462463             
    463464            return attrCert 
     465 
     466    
     467class SamlPIPMiddlewareError(Exception): 
     468    """Base class for SAML based Policy Information Point WSGI middleware  
     469    exception types 
     470    """ 
     471 
     472   
     473class SamlPIPMiddlewareConfigError(NdgPIPMiddlewareError): 
     474    """Configuration related error for Policy Information Point WSGI middleware 
     475    """ 
    464476     
    465477 
     
    476488   
    477489    CREDENTIAL_WALLET_SESSION_KEYNAME = \ 
    478         SessionHandlerMiddlewareBase.CREDENTIAL_WALLET_SESSION_KEYNAME 
     490        SessionHandlerMiddleware.CREDENTIAL_WALLET_SESSION_KEYNAME 
    479491    USERNAME_SESSION_KEYNAME = \ 
    480         SessionHandlerMiddlewareBase.USERNAME_SESSION_KEYNAME  
     492        SessionHandlerMiddleware.USERNAME_SESSION_KEYNAME  
    481493           
    482494    def __init__(self, app, global_conf, prefix='', **local_conf): 
     
    492504        dictionary 
    493505        ''' 
     506        self.session = None 
     507         
    494508        # Hold SSL Attribute Authority connection settings in SSL Context Proxy 
    495509        # object 
     
    515529    def __call__(self, environ, start_response): 
    516530        """Take a copy of the session object so that it is in scope for 
    517         _getAttributeCertificate call and add this instance to the environ 
     531        attributeQuery call and add this instance to the environ 
    518532        so that the PEPFilter can retrieve it and pass on to the PDP 
    519533         
     
    555569        # any other security keys when the user logs out 
    556570        credentialWalletKeyName = \ 
    557             SamlPIPMiddleware.CREDENTIAL_WALLET_SESSION_KEYNAME 
     571                            SamlPIPMiddleware.CREDENTIAL_WALLET_SESSION_KEYNAME 
    558572        usernameKeyName = SamlPIPMiddleware.USERNAME_SESSION_KEYNAME 
    559573             
  • TI12-security/trunk/python/ndg_security_test/ndg/security/test/unit/attributeauthorityclient/attAuthorityClientTest.cfg

    r5738 r6050  
    7070siteAttributeName = invalid-attr 
    7171 
     72[test05AttributeQuerySOAPBindingInterface] 
     73uri = http://localhost:5000/AttributeAuthority/saml 
     74subject = https://openid.localhost/philip.kershaw 
     75 
     76[test06AttributeQueryFromConfig] 
     77uri = http://localhost:5000/AttributeAuthority/saml 
     78subject = https://openid.localhost/philip.kershaw 
     79attributeQuery.clockSkew = 0. 
     80attributeQuery.issuerDN = /O=Site A/CN=Authorisation Service 
     81attributeQuery.queryAttributes.0 = urn:esg:first:name, FirstName, http://www.w3.org/2001/XMLSchema#string 
     82attributeQuery.queryAttributes.roles = urn:siteA:security:authz:1.0:attr, , http://www.w3.org/2001/XMLSchema#string 
     83 
     84[test07AttributeQuerySslSOAPBindingInterface] 
     85uri = http://localhost:5000/AttributeAuthority/saml 
     86subject = https://openid.localhost/philip.kershaw 
     87 
     88attributeQuery.clockSkew = 0. 
     89attributeQuery.issuerDN = /O=Site A/CN=Authorisation Service 
     90attributeQuery.queryAttributes.0 = urn:esg:email:address, EmailAddress, http://www.w3.org/2001/XMLSchema#string 
     91attributeQuery.queryAttributes.roles = urn:siteA:security:authz:1.0:attr, , http://www.w3.org/2001/XMLSchema#string 
     92 
     93# SSL Context Proxy settings 
     94attributeQuery.sslCACertDir = $NDGSEC_TEST_CONFIG_DIR/ca 
     95attributeQuery.sslCertFilePath = $NDGSEC_TEST_CONFIG_DIR/pki/test.crt 
     96attributeQuery.sslPriKeyFilePath = $NDGSEC_TEST_CONFIG_DIR/pki/test.key 
     97attributeQuery.sslValidDNs = /C=UK/ST=Oxfordshire/O=BADC/OU=Security/CN=localhost, /O=Site A/CN=Attribute Authority 
     98 
    7299[wsse] 
    73100# WS-Security settings for unit test AA clients 
  • TI12-security/trunk/python/ndg_security_test/ndg/security/test/unit/attributeauthorityclient/test_attributeauthorityclient.py

    r6034 r6050  
    2020mkPath = lambda file: jnPath(os.environ['NDGSEC_AACLNT_UNITTEST_DIR'], file) 
    2121 
    22 from datetime import datetime 
     22from datetime import datetime, timedelta 
    2323from uuid import uuid4 
    2424from xml.etree import ElementTree 
     
    3535    CaseSensitiveConfigParser) 
    3636 
     37from saml.utils import SAMLDateTime 
    3738from saml.common.xml import SAMLConstants 
    3839from saml.saml2.core import (Attribute, SAMLVersion, Subject, NameID, Issuer,  
     
    4041from saml.xml.etree import ResponseElementTree 
    4142 
    42 from ndg.security.common.saml_utils.bindings import SOAPBinding as SamlSoapBinding 
     43from ndg.security.common.saml_utils.bindings import SOAPBinding as \ 
     44                                                            SamlSoapBinding 
     45from ndg.security.common.saml_utils.bindings import AttributeQuerySOAPBinding 
     46from ndg.security.common.saml_utils.bindings import AttributeQuerySslSOAPBinding 
    4347from ndg.security.common.saml_utils.esg import (EsgSamlNamespaces,  
    44                                           XSGroupRoleAttributeValue) 
     48                                                XSGroupRoleAttributeValue, 
     49                                                EsgDefaultQueryAttributes) 
    4550 
    4651 
     
    5459 
    5560        self.cfgParser = CaseSensitiveConfigParser() 
    56         cfgFilePath = jnPath(os.environ['NDGSEC_AACLNT_UNITTEST_DIR'], 
    57                              'attAuthorityClientTest.cfg') 
    58         self.cfgParser.read(cfgFilePath) 
     61        self.cfgFilePath = jnPath(os.environ['NDGSEC_AACLNT_UNITTEST_DIR'], 
     62                                  'attAuthorityClientTest.cfg') 
     63        self.cfgParser.read(self.cfgFilePath) 
    5964         
    6065        self.cfg = {} 
     
    308313                                        AttributeAuthorityClientBaseTestCase): 
    309314    """Separate class for Attribute Authority SAML Attribute Query interface""" 
    310             
     315     
     316    def __init__(self, *arg, **kw): 
     317        super(AttributeAuthoritySAMLInterfaceTestCase, self).__init__(*arg,  
     318                                                                      **kw) 
     319        self.startSiteAAttributeAuthority(withSSL=True, port=5443) 
     320        
    311321    def test01SAMLAttributeQuery(self): 
    312322        _cfg = self.cfg['test01SAMLAttributeQuery'] 
     
    318328         
    319329        attributeQuery.issuer = Issuer() 
    320         attributeQuery.issuer.format = "urn:esg:issuer" 
     330        attributeQuery.issuer.format = Issuer.X509_SUBJECT 
    321331        attributeQuery.issuer.value = "/O=Site A/CN=Authorisation Service"     
    322332                         
     
    355365 
    356366        binding = SamlSoapBinding() 
    357         response = binding.attributeQuery(attributeQuery, _cfg['uri']) 
     367        response = binding.send(attributeQuery, _cfg['uri']) 
    358368         
    359369        self.assert_(response.status.statusCode.value==StatusCode.SUCCESS_URI) 
     
    384394         
    385395        attributeQuery.issuer = Issuer() 
    386         attributeQuery.issuer.format = "urn:esg:issuer" 
     396        attributeQuery.issuer.format = Issuer.X509_SUBJECT 
    387397        attributeQuery.issuer.value = "Invalid Site"     
    388398                         
     
    401411 
    402412        binding = SamlSoapBinding() 
    403         response = binding.attributeQuery(attributeQuery, _cfg['uri']) 
     413        response = binding.send(attributeQuery, _cfg['uri']) 
    404414 
    405415        samlResponseElem = ResponseElementTree.toXML(response) 
     
    422432         
    423433        attributeQuery.issuer = Issuer() 
    424         attributeQuery.issuer.format = "urn:esg:issuer" 
     434        attributeQuery.issuer.format = Issuer.X509_SUBJECT 
    425435        attributeQuery.issuer.value = "/O=Site A/CN=Authorisation Service"     
    426436                         
     
    439449 
    440450        binding = SamlSoapBinding() 
    441         response = binding.attributeQuery(attributeQuery, _cfg['uri']) 
     451        response = binding.send(attributeQuery, _cfg['uri']) 
    442452         
    443453        samlResponseElem = ResponseElementTree.toXML(response) 
     
    459469         
    460470        attributeQuery.issuer = Issuer() 
    461         attributeQuery.issuer.format = "urn:esg:issuer" 
     471        attributeQuery.issuer.format = Issuer.X509_SUBJECT 
    462472        attributeQuery.issuer.value = "/O=Site A/CN=Authorisation Service"     
    463473                         
     
    476486 
    477487        binding = SamlSoapBinding() 
    478         response = binding.attributeQuery(attributeQuery, _cfg['uri']) 
     488        response = binding.send(attributeQuery, _cfg['uri']) 
    479489         
    480490        samlResponseElem = ResponseElementTree.toXML(response) 
     
    488498                     StatusCode.INVALID_ATTR_NAME_VALUE_URI) 
    489499         
    490                                                          
     500    def test05AttributeQuerySOAPBindingInterface(self): 
     501        _cfg = self.cfg['test05AttributeQuerySOAPBindingInterface'] 
     502         
     503        binding = AttributeQuerySOAPBinding() 
     504         
     505        binding.subjectID = AttributeAuthoritySAMLInterfaceTestCase.OPENID_URI 
     506        binding.issuerDN = \ 
     507            AttributeAuthoritySAMLInterfaceTestCase.VALID_REQUESTOR_IDS[0]         
     508         
     509        binding.queryAttributes = EsgDefaultQueryAttributes.ATTRIBUTES 
     510         
     511        response = binding.send(uri=_cfg['uri']) 
     512        samlResponseElem = ResponseElementTree.toXML(response) 
     513         
     514        print("SAML Response ...") 
     515        print(ElementTree.tostring(samlResponseElem)) 
     516        print("Pretty print SAML Response ...") 
     517        print(prettyPrint(samlResponseElem)) 
     518         
     519        self.assert_(response.status.statusCode.value==StatusCode.SUCCESS_URI) 
     520 
     521    def test06AttributeQueryFromConfig(self): 
     522        thisSection = 'test06AttributeQueryFromConfig' 
     523        _cfg = self.cfg[thisSection] 
     524         
     525        binding = AttributeQuerySOAPBinding.fromConfig(self.cfgFilePath,  
     526                                                       section=thisSection, 
     527                                                       prefix='attributeQuery.') 
     528        binding.subjectID = _cfg['subject'] 
     529        response = binding.send(uri=_cfg['uri']) 
     530        samlResponseElem = ResponseElementTree.toXML(response) 
     531         
     532        print("SAML Response ...") 
     533        print(ElementTree.tostring(samlResponseElem)) 
     534        print("Pretty print SAML Response ...") 
     535        print(prettyPrint(samlResponseElem)) 
     536         
     537        self.assert_(response.status.statusCode.value==StatusCode.SUCCESS_URI) 
     538         
     539    def test07AttributeQuerySslSOAPBindingInterface(self): 
     540        thisSection = 'test07AttributeQuerySslSOAPBindingInterface' 
     541        _cfg = self.cfg[thisSection] 
     542         
     543        binding = AttributeQuerySslSOAPBinding.fromConfig(self.cfgFilePath,  
     544                                                       section=thisSection, 
     545                                                       prefix='attributeQuery.') 
     546         
     547        binding.subjectID = _cfg['subject'] 
     548        response = binding.send(uri=_cfg['uri']) 
     549        samlResponseElem = ResponseElementTree.toXML(response) 
     550         
     551        print("SAML Response ...") 
     552        print(ElementTree.tostring(samlResponseElem)) 
     553        print("Pretty print SAML Response ...") 
     554        print(prettyPrint(samlResponseElem)) 
     555         
     556        self.assert_(response.status.statusCode.value==StatusCode.SUCCESS_URI) 
     557         
    491558if __name__ == "__main__": 
    492559    unittest.main() 
  • TI12-security/trunk/python/ndg_security_test/ndg/security/test/unit/credentialwallet/test_credentialwallet.py

    r6040 r6050  
    11#!/usr/bin/env python 
    2 """Unit tests for Credential Wallet class 
    3  
    4 NERC Data Grid Project 
     2"""Unit tests for Credential Wallet classes 
     3 
     4NERC DataGrid Project 
    55""" 
    66__author__ = "P J Kershaw" 
     
    1515import traceback 
    1616 
     17from string import Template 
     18from cStringIO import StringIO 
     19from elementtree import ElementTree 
     20 
     21from time import sleep 
     22from datetime import datetime, timedelta 
     23from saml.utils import SAMLDateTime 
    1724from saml.xml.etree import AssertionElementTree 
    1825 
     
    2330from ndg.security.common.utils.etree import prettyPrint 
    2431from ndg.security.common.X509 import X509CertParse 
    25 from ndg.security.common.credentialwallet import (CredentialWallet,  
    26                                         CredentialWalletAttributeRequestDenied) 
     32from ndg.security.common.credentialwallet import (NdgCredentialWallet,  
     33    CredentialWalletAttributeRequestDenied, SamlCredentialWallet) 
    2734from ndg.security.server.attributeauthority import AttributeAuthority 
    2835 
    2936from os.path import expandvars as xpdVars 
    3037from os.path import join as jnPath 
    31 mkPath = lambda file: jnPath(os.environ['NDGSEC_CREDWALLET_UNITTEST_DIR'],file) 
     38mkPath = lambda file: jnPath(os.environ['NDGSEC_CREDWALLET_UNITTEST_DIR'], file) 
    3239 
    3340import logging 
     
    3542 
    3643 
    37 class CredentialWalletTestCase(BaseTestCase): 
    38     """Unit test case for ndg.security.common.credentialwallet.CredentialWallet 
    39     class. 
     44class NdgCredentialWalletTestCase(BaseTestCase): 
     45    """Unit test case for  
     46    ndg.security.common.credentialwallet.NdgCredentialWallet class. 
    4047    """ 
    4148    def __init__(self, *arg, **kw): 
    42         super(CredentialWalletTestCase, self).__init__(*arg, **kw) 
     49        super(NdgCredentialWalletTestCase, self).__init__(*arg, **kw) 
    4350        self.startAttributeAuthorities() 
    4451     
    4552    def setUp(self): 
    46         super(CredentialWalletTestCase, self).setUp() 
     53        super(NdgCredentialWalletTestCase, self).setUp() 
    4754         
    4855        if 'NDGSEC_INT_DEBUG' in os.environ: 
     
    6673         
    6774        try: 
    68             CredentialWallet.accessDenied = 'yes' 
     75            NdgCredentialWallet.accessDenied = 'yes' 
    6976            self.fail("accessDenied class variable should be read-only") 
    7077        except Exception, e: 
     
    7279 
    7380        try: 
    74             CredentialWallet.accessGranted = False 
     81            NdgCredentialWallet.accessGranted = False 
    7582            self.fail("accessGranted class variable should be read-only") 
    7683        except Exception, e: 
    7784            print("PASS - accessGranted class variable is read-only") 
    7885             
    79         assert(not CredentialWallet.accessDenied) 
    80         assert(CredentialWallet.accessGranted) 
     86        assert(not NdgCredentialWallet.accessDenied) 
     87        assert(NdgCredentialWallet.accessGranted) 
    8188         
    8289         
    8390    def test02SetAttributes(self): 
    8491         
    85         credWallet = CredentialWallet() 
     92        credWallet = NdgCredentialWallet() 
    8693        credWallet.userX509Cert=open(xpdVars(self.userX509CertFilePath)).read() 
    8794        print("userX509Cert=%s" % credWallet.userX509Cert) 
     
    109116    def test03GetAttCertWithUserId(self): 
    110117                     
    111         credWallet = CredentialWallet(cfg=self.cfg.get('setUp', 'cfgFilePath')) 
     118        credWallet = NdgCredentialWallet(cfg=self.cfg.get('setUp',  
     119                                                          'cfgFilePath')) 
    112120        attCert = credWallet.getAttCert() 
    113121         
     
    119127    def test04GetAttCertWithUserX509Cert(self): 
    120128                     
    121         credWallet = CredentialWallet(cfg=self.cfg.get('setUp', 'cfgFilePath')) 
     129        credWallet = NdgCredentialWallet(cfg=self.cfg.get('setUp',  
     130                                                          'cfgFilePath')) 
    122131         
    123132        # Set a test individual user certificate to override the client  
     
    137146        # This flag prevents role mapping from a trusted AA and so in this case 
    138147        # forces refusal of the request 
    139         credWallet = CredentialWallet(cfg=self.cfg.get('setUp', 'cfgFilePath'), 
    140                                       mapFromTrustedHosts=False)     
     148        credWallet = NdgCredentialWallet(cfg=self.cfg.get('setUp',  
     149                                                          'cfgFilePath'), 
     150                                         mapFromTrustedHosts=False)     
    141151        credWallet.userX509CertFilePath = self.userX509CertFilePath 
    142152        credWallet.userPriKeyFilePath = self.userPriKeyFilePath 
     
    145155        # implicit call to create a new AA Client when the URI is set 
    146156        credWallet.attributeAuthorityURI = self.cfg.get('setUp',  
    147                                                     'attributeAuthorityURI') 
     157                                                        'attributeAuthorityURI') 
    148158        try: 
    149159            attCert = credWallet.getAttCert() 
     
    158168         
    159169        # Call Site A Attribute Authority where user is registered 
    160         credWallet = CredentialWallet(cfg=self.cfg.get('setUp', 'cfgFilePath')) 
     170        credWallet = NdgCredentialWallet(cfg=self.cfg.get('setUp',  
     171                                                          'cfgFilePath')) 
    161172        attCert = credWallet.getAttCert() 
    162173 
     
    174185                                      'attributeAuthorityPropFilePath')  
    175186                   
    176         credWallet = CredentialWallet(cfg=self.cfg.get('setUp', 'cfgFilePath')) 
     187        credWallet = NdgCredentialWallet(cfg=self.cfg.get('setUp',  
     188                                                          'cfgFilePath')) 
    177189        credWallet.attributeAuthority = AttributeAuthority.fromPropertyFile( 
    178190                                            propFilePath=aaPropFilePath) 
     
    183195        assert(attCert.userId == credWallet.userId) 
    184196        print("Attribute Certificate:\n%s" % attCert)   
    185   
    186   
    187 from ndg.security.common.credentialwallet import SamlCredentialWallet 
    188197 
    189198 
     
    193202    CONFIG_FILEPATH = os.path.join(THIS_DIR, CONFIG_FILENAME) 
    194203     
     204    ASSERTION_STR = ( 
     205"""<saml:Assertion ID="192c67d9-f9cd-457a-9242-999e7b943166" IssueInstant="$timeNow" Version="2.0" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"> 
     206   <saml:Issuer Format="urn:esg:issuer">$issuerName</saml:Issuer> 
     207   <saml:Subject> 
     208      <saml:NameID Format="urn:esg:openid">https://esg.prototype.ucar.edu/myopenid/testUser</saml:NameID> 
     209   </saml:Subject> 
     210   <saml:Conditions NotBefore="$timeNow" NotOnOrAfter="$timeExpires" /> 
     211   <saml:AttributeStatement> 
     212      <saml:Attribute FriendlyName="FirstName" Name="urn:esg:first:name" NameFormat="http://www.w3.org/2001/XMLSchema#string"> 
     213         <saml:AttributeValue xsi:type="xs:string" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Test</saml:AttributeValue> 
     214      </saml:Attribute> 
     215      <saml:Attribute FriendlyName="LastName" Name="urn:esg:last:name" NameFormat="http://www.w3.org/2001/XMLSchema#string"> 
     216         <saml:AttributeValue xsi:type="xs:string" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">User</saml:AttributeValue> 
     217      </saml:Attribute> 
     218      <saml:Attribute FriendlyName="EmailAddress" Name="urn:esg:first:email:address" NameFormat="http://www.w3.org/2001/XMLSchema#string"> 
     219         <saml:AttributeValue xsi:type="xs:string" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">test@sitea.ac.uk</saml:AttributeValue> 
     220      </saml:Attribute> 
     221   </saml:AttributeStatement> 
     222</saml:Assertion> 
     223""" 
     224    ) 
     225     
    195226    def __init__(self, *arg, **kw): 
    196227        super(SamlCredentialWalletTestCase, self).__init__(*arg, **kw) 
    197         self.startSiteAAttributeAuthority(withSSL=True, port=5443) 
    198          
    199     def test01Query(self): 
    200         wallet = SamlCredentialWallet() 
    201          
    202         wallet.userId = SamlCredentialWalletTestCase.OPENID_URI 
    203         wallet.issuerDN = SamlCredentialWalletTestCase.VALID_REQUESTOR_IDS[0]         
    204         wallet.attributeAuthorityURI = \ 
    205             SamlCredentialWalletTestCase.SITEA_ATTRIBUTEAUTHORITY_SAML_URI 
    206          
    207         wallet.queryAttributes = SamlCredentialWallet.DEFAULT_QUERY_ATTRIBUTES 
    208          
    209         wallet.attributeQuery() 
     228         
     229    def setUp(self): 
     230        self.assertion =self._createAssertion() 
     231         
     232    def _createAssertion(self, timeNow=None, validityDuration=60*60*8, 
     233                         issuerName=BaseTestCase.SITEA_SAML_ISSUER_NAME): 
     234        if timeNow is None: 
     235            timeNow = datetime.utcnow() 
     236             
     237        timeExpires = timeNow + timedelta(seconds=validityDuration) 
     238        assertionStr = Template( 
     239            SamlCredentialWalletTestCase.ASSERTION_STR).substitute( 
     240                dict( 
     241                 issuerName=issuerName, 
     242                 timeNow=SAMLDateTime.toString(timeNow),  
     243                 timeExpires=SAMLDateTime.toString(timeExpires) 
     244                ) 
     245            ) 
     246 
     247        assertionStream = StringIO() 
     248        assertionStream.write(assertionStr) 
     249        assertionStream.seek(0) 
     250        assertionElem = ElementTree.parse(assertionStream).getroot() 
     251        return AssertionElementTree.fromXML(assertionElem) 
     252 
     253    def _addCredential(self): 
     254        wallet = SamlCredentialWallet()    
     255        wallet.addCredential( 
     256            self.assertion,  
     257            attributeAuthorityURI=\ 
     258                SamlCredentialWalletTestCase.SITEA_ATTRIBUTEAUTHORITY_SAML_URI) 
     259        return wallet 
     260     
     261    def test01AddCredential(self): 
     262        wallet = self._addCredential() 
     263         
    210264        self.assert_(len(wallet.credentials) == 1) 
    211265        self.assert_( 
     
    214268        self.assert_(SamlCredentialWalletTestCase.SITEA_SAML_ISSUER_NAME in \ 
    215269                     wallet.credentials) 
    216         self.assert_( 
    217             wallet.credentials[ 
    218                 SamlCredentialWalletTestCase.SITEA_SAML_ISSUER_NAME 
    219             ].credential.subject.nameID.value == \ 
    220             wallet.userId) 
    221270         
    222271        assertion = wallet.credentials[ 
     
    226275        print("SAML Assertion:\n%s" %  
    227276              prettyPrint(AssertionElementTree.toXML(assertion))) 
    228  
    229     def test02ReadCustomQueryAttributes(self): 
    230         wallet = SamlCredentialWallet.fromConfig( 
    231                                 SamlCredentialWalletTestCase.CONFIG_FILEPATH) 
    232         wallet.attributeQuery() 
    233          
    234         self.assert_(SamlCredentialWalletTestCase.SITEA_SAML_ISSUER_NAME in \ 
    235                      wallet.credentials) 
    236          
    237         assertion = wallet.credentials[ 
    238             SamlCredentialWalletTestCase.SITEA_SAML_ISSUER_NAME 
    239         ].credential 
    240          
    241         print("SAML Assertion:\n%s" %  
    242               prettyPrint(AssertionElementTree.toXML(assertion))) 
     277     
     278    def test02VerifyCredential(self): 
     279        wallet = SamlCredentialWallet() 
     280        self.assert_(wallet.isValidCredential(self.assertion)) 
     281         
     282        expiredAssertion = self._createAssertion( 
     283                                timeNow=datetime.utcnow() - timedelta(hours=24)) 
     284                                 
     285        self.assert_(not wallet.isValidCredential(expiredAssertion)) 
     286         
     287        futureAssertion = self._createAssertion( 
     288                                timeNow=datetime.utcnow() + timedelta(hours=24)) 
     289 
     290        self.assert_(not wallet.isValidCredential(futureAssertion)) 
     291         
     292    def test03AuditCredential(self): 
     293        # Add a short lived credential and ensure it's removed when an audit 
     294        # is carried to prune expired credentials 
     295        shortExpiryAssertion = self._createAssertion(validityDuration=1) 
     296        wallet = SamlCredentialWallet() 
     297        wallet.addCredential(shortExpiryAssertion) 
     298         
     299        self.assert_(len(wallet.credentials) == 1) 
     300        sleep(1) 
     301        wallet.audit() 
     302        self.assert_(len(wallet.credentials) == 0) 
     303 
     304    def test04ReplaceCredential(self): 
     305        # Replace an existing credential from a given institution with a more 
     306        # up to date one 
     307        wallet = self._addCredential() 
     308        self.assert_(len(wallet.credentials) == 1) 
     309         
     310        newAssertion = self._createAssertion()   
     311 
     312        wallet.addCredential(newAssertion) 
     313        self.assert_(len(wallet.credentials) == 1) 
     314        self.assert_(newAssertion.conditions.notOnOrAfter==\ 
     315                     wallet.credentials[ 
     316                        SamlCredentialWalletTestCase.SITEA_SAML_ISSUER_NAME 
     317                    ].credential.conditions.notOnOrAfter) 
     318         
     319    def test05CredentialsFromSeparateSites(self): 
     320        wallet = self._addCredential() 
     321        wallet.addCredential(self._createAssertion(issuerName="MySite")) 
     322        self.assert_(len(wallet.credentials) == 2) 
    243323         
    244324         
  • TI12-security/trunk/python/ndg_security_test/ndg/security/test/unit/wsgi/authz/test.ini

    r5330 r6050  
    2828# retrieve subject attributes from the Attribute Authority associated with the 
    2929# resource to be accessed 
    30 pip.sslCACertFilePathList= 
    31  
    32 # List of CA certificates used to verify the signatures of  
    33 # Attribute Certificates retrieved 
    34 pip.caCertFilePathList=%(testConfigDir)s/ca/ndg-test-ca.crt 
    35  
    36 # 
    37 # WS-Security Settings for call to Session Manager 
    38  
    39 # Signature of an outbound message 
    40  
    41 # Certificate associated with private key used to sign a message.  The sign  
    42 # method will add this to the BinarySecurityToken element of the WSSE header.   
    43 # binSecTokValType attribute must be set to 'X509' or 'X509v3' ValueType.   
    44 # As an alternative, use signingCertChain - see below... 
    45  
    46 # PEM encode cert 
    47 pip.wssecurity.signingCertFilePath=%(testConfigDir)s/pki/wsse-server.crt 
    48  
    49 # PEM encoded private key file 
    50 pip.wssecurity.signingPriKeyFilePath=%(testConfigDir)s/pki/wsse-server.key 
    51  
    52 # Password protecting private key.  Leave blank if there is no password. 
    53 pip.wssecurity.signingPriKeyPwd= 
    54  
    55 # For signature verification.  Provide a space separated list of file paths 
    56 pip.wssecurity.caCertFilePathList=%(testConfigDir)s/ca/ndg-test-ca.crt 
    57  
    58 # ValueType for the BinarySecurityToken added to the WSSE header 
    59 pip.wssecurity.reqBinSecTokValType=X509v3 
    60  
    61 # Add a timestamp element to an outbound message 
    62 pip.wssecurity.addTimestamp=True 
     30pip.sslCACertDir=%(testConfigDir)s/ca 
     31pip.sslCertFilePath=%(testConfigDir)s/pki/test.crt 
     32pip.sslPriKeyFilePath=%(testConfigDir)s/pki/test.key 
  • TI12-security/trunk/python/ndg_security_test/ndg/security/test/unit/wsgi/authz/test_authz.py

    r5560 r6050  
    2525from paste.deploy import loadapp 
    2626from ndg.security.server.wsgi import NDGSecurityMiddlewareBase 
    27 from ndg.security.server.wsgi.authz import PIPMiddlewareConfigError, \ 
    28     PEPResultHandlerMiddleware 
     27from ndg.security.server.wsgi.authz import (SamlPIPMiddlewareConfigError,  
     28                                            PEPResultHandlerMiddleware) 
    2929from ndg.security.common.authz.msi import Response 
    3030 
     
    105105        try: 
    106106            response = self.app.get('/test_200') 
    107         except PIPMiddlewareConfigError, e: 
     107        except SamlPIPMiddlewareConfigError, e: 
    108108            print("PASS - expected: %s exception: %s" % (e.__class__, e)) 
    109109        
Note: See TracChangeset for help on using the changeset viewer.