Changeset 6807 for TI12-security


Ignore:
Timestamp:
15/04/10 13:00:35 (10 years ago)
Author:
pjkersha
Message:

Completed moving of matching and rule evaluation code out of the PDP class and into the Target, Match, Apply, AttributeDesignator? and Condition classes. Policy evaluation is now working apart from the rule combining algorithm.

Location:
TI12-security/trunk/NDG_XACML/ndg/xacml
Files:
12 edited

Legend:

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

    r6790 r6807  
    9595    XACML_2_0_POLICY_NS = (XacmlCoreBase.XACML_2_0_NS_PREFIX + 
    9696                           ":policy:schema:os") 
     97    __slots__ = () 
    9798     
    9899    def __init__(self): 
  • TI12-security/trunk/NDG_XACML/ndg/xacml/core/apply.py

    r6805 r6807  
    5050                            'attribute; got %r' % (bool, type(value))) 
    5151             
    52         self.__functionId = value 
     52        self.__loadFunctionFromId = value 
    5353         
    5454    def _get_functionId(self): 
  • TI12-security/trunk/NDG_XACML/ndg/xacml/core/attributedesignator.py

    r6805 r6807  
    3737        self.__issuer = None 
    3838        self.__mustBePresent = False 
     39         
     40        # When evaluating matches, use an attribute value class factory to  
     41        # create attribute values for match bag of the correct DataType to  
     42        # respect type based rule functions 
    3943        self.__attributeValueFactory = AttributeValueClassFactory() 
    4044 
     
    170174            for attr in resource.attributes: 
    171175                if _attributeMatch(attr): 
    172                     attributeValueBag.extend([i for i in attr.attributeValueBag 
     176                    attributeValueBag.extend([i for i in attr.attributeValues 
    173177                                              if i.dataType == dataType]) 
    174178                     
     
    182186                                         self.issuer))  
    183187                         
    184         return attributeValueBag     
     188        return attributeValueBag 
    185189     
    186190         
  • TI12-security/trunk/NDG_XACML/ndg/xacml/core/attributevalue.py

    r6805 r6807  
    5151             
    5252        self.__value = None 
     53         
     54        # Allow derived classes to make an implicit data type setting 
     55        self.dataType = self.__class__.IDENTIFIER 
    5356 
    5457    def __repr__(self): 
     
    123126 
    124127    className = typeName + AttributeValue.CLASS_NAME_SUFFIX                
    125     classVars = {'TYPE': _type, 'IDENTIFIER_NS': identifier} 
     128    classVars = {'TYPE': _type, 'IDENTIFIER': identifier} 
    126129     
    127130    attributeValueClass = type(className, (AttributeValue, ), classVars) 
  • TI12-security/trunk/NDG_XACML/ndg/xacml/core/context/pdp.py

    r6806 r6807  
    1818from ndg.xacml.core.context.pdpinterface import PDPInterface 
    1919from ndg.xacml.core.policy import Policy 
    20 from ndg.xacml.core.apply import Apply 
    2120from ndg.xacml.core.context.request import Request 
    2221from ndg.xacml.core.context.response import Response 
    2322from ndg.xacml.core.context.result import Result, Decision 
    2423from ndg.xacml.core.context.result import StatusCode 
    25 from ndg.xacml.core.exceptions import (UnsupportedStdFunctionError, 
    26                                        UnsupportedFunctionError)      
    2724        
    2825 
     
    127124        # INDETERMINATE response from any exceptions raised 
    128125        try: 
    129             log.debug('Checking policy target for match with request...') 
     126            log.debug('Checking policy %r target for match with request...', 
     127                      self.policy.policyId) 
    130128             
    131129            target = self.policy.target 
    132130             
    133             if target is None: 
    134                 log.debug('No target set so no match with request context') 
    135                 result.decision = Decision.NOT_APPLICABLE 
    136                 return response 
     131            # If no target is set, ALL requests are counted as matches 
     132            if target is not None: 
     133                if not target.match(request): 
     134                    # The target didn't match so the whole policy does not apply 
     135                    # to this request 
     136                    log.debug('No match for policy target setting %r decision', 
     137                              Decision.NOT_APPLICABLE_STR) 
     138                     
     139                    result.decision = Decision.NOT_APPLICABLE 
     140                    return response 
    137141             
    138             if not target.match(request): 
    139                 log.debug('No match for policy target setting %r decision', 
    140                           Decision.NOT_APPLICABLE_STR) 
    141                  
    142                 result.decision = Decision.NOT_APPLICABLE 
    143                 return response 
    144              
    145             log.debug('Request matches the Policy target') 
     142            log.debug('Request matches the Policy %r target',  
     143                      self.policy.policyId) 
    146144             
    147145            # Check rules 
    148             ruleStatusValues = [False]*len(self.policy.rules) 
     146            ruleDecisions = [False]*len(self.policy.rules) 
    149147            for i, rule in enumerate(self.policy.rules): 
    150                 ruleStatusValues[i] = rule.evaluate() 
    151 #                log.debug('Checking policy rule %r for match...', rule.id) 
    152 #                if not self.matchTarget(rule.target, request): 
    153 #                    log.debug('No match to request context for target in rule ' 
    154 #                              '%r', rule.id) 
    155 #                    ruleStatusValues[i] = True 
    156 #                    continue    
    157 #                 
    158 #                # Apply the condition 
    159 #                ruleStatusValues[i] = self.evaluateCondition(rule.condition)  
     148                ruleDecisions[i] = rule.evaluate(request) 
     149                 
     150            # TODO: Apply the rule combining algorithm here combining the  
     151            # effects from the rules evaluated into an overall decision 
    160152                      
    161153        except PDPError, e: 
  • TI12-security/trunk/NDG_XACML/ndg/xacml/core/context/pdpinterface.py

    r6776 r6807  
    1414 
    1515 
    16 class PDPInterface: 
     16class PDPInterface(object): 
     17    """Interface class for XACML Policy Enforcement Point""" 
    1718    __metaclass__ = ABCMeta 
    18      
    19     @classmethod 
    20     def __subclasshook__(cls, C): 
    21         """Derived class must implement __call__""" 
    22         if cls is PDPInterface: 
    23             if any("evaluate" in B.__dict__ for B in C.__mro__): 
    24                 return True 
    25              
    26         return NotImplemented 
    2719 
    2820    @abstractmethod 
  • TI12-security/trunk/NDG_XACML/ndg/xacml/core/context/result.py

    r6790 r6807  
    237237        return self.__value 
    238238 
     239    def __repr__(self): 
     240        return "%s = %r" % (super(Decision, self).__repr__(), self.__value) 
     241     
    239242    def __eq__(self, decision): 
    240243        if isinstance(decision, Decision): 
  • TI12-security/trunk/NDG_XACML/ndg/xacml/core/match.py

    r6806 r6807  
    1010__contact__ = "Philip.Kershaw@stfc.ac.uk" 
    1111__revision__ = "$Id: $" 
     12import logging 
     13log = logging.getLogger(__name__) 
     14 
    1215from ndg.xacml.core import XacmlCoreBase 
     16from ndg.xacml.core.attributevalue import AttributeValue 
    1317from ndg.xacml.core.attributedesignator import AttributeDesignator 
    1418from ndg.xacml.core.attributeselector import AttributeSelector 
    15 from ndg.xacml.core.attributevalue import AttributeValue 
     19from ndg.xacml.core.functions import FunctionMap 
     20from ndg.xacml.core.context.exceptions import XacmlContextError 
     21from ndg.xacml.core.exceptions import (UnsupportedStdFunctionError, 
     22                                       UnsupportedFunctionError) 
    1623 
    1724 
    1825class MatchBase(XacmlCoreBase): 
     26    """Base class for representation of SubjectMatch, ResourceMatch,  
     27    ActionMatch and EnvironmentMatch Target elements 
     28    """ 
    1929    ELEMENT_LOCAL_NAME = None 
    2030    MATCH_ID_ATTRIB_NAME = 'MatchId' 
     
    2535        '__attributeDesignator',  
    2636        '__attributeSelector', 
    27         '__matchId' 
     37        '__matchId', 
     38        '__function',  
     39        '__functionMap', 
     40        '__loadFunctionFromId', 
    2841    ) 
    2942     
    3043    def __init__(self): 
     44        """Initial attributes corresponding to the equivalent XACML schema type 
     45        and also create a function map to map functions from MatchIds 
     46        """ 
    3147        self.__attributeValue = None 
    3248          
     
    3652         
    3753        self.__matchId = None 
    38  
    39  
     54         
     55        self.__function = None 
     56        self.__functionMap = FunctionMap.withLoadedMap() 
     57        self.__loadFunctionFromId = True 
     58         
     59    @property 
     60    def attributeValue(self): 
     61        """Match attribute value""" 
     62        return self.__attributeValue 
     63     
     64    @attributeValue.setter 
     65    def attributeValue(self, value): 
     66        """Set match attribute value. 
     67        """ 
     68        if not isinstance(value, AttributeValue): 
     69            raise TypeError('Expecting %r type for "matchId" ' 
     70                            'attribute; got %r' %  
     71                            (AttributeValue, type(value))) 
     72             
     73        self.__attributeValue = value 
     74         
    4075    @property 
    4176    def attributeDesignator(self): 
     
    75110                        
    76111    def _getMatchId(self): 
     112        """Match identifier for match function""" 
    77113        return self.__matchId 
    78114 
    79115    def _setMatchId(self, value): 
     116        """Match identifier for match function""" 
    80117        if not isinstance(value, basestring): 
    81118            raise TypeError('Expecting string type for "matchId" ' 
     
    83120             
    84121        self.__matchId = value 
    85  
    86     matchId = property(_getMatchId, _setMatchId, None, "Match Id") 
    87      
    88     def evaluate(self): 
     122         
     123        # Also retrieve function for this match ID if a map has been set 
     124        if self.__loadFunctionFromId: 
     125            self.setFunctionFromMap(self.__functionMap)    
     126 
     127    matchId = property(_getMatchId, _setMatchId, None,  
     128                       "Match identifier for match function") 
     129       
     130    @property 
     131    def loadFunctionFromId(self): 
     132        """Set to False to stop the functionId property set method automatically 
     133        trying to load the corresponding function for the given functionId"""  
     134        return self.__loadFunctionFromId 
     135     
     136    @loadFunctionFromId.setter 
     137    def loadFunctionFromId(self, value): 
     138        if not isinstance(value, bool): 
     139            raise TypeError('Expecting %r type for "loadFunctionFromId" ' 
     140                            'attribute; got %r' % (bool, type(value))) 
     141             
     142        self.__loadFunctionFromId = value 
     143         
     144    def setFunctionFromMap(self, functionMap): 
     145        """Set the function from a function map - a dictionary of function ID to 
     146        function mappings.  The function is looked up based on the "functionId" 
     147        attribute.  This method is automatically called when the functionId set 
     148        property method is invoked.  To switch off this behaviour set 
     149         
     150        loadFunctionFromId = False 
     151         
     152        @raise UnsupportedStdFunctionError: policy references a function type  
     153        which is in the XACML spec. but is not supported by this implementation 
     154        @raise UnsupportedFunctionError: policy references a function type which 
     155        is not supported by this implementation 
     156        """ 
     157        if self.matchId is None: 
     158            raise AttributeError('"functionId" attribute must be set in order ' 
     159                                 'to retrieve the required function') 
     160             
     161        # Get function class for this <Apply> statement          
     162        functionClass = functionMap.get(self.matchId) 
     163        if functionClass is NotImplemented: 
     164            raise UnsupportedStdFunctionError('No match function class ' 
     165                                              'implemented for MatchId="%s"' %  
     166                                              self.matchId) 
     167        elif functionClass is None: 
     168            raise UnsupportedFunctionError('<Apply> function namespace %r is ' 
     169                                           'not recognised' %  
     170                                           self.matchId)  
     171             
     172        self.__function = functionClass() 
     173     
     174    @property 
     175    def functionMap(self): 
     176        """functionMap object for PDP to retrieve functions from given XACML 
     177        function URNs""" 
     178        return self.__functionMap 
     179     
     180    @functionMap.setter 
     181    def functionMap(self, value): 
     182        '''functionMap object for PDP to retrieve functions from given XACML 
     183        function URNs''' 
     184        if not isinstance(value, FunctionMap): 
     185            raise TypeError('Expecting %r derived type for "functionMap" ' 
     186                            'input; got %r instead' % (FunctionMap,  
     187                                                       type(value))) 
     188        self.__functionMap = value 
     189           
     190    @property   
     191    def function(self): 
     192        "Function for this <Apply> instance" 
     193        return self.__function 
     194         
     195    def evaluate(self, context): 
    89196        """Evaluate the match object against the relevant element in the request 
    90197        context 
    91198        """ 
    92         # Get the match function from the Match ID 
    93 #        matchFunctionClass = self.__functionMap.get(self.matchId) 
    94 #        if matchFunctionClass is NotImplemented: 
    95 #            raise UnsupportedStdFunctionError('No match function class ' 
    96 #                                              'implemented for MatchId="%s"' 
    97 #                                              % self.matchId) 
    98 #        elif matchFunctionClass is None: 
    99 #            raise UnsupportedFunctionError('Match function namespace %r is ' 
    100 #                                           'not recognised' %  
    101 #                                           self.matchId) 
    102 #             
    103 #        matchAttributeValue = self.attributeValue.value 
    104199         
    105200        # Create a match function based on the presence or absence of an 
    106201        # AttributeDesignator or AttributeSelector 
    107202        if self.attributeDesignator is not None: 
    108 #            _attributeMatch = self.attributeDesignatorMatchFuncFactory( 
    109 #                                            matchFunctionClass(), 
    110 #                                            self.attributeValue, 
    111 #                                            self.attributeDesignator) 
     203            requestAttributeValues = self.attributeDesignator.evaluate(context) 
    112204             
    113205        elif self.attributeSelector is not None: 
    114             # Nb. This will require that the request provide a reference to 
    115             # it's XML representation and an abstraction of the XML parser 
    116             # for executing XPath searches into that representation 
    117             raise UnsupportedElementError('This PDP implementation does ' 
    118                                           'not support <AttributeSelector> ' 
    119                                           'elements') 
     206            # Nb. Evaluation is not currently supported.  This will require that 
     207            # the request provide a reference to it's XML representation and an  
     208            # abstraction of the XML parser for executing XPath searches into  
     209            # that representation 
     210            requestAttributeValues = self.attributeSelector.evaluate(context) 
    120211        else: 
    121             _attributeMatch = lambda requestChildAttribute: ( 
    122                 matchFunc.evaluate(matchAttributeValue,  
    123                                 requestChildAttribute.attributeValue.value) 
    124             ) 
    125      
     212            raise XacmlContextError('No attribute designator or selector set ' 
     213                                    'for Target Match element %r with MatchId ' 
     214                                    '= %r and attributeValue = %r' % 
     215                                    (self.__class__.ELEMENT_LOCAL_NAME, 
     216                                     self.matchId, 
     217                                     self.attributeValue)) 
     218             
    126219        # Iterate through each attribute in the request in turn matching it 
    127220        # against the target using the generated _attributeMatch function 
     
    134227        # error occurs.  In this case the top-level PDP exception handling  
    135228        # block will catch it and set an overall decision of INDETERMINATE 
    136         attrMatchStatusValues = [False]*len(requestChild.attributes) 
    137          
    138         for attribute, attrMatchStatus in zip(requestChild.attributes,  
    139                                               attrMatchStatusValues): 
    140             attrMatchStatus = _attributeMatch(attribute) 
    141             if attrMatchStatus == True: 
     229        attrMatchStatusValues = [False]*len(requestAttributeValues) 
     230        matchFunction = self.function 
     231        matchAttributeValue = self.attributeValue 
     232         
     233        for i, requestAttributeValue in enumerate(requestAttributeValues): 
     234             
     235            attrMatchStatusValues[i] = matchFunction.evaluate( 
     236                                                         matchAttributeValue, 
     237                                                         requestAttributeValue) 
     238            if attrMatchStatusValues[i] == True: 
    142239                if log.getEffectiveLevel() <= logging.DEBUG: 
    143                     log.debug('Request attribute %r set to %r matches ' 
    144                               'target', 
    145                               attribute.attributeId, 
    146                               [a.value for a in attribute.attributeValues]) 
    147                      
    148         matchStatus = all(attrMatchStatusValues) 
     240                    log.debug('Target attribute value %r matches request ' 
     241                              'attribute value %r matches using match ' 
     242                              'function Id %r', 
     243                              matchAttributeValue, 
     244                              requestAttributeValue, 
     245                              self.matchId) 
     246         
     247        # Need check for len() because any([]) yields True!            
     248        matchStatus = (len(attrMatchStatusValues) > 0 and  
     249                       all(attrMatchStatusValues)) 
    149250         
    150251        return matchStatus 
  • TI12-security/trunk/NDG_XACML/ndg/xacml/core/rule.py

    r6806 r6807  
    1010__contact__ = "Philip.Kershaw@stfc.ac.uk" 
    1111__revision__ = "$Id: $" 
    12 from ndg.xacml.utils import TypedList 
     12import logging 
     13log = logging.getLogger(__name__) 
     14 
    1315from ndg.xacml.core import XacmlCoreBase 
    1416from ndg.xacml.core.target import Target 
    1517from ndg.xacml.core.condition import Condition 
     18from ndg.xacml.core.context.result import Decision 
    1619 
    1720 
     
    8386                                 '%r instead' % (Effect.TYPES, value)) 
    8487             
    85         return self.__value == value         
     88        return self.__value == value  
     89     
     90    def __nonzero__(self): 
     91        """Boolean evaluation of a rule effect - True = Allow; False = Deny 
     92        """ 
     93        return self.__value == Effect.PERMIT_STR        
    8694 
    8795 
     
    203211        @rtype:  
    204212        """ 
    205         log.debug('Evaluating rule %r ...', rule.id) 
    206          
    207         if not self.matchTarget(self.target, request): 
    208             log.debug('No match to request context for target in rule ' 
    209                       '%r', self.id) 
    210             #ruleStatusValues[i] = True 
    211             continue    
    212          
    213         # Apply the condition 
    214         ruleStatusValues[i] = self.condition.evaluate() 
    215  
     213        log.debug('Evaluating rule %r ...', self.id) 
     214         
     215        # Default to indeterminate 
     216        decision = Decision() 
     217         
     218        # Check for a rule target 
     219        if self.target is not None: 
     220            targetMatch = self.target.match(context) 
     221            if targetMatch: 
     222                log.debug('Match to request context for target in rule ' 
     223                          '%r', self.id) 
     224        else: 
     225            log.debug('No target set in rule %r', self.id) 
     226            targetMatch = True 
     227   
     228        if not targetMatch: 
     229            decision = Decision.NOT_APPLICABLE 
     230            return decision 
     231         
     232        # Apply the condition if present 
     233        if self.condition is not None: 
     234            conditionStatus = self.condition.evaluate(context) 
     235        else: 
     236            # No condition set to True - 7.8 in spec.: 
     237            # 
     238            # The condition value SHALL be "True" if the <Condition> element is  
     239            # absent 
     240            conditionStatus = True 
     241             
     242        if conditionStatus: 
     243            decision = Decision(decision=self.effect.value) 
     244             
     245        return decision 
     246 
  • TI12-security/trunk/NDG_XACML/ndg/xacml/core/target.py

    r6806 r6807  
    117117                requestElem = getattr(request, attrName)  
    118118                for requestSubElem in requestElem: 
    119                     if self._matchChild(targetSubElem, requestSubElem): 
     119                    if self._matchChild(targetSubElem, request): 
    120120                        # Within the list of e.g. subjects if one subject  
    121121                        # matches then this counts as a subject match overall  
     
    128128        return all(statusValues) 
    129129     
    130     def _matchChild(self, targetChild, requestChild): 
     130    def _matchChild(self, targetChild, request): 
    131131        """Match a request child element (a <Subject>, <Resource>, <Action> or  
    132132        <Environment>) with the corresponding target's <Subject>, <Resource>,  
     
    163163        # 
    164164        # e.g. for <SubjectMatch>es in <Subject> ... 
    165         for childMatch, matchStatus in zip(targetChild.matches,  
    166                                            matchStatusValues): 
    167              
    168             matchStatusValues[i] = childMatch.evaluate(requestChild) 
     165        for i, childMatch in enumerate(targetChild.matches): 
     166            matchStatusValues[i] = childMatch.evaluate(request) 
    169167             
    170168        # Any match => overall match       
    171169        return any(matchStatusValues) 
    172      
    173                  
    174     def attributeDesignatorMatchFuncFactory(self, 
    175                                             matchFunc, 
    176                                             matchAttributeValue,  
    177                                             attributeDesignator): 
    178         """Define a match function to match a given request attribute against  
    179         the input attribute value and AttributeDesignator defined in a policy  
    180         target 
    181         """             
    182         attributeId = attributeDesignator.attributeId 
    183         dataType = attributeDesignator.dataType 
    184          
    185         # Issuer is an optional match - see core spec. 7.2.4 
    186         issuer = attributeDesignator.issuer 
    187         if issuer is not None: 
    188             # Issuer found - set lambda to match this against the  
    189             # issuer setting in the request 
    190             _issuerMatch = lambda requestChildIssuer: ( 
    191                                                 issuer == requestChildIssuer) 
    192         else: 
    193             # No issuer set - lambda returns True regardless 
    194             _issuerMatch = lambda requestChildIssuer: True 
    195          
    196          
    197         _attributeMatch = lambda attribute: ( 
    198             any([matchFunc.evaluate(matchAttributeValue, attrVal)  
    199                  for attrVal in attribute.attributeValues]) and 
    200             attribute.attributeId == attributeId and 
    201             attribute.dataType == dataType and 
    202             _issuerMatch(attribute.issuer) 
    203         ) 
    204          
    205         return _attributeMatch 
     170 
  • TI12-security/trunk/NDG_XACML/ndg/xacml/test/ndg1.xml

    r6805 r6807  
    2020                        AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id" 
    2121                        DataType="http://www.w3.org/2001/XMLSchema#anyURI"/> 
    22                     <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#anyURI">^http://www.localhost/.*$</AttributeValue> 
     22                    <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#anyURI">^http://localhost/.*$</AttributeValue> 
    2323                </ResourceMatch> 
    2424            </Resource> 
  • TI12-security/trunk/NDG_XACML/ndg/xacml/test/test_context.py

    r6805 r6807  
    6868         
    6969        openidSubjectAttribute.attributeId = "urn:esg:openid" 
    70         openidSubjectAttribute.dataType = \ 
    71                                     'http://www.w3.org/2001/XMLSchema#anyURI' 
    72  
    7370        AnyUriAttributeValue = attributeValueFactory( 
    7471                                    'http://www.w3.org/2001/XMLSchema#anyURI') 
     72        openidSubjectAttribute.dataType = AnyUriAttributeValue.IDENTIFIER 
     73         
    7574        openidSubjectAttribute.attributeValues.append(AnyUriAttributeValue()) 
    76      
    77         openidSubjectAttribute.attributeValues[-1].dataType = \ 
    78                                     'http://www.w3.org/2001/XMLSchema#anyURI' 
    79                                      
    80                                      
    8175        openidSubjectAttribute.attributeValues[-1].value = \ 
    8276                                    'https://my.name.somewhere.ac.uk' 
     
    8882 
    8983        roleAttribute.attributeId = "urn:ndg:security:authz:1.0:attr" 
    90         roleAttribute.dataType = 'http://www.w3.org/2001/XMLSchema#string' 
     84        roleAttribute.dataType = StringAttributeValue.IDENTIFIER 
    9185         
    9286        roleAttribute.attributeValues.append(StringAttributeValue()) 
    93         roleAttribute.attributeValues[-1].dataType = \ 
    94                                     'http://www.w3.org/2001/XMLSchema#string' 
    9587        roleAttribute.attributeValues[-1].value = 'staff'  
    9688         
     
    10698                            "urn:oasis:names:tc:xacml:1.0:resource:resource-id" 
    10799                             
    108         resourceAttribute.dataType = "http://www.w3.org/2001/XMLSchema#anyURI" 
     100        resourceAttribute.dataType = AnyUriAttributeValue.IDENTIFIER 
    109101        resourceAttribute.attributeValues.append(AnyUriAttributeValue()) 
    110102        resourceAttribute.attributeValues[-1].value = \ 
    111                                         'http://www.localhost/test_securedURI' 
     103                                            'http://localhost/test_securedURI' 
    112104 
    113105        request.resources.append(resource) 
     
    119111        actionAttribute.attributeId = \ 
    120112                                "urn:oasis:names:tc:xacml:1.0:action:action-id" 
    121         actionAttribute.dataType = "http://www.w3.org/2001/XMLSchema#string" 
     113        actionAttribute.dataType = StringAttributeValue.IDENTIFIER 
    122114        actionAttribute.attributeValues.append(StringAttributeValue()) 
    123115        actionAttribute.attributeValues[-1].value = 'read' 
Note: See TracChangeset for help on using the changeset viewer.