source: TI12-security/trunk/NDG_XACML/ndg/xacml/core/rule.py @ 6822

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

Added rule combining algorithm module with class factory and implementation for permit overrides algorithm.

Line 
1"""NDG Security Rule type definition
2
3NERC DataGrid Project
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    DENY_STR = 'Deny'
25    PERMIT_STR = 'Permit'
26    TYPES = (DENY_STR, PERMIT_STR)
27    __slots__ = ('__value',)
28   
29    def __init__(self, effect=DENY_STR):
30        self.__value = None
31        self.value = effect
32
33    def __getstate__(self):
34        '''Enable pickling'''
35        _dict = {}
36        for attrName in Effect.__slots__:
37            # Ugly hack to allow for derived classes setting private member
38            # variables
39            if attrName.startswith('__'):
40                attrName = "_Effect" + attrName
41               
42            _dict[attrName] = getattr(self, attrName)
43           
44        return _dict
45 
46    def __setstate__(self, attrDict):
47        '''Enable pickling'''
48        for attrName, val in attrDict.items():
49            setattr(self, attrName, val)
50           
51    def _setValue(self, value):
52        if isinstance(value, Effect):
53            # Cast to string
54            value = str(value)
55           
56        elif not isinstance(value, basestring):
57            raise TypeError('Expecting string or Effect instance for '
58                            '"value" attribute; got %r instead' % type(value))
59           
60        if value not in self.__class__.TYPES:
61            raise AttributeError('Permissable effect types are %r; got '
62                                 '%r instead' % (Effect.TYPES, value))
63        self.__value = value
64       
65    def _getValue(self):
66        return self.__value
67   
68    value = property(fget=_getValue, fset=_setValue, doc="Effect value")
69   
70    def __str__(self):
71        return self.__value
72
73    def __eq__(self, effect):
74        if isinstance(effect, Effect):
75            # Cast to string
76            value = effect.value
77           
78        elif isinstance(effect, basestring):
79            value = effect
80           
81        else:
82            raise TypeError('Expecting string or Effect instance for '
83                            'input effect value; got %r instead' % type(value))
84           
85        if value not in self.__class__.TYPES:
86            raise AttributeError('Permissable effect types are %r; got '
87                                 '%r instead' % (Effect.TYPES, value))
88           
89        return self.__value == value
90   
91    def __nonzero__(self):
92        """Boolean evaluation of a rule effect - True = Allow; False = Deny
93        """
94        return self.__value == Effect.PERMIT_STR       
95
96
97class PermitEffect(Effect):
98    """Permit authorisation Effect"""
99    __slots__ = ()
100
101    def __init__(self):
102        super(PermitEffect, self).__init__(Effect.PERMIT_STR)
103       
104    def _setValue(self): 
105        raise AttributeError("can't set attribute")
106
107
108class DenyEffect(Effect):
109    """Deny authorisation Effect"""
110    __slots__ = ()
111   
112    def __init__(self):
113        super(DenyEffect, self).__init__(Effect.DENY_STR)
114       
115    def _setValue(self, value): 
116        raise AttributeError("can't set attribute")
117
118# Add instances of each for convenience
119Effect.PERMIT = PermitEffect()
120Effect.DENY = DenyEffect()
121
122
123class Rule(XacmlCoreBase):
124    """XACML Policy Rule"""
125    ELEMENT_LOCAL_NAME = 'Rule'
126    RULE_ID_ATTRIB_NAME = 'RuleId'
127    EFFECT_ATTRIB_NAME = 'Effect'
128   
129    DESCRIPTION_LOCAL_NAME = 'Description'
130   
131    __slots__ = (
132        '__target', 
133        '__condition', 
134        '__description', 
135        '__id', 
136        '__effect'
137    )
138   
139    def __init__(self):
140        self.__id = None
141        self.__effect = None
142        self.__target = None
143        self.__condition = None
144       
145    @property
146    def target(self):
147        "Rule target"
148        return self.__target
149   
150    @target.setter
151    def target(self, value):
152        if not isinstance(value, Target):
153            raise TypeError('Expecting %r type for "id" '
154                            'attribute; got %r' % (Target, type(value))) 
155        self.__target = value
156           
157    @property
158    def condition(self):
159        "rule condition"
160        return self.__condition
161   
162    @condition.setter
163    def condition(self, value):
164        if not isinstance(value, Condition):
165            raise TypeError('Expecting %r type for "id" '
166                            'attribute; got %r' % (Condition, type(value))) 
167           
168        self.__condition = value
169             
170    def _get_id(self):
171        return self.__id
172
173    def _set_id(self, value):
174        if not isinstance(value, basestring):
175            raise TypeError('Expecting %r type for "id" '
176                            'attribute; got %r' % (basestring, type(value)))
177         
178        self.__id = value   
179
180    id = property(_get_id, _set_id, None, "Rule identifier attribute") 
181     
182    def _get_effect(self):
183        return self.__effect
184
185    def _set_effect(self, value):
186        if not isinstance(value, Effect):
187            raise TypeError('Expecting %r type for "effect" '
188                            'attribute; got %r' % (Effect, type(value)))
189           
190        self.__effect = value   
191
192    effect = property(_get_effect, _set_effect, None, 
193                      "Rule effect attribute") 
194
195    def _getDescription(self):
196        return self.__description
197
198    def _setDescription(self, value):
199        if not isinstance(value, basestring):
200            raise TypeError('Expecting string type for "description" '
201                            'attribute; got %r' % type(value))
202        self.__description = value
203
204    description = property(_getDescription, _setDescription, 
205                           doc="Rule Description text")
206   
207    def evaluate(self, context):
208        """Evaluate a rule
209        @param context: the request context
210        @type context: ndg.xacml.core.request.Request
211        @return: result of the evaluation - the decision for this rule
212        @rtype: ndg.xacml.core.context.result.Decision
213        """
214       
215        # Place exception block to enable rule combining algorithm which calls
216        # this method to correctly handle Indeterminate results
217        try:
218            log.debug('Evaluating rule %r ...', self.id)
219           
220            # Default to indeterminate
221            decision = Decision()
222           
223            # Check for a rule target
224            if self.target is not None:
225                targetMatch = self.target.match(context)
226                if targetMatch:
227                    log.debug('Match to request context for target in rule '
228                              '%r', self.id)
229            else:
230                log.debug('No target set in rule %r', self.id)
231                targetMatch = True
232     
233            if not targetMatch:
234                decision = Decision.NOT_APPLICABLE
235                return decision
236           
237            # Apply the condition if present
238            if self.condition is not None:
239                conditionStatus = self.condition.evaluate(context)
240            else:
241                # No condition set to True - 7.8 in spec.:
242                #
243                # The condition value SHALL be "True" if the <Condition> element
244                # is absent
245                conditionStatus = True
246               
247            if conditionStatus:
248                decision = Decision(decision=self.effect.value)
249               
250            return decision
251       
252        except Exception:
253            log.error('Error occured evaluating rule %r, returning '
254                      'Indeterminate result to caller: %s',
255                      self.id,
256                      traceback.format_exc())
Note: See TracBrowser for help on using the repository browser.