Changeset 7072


Ignore:
Timestamp:
24/06/10 14:30:12 (9 years ago)
Author:
pjkersha
Message:

Incomplete - task 2: XACML-Security Integration

  • Major cleanup of function factories for efficiency. Only the required factories and function classes are loaded and any loaded classes are cached for future calls. All unit tests pass.
Location:
TI12-security/trunk/NDG_XACML/ndg/xacml
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • TI12-security/trunk/NDG_XACML/ndg/xacml/core/apply.py

    r7064 r7072  
    3434        self.__functionId = None 
    3535        self.__function = None 
    36         self.__functionMap = FunctionMap.withLoadedMap() 
     36        self.__functionMap = FunctionMap() 
    3737        self.__loadFunctionFromId = True 
    3838        self.__expressions = TypedList(Expression) 
  • TI12-security/trunk/NDG_XACML/ndg/xacml/core/attributevalue.py

    r7064 r7072  
    135135        # enable derived class to override them as standard methods without 
    136136        # needing to redefine this __init__ method             
    137         super(AttributeValueClassMap, self).__init__(self.keyFilter,  
    138                                                      self.valueFilter) 
     137        VettedDict.__init__(self, self.keyFilter, self.valueFilter) 
    139138         
    140139    @staticmethod 
  • TI12-security/trunk/NDG_XACML/ndg/xacml/core/context/handler.py

    r7064 r7072  
    1111__revision__ = "$Id$" 
    1212from abc import ABCMeta, abstractmethod 
    13 from ndg.xacml.core.context.pdpinterface import PDPInterface 
    1413 
    1514 
     
    1817    __metaclass__ = ABCMeta 
    1918    __slots__ = () 
    20      
    21     @classmethod 
    22     def __subclasshook__(cls, C): 
    23         """Derived class must implement __call__""" 
    24         if cls is PEPInterface: 
    25             if any("handlePEPRequest" in B.__dict__ for B in C.__mro__): 
    26                 return True 
    2719             
    28         return NotImplemented 
    29          
    3020    @abstractmethod 
    3121    def handlePEPRequest(self, pepRequest): 
     
    4636    __slots__ = () 
    4737     
    48     def pepQuery(self, request, designator): 
     38    def pipQuery(self, request, designator): 
    4939        """Query a Policy Information Point to retrieve the attribute values 
    5040        corresponding to the specified input designator.  Optionally, update the 
  • TI12-security/trunk/NDG_XACML/ndg/xacml/core/functions/__init__.py

    r7064 r7072  
    282282 
    283283class FunctionClassFactoryBase(FunctionClassFactoryInterface): 
    284     """Base implementation for XACML Function Class Factory.  Derived types  
    285     should be implemented in sub-modules of ndg.xacml.core.functions  
     284    """Base implementation for XACML Function Class Factory.  There should be 
     285    one derived type for each function family implemented in sub-modules of  
     286    ndg.xacml.core.functions  
    286287     
    287288    e.g. 
    288289     
    289     for urn:oasis:names:tc:xacml:1.0:function:string-at-least-one-member-of a  
     290    for urn:oasis:names:tc:xacml:1.0:function:<type>-at-least-one-member-of a  
    290291    class factory should exist, 
    291292     
    292293    ndg.xacml.core.functions.v1.at_least_one_member_of.FunctionClassFactory 
    293294     
    294     which will be capable of returning an AbstractFunction derived type: 
    295      
    296     StringAtLeastOneMemberOf     
     295    which will be capable of returning a type derived from AbstractFunction: 
     296     
     297    <type>AtLeastOneMemberOf     
     298     
     299    e.g. StringAtLeastOneMemberOf, BooleanAtLeastOneMemberOf. 
    297300     
    298301    This class is for convenience only some function factories are better  
     
    318321    ndg.xacml.core.functions.v1.at_least_one_member_of.AtLeastOneMemberOfBase 
    319322    @type FUNCTION_BASE_CLASS: NoneType (but AbstractFunction derived type in  
    320     derived function facotry class) 
     323    derived function factory class) 
    321324    """ 
    322325     
     
    327330    URN_SEP = ':' 
    328331    FUNCTION_NAME_SEP = '-' 
    329  
     332    __slots__ = ('__map', 'attributeValueClassFactory', 'functionSuffix') 
     333     
    330334    def __init__(self): 
    331         """Create classes for each <type>-at-least-one-member-of function and 
    332         place in a look-up table 
    333         """ 
     335        '''This class is in fact abstract - derived types must define the  
     336        FUNCTION_NS_SUFFIX and FUNCTION_BASE_CLASS class variables 
     337        ''' 
    334338        if None in (self.__class__.FUNCTION_NS_SUFFIX,  
    335339                    self.__class__.FUNCTION_BASE_CLASS): 
     
    343347                            'iterable of string type function identifiers; got ' 
    344348                            '%r' % self.__class__.FUNCTION_NAMES) 
    345              
    346         self.__map = {} 
     349 
     350        self.__map = {}    
     351         
     352        # Enables creation of matching attribute types to relevant to the  
     353        # function classes     
     354        self.attributeValueClassFactory = AttributeValueClassFactory() 
     355             
     356         
    347357        functionSuffixParts = self.__class__.FUNCTION_NS_SUFFIX.split( 
    348358                                            self.__class__.FUNCTION_NAME_SEP) 
    349         functionSuffix = ''.join([n[0].upper() + n[1:]  
     359        self.functionSuffix = ''.join([n[0].upper() + n[1:]  
    350360                                  for n in functionSuffixParts if n]) 
    351361         
    352         attributeValueClassFactory = AttributeValueClassFactory() 
    353          
    354         for identifier in self.__class__.FUNCTION_NAMES:             
    355             # Extract the function name and the type portion of the function 
    356             # name in order to make an implementation of a class to handle it 
    357             functionName = identifier.split(self.__class__.URN_SEP)[-1] 
    358             if functionName == 'xpath-node-match': 
    359                 pass 
    360             typePart = functionName.split(self.__class__.FUNCTION_NS_SUFFIX)[0] 
    361              
    362             # Attempt to infer from the function name the associated type 
    363             typeName = typePart[0].upper() + typePart[1:] 
    364              
    365             # Remove any hyphens converting to camel case 
    366             if '-' in typeName: 
    367                 typeName = ''.join([i[0].upper() + i[1:] 
    368                                     for i in typeName.split('-')]) 
    369                  
    370             typeURI = AttributeValue.TYPE_URI_MAP.get(typeName) 
    371             if typeURI is None: 
    372                 # Ugly hack to allow for XPath node functions 
    373                 if typePart == 'xpath-node': 
    374                     typeURI = AttributeValue.TYPE_URI_MAP['String'] 
    375                 else: 
    376                     raise TypeError('No AttributeValue.TYPE_URI_MAP entry for ' 
    377                                     '%r type' % typePart)  
    378                  
    379             _type = attributeValueClassFactory(typeURI) 
    380             if _type is None: 
    381                 raise TypeError('No AttributeValue.TYPE_MAP entry for %r type' % 
    382                                 typeName) 
    383                
    384             className = typeName + functionSuffix 
    385             classVars = { 
    386                 'TYPE': _type, 
    387                 'FUNCTION_NS': identifier 
    388             } 
    389              
    390             functionClass = type(className,  
    391                                  (self.__class__.FUNCTION_BASE_CLASS, ),  
    392                                  classVars) 
    393              
    394             self.__map[identifier] = functionClass 
     362    def initAllFunctionClasses(self): 
     363        """Create classes for all functions for a data type e.g. a derived class 
     364        could implement a factory for <type>-at-least-one-member-of functions: 
     365        string-at-least-one-member-of, boolean-at-least-one-member-of, etc.  
     366         
     367        Function classes are placed in a look-up table __map for the __call__() 
     368        method to access 
     369         
     370        In practice, there shouldn't be a need to load all the functions in 
     371        one go.  The __call__ method loads functions and caches them as needed. 
     372        """         
     373        for identifier in self.__class__.FUNCTION_NAMES: 
     374            self.loadFunction(identifier)         
     375 
     376    def loadFunction(self, identifier): 
     377        """Create a class for the given function namespace and cache it in the  
     378        function class look-up table for future requests.  Note that this call 
     379        overwrites any existing entry in the cache whereas __call__ will try 
     380        to use an entry in the cache if it already exists 
     381         
     382        @param identifier: XACML function namespace 
     383        @type identifier: basestring 
     384        """ 
     385 
     386        # str.capitalize doesn't do what's required: need to capitalize the  
     387        # first letter of the word BUT retain camel case for the rest of it 
     388        _capitalize = lambda s: s[0].upper() + s[1:] 
     389         
     390        # Extract the function name and the type portion of the function 
     391        # name in order to make an implementation of a class to handle it 
     392        functionName = identifier.split(self.__class__.URN_SEP)[-1] 
     393        typePart = functionName.split(self.__class__.FUNCTION_NS_SUFFIX)[0] 
     394         
     395        # Attempt to infer from the function name the associated type 
     396        typeName = _capitalize(typePart) 
     397         
     398        # Remove any hyphens converting to camel case 
     399        if '-' in typeName: 
     400            typeName = ''.join([_capitalize(i) for i in typeName.split('-')]) 
     401             
     402        typeURI = AttributeValue.TYPE_URI_MAP.get(typeName) 
     403        if typeURI is None: 
     404            # Ugly hack to allow for XPath node functions 
     405            if typePart == 'xpath-node': 
     406                typeURI = AttributeValue.TYPE_URI_MAP['String'] 
     407            else: 
     408                raise TypeError('No AttributeValue.TYPE_URI_MAP entry for ' 
     409                                '%r type' % typePart)  
     410             
     411        _type = self.attributeValueClassFactory(typeURI) 
     412        if _type is None: 
     413            raise TypeError('No AttributeValue.TYPE_MAP entry for %r type' % 
     414                            typeName) 
     415           
     416        className = typeName + self.functionSuffix 
     417        classVars = { 
     418            'TYPE': _type, 
     419            'FUNCTION_NS': identifier 
     420        } 
     421         
     422        functionClass = type(className,  
     423                             (self.__class__.FUNCTION_BASE_CLASS, ),  
     424                             classVars) 
     425         
     426        self.__map[identifier] = functionClass 
    395427             
    396428    def __call__(self, identifier): 
     
    405437        found 
    406438        """ 
     439        # Check the cache first 
     440        functionClass = self.__map.get(identifier) 
     441        if functionClass is None: 
     442            # No class set in the cache - try loading the new class and updating 
     443            # the cache. 
     444            self.loadFunction(identifier) 
     445             
     446        # This should result in a safe retrieval from the cache because of the 
     447        # above check - None return would result otherwise. 
    407448        return self.__map.get(identifier) 
    408449         
     
    417458         
    418459class FunctionMap(VettedDict): 
    419     """Map function IDs to their implementations""" 
     460    """Map function IDs to their class implementations in the various function 
     461    sub-modules.  It provide a layer over the various  
     462    FunctionClassFactoryInterface implementations so that a function class can  
     463    be obtained directly from a given XACML function URN.   
     464    """ 
    420465    FUNCTION_PKG_PREFIX = 'ndg.xacml.core.functions.' 
    421466     
     
    433478     
    434479    def __init__(self): 
    435         """Force function entries to derive from AbstractFunction and IDs to 
    436         be string type 
     480        """Force type for dictionary key value pairs: function values must be 
     481        of AbstractFunction derived type and ID keys string type 
    437482        """         
    438483        # Filters are defined as staticmethods but reference via self here to  
    439484        # enable derived class to override them as standard methods without 
    440485        # needing to redefine this __init__ method             
    441         super(FunctionMap, self).__init__(self.keyFilter, self.valueFilter) 
     486        VettedDict.__init__(self, self.keyFilter, self.valueFilter) 
     487         
     488        # This classes maintains a list of XACML function URN -> Function class 
     489        # mappings.  This additional dict enables caching of class factories  
     490        # used to obtain the function classes.  There is one class factory per 
     491        # function module e.g. ndg.xacml.core.functions.v1.equal contains a  
     492        # class factory which creates the various  
     493        # urn:oasis:names:tc:xacml:1.0:function:<type>-equal function classes 
     494        self.__classFactoryMap = {} 
    442495         
    443496    @staticmethod 
     
    462515        return True  
    463516            
    464     def load(self): 
     517    def loadAll(self): 
    465518        """Load function map with implementations from the relevant function 
    466519        package""" 
    467520         
    468521        for functionNs in XacmlFunctionNames.FUNCTION_NAMES: 
    469             self._loadFunction(functionNs) 
    470              
    471     @classmethod 
    472     def withLoadedMap(cls): 
    473         """Return a pre-loaded map""" 
    474         functionMap = cls() 
    475         functionMap.load() 
    476         return functionMap 
    477              
    478     def _loadFunction(self, functionNs): 
    479         """Get package to retrieve function class from for given namespace 
     522            self.loadFunction(functionNs) 
     523             
     524    def loadFunction(self, functionNs): 
     525        """Get package to retrieve function class for the given XACML function 
     526        namespace 
     527         
     528        @param functionNs: XACML function namespace 
     529        @type functionNs: basestring 
    480530        """ 
     531        functionFactory = self.__classFactoryMap.get(functionNs) 
     532        if functionFactory is not None: 
     533            # Get function class from previously cached factory 
     534            self[functionNs] = functionFactory(functionNs) 
     535            return 
     536             
     537        # No Factory has been cached for this function yet 
    481538        cls = FunctionMap 
    482539        classPath = None 
     
    484541        for namespacePrefix, pkgNamePrefix in cls.SUPPORTED_NSS.items(): 
    485542            if functionNs.startswith(namespacePrefix): 
    486                 # Namespace is recognised - translate into a path to a function 
    487                 # class in the right functions package 
     543                # Namespace is recognised - translate into a path to a  
     544                # function class in the right functions package 
    488545                functionName = functionNs.split(namespacePrefix)[-1] 
    489546                functionNameParts = functionName.split('-') 
     
    506563                                         'recognised: %r' % functionNs)  
    507564                        
    508         # Try instantiating the function class and loading it into the  
    509         # map 
     565        # Try instantiating the function class and loading it into the map 
    510566        try: 
    511567            functionFactory = callModuleObject(classPath) 
    512568                       
    513         except (ImportError, AttributeError): 
     569        except (ImportError, AttributeError), e: 
    514570            log.error("Error importing function factory class %r for function " 
    515                       "identifier %r: %s", classPath, functionNs,  
    516                       traceback.format_exc()) 
     571                      "identifier %r: %s", classPath, functionNs, str(e)) 
    517572             
    518573            # No implementation exists - default to Abstract function 
     
    520575        else: 
    521576            self[functionNs] = functionFactory(functionNs) 
    522              
    523          
    524              
    525  
    526  
    527          
     577            self.__classFactoryMap[functionNs] = functionFactory 
     578                        
     579    def __getitem__(self, key): 
     580        """Override base class implementation to load and cache function classes 
     581        if they don't otherwise exist 
     582        """ 
     583        functionClass = VettedDict.get(self, key) 
     584        if functionClass is None: 
     585            self.loadFunction(key) 
     586             
     587        return VettedDict.__getitem__(self, key) 
     588         
     589    def get(self, key, *arg): 
     590        """Likewise to __getitem__, enable loading and caching of function  
     591        classes if they don't otherwise exist 
     592        """ 
     593        functionClass = VettedDict.get(self, key, *arg) 
     594        if functionClass is None: 
     595            self.loadFunction(key) 
     596            return VettedDict.get(self, key, *arg)     
     597        else: 
     598            return functionClass 
     599 
     600 
     601         
  • TI12-security/trunk/NDG_XACML/ndg/xacml/core/match.py

    r7064 r7072  
    5454         
    5555        self.__function = None 
    56         self.__functionMap = FunctionMap.withLoadedMap() 
     56        self.__functionMap = FunctionMap() 
    5757        self.__loadFunctionFromId = True 
    5858         
  • TI12-security/trunk/NDG_XACML/ndg/xacml/parsers/etree/factory.py

    r7064 r7072  
    5151                                                        xacmlTypeName) 
    5252        readerClass = importModuleObject(readerClassName) 
    53          
    54         log.debug('Returning %r ElementTree based reader for %r type', 
    55                   readerClass, xacmlType) 
    5653        return readerClass 
    5754             
  • TI12-security/trunk/NDG_XACML/ndg/xacml/test/test_context.py

    r7064 r7072  
    2020from ndg.xacml.core.context.pdpinterface import PDPInterface 
    2121from ndg.xacml.core.context.pdp import PDP 
    22 from ndg.xacml.core.context.handler import AbstractContextHandler 
     22from ndg.xacml.core.context.handler import CtxHandlerInterface 
    2323from ndg.xacml.core.attribute import Attribute 
    2424from ndg.xacml.core.attributevalue import AttributeValueClassFactory 
     
    3131 
    3232                 
    33 class TestContextHandler(AbstractContextHandler): 
     33class TestContextHandler(CtxHandlerInterface): 
    3434    """Test implementation of Context Handler""" 
    3535     
    3636    def __init__(self): 
     37        """Add an attribute to hold a reference to a policy information point""" 
     38         
    3739        super(TestContextHandler, self).__init__() 
    3840        self.pip = None         
    3941         
    4042    def handlePEPRequest(self, myRequest): 
     43        """Handle request from Policy Enforcement Point 
    4144         
    42         # Convert myRequest to XACML context request 
     45        @param pepRequest: request from PEP, derived class determines its type 
     46        e.g. SAML AuthzDecisionQuery 
     47        @type pepRequest: type 
     48        @return: PEP response - derived class determines type 
     49        @rtype: None 
     50        """ 
     51         
     52        # Convert myRequest to XACML context request - var assignment here is  
     53        # representative of this process rather than actually doing anything. 
    4354        request = myRequest 
    4455         
     
    128139         
    129140    def test03AbstractCtxHandler(self): 
    130         self.assertRaises(TypeError, AbstractContextHandler) 
     141        self.assertRaises(TypeError, CtxHandlerInterface,  
     142                          "Context handler is an abstract base class") 
    131143         
    132144    def test04CreateCtxHandler(self): 
  • TI12-security/trunk/NDG_XACML/ndg/xacml/test/test_matchfunctions.py

    r7064 r7072  
    1717 
    1818from ndg.xacml.core.functions import FunctionMap 
    19 from ndg.xacml.core.functions.v2.anyuri_regexp_match import AnyURIRegexpMatch 
     19from ndg.xacml.core.functions.v2.regexp_match import RegexpMatchBase 
    2020 
    2121 
     
    2929            'urn:oasis:names:tc:xacml:2.0:function:anyURI-regexp-match' 
    3030             
    31         self.assert_(isinstance(funcMap.get(anyUriMatchNs), AnyURIRegexpMatch)) 
     31        self.assert_(issubclass(funcMap.get(anyUriMatchNs), RegexpMatchBase)) 
    3232 
    3333         
  • TI12-security/trunk/NDG_XACML/ndg/xacml/utils/__init__.py

    r7064 r7072  
    99__contact__ = "Philip.Kershaw@stfc.ac.uk" 
    1010__revision__ = '$Id$' 
     11import UserDict 
    1112 
    1213# Interpret a string as a boolean 
     
    114115 
    115116   
    116 class VettedDict(dict): 
    117     """Enforce custom checking on keys and items before addition to the  
     117class VettedDict(UserDict.DictMixin): 
     118    """Enforce custom checking on keys and items before addition to a  
    118119    dictionary""" 
    119120     
     
    126127        @type args: tuple 
    127128        """ 
    128         super(VettedDict, self).__init__() 
    129          
    130129        if len(args) != 2: 
    131130            raise TypeError('__init__() takes 2 arguments, KeyFilter and ' 
     
    139138 
    140139        self.__KeyFilter, self.__valueFilter = args 
     140         
     141        self.__map = {} 
    141142         
    142143    def _verifyKeyValPair(self, key, val): 
     
    155156                   
    156157    def __setitem__(self, key, val): 
    157         """Override base class implementation to enforce type checking"""        
     158        """Enforce type checking when setting new items"""        
    158159        if self._verifyKeyValPair(key, val): 
    159             dict.__setitem__(self, key, val) 
     160            self.__map[key] = val 
    160161 
    161     def update(self, d, **kw):  
    162         """Override base class implementation to enforce type checking"""        
    163         for dictArg in (d, kw): 
    164             for key, val in dictArg.items(): 
    165                 if not self._verifyKeyValPair(key, val): 
    166                     del dictArg[key] 
     162    def __getitem__(self, key): 
     163        """Provde implementation for getting items""" 
     164        if key not in self.__map: 
     165            raise KeyError('%r key not found in dict' % key) 
    167166         
    168         dict.update(self, d, **kw)         
     167        return self.__map[key] 
    169168     
     169    def get(self, key, *arg): 
     170        """Provide implementation of get item with default""" 
     171        if key in self.__map: 
     172            return self.__map[key] 
     173         
     174        elif len(arg) > 1: 
     175            # Default value set 
     176            return arg[1] 
     177        else: 
     178            return None 
     179             
     180 
     181     
  • TI12-security/trunk/NDG_XACML/ndg/xacml/utils/factory.py

    r7064 r7072  
    4141            objectName = [objectName] 
    4242     
    43     log.debug("Importing %r ..." % objectName)  
    44        
    4543    module = __import__(_moduleName, globals(), locals(), []) 
    4644    components = _moduleName.split('.') 
     
    5048    except AttributeError, e: 
    5149        raise AttributeError("Error importing %r: %s" % 
    52                              (objectName, traceback.format_exc())) 
     50                             (objectName[0], traceback.format_exc())) 
    5351 
    5452    importedObject = module 
     
    6159                        (objectName, objectType, importedObject)) 
    6260     
    63     log.info('Imported %r from module, %r', objectName, _moduleName) 
     61    log.info('Imported %r from module %r', objectName[0], _moduleName) 
    6462    return importedObject 
    6563 
     
    121119                             
    122120    except Exception, e: 
    123         log.error('%r module import raised %r type exception: %r' %  
    124                   (moduleName, e.__class__, traceback.format_exc())) 
     121        log.debug('%r module import raised %r type exception: %s',  
     122                  moduleName, e.__class__, traceback.format_exc()) 
    125123        raise  
    126124 
    127125    # Instantiate class 
    128     log.debug('Instantiating object "%s"' % importedObject.__name__) 
    129     try: 
    130         if objectArgs: 
    131             object = importedObject(*objectArgs, **objectProperties) 
    132         else: 
    133             object = importedObject(**objectProperties) 
    134              
    135         return object 
    136  
    137     except Exception, e: 
    138         log.error("Instantiating module object, %r: %r" %  
    139                                                     (importedObject.__name__,  
    140                                                      traceback.format_exc())) 
    141         raise 
     126    if objectArgs: 
     127        object = importedObject(*objectArgs, **objectProperties) 
     128    else: 
     129        object = importedObject(**objectProperties) 
     130         
     131    return object 
Note: See TracChangeset for help on using the changeset viewer.