source: TI12-security/trunk/ndg_xacml/ndg/xacml/core/rule.py @ 7365

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/ndg_xacml/ndg/xacml/core/rule.py@7365
Revision 7365, 12.7 KB checked in by pjkersha, 10 years ago (diff)

Incomplete - task 2: XACML-Security Integration

  • added identifier URN constants
  • Property svn:keywords set to Id
Line 
1"""NDG Security Rule type definition
2
3NERC DataGrid
4"""
5__author__ = "P J Kershaw"
6__date__ = "25/02/10"
7__copyright__ = "(C) 2010 Science and Technology Facilities Council"
8__contact__ = "Philip.Kershaw@stfc.ac.uk"
9__license__ = "BSD - see LICENSE file in top-level directory"
10__contact__ = "Philip.Kershaw@stfc.ac.uk"
11__revision__ = "$Id$"
12import traceback
13import logging
14log = logging.getLogger(__name__)
15
16from ndg.xacml.core import XacmlCoreBase
17from ndg.xacml.core.target import Target
18from ndg.xacml.core.condition import Condition
19from ndg.xacml.core.context.result import Decision
20
21
22class Effect(object):
23    """Rule Effect
24   
25    @cvar PERMIT_STR: permit decision string
26    @type PERMIT_STR: string
27   
28    @cvar DENY_STR: deny decision string
29    @type DENY_STR: string
30   
31    @cvar TYPES: list of valid effect strings
32    @type TYPES: tuple
33   
34    @ivar value: effect value
35    @type value: string
36    """
37    DENY_STR = 'Deny'
38    PERMIT_STR = 'Permit'
39    TYPES = (DENY_STR, PERMIT_STR)
40    __slots__ = ('__value',)
41   
42    def __init__(self, effect=DENY_STR):
43        """@param effect: initialise effect value, defaults to deny
44        @type effect: basestring / ndg.xacml.core.rule.Effect
45        """
46        self.__value = None
47        self.value = effect
48
49    def __getstate__(self):
50        '''Enable pickling
51       
52        @return: class instance attributes dictionary
53        @rtype: dict
54        '''
55        _dict = {}
56        for attrName in Effect.__slots__:
57            # Ugly hack to allow for derived classes setting private member
58            # variables
59            if attrName.startswith('__'):
60                attrName = "_Effect" + attrName
61               
62            _dict[attrName] = getattr(self, attrName)
63           
64        return _dict
65 
66    def __setstate__(self, attrDict):
67        '''Enable pickling
68       
69        @param attrDict: class instance attributes dictionary
70        @type attrDict: dict
71        '''
72        for attrName, val in attrDict.items():
73            setattr(self, attrName, val)
74           
75    def _setValue(self, value):
76        """Set effect value
77       
78        @param value: effect value - constrained vocabulary to Effect.TYPES
79        @type value: string or ndg.xacml.core.rule.Effect
80        @raise AttributeError: invalid decision string value input
81        @raise TypeError: invalid type for input decision value
82        """
83        if isinstance(value, Effect):
84            # Cast to string
85            value = str(value)
86           
87        elif not isinstance(value, basestring):
88            raise TypeError('Expecting string or Effect instance for '
89                            '"value" attribute; got %r instead' % type(value))
90           
91        if value not in self.__class__.TYPES:
92            raise AttributeError('Permissable effect types are %r; got '
93                                 '%r instead' % (Effect.TYPES, value))
94        self.__value = value
95       
96    def _getValue(self):
97        """Get effect value
98       
99        @return: effect value
100        @rtype: string
101        """
102        return self.__value
103   
104    value = property(fget=_getValue, fset=_setValue, doc="Effect value")
105   
106    def __str__(self):
107        """represent decision as a string
108       
109        @return: decision value
110        @rtype: string
111        """
112        return self.__value
113
114    def __eq__(self, effect):
115        """
116        @param effect: effect value to compare with self's
117        @type effect: string or ndg.xacml.core.rule.Effect
118        @return: True if the decision values match, False otherwise
119        @rtype: bool
120        @raise AttributeError: invalid decision string value input
121        @raise TypeError: invalid type for input decision value
122        """
123        if isinstance(effect, Effect):
124            # Cast to string
125            value = effect.value
126           
127        elif isinstance(effect, basestring):
128            value = effect
129           
130        else:
131            raise TypeError('Expecting string or Effect instance for '
132                            'input effect value; got %r instead' % type(value))
133           
134        if value not in self.__class__.TYPES:
135            raise AttributeError('Permissable effect types are %r; got '
136                                 '%r instead' % (Effect.TYPES, value))
137           
138        return self.__value == value
139   
140    def __nonzero__(self):
141        """Boolean evaluation of a rule effect - True = Allow; False = Deny
142       
143        @return: True if the effect value is permit, False otherwise
144        @rtype: bool
145        """
146        return self.__value == Effect.PERMIT_STR       
147
148
149class PermitEffect(Effect):
150    """Permit authorisation Effect"""
151    __slots__ = ()
152
153    def __init__(self):
154        """Initialise set with Permit value"""
155        super(PermitEffect, self).__init__(Effect.PERMIT_STR)
156       
157    def _setValue(self, value): 
158        """Make value read-only
159        @raise AttributeError: value can't be set
160        """
161        raise AttributeError("can't set attribute")
162
163
164class DenyEffect(Effect):
165    """Deny authorisation Effect"""
166    __slots__ = ()
167   
168    def __init__(self):
169        """Initialise set with Permit value"""
170        super(DenyEffect, self).__init__(Effect.DENY_STR)
171       
172    def _setValue(self, value): 
173        """Make value read-only
174        @raise AttributeError: value can't be set
175        """
176        raise AttributeError("can't set attribute")
177
178# Add instances of each for convenience
179Effect.PERMIT = PermitEffect()
180Effect.DENY = DenyEffect()
181
182
183class Rule(XacmlCoreBase):
184    """XACML Policy Rule
185   
186    @cvar ELEMENT_LOCAL_NAME: XML local name for this element
187    @type ELEMENT_LOCAL_NAME: string
188    @cvar DESCRIPTION_LOCAL_NAME: XML local name for the description element
189    @type DESCRIPTION_LOCAL_NAME: string
190    @cvar RULE_ID_ATTRIB_NAME: rule id XML attribute name
191    @type RULE_ID_ATTRIB_NAME: string
192    @cvar EFFECT_ATTRIB_NAME: effect XML attribute name
193    @type EFFECT_ATTRIB_NAME: string
194
195    @ivar __target: rule target
196    @type __target: ndg.xacml.core.target.Target / NoneType
197    @ivar __condition: rule condition
198    @type __condition: ndg.xacml.core.condition.Condition / NoneType
199    @ivar __description: rule description text
200    @type __description: basestring / NoneType
201    @ivar __id: rule ID
202    @type __id: basestring / NoneType
203    @ivar __effect: rule effect
204    @type __effect: ndg.xacml.core.rule.Effect / NoneType
205    """
206    ELEMENT_LOCAL_NAME = 'Rule'
207    RULE_ID_ATTRIB_NAME = 'RuleId'
208    EFFECT_ATTRIB_NAME = 'Effect'
209   
210    DESCRIPTION_LOCAL_NAME = 'Description'
211   
212    __slots__ = (
213        '__target', 
214        '__condition', 
215        '__description', 
216        '__id', 
217        '__effect'
218    )
219   
220    def __init__(self):
221        """Initialise attributes"""
222        super(Rule, self).__init__()
223       
224        self.__id = None
225        self.__effect = None
226        self.__target = None
227        self.__condition = None
228       
229    @property
230    def target(self):
231        """Get Rule target
232        @return: rule target
233        @rtype: ndg.xacml.core.target import Target / NoneType
234        """
235        return self.__target
236   
237    @target.setter
238    def target(self, value):
239        """Set rule target
240        @param value: rule target
241        @type value: ndg.xacml.core.target import Target
242        @raise TypeError: incorrect type set
243        """
244        if not isinstance(value, Target):
245            raise TypeError('Expecting %r type for "id" '
246                            'attribute; got %r' % (Target, type(value))) 
247        self.__target = value
248           
249    @property
250    def condition(self):
251        """Get rule condition
252       
253        @return: rule condition
254        @rtype: ndg.xacml.core.condition.Condition / NoneType
255        """
256        return self.__condition
257   
258    @condition.setter
259    def condition(self, value):
260        """Set rule condition
261       
262        @param value: rule condition
263        @type value: ndg.xacml.core.condition.Condition
264        @raise TypeError: incorrect type set
265        """
266        if not isinstance(value, Condition):
267            raise TypeError('Expecting %r type for "id" attribute; got %r' % 
268                            (Condition, type(value))) 
269           
270        self.__condition = value
271             
272    def _get_id(self):
273        """Get rule ID
274       
275        @return: rule ID
276        @rtype: ndg.xacml.core.condition.Condition / NoneType
277        """
278        return self.__id
279
280    def _set_id(self, value):
281        """Set rule ID
282       
283        @param value: rule ID
284        @type value: basestring
285        @raise TypeError: incorrect type set
286        """
287        if not isinstance(value, basestring):
288            raise TypeError('Expecting %r type for "id" attribute; got %r' % 
289                            (basestring, type(value)))
290         
291        self.__id = value   
292
293    id = property(_get_id, _set_id, None, "Rule identifier attribute") 
294     
295    def _get_effect(self):
296        """Get rule effect
297       
298        @return: rule effect
299        @rtype: ndg.xacml.core.rule.Effect / NoneType
300        """
301        return self.__effect
302
303    def _set_effect(self, value):
304        """Set rule effect
305       
306        @param value: rule effect
307        @type value: ndg.xacml.core.rule.Effect
308        @raise TypeError: incorrect type set
309        """
310        if not isinstance(value, Effect):
311            raise TypeError('Expecting %r type for "effect" '
312                            'attribute; got %r' % (Effect, type(value)))
313           
314        self.__effect = value   
315
316    effect = property(_get_effect, _set_effect, None, 
317                      "Rule effect attribute") 
318
319    def _getDescription(self):
320        """Get rule description
321       
322        @return: rule description
323        @rtype: basestring / NoneType
324        """
325        return self.__description
326
327    def _setDescription(self, value):
328        """Set rule description
329       
330        @param value: rule description
331        @type value: basestring
332        @raise TypeError: incorrect type set
333        """
334        if not isinstance(value, basestring):
335            raise TypeError('Expecting string type for "description" '
336                            'attribute; got %r' % type(value))
337        self.__description = value
338
339    description = property(_getDescription, _setDescription, 
340                           doc="Rule Description text")
341   
342    def evaluate(self, context):
343        """Evaluate a rule
344        @param context: the request context
345        @type context: ndg.xacml.core.request.Request
346        @return: result of the evaluation - the decision for this rule
347        @rtype: ndg.xacml.core.context.result.Decision
348        """
349       
350        # Place exception block to enable rule combining algorithm which calls
351        # this method to correctly handle Indeterminate results
352        try:
353            log.debug('Evaluating rule %r ...', self.id)
354           
355            # Instantiation implicitly sets to default value of Indeterminate
356            decision = Decision()
357           
358            # Check for a rule target
359            if self.target is not None:
360                targetMatch = self.target.match(context)
361                if targetMatch:
362                    log.debug('Match to request context for target in rule '
363                              '%r', self.id)
364            else:
365                log.debug('No target set in rule %r', self.id)
366                targetMatch = True
367     
368            if not targetMatch:
369                log.debug('No match to request context for target in rule '
370                          '%r returning NotApplicable status', self.id)
371                decision = Decision.NOT_APPLICABLE
372                return decision
373           
374            # Apply the condition if present
375            if self.condition is not None:
376                conditionStatus = self.condition.evaluate(context)
377            else:
378                # No condition set to True - 7.8 in spec.:
379                #
380                # The condition value SHALL be "True" if the <Condition> element
381                # is absent
382                log.debug('No condition set for rule %r: setting condition '
383                          'status True', self.id)
384                conditionStatus = True
385               
386            # Ref. Spec. 7.9 Rule evaluation, Nb. to get this far, the target
387            # must evaluated as True
388            if conditionStatus:
389                decision = Decision(decision=self.effect.value)
390            else:
391                decision = Decision.NOT_APPLICABLE
392               
393            return decision
394       
395        except Exception, e:
396            log.error('Error occurred evaluating rule %r, returning '
397                      'Indeterminate result to caller: %s',
398                      self.id,
399                      traceback.format_exc())
400            return Decision.INDETERMINATE
Note: See TracBrowser for help on using the repository browser.