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

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

Moved PDP evaluate content into Policy.evaluate

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                log.debug('No match to request context for target in rule '
235                          '%r returning NotApplicable status', self.id)
236                decision = Decision.NOT_APPLICABLE
237                return decision
238           
239            # Apply the condition if present
240            if self.condition is not None:
241                conditionStatus = self.condition.evaluate(context)
242            else:
243                # No condition set to True - 7.8 in spec.:
244                #
245                # The condition value SHALL be "True" if the <Condition> element
246                # is absent
247                log.debug('No condition set for rule %r: setting condition '
248                          'status True', self.id)
249                conditionStatus = True
250               
251            if conditionStatus:
252                decision = Decision(decision=self.effect.value)
253               
254            return decision
255       
256        except Exception:
257            log.error('Error occurred evaluating rule %r, returning '
258                      'Indeterminate result to caller: %s',
259                      self.id,
260                      traceback.format_exc())
261            return Decision.INDETERMINATE
Note: See TracBrowser for help on using the repository browser.