Changeset 7327


Ignore:
Timestamp:
16/08/10 13:54:09 (9 years ago)
Author:
pjkersha
Message:

Incomplete - task 2: XACML-Security Integration

  • added unit tests for XACML Context handler
Location:
TI12-security/trunk/NDGSecurity/python
Files:
2 added
1 deleted
8 edited

Legend:

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

    r7155 r7327  
    698698        'userid':                       'UID' 
    699699    } 
    700     PARSER_RE_STR = '/(%s)=' % '|'.join(__shortNameLUT.keys() +  
    701                                         __shortNameLUT.values()) 
    702      
    703     PARSER_RE = re.compile(PARSER_RE_STR) 
     700    SLASH_PARSER_RE_STR = '/(%s)=' % '|'.join(__shortNameLUT.keys() +  
     701                                              __shortNameLUT.values())     
     702    SLASH_PARSER_RE = re.compile(SLASH_PARSER_RE_STR) 
     703 
     704    COMMA_PARSER_RE_STR = '[,]?\s*(%s)=' % '|'.join(__shortNameLUT.keys() +  
     705                                                    __shortNameLUT.values())     
     706    COMMA_PARSER_RE = re.compile(COMMA_PARSER_RE_STR) 
    704707     
    705708    def __init__(self, dn=None, m2CryptoX509Name=None, separator=None): 
     
    894897            self.__separator = separator 
    895898 
    896  
    897899        # If no separator has been set, parse if from the DN string             
    898900        if self.__separator is None: 
    899901            self.__separator = self.parseSeparator(dn) 
    900902 
     903        if self.__separator == '/': 
     904            parserRe = self.__class__.SLASH_PARSER_RE 
     905             
     906        elif self.__separator == ',': 
     907            parserRe = self.__class__.COMMA_PARSER_RE 
     908        else: 
     909            raise X500DNError("DN field separator %r not recognised" %  
     910                              self.__separator) 
     911         
    901912        try: 
    902             dnFields = X500DN.PARSER_RE.split(dn) 
     913            dnFields = parserRe.split(dn) 
    903914            if len(dnFields) < 2: 
    904915                raise X500DNError("Error parsing DN string: \"%s\"" % dn) 
  • TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/wsgi/authz/service.py

    r7257 r7327  
    4949     
    5050    POLICY_FILEPATH_OPTNAME = 'policyFilePath' 
    51     PIP_CFG_PREFIX = 'pip.' 
    5251     
    5352    __slots__ = ( 
     
    9897             
    9998        # Initialise the XACML Context handler 
    100         ctxHandlerPrefix = prefix + \ 
    101                                 self.__class__.XACML_CTX_HANDLER_PARAM_PREFIX 
    102         lenCtxHandlerPrefix = len(ctxHandlerPrefix) 
    103                                  
    104         for optName, value in app_conf.items(): 
    105             if optName.startswith(ctxHandlerPrefix): 
    106                 attrName = optName[lenCtxHandlerPrefix:] 
    107                 setattr(self.__xacmlCtxHandler, attrName, value) 
    108                  
    109         # Read XACML policy into PDP 
    110         self.__xacmlCtxHandler.pdp = PDP.fromPolicySource(policyFilePath, 
    111                                                 XacmlEtreePolicyReaderFactory) 
    11299         
    113100    @classmethod 
  • TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/xacml/ctx_handler/saml_ctx_handler.py

    r7298 r7327  
    1212log = logging.getLogger(__name__) 
    1313 
     14from os import path 
     15from ConfigParser import SafeConfigParser, ConfigParser 
    1416from datetime import datetime, timedelta 
    1517from uuid import uuid4 
     
    1820from ndg.saml.common import SAMLVersion 
    1921 
     22from ndg.xacml.core.context.pdp import PDP 
    2023from ndg.xacml.core import context as _xacmlContext 
    2124from ndg.xacml.core.attribute import Attribute as XacmlAttribute 
    2225from ndg.xacml.core.attributevalue import AttributeValueClassFactory as \ 
    2326    XacmlAttributeValueClassFactory 
    24 from ndg.xacml.parsers.etree.factory import ReaderFactory 
     27from ndg.xacml.parsers.etree.factory import ReaderFactory as \ 
     28    XacmlPolicyReaderFactory 
    2529 
    2630from ndg.security.server.xacml.pip.saml_pip import PIP 
     
    6569    Interface 
    6670    """ 
     71    DEFAULT_OPT_PREFIX = 'saml_ctx_handler.' 
     72    PIP_OPT_PREFIX = 'pip.' 
     73     
    6774    __slots__ = ( 
    6875        '__policyFilePath', 
     
    7481        super(SamlCtxHandler, self).__init__() 
    7582         
    76         # Proxy object for SAML Response Issuer attributes.  By generating a  
    77         # proxy the Response objects inherent attribute validation can be  
    78         # applied to Issuer related config parameters before they're assigned to 
    79         # the response issuer object generated in the authorisation decision  
    80         # query response 
     83        # Proxy object for SAML AuthzDecisionQueryResponse Issuer attributes.  
     84        # By generating a proxy the Response objects inherent attribute  
     85        # validation can be applied to Issuer related config parameters before  
     86        # they're assigned to the response issuer object generated in the  
     87        # authorisation decision query response 
    8188        self.__issuerProxy = _saml.Issuer() 
    8289        self.__assertionLifetime = 0. 
     90        self.__policyFilePath = None 
    8391         
    8492        # Policy Information Point 
    8593        self.pip = PIP() 
    86  
     94     
     95    @classmethod 
     96    def fromConfig(cls, cfg, **kw): 
     97        '''Alternative constructor makes object from config file settings 
     98        @type cfg: basestring /ConfigParser derived type 
     99        @param cfg: configuration file path or ConfigParser type object 
     100        @rtype: ndg.security.server.xacml.ctx_handler.saml_ctx_handler 
     101        @return: new instance of this class 
     102        ''' 
     103        obj = cls() 
     104        obj.parseConfig(cfg, **kw) 
     105         
     106        if obj.policyFilePath: 
     107            obj.pdp = PDP.fromPolicySource(obj.policyFilePath,  
     108                                           XacmlPolicyReaderFactory) 
     109             
     110        return obj 
     111 
     112    def parseConfig(self, cfg, prefix=DEFAULT_OPT_PREFIX, section='DEFAULT'): 
     113        '''Read config settings from a file, config parser object or dict 
     114         
     115        @type cfg: basestring / ConfigParser derived type / dict 
     116        @param cfg: configuration file path or ConfigParser type object 
     117        @type prefix: basestring 
     118        @param prefix: prefix for option names e.g. "attributeQuery." 
     119        @type section: basetring 
     120        @param section: configuration file section from which to extract 
     121        parameters. 
     122        '''   
     123        if isinstance(cfg, basestring): 
     124            cfgFilePath = path.expandvars(cfg) 
     125             
     126            # Add a 'here' helper option for setting dir paths in the config 
     127            # file 
     128            hereDir = path.abspath(path.dirname(cfgFilePath)) 
     129            _cfg = SafeConfigParser(defaults={'here': hereDir}) 
     130             
     131            # Make option name reading case sensitive 
     132            _cfg.optionxform = str 
     133            _cfg.read(cfgFilePath) 
     134            items = _cfg.items(section) 
     135             
     136        elif isinstance(cfg, ConfigParser): 
     137            items = cfg.items(section) 
     138          
     139        elif isinstance(cfg, dict): 
     140            items = cfg.items()      
     141        else: 
     142            raise AttributeError('Expecting basestring, ConfigParser or dict ' 
     143                                 'type for "cfg" attribute; got %r type' %  
     144                                 type(cfg)) 
     145         
     146        self.__parseFromItems(items, prefix=prefix) 
     147         
     148    def __parseFromItems(self, items, prefix=DEFAULT_OPT_PREFIX):  
     149        """Update from list of tuple name, value pairs - for internal use 
     150        by parseKeywords and parseConfig 
     151        """ 
     152        prefixLen = len(prefix)  
     153        pipPrefix = self.__class__.PIP_OPT_PREFIX 
     154        pipPrefixLen = len(pipPrefix) 
     155         
     156        def _setAttr(__optName): 
     157            # Check for PIP attribute related items 
     158            if __optName.startswith(pipPrefix): 
     159                setattr(self.pip, __optName[pipPrefixLen:], val) 
     160            else: 
     161                setattr(self, __optName, val) 
     162                 
     163        for optName, val in items: 
     164            if prefix: 
     165                # Filter attributes based on prefix 
     166                if optName.startswith(prefix): 
     167                    _optName = optName[prefixLen:] 
     168                    _setAttr(_optName) 
     169            else: 
     170                # No prefix set - attempt to set all attributes    
     171                _setAttr(optName) 
     172         
     173    def parseKeywords(self, prefix=DEFAULT_OPT_PREFIX, **kw): 
     174        """Update object from input keywords 
     175         
     176        @type prefix: basestring 
     177        @param prefix: if a prefix is given, only update self from kw items  
     178        where keyword starts with this prefix 
     179        @type kw: dict 
     180        @param kw: items corresponding to class instance variables to  
     181        update.  Keyword names must match their equivalent class instance  
     182        variable names.  However, they may prefixed with <prefix> 
     183        """ 
     184        self.__parseFromItems(kw.items(), prefix=prefix) 
     185                 
     186    @classmethod 
     187    def fromKeywords(cls, prefix=DEFAULT_OPT_PREFIX, **kw): 
     188        """Create a new instance initialising instance variables from the  
     189        keyword inputs 
     190        @type prefix: basestring 
     191        @param prefix: if a prefix is given, only update self from kw items  
     192        where keyword starts with this prefix 
     193        @type kw: dict 
     194        @param kw: items corresponding to class instance variables to  
     195        update.  Keyword names must match their equivalent class instance  
     196        variable names.  However, they may prefixed with <prefix> 
     197        @return: new instance of this class 
     198        @rtype: ndg.saml.saml2.binding.soap.client.SOAPBinding or derived type 
     199        """ 
     200        obj = cls() 
     201        obj.parseKeywords(prefix=prefix, **kw) 
     202         
     203        if obj.policyFilePath: 
     204            obj.pdp = PDP.fromPolicySource(obj.policyFilePath,  
     205                                           XacmlPolicyReaderFactory) 
     206                     
     207        return obj 
     208                                        
     209    def _getPolicyFilePath(self): 
     210        return self.__policyFilePath 
     211 
     212    def _setPolicyFilePath(self, value): 
     213        if not isinstance(value, basestring): 
     214            raise TypeError('Expecting string type for "policyFilePath"; got ' 
     215                            '%r' % type(value)) 
     216        self.__policyFilePath = path.expandvars(value) 
     217 
     218    policyFilePath = property(_getPolicyFilePath,  
     219                              _setPolicyFilePath,  
     220                              doc="Policy file path for policy used by the PDP") 
     221        
    87222    def _getIssuerFormat(self): 
    88223        if self.__issuerProxy is None: 
     
    131266                                     "set assertion conditions notOnOrAfter " 
    132267                                     "time") 
    133          
     268  
    134269    def handlePEPRequest(self, pepRequest): 
    135270        """Handle request from Policy Enforcement Point 
     
    181316         
    182317    def pipQuery(self, request, designator): 
    183         """Query a Policy Information Point to retrieve the attribute values 
     318        """Implements interface method: 
     319         
     320        Query a Policy Information Point to retrieve the attribute values 
    184321        corresponding to the specified input designator.  Optionally, update the 
    185322        request context.  This could be a subject, environment or resource.   
    186323        Matching attributes values are returned 
    187         """ 
    188         return self.pip.query(request, designator) 
     324         
     325        @param request: request context 
     326        @type request: ndg.xacml.core.context.request.Request 
     327        @param designator: designator requiring additional subject attribute  
     328        information 
     329        @type designator: ndg.xacml.core.expression.Expression derived type 
     330        @return: list of attribute values for subject corresponding to given 
     331        policy designator.  Return None if none can be found 
     332        @rtype: ndg.xacml.utils.TypedList(<designator attribute type>) / None 
     333        """ 
     334        return self.pip.attributeQuery(request, designator) 
    189335     
    190336    def _createXacmlRequestCtx(self, samlAuthzDecisionQuery): 
  • TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/xacml/pip/saml_pip.py

    r7314 r7327  
    1515from ConfigParser import SafeConfigParser, ConfigParser 
    1616 
     17from ndg.xacml.core.attributedesignator import SubjectAttributeDesignator 
     18from ndg.xacml.core.attribute import Attribute as XacmlAttribute 
     19from ndg.xacml.core.attributevalue import AttributeValueClassFactory as \ 
     20    XacmlAttributeValueClassFactory 
    1721from ndg.xacml.core.context.pipinterface import PIPInterface 
    18 from ndg.xacml.core.context.request import Request 
     22from ndg.xacml.core.context.request import Request as XacmlRequestCtx 
    1923 
    2024from ndg.saml.saml2.core import (AttributeQuery as SamlAttributeQuery,  
    2125                                 Attribute as SamlAttribute) 
    22 from ndg.saml.utils import TypedList 
     26from ndg.saml.utils import TypedList as SamlTypedList 
    2327from ndg.saml.saml2.binding.soap.client.attributequery import \ 
    2428                                            AttributeQuerySslSOAPBinding 
     
    4751    DEFAULT_OPT_PREFIX = 'saml_pip.' 
    4852 
     53    XACML_ATTR_VAL_CLASS_FACTORY = XacmlAttributeValueClassFactory() 
     54     
    4955    __slots__ = ( 
    5056        '__subjectAttributeId', 
     
    113119        @type cfg: basestring /ConfigParser derived type 
    114120        @param cfg: configuration file path or ConfigParser type object 
    115         @rtype: ndg.saml.saml2.binding.soap.client.SOAPBinding 
     121        @rtype: ndg.security.server.xacml.pip.saml_pip.PIP 
    116122        @return: new instance of this class 
    117123        ''' 
     
    120126         
    121127        return obj 
    122      
    123     def __setAttr(self, optName, val):  
    124         """Helper method to differentiate attribute query binding option names 
    125         from other config options 
    126          
    127         @param optName: config file option name 
    128         @type optName: basestring 
    129         @param val: corresponding value 
    130         @type val: basestring 
    131         """ 
    132         if optName.startswith(self.__class__.ATTRIBUTE_QUERY_ATTRNAME): 
    133             setattr(self,  
    134                     optName[self.__class__.ATTRIBUTE_QUERY_ATTRNAME_OFFSET:], 
    135                     val) 
    136         else: 
    137             setattr(self, optName, val) 
    138128 
    139129    def parseConfig(self, cfg, prefix=DEFAULT_OPT_PREFIX, section='DEFAULT'): 
     
    204194            setattr(self.__attributeQueryBinding, queryAttrName, value) 
    205195        else: 
    206             super(PIP, self).__setattr__(name, value)     
     196            super(PIP, self).__setattr__(name, value) 
    207197     
    208198    def readMappingFile(self): 
     
    218208         
    219209    def attributeQuery(self, context, attributeDesignator): 
    220         """Query this PIP for attributes.    
     210        """Query this PIP for the given request context attribute specified by 
     211        the attribute designator.  Nb. this implementation is only intended to 
     212        accept queries for a given *subject* in the request 
    221213         
    222214        @param context: the request context 
    223215        @type context: ndg.xacml.core.context.request.Request 
    224         @rtype:  
    225         @return: attribute values found for query subject 
     216        @param designator:  
     217        @type designator: ndg.xacml.core.attributedesignator.SubjectAttributeDesignator 
     218        @rtype: ndg.xacml.utils.TypedList(<attributeDesignator.dataType>) / None 
     219        @return: attribute values found for query subject or None if none  
     220        could be found 
    226221        """ 
    227         if not isinstance(context, Request): 
     222         
     223        # Check the attribute designator type - this implementation takes 
     224        # queries for request context subjects only 
     225        if not isinstance(attributeDesignator, SubjectAttributeDesignator): 
     226            log.debug('This PIP query interface can only accept subject ' 
     227                      'attribute designator related queries') 
     228            return None 
     229         
     230        if not isinstance(context, XacmlRequestCtx): 
    228231            raise TypeError('Expecting %r type for context input; got %r' % 
    229                             (Request, type(context))) 
     232                            (XacmlRequestCtx, type(context))) 
    230233         
    231234        # TODO: Check for cached attributes for this subject (i.e. user)         
     
    243246                      attributeDesignator.attributeId) 
    244247             
    245             return [] 
     248            return None 
    246249         
    247250        # Get subject from the request context 
     
    283286            self.attributeQueryBinding.subjectID = '' 
    284287            self.attributeQueryBinding.subjectIdFormat = '' 
    285             self.attributeQueryBinding.query.attributes = TypedList( 
     288            self.attributeQueryBinding.query.attributes = SamlTypedList( 
    286289                                                                SamlAttribute) 
    287290         
    288         # Unpack SAML assertion attribute values corresponding to the name  
    289         # format specified 
    290         attributeValues = [] 
     291        # Unpack SAML assertion attribute corresponding to the name  
     292        # format specified and copy into XACML attributes       
     293        xacmlAttribute = XacmlAttribute() 
     294        xacmlAttribute.attributeId = attributeDesignator.attributeId 
     295        xacmlAttribute.dataType = attributeFormat 
     296         
     297        # Create XACML class from SAML type identifier 
     298        factory = self.__class__.XACML_ATTR_VAL_CLASS_FACTORY 
     299        xacmlAttrValClass = factory(attributeFormat) 
     300         
    291301        for assertion in response.assertions: 
    292302            for statement in assertion.attributeStatements: 
    293303                for attribute in statement.attributes: 
    294304                    if attribute.nameFormat == attributeFormat: 
    295                         attributeValues.append(attribute.attributeValues) 
     305                        # Convert SAML Attribute values to XACML equivalent  
     306                        # types 
     307                        for samlAttrVal in attribute.attributeValues:   
     308                            # Instantiate and initial new XACML value 
     309                            xacmlAttrVal = xacmlAttrValClass( 
     310                                                        value=samlAttrVal.value) 
     311                             
     312                            xacmlAttribute.attributeValues.append(xacmlAttrVal) 
    296313         
    297314        # Update the XACML request context subject with the new attributes 
    298         xacmlCtxSubject.attributes.append(attributeValues) 
     315        xacmlCtxSubject.attributes.append(xacmlAttribute) 
    299316         
    300317        # Return the attributes to the caller to comply with the interface 
    301         return attributeValues 
     318        return xacmlAttribute.attributeValues 
    302319        
  • TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/unit/__init__.py

    r7168 r7327  
    131131        X500DN.fromString("/O=Site A/CN=Authorisation Service"),  
    132132        X500DN.fromString("/O=Site B/CN=Authorisation Service"), 
    133         X500DN.fromString('/CN=test/O=NDG/OU=BADC') 
     133        X500DN.fromString('/CN=test/O=NDG/OU=BADC'), 
     134        X500DN.fromString('/O=NDG/OU=Security/CN=localhost') 
    134135    ) 
    135136     
  • TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/unit/x509/test_x509.py

    r6861 r7327  
    2525 
    2626from ConfigParser import SafeConfigParser 
     27 
    2728from ndg.security.test.unit import BaseTestCase 
    28  
    29 import warnings 
    30 _warningMsg = None 
    31 _origWarn = warnings.warn 
    32 def _warnWrapper(*arg, **kw): 
    33     global _warningMsg 
    34     _warningMsg = arg[0] 
    35     _origWarn(*arg, **kw) 
    36  
    37 warnings.warn = _warnWrapper 
    38  
    39 from ndg.security.common.X509 import X509CertRead, X509CertParse, X500DN, \ 
    40     X509Stack, X509StackEmptyError, SelfSignedCert, X509CertIssuerNotFound 
     29from ndg.security.common.X509 import (X509CertRead, X509CertParse, X500DN,  
     30    X509Stack, X509StackEmptyError, SelfSignedCert, X509CertIssuerNotFound) 
     31 
    4132 
    4233class X509TestCase(BaseTestCase): 
    4334    """Unit test X509 module""" 
    4435    CA_DIR = os.path.join(BaseTestCase.NDGSEC_TEST_CONFIG_DIR, 'ca') 
    45      
    46     def __del__(self): 
    47         warnings.warn = _origWarn 
    48         if getattr(super(X509TestCase, self), "__del__", None): 
    49             super(X509TestCase, self).__del__() 
    5036         
    5137    def setUp(self): 
     
    189175        self.test01X509CertRead() 
    190176         
    191         # Set ridiculous bounds for expiry warning to ensure a warning message 
    192         # is output 
    193         self.assert_(self.x509Cert.isValidTime(nDaysBeforeExpiryLimit=36500),  
    194                                                "Certificate has expired") 
    195         if not _warningMsg: 
    196             self.fail("No warning message was set") 
    197         else: 
    198             print("PASSED - Got warning message from X509Cert." 
    199                   "isValidTime: %s" % _warningMsg) 
    200  
    201     def test10ReadStackFromCADir(self): 
    202          
    203         stack = X509Stack.fromCADir(X509TestCase.CA_DIR) 
    204         self.assert_(stack) 
    205         self.assert_(len(stack) > 0) 
     177        warningMsg = None 
     178         
     179        # Capture stderr 
     180        try: 
     181            warningOutput = StringIO() 
     182            _stderr = sys.stderr 
     183            sys.stderr = warningOutput 
     184             
     185            # Set ridiculous bounds for expiry warning to ensure a warning  
     186            # message is output 
     187            validStatus = self.x509Cert.isValidTime( 
     188                                                nDaysBeforeExpiryLimit=36500) 
     189            self.assert_(validStatus, "Certificate has expired") 
     190        finally: 
     191            sys.stderr = _stderr 
     192            warningMsg = warningOutput.getvalue() 
     193             
     194        self.assert_("UserWarning" in str(warningMsg),  
     195                     "No warning message was set") 
     196         
     197        print("PASSED - Got warning message from X509Cert.isValidTime: %s" %  
     198              warningMsg) 
     199 
    206200         
    207201class X500DNTestCase(BaseTestCase): 
     
    215209        self.assert_(str(dn)) 
    216210        print(dn) 
     211         
     212    def test02VerifyCommaSeparatedDnParsing(self): 
     213        # Test parsing for ',' delimited fields 
     214        dnStr = 'O=NDG, OU=Security, CN=localhost' 
     215        dn = X500DN.fromString(dnStr) 
     216        self.assert_(str(dn)) 
     217        print(dn) 
     218         
    217219                                       
    218220if __name__ == "__main__": 
  • TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/unit/xacml/pip-mapping.txt

    r7314 r7327  
    1919 
    2020# Entries are whitespace delimited <attribute id> <attribute authority> 
    21 urn:ndg:security:attributes https://localhost:5443/AttributeAuthority 
     21urn:siteA:security:authz:1.0:attr https://localhost:5443/AttributeAuthority 
    2222myattributeid https://myattributeauthority.ac.uk/ 
    2323http://someotherattributeid.schema https://another.ac.uk/attributeservice/ 
  • TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/unit/xacml/test_saml_pip.py

    r7314 r7327  
    3838    CONFIG_FILEPATH = path.join(THIS_DIR, CONFIG_FILENAME) 
    3939     
    40     NDGS_ATTR_ID = 'urn:ndg:security:attributes' 
     40    NDGS_ATTR_ID = BaseTestCase.ATTRIBUTE_NAMES[0] 
    4141    OPENID_ATTR_ID = 'urn:esg:openid' 
    42     OPENID = 'https://localhost:7443/pjkershaw' 
    4342     
    4443    CLNT_CERT_FILEPATH = path.join(BaseTestCase.PKI_DIR, 'localhost.crt') 
     
    8382                                                            openidAttr.dataType) 
    8483         
    85         openidAttrVal = anyUriAttrValue(self.__class__.OPENID) 
     84        openidAttrVal = anyUriAttrValue(self.__class__.OPENID_URI) 
    8685        openidAttr.attributeValues.append(openidAttrVal)  
    8786         
     
    115114        ctx = self._createXacmlRequestCtx() 
    116115         
    117         attributes = pip.attributeQuery(ctx, designator) 
    118         self.assert_(len(attributes) > 0) 
     116        attributeValues = pip.attributeQuery(ctx, designator) 
     117        self.assert_(len(attributeValues) > 0) 
     118        print("PIP retrieved attribute values %r" % attributeValues) 
    119119         
    120120    def test04InitFromConfigFile(self): 
     
    122122        pip = PIP.fromConfig(self.__class__.CONFIG_FILEPATH) 
    123123        self.assert_(pip.mappingFilePath) 
    124          
    125         for i in dir(PIP): 
    126             print("%s = %r" % (i, getattr(pip, i))) 
     124 
    127125         
    128126if __name__ == "__main__": 
Note: See TracChangeset for help on using the changeset viewer.