1 | """NDG Security Match type definition |
---|
2 | |
---|
3 | NERC 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$" |
---|
12 | import logging |
---|
13 | log = logging.getLogger(__name__) |
---|
14 | |
---|
15 | from ndg.xacml.core import XacmlCoreBase |
---|
16 | from ndg.xacml.core.attributevalue import AttributeValue |
---|
17 | from ndg.xacml.core.attributedesignator import AttributeDesignator |
---|
18 | from ndg.xacml.core.attributeselector import AttributeSelector |
---|
19 | from ndg.xacml.core.functions import FunctionMap |
---|
20 | from ndg.xacml.core.context.exceptions import XacmlContextError |
---|
21 | from ndg.xacml.core.exceptions import (UnsupportedStdFunctionError, |
---|
22 | UnsupportedFunctionError) |
---|
23 | |
---|
24 | |
---|
25 | class MatchBase(XacmlCoreBase): |
---|
26 | """Base class for representation of SubjectMatch, ResourceMatch, |
---|
27 | ActionMatch and EnvironmentMatch Target elements |
---|
28 | |
---|
29 | @cvar ELEMENT_LOCAL_NAME: XML Local Name of this element |
---|
30 | @type ELEMENT_LOCAL_NAME: string |
---|
31 | |
---|
32 | @cvar MATCH_ID_ATTRIB_NAME: XML attribute name for match ID |
---|
33 | @type MATCH_ID_ATTRIB_NAME: string |
---|
34 | |
---|
35 | @cvar ATTRIBUTE_VALUE_ELEMENT_LOCAL_NAME: XML Local Name of attribute value |
---|
36 | child element |
---|
37 | @type ATTRIBUTE_VALUE_ELEMENT_LOCAL_NAME: string |
---|
38 | |
---|
39 | @ivar __attributeValue: attribute value associated with this match |
---|
40 | @type __attributeValue: ndg.xacml.core.attributevalue.AttributeValue |
---|
41 | @ivar __attributeDesignator: attribute designator - only a designator or |
---|
42 | selector may be set for a given instance not both |
---|
43 | @type __attributeDesignator: ndg.xacml.core.attributedesignator.AttributeDesignator |
---|
44 | @ivar __attributeSelector: attribute selector - only a designator or |
---|
45 | selector may be set for a given instance not both |
---|
46 | @type __attributeSelector: ndg.xacml.core.attributeselector.AttributeSelector |
---|
47 | @ivar __matchId: match identifier |
---|
48 | @type __matchId: NoneType / basestring |
---|
49 | @ivar __function: function to be applied |
---|
50 | @type __function: ndg.xacml.core.functions.AbstractFunction derived type |
---|
51 | @ivar __functionMap: function mapping object to map URNs to function class |
---|
52 | implementations |
---|
53 | @type __functionMap: ndg.xacml.core.functions.FunctionMap |
---|
54 | @ivar __loadFunctionFromId: boolean determines whether or not to load |
---|
55 | function classes for given function URN in functionId set property method |
---|
56 | @type __loadFunctionFromId: bool |
---|
57 | """ |
---|
58 | ELEMENT_LOCAL_NAME = None |
---|
59 | MATCH_ID_ATTRIB_NAME = 'MatchId' |
---|
60 | ATTRIBUTE_VALUE_ELEMENT_LOCAL_NAME = 'AttributeValue' |
---|
61 | |
---|
62 | __slots__ = ( |
---|
63 | '__attributeValue', |
---|
64 | '__attributeDesignator', |
---|
65 | '__attributeSelector', |
---|
66 | '__matchId', |
---|
67 | '__function', |
---|
68 | '__functionMap', |
---|
69 | '__loadFunctionFromId', |
---|
70 | ) |
---|
71 | |
---|
72 | def __init__(self): |
---|
73 | """Initial attributes corresponding to the equivalent XACML schema type |
---|
74 | and also create a function map to map functions from MatchIds |
---|
75 | """ |
---|
76 | self.__attributeValue = None |
---|
77 | |
---|
78 | # Either/or in schema |
---|
79 | self.__attributeDesignator = None |
---|
80 | self.__attributeSelector = None |
---|
81 | |
---|
82 | self.__matchId = None |
---|
83 | |
---|
84 | self.__function = None |
---|
85 | self.__functionMap = FunctionMap() |
---|
86 | self.__loadFunctionFromId = True |
---|
87 | |
---|
88 | @property |
---|
89 | def attributeValue(self): |
---|
90 | """Match attribute value |
---|
91 | |
---|
92 | @return: attribute value |
---|
93 | @rtype: ndg.xacml.core.attributevalue.Attribute""" |
---|
94 | return self.__attributeValue |
---|
95 | |
---|
96 | @attributeValue.setter |
---|
97 | def attributeValue(self, value): |
---|
98 | """Set match attribute value. |
---|
99 | @param value: attribute value |
---|
100 | @type value: ndg.xacml.core.attributevalue.AttributeValue |
---|
101 | @raise TypeError: incorrect type set |
---|
102 | """ |
---|
103 | if not isinstance(value, AttributeValue): |
---|
104 | raise TypeError('Expecting %r type for "matchId" ' |
---|
105 | 'attribute; got %r' % |
---|
106 | (AttributeValue, type(value))) |
---|
107 | |
---|
108 | self.__attributeValue = value |
---|
109 | |
---|
110 | @property |
---|
111 | def attributeDesignator(self): |
---|
112 | """@return: attribute designator - only a designator or |
---|
113 | selector may be set for a given instance not both |
---|
114 | @rtype: ndg.xacml.core.attributedesignator.AttributeDesignator |
---|
115 | """ |
---|
116 | return self.__attributeDesignator |
---|
117 | |
---|
118 | @attributeDesignator.setter |
---|
119 | def attributeDesignator(self, value): |
---|
120 | """Set match attribute designator. Match may have an |
---|
121 | attributeDesignator or an attributeSelector setting a designator DELETES |
---|
122 | any attributeSelector previously set |
---|
123 | |
---|
124 | @param value: attribute selector - only a designator or |
---|
125 | selector may be set for a given instance not both |
---|
126 | @type value: ndg.xacml.core.attributeselector.AttributeSelector |
---|
127 | @raise TypeError: incorrect type for input value |
---|
128 | """ |
---|
129 | if not isinstance(value, AttributeDesignator): |
---|
130 | raise TypeError('Expecting %r type for "attributeDesignator" ' |
---|
131 | 'attribute; got %r' % |
---|
132 | (AttributeDesignator, type(value))) |
---|
133 | |
---|
134 | self.__attributeDesignator = value |
---|
135 | self.__attributeSelector = None |
---|
136 | |
---|
137 | @property |
---|
138 | def attributeSelector(self): |
---|
139 | ''' |
---|
140 | @return: attribute selector |
---|
141 | @rtype: ndg.xacml.core.attributeselector.AttributeSelector |
---|
142 | ''' |
---|
143 | return self.__attributeSelector |
---|
144 | |
---|
145 | @attributeSelector.setter |
---|
146 | def attributeSelector(self, value): |
---|
147 | """Set match attribute selector. Match may have an |
---|
148 | attributeDesignator or an attributeSelector setting a selector DELETES |
---|
149 | any attributeDesignator previously set |
---|
150 | |
---|
151 | @param value: attribute selector |
---|
152 | @type value: ndg.xacml.core.attributeselector.AttributeSelector |
---|
153 | """ |
---|
154 | if not isinstance(value, AttributeSelector): |
---|
155 | raise TypeError('Expecting %r type for "matchId" ' |
---|
156 | 'attribute; got %r' % |
---|
157 | (AttributeSelector, type(value))) |
---|
158 | |
---|
159 | self.__attributeSelector = value |
---|
160 | self.__attributeDesignator = None |
---|
161 | |
---|
162 | def _getMatchId(self): |
---|
163 | """Match identifier for match function |
---|
164 | @return: match identifier |
---|
165 | @rtype: NoneType / basestring |
---|
166 | """ |
---|
167 | return self.__matchId |
---|
168 | |
---|
169 | def _setMatchId(self, value): |
---|
170 | """Match identifier for match function |
---|
171 | @param value: match identifier |
---|
172 | @type value: basestring |
---|
173 | @raise TypeError: if incorrect input type |
---|
174 | """ |
---|
175 | if not isinstance(value, basestring): |
---|
176 | raise TypeError('Expecting string type for "matchId" ' |
---|
177 | 'attribute; got %r' % type(value)) |
---|
178 | |
---|
179 | self.__matchId = value |
---|
180 | |
---|
181 | # Also retrieve function for this match ID if a map has been set |
---|
182 | if self.__loadFunctionFromId: |
---|
183 | self.setFunctionFromMap(self.__functionMap) |
---|
184 | |
---|
185 | matchId = property(_getMatchId, _setMatchId, None, |
---|
186 | "Match identifier for match function") |
---|
187 | |
---|
188 | @property |
---|
189 | def loadFunctionFromId(self): |
---|
190 | """Set to False to stop the functionId property set method automatically |
---|
191 | trying to load the corresponding function for the given functionId |
---|
192 | |
---|
193 | @return: boolean determines whether or not to load |
---|
194 | function classes for given function URN in functionId set property |
---|
195 | method |
---|
196 | @rtype: bool |
---|
197 | """ |
---|
198 | return self.__loadFunctionFromId |
---|
199 | |
---|
200 | @loadFunctionFromId.setter |
---|
201 | def loadFunctionFromId(self, value): |
---|
202 | """ |
---|
203 | @param value: boolean determines whether or not to load |
---|
204 | function classes for given function URN in functionId set property |
---|
205 | method |
---|
206 | @type value: bool |
---|
207 | """ |
---|
208 | if not isinstance(value, bool): |
---|
209 | raise TypeError('Expecting %r type for "loadFunctionFromId" ' |
---|
210 | 'attribute; got %r' % (bool, type(value))) |
---|
211 | |
---|
212 | self.__loadFunctionFromId = value |
---|
213 | |
---|
214 | def setFunctionFromMap(self, functionMap): |
---|
215 | """Set the function from a function map - a dictionary of function ID to |
---|
216 | function mappings. The function is looked up based on the "functionId" |
---|
217 | attribute. This method is automatically called when the functionId set |
---|
218 | property method is invoked. To switch off this behaviour set |
---|
219 | |
---|
220 | loadFunctionFromId = False |
---|
221 | |
---|
222 | @param functionMap: mapping of function URNs to function classes |
---|
223 | @type functionMap: dict like object |
---|
224 | @raise UnsupportedStdFunctionError: policy references a function type |
---|
225 | which is in the XACML spec. but is not supported by this implementation |
---|
226 | @raise UnsupportedFunctionError: policy references a function type which |
---|
227 | is not supported by this implementation |
---|
228 | """ |
---|
229 | if self.matchId is None: |
---|
230 | raise AttributeError('"functionId" attribute must be set in order ' |
---|
231 | 'to retrieve the required function') |
---|
232 | |
---|
233 | # Get function class for this <Apply> statement |
---|
234 | functionClass = functionMap.get(self.matchId) |
---|
235 | if functionClass is NotImplemented: |
---|
236 | raise UnsupportedStdFunctionError('No match function class ' |
---|
237 | 'implemented for MatchId="%s"' % |
---|
238 | self.matchId) |
---|
239 | elif functionClass is None: |
---|
240 | raise UnsupportedFunctionError('<Apply> function namespace %r is ' |
---|
241 | 'not recognised' % |
---|
242 | self.matchId) |
---|
243 | |
---|
244 | self.__function = functionClass() |
---|
245 | |
---|
246 | @property |
---|
247 | def functionMap(self): |
---|
248 | """functionMap object for PDP to retrieve functions from given XACML |
---|
249 | function URNs |
---|
250 | @return: function mapping object to map URNs to function |
---|
251 | class implementations |
---|
252 | @rtype: ndg.xacml.core.functions.FunctionMap |
---|
253 | """ |
---|
254 | return self.__functionMap |
---|
255 | |
---|
256 | @functionMap.setter |
---|
257 | def functionMap(self, value): |
---|
258 | '''functionMap object for PDP to retrieve functions from given XACML |
---|
259 | function URNs |
---|
260 | |
---|
261 | @param value: function mapping object to map URNs to function class |
---|
262 | implementations |
---|
263 | @type value: ndg.xacml.core.functions.FunctionMap |
---|
264 | @raise TypeError: raise if input value is incorrect type |
---|
265 | ''' |
---|
266 | if not isinstance(value, FunctionMap): |
---|
267 | raise TypeError('Expecting %r derived type for "functionMap" ' |
---|
268 | 'input; got %r instead' % (FunctionMap, |
---|
269 | type(value))) |
---|
270 | self.__functionMap = value |
---|
271 | |
---|
272 | @property |
---|
273 | def function(self): |
---|
274 | """Function for this <Apply> instance |
---|
275 | @return: function to be applied |
---|
276 | @rtype: ndg.xacml.core.functions.AbstractFunction derived type |
---|
277 | """ |
---|
278 | return self.__function |
---|
279 | |
---|
280 | def evaluate(self, context): |
---|
281 | """Evaluate the match object against the relevant element in the request |
---|
282 | context |
---|
283 | |
---|
284 | @param context: the request context |
---|
285 | @type context: ndg.xacml.core.context.request.Request |
---|
286 | @return: match status |
---|
287 | @rtype: bool |
---|
288 | """ |
---|
289 | |
---|
290 | # Create a match function based on the presence or absence of an |
---|
291 | # AttributeDesignator or AttributeSelector |
---|
292 | if self.attributeDesignator is not None: |
---|
293 | requestAttributeValues = self.attributeDesignator.evaluate(context) |
---|
294 | |
---|
295 | elif self.attributeSelector is not None: |
---|
296 | # Nb. Evaluation is not currently supported. This will require that |
---|
297 | # the request provide a reference to it's XML representation and an |
---|
298 | # abstraction of the XML parser for executing XPath searches into |
---|
299 | # that representation |
---|
300 | requestAttributeValues = self.attributeSelector.evaluate(context) |
---|
301 | else: |
---|
302 | raise XacmlContextError('No attribute designator or selector set ' |
---|
303 | 'for Target Match element %r with MatchId ' |
---|
304 | '= %r and attributeValue = %r' % |
---|
305 | (self.__class__.ELEMENT_LOCAL_NAME, |
---|
306 | self.matchId, |
---|
307 | self.attributeValue)) |
---|
308 | |
---|
309 | # Iterate through each attribute in the request in turn matching it |
---|
310 | # against the target using the generated _attributeMatch function |
---|
311 | # |
---|
312 | # Any Match element NOT matching will result in an overall status of |
---|
313 | # no match. |
---|
314 | # |
---|
315 | # Continue iterating through the whole list even if a False status |
---|
316 | # is found. The other attributes need to be checked in case an |
---|
317 | # error occurs. In this case the top-level PDP exception handling |
---|
318 | # block will catch it and set an overall decision of INDETERMINATE |
---|
319 | attrMatchStatusValues = [False]*len(requestAttributeValues) |
---|
320 | matchFunction = self.function |
---|
321 | matchAttributeValue = self.attributeValue |
---|
322 | |
---|
323 | for i, requestAttributeValue in enumerate(requestAttributeValues): |
---|
324 | |
---|
325 | attrMatchStatusValues[i] = matchFunction.evaluate( |
---|
326 | matchAttributeValue, |
---|
327 | requestAttributeValue) |
---|
328 | if attrMatchStatusValues[i] == True: |
---|
329 | if log.getEffectiveLevel() <= logging.DEBUG: |
---|
330 | log.debug('Target attribute value %r matches request ' |
---|
331 | 'attribute value %r matches using match ' |
---|
332 | 'function Id %r', |
---|
333 | matchAttributeValue, |
---|
334 | requestAttributeValue, |
---|
335 | self.matchId) |
---|
336 | |
---|
337 | # Need check for len() because any([]) yields True! |
---|
338 | matchStatus = (len(attrMatchStatusValues) > 0 and |
---|
339 | all(attrMatchStatusValues)) |
---|
340 | |
---|
341 | return matchStatus |
---|
342 | |
---|
343 | |
---|
344 | class SubjectMatch(MatchBase): |
---|
345 | "Subject Match Type" |
---|
346 | ELEMENT_LOCAL_NAME = 'SubjectMatch' |
---|
347 | ATTRIBUTE_DESIGNATOR_ELEMENT_LOCAL_NAME = 'SubjectAttributeDesignator' |
---|
348 | |
---|
349 | |
---|
350 | class ResourceMatch(MatchBase): |
---|
351 | "Resource Match" |
---|
352 | ELEMENT_LOCAL_NAME = 'ResourceMatch' |
---|
353 | ATTRIBUTE_DESIGNATOR_ELEMENT_LOCAL_NAME = 'ResourceAttributeDesignator' |
---|
354 | |
---|
355 | |
---|
356 | class ActionMatch(MatchBase): |
---|
357 | "Action match" |
---|
358 | ELEMENT_LOCAL_NAME = 'ActionMatch' |
---|
359 | ATTRIBUTE_DESIGNATOR_ELEMENT_LOCAL_NAME = 'ActionAttributeDesignator' |
---|
360 | |
---|
361 | |
---|
362 | class EnvironmentMatch(MatchBase): |
---|
363 | "Environment Match" |
---|
364 | ELEMENT_LOCAL_NAME = 'EnvironmentMatch' |
---|
365 | ATTRIBUTE_DESIGNATOR_ELEMENT_LOCAL_NAME = 'EnvironmentAttributeDesignator' |
---|