source: TI12-security/trunk/NDG_XACML/ndg/xacml/core/context/pdp.py @ 6777

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

Added matching functions component.

Line 
1"""NDG Security Policy Decision Point 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 logging
13log = logging.getLogger(__name__)
14
15import traceback
16
17from ndg.xacml.core.context.pdpinterface import PDPInterface
18from ndg.xacml.core.policy import Policy
19from ndg.xacml.core.context.request import Request
20from ndg.xacml.core.context.response import Response
21from ndg.xacml.core.context.result import Result, Decision
22from ndg.xacml.core.functions import FunctionMap
23from ndg.xacml.parsers import AbstractReader
24                           
25       
26class PDP(PDPInterface):
27    """A XACML Policy Decision Point implementation.  It supports the use of a
28    single policy but not policy sets
29    """
30    __slots__ = ('__policy',)
31    TARGET_CHILD_ATTRS = ('subjects', 'resources', 'actions', 'environments')
32   
33    def __init__(self, policy=None):
34        """
35        @param policy: policy object for PDP to use to apply access control
36        decisions, may be omitted.
37        @type policy: ndg.xacml.core.policy.Policy / None
38        """
39        self.__policy = None
40        if policy is not None:
41            self.policy = policy
42           
43        self.matchFunc = FunctionMap.withLoadedMap()
44
45       
46    @classmethod
47    def fromPolicySource(cls, source, readerFactory):
48        """Create a new PDP instance with a given policy
49        @param source: source for policy
50        @type source: type (dependent on the reader set, it could be for example
51        a file path string, file object, XML element instance)
52        @param readerFactory: reader factory returns the reader to use to read
53        this policy
54        @type readerFactory: ndg.xacml.parsers.AbstractReader derived type
55        """           
56        pdp = cls()
57        pdp.policy = Policy.fromSource(source, readerFactory)
58        return pdp
59   
60    @property
61    def policy(self):
62        """policy object for PDP to use to apply access control decisions"""
63        return self.__policy
64   
65    @policy.setter
66    def policy(self, value):
67        '''policy object for PDP to use to apply access control decisions'''
68        if not isinstance(value, Policy):
69            raise TypeError('Expecting %r derived type for "policy" input; got '
70                            '%r instead' % (Policy, type(value)))
71        self.__policy = value
72                   
73    def evaluate(self, request):
74        """Make an access control decision for the given request based on the
75        policy set
76       
77        @param request: XACML request context
78        @type request: ndg.xacml.core.context.request.Request
79        @return: XACML response instance
80        @rtype: ndg.xacml.core.context.response.Response
81        """
82        response = Response()
83        result = Result()
84        response.results.append(result)
85        result.decision = Decision.NOT_APPLICABLE
86       
87        if not isinstance(request, Request):
88            log.error('Expecting %r derived type for "reader" input; got '
89                      '%r instead' % Request, type(request))
90            result.decision = Decision.INDETERMINATE
91            return response
92           
93        # Exception block around all rule processing in order to set
94        # INDETERMINATE response from any exceptions raised
95        try:
96            log.debug('Checking policy target for match...')
97           
98            if not self.matchTarget(self.policy.target, request):
99                log.debug('No match for policy target setting Decision=%s',
100                          Decision.NOT_APPLICABLE_STR)
101               
102                result.decision = Decision.NOT_APPLICABLE
103                return response
104           
105            # Check rules
106            for rule in self.policy.rules:
107                log.debug('Checking policy rule %r for match...', rule.id)
108                if not self.matchTarget(rule.target, request):
109                    log.debug('No match to request context for target in rule '
110                              '%r', rule.id)
111                    continue         
112        except:
113            log.error('Exception raised evaluating request context, returning '
114                      'Decision=%s:%s', 
115                      Decision.INDETERMINATE_STR, 
116                      traceback.format_exc())
117            result.decision = Decision.INDETERMINATE
118           
119        return response
120           
121    def matchTarget(self, target, request):
122        """Generic method to match a <Target> element to the request context
123       
124        @param target: XACML target element
125        @type target: ndg.xacml.core.target.Target
126        @param request: XACML request context
127        @type request: ndg.xacml.core.context.request.Request
128        @return: True if request context matches the given target,
129        False otherwise
130        @rtype: bool
131        """
132        if target is None:
133            log.debug('No target set so no match with request context')
134            return False
135       
136        # From section 5.5 of the XACML 2.0 Core Spec:
137        #
138        # For the parent of the <Target> element to be applicable to the
139        # decision request, there MUST be at least one positive match between
140        # each section of the <Target> element and the corresponding section of
141        # the <xacml-context:Request> element.       
142        for i in self.__class__.TARGET_CHILD_ATTRS:
143            for targetChild in getattr(target, i):
144                for requestChild in getattr(request, i):
145                    if self.matchTargetChild(targetChild, requestChild):
146                        return True
147 
148        return False
149   
150    def matchTargetChild(self, targetChild, requestChild):
151        """Match a child (Subject, Resource, Action or Environment) from the
152        request context with a given target's child
153       
154        @param targetChild: Target Subject, Resource, Action or Environment
155        object
156        @type targetChild: ndg.xacml.core.TargetChildBase
157        @param requestChild: Request Subject, Resource, Action or Environment
158        object
159        @type requestChild: ndg.xacml.core.context.RequestChildBase
160        @return: True if request context matches something in the target
161        @rtype: bool
162        @raise NotImplementedError: AttributeSelector processing is not
163        currently supported.  If an AttributeSelector is found in the policy,
164        this exception will be raised.
165        """
166        if targetChild is None:
167            # Default if target child is not set is to match all children
168            return True
169       
170        for childMatch in targetChild.matches:
171            # Get the match function from the Match ID
172            matchFunc = self.matchFunc.get(childMatch.matchId)
173            if matchFunc is NotImplemented:
174                raise NotImplementedError('No match function implemented for '
175                                          'MatchId="%s"' % childMatch.matchId)
176               
177            if matchFunc is None:
178                raise Exception('Match function namespace %r is not recognised' 
179                                % childMatch.matchId)
180               
181            matchAttributeValue = childMatch.attributeValue.value
182           
183            # Create a match function based on the presence or absence of an
184            # AttributeDesignator or AttributeSelector
185            if childMatch.attributeDesignator is not None:
186                attributeId = childMatch.attributeDesignator.attributeId
187                dataType = childMatch.attributeDesignator.dataType
188               
189                _attributeMatch = lambda requestChildAttribute: (
190                    matchFunc(matchAttributeValue, 
191                              requestChildAttribute.attributeValue.value) and
192                    requestChildAttribute.attributeId == attributeId and
193                    requestChildAttribute.dataType == dataType
194                )
195               
196            elif childMatch.attributeSelector is not None:
197                # Nb. This will require that the request provide a reference to
198                # it's XML representation and an abstraction of the XML parser
199                # for executing XPath searches into that representation
200                raise NotImplementedError('This PDP implementation does not '
201                                          'support <AttributeSelector> '
202                                          'elements')
203            else:
204                _attributeMatch = lambda requestChildAttribute: (
205                    matchFunc(matchAttributeValue, 
206                              requestChildAttribute.attributeValue.value)
207                )
208               
209            for attribute in requestChild.attributes:
210                if _attributeMatch(attribute):
211                    return True
212                   
213        return False
214
Note: See TracBrowser for help on using the repository browser.