source: TI12-security/trunk/ndg_xacml/ndg/xacml/core/functions/__init__.py @ 7351

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

Incomplete - task 2: XACML-Security Integration

  • important fix to ndg.xacml.core.rule.Rule.evaluate: return NotApplicable? where Target matches but Condition does not match
  • optimised ndg.xacml.core.functions.v1.at_least_one_member_of.AtLeastOneMemberOfBase? to use set and '&' operator for this function.
  • fix to ndg.xacml.core.functions.FunctionMap?.loadFunction to catch function namespace not recognised.
  • Property svn:keywords set to Id
Line 
1"""NDG XACML package for functions
2
3NERC DataGrid
4"""
5__author__ = "P J Kershaw"
6__date__ = "26/03/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$"
12from abc import ABCMeta, abstractmethod
13from datetime import datetime, timedelta
14import traceback
15import logging
16log = logging.getLogger(__name__)
17
18from ndg.xacml.core.attributevalue import (AttributeValue, 
19                                           AttributeValueClassFactory)
20from ndg.xacml.utils import VettedDict, _isIterable
21from ndg.xacml.utils.factory import callModuleObject
22
23
24class AbstractFunction(object):
25    """Abstract Base class for all XACML matching functions
26    @cvar FUNCTION_NS: namespace for the given function
27    @type FUNCTION_NS: NoneType (must be string in derived type)
28   
29    @cvar V1_0_FUNCTION_NS: XACML 1.0 function namespace prefix
30    @type V1_0_FUNCTION_NS: string
31   
32    @cvar V2_0_FUNCTION_NS: XACML 2.0 function namespace prefix
33    @type V2_0_FUNCTION_NS: string
34    """
35    __metaclass__ = ABCMeta
36   
37    FUNCTION_NS = None
38    V1_0_FUNCTION_NS = "urn:oasis:names:tc:xacml:1.0:function:"
39    V2_0_FUNCTION_NS = "urn:oasis:names:tc:xacml:2.0:function:"
40   
41    def __init__(self):
42        """
43        @raise TypeError: if FUNCTION_NS not set correctly
44        """
45        if self.__class__.FUNCTION_NS is None:
46            raise TypeError('"FUNCTION_NS" class variable must be defined in '
47                            'derived classes')
48           
49    @abstractmethod
50    def evaluate(self, *inputs):
51        """Evaluate the function from the given input arguments and context
52       
53        @param inputs: input arguments need to evaluate the function
54        @type inputs: tuple
55        @return: derived type should return True for match, False otherwise
56        @rtype: bool (derived type), NoneType for THIS implementation
57        """
58       
59class XacmlFunctionNames(object):
60    """XACML standard match function names
61   
62    @cvar FUNCTION_NAMES: list of all the XACML function URNs
63    @type FUNCTION_NAMES: tuple   
64    """
65    FUNCTION_NAMES = (
66'urn:oasis:names:tc:xacml:1.0:function:string-equal',
67'urn:oasis:names:tc:xacml:1.0:function:boolean-equal',
68'urn:oasis:names:tc:xacml:1.0:function:integer-equal',
69'urn:oasis:names:tc:xacml:1.0:function:double-equal',
70'urn:oasis:names:tc:xacml:1.0:function:date-equal',
71'urn:oasis:names:tc:xacml:1.0:function:time-equal',
72'urn:oasis:names:tc:xacml:1.0:function:dateTime-equal',
73'urn:oasis:names:tc:xacml:1.0:function:dayTimeDuration-equal',
74'urn:oasis:names:tc:xacml:1.0:function:yearMonthDuration-equal',
75'urn:oasis:names:tc:xacml:1.0:function:anyURI-equal',
76'urn:oasis:names:tc:xacml:1.0:function:x500Name-equal',
77'urn:oasis:names:tc:xacml:1.0:function:rfc822Name-equal',
78'urn:oasis:names:tc:xacml:1.0:function:hexBinary-equal',
79'urn:oasis:names:tc:xacml:1.0:function:base64Binary-equal',
80'urn:oasis:names:tc:xacml:1.0:function:integer-add',
81'urn:oasis:names:tc:xacml:1.0:function:double-add',
82'urn:oasis:names:tc:xacml:1.0:function:integer-subtract',
83'urn:oasis:names:tc:xacml:1.0:function:double-subtract',
84'urn:oasis:names:tc:xacml:1.0:function:integer-multiply',
85'urn:oasis:names:tc:xacml:1.0:function:double-multiply',
86'urn:oasis:names:tc:xacml:1.0:function:integer-divide',
87'urn:oasis:names:tc:xacml:1.0:function:double-divide',
88'urn:oasis:names:tc:xacml:1.0:function:integer-mod',
89'urn:oasis:names:tc:xacml:1.0:function:integer-abs',
90'urn:oasis:names:tc:xacml:1.0:function:double-abs',
91'urn:oasis:names:tc:xacml:1.0:function:round',
92'urn:oasis:names:tc:xacml:1.0:function:floor',
93'urn:oasis:names:tc:xacml:1.0:function:string-normalize-space',
94'urn:oasis:names:tc:xacml:1.0:function:string-normalize-to-lower-case',
95'urn:oasis:names:tc:xacml:1.0:function:double-to-integer',
96'urn:oasis:names:tc:xacml:1.0:function:integer-to-double',
97'urn:oasis:names:tc:xacml:1.0:function:or',
98'urn:oasis:names:tc:xacml:1.0:function:and',
99'urn:oasis:names:tc:xacml:1.0:function:n-of',
100'urn:oasis:names:tc:xacml:1.0:function:not',
101'urn:oasis:names:tc:xacml:1.0:function:integer-greater-than',
102'urn:oasis:names:tc:xacml:1.0:function:integer-greater-than-or-equal',
103'urn:oasis:names:tc:xacml:1.0:function:integer-less-than',
104'urn:oasis:names:tc:xacml:1.0:function:integer-less-than-or-equal',
105'urn:oasis:names:tc:xacml:1.0:function:double-greater-than',
106'urn:oasis:names:tc:xacml:1.0:function:double-greater-than-or-equal',
107'urn:oasis:names:tc:xacml:1.0:function:double-less-than',
108'urn:oasis:names:tc:xacml:1.0:function:double-less-than-or-equal',
109'urn:oasis:names:tc:xacml:1.0:function:dateTime-add-dayTimeDuration',
110'urn:oasis:names:tc:xacml:1.0:function:dateTime-add-yearMonthDuration',
111'urn:oasis:names:tc:xacml:1.0:function:dateTime-subtract-dayTimeDuration',
112'urn:oasis:names:tc:xacml:1.0:function:dateTime-subtract-yearMonthDuration', 
113'urn:oasis:names:tc:xacml:1.0:function:date-add-yearMonthDuration',
114'urn:oasis:names:tc:xacml:1.0:function:date-subtract-yearMonthDuration',
115'urn:oasis:names:tc:xacml:1.0:function:string-greater-than',
116'urn:oasis:names:tc:xacml:1.0:function:string-greater-than-or-equal',
117'urn:oasis:names:tc:xacml:1.0:function:string-less-than',
118'urn:oasis:names:tc:xacml:1.0:function:string-less-than-or-equal',
119'urn:oasis:names:tc:xacml:1.0:function:time-greater-than',
120'urn:oasis:names:tc:xacml:1.0:function:time-greater-than-or-equal',
121'urn:oasis:names:tc:xacml:1.0:function:time-less-than',
122'urn:oasis:names:tc:xacml:1.0:function:time-less-than-or-equal',
123'urn:oasis:names:tc:xacml:2.0:function:time-in-range',
124'urn:oasis:names:tc:xacml:1.0:function:dateTime-greater-than',
125'urn:oasis:names:tc:xacml:1.0:function:dateTime-greater-than-or-equal',
126'urn:oasis:names:tc:xacml:1.0:function:dateTime-less-than',
127'urn:oasis:names:tc:xacml:1.0:function:dateTime-less-than-or-equal',
128'urn:oasis:names:tc:xacml:1.0:function:date-greater-than',
129'urn:oasis:names:tc:xacml:1.0:function:date-greater-than-or-equal',
130'urn:oasis:names:tc:xacml:1.0:function:date-less-than',
131'urn:oasis:names:tc:xacml:1.0:function:date-less-than-or-equal',
132'urn:oasis:names:tc:xacml:1.0:function:string-one-and-only',
133'urn:oasis:names:tc:xacml:1.0:function:string-bag-size',
134'urn:oasis:names:tc:xacml:1.0:function:string-is-in',
135'urn:oasis:names:tc:xacml:1.0:function:string-bag',
136'urn:oasis:names:tc:xacml:1.0:function:boolean-one-and-only',
137'urn:oasis:names:tc:xacml:1.0:function:boolean-bag-size',
138'urn:oasis:names:tc:xacml:1.0:function:boolean-is-in',
139'urn:oasis:names:tc:xacml:1.0:function:boolean-bag',
140'urn:oasis:names:tc:xacml:1.0:function:integer-one-and-only',
141'urn:oasis:names:tc:xacml:1.0:function:integer-bag-size',
142'urn:oasis:names:tc:xacml:1.0:function:integer-is-in',
143'urn:oasis:names:tc:xacml:1.0:function:integer-bag',
144'urn:oasis:names:tc:xacml:1.0:function:double-one-and-only',
145'urn:oasis:names:tc:xacml:1.0:function:double-bag-size',
146'urn:oasis:names:tc:xacml:1.0:function:double-is-in',
147'urn:oasis:names:tc:xacml:1.0:function:double-bag',
148'urn:oasis:names:tc:xacml:1.0:function:time-one-and-only',
149'urn:oasis:names:tc:xacml:1.0:function:time-bag-size',
150'urn:oasis:names:tc:xacml:1.0:function:time-is-in',
151'urn:oasis:names:tc:xacml:1.0:function:time-bag',
152'urn:oasis:names:tc:xacml:1.0:function:date-one-and-only',
153'urn:oasis:names:tc:xacml:1.0:function:date-bag-size',
154'urn:oasis:names:tc:xacml:1.0:function:date-is-in',
155'urn:oasis:names:tc:xacml:1.0:function:date-bag',
156'urn:oasis:names:tc:xacml:1.0:function:dateTime-one-and-only',
157'urn:oasis:names:tc:xacml:1.0:function:dateTime-bag-size',
158'urn:oasis:names:tc:xacml:1.0:function:dateTime-is-in',
159'urn:oasis:names:tc:xacml:1.0:function:dateTime-bag',
160'urn:oasis:names:tc:xacml:1.0:function:anyURI-one-and-only',
161'urn:oasis:names:tc:xacml:1.0:function:anyURI-bag-size',
162'urn:oasis:names:tc:xacml:1.0:function:anyURI-is-in',
163'urn:oasis:names:tc:xacml:1.0:function:anyURI-bag',
164'urn:oasis:names:tc:xacml:1.0:function:hexBinary-one-and-only',
165'urn:oasis:names:tc:xacml:1.0:function:hexBinary-bag-size',
166'urn:oasis:names:tc:xacml:1.0:function:hexBinary-is-in',
167'urn:oasis:names:tc:xacml:1.0:function:hexBinary-bag',
168'urn:oasis:names:tc:xacml:1.0:function:base64Binary-one-and-only',
169'urn:oasis:names:tc:xacml:1.0:function:base64Binary-bag-size',
170'urn:oasis:names:tc:xacml:1.0:function:base64Binary-is-in',
171'urn:oasis:names:tc:xacml:1.0:function:base64Binary-bag',
172'urn:oasis:names:tc:xacml:1.0:function:dayTimeDuration-one-and-only',
173'urn:oasis:names:tc:xacml:1.0:function:dayTimeDuration-bag-size',
174'urn:oasis:names:tc:xacml:1.0:function:dayTimeDuration-is-in',
175'urn:oasis:names:tc:xacml:1.0:function:dayTimeDuration-bag',
176'urn:oasis:names:tc:xacml:1.0:function:yearMonthDuration-one-and-only',
177'urn:oasis:names:tc:xacml:1.0:function:yearMonthDuration-bag-size',
178'urn:oasis:names:tc:xacml:1.0:function:yearMonthDuration-is-in',
179'urn:oasis:names:tc:xacml:1.0:function:yearMonthDuration-bag',
180'urn:oasis:names:tc:xacml:1.0:function:x500Name-one-and-only',
181'urn:oasis:names:tc:xacml:1.0:function:x500Name-bag-size',
182'urn:oasis:names:tc:xacml:1.0:function:x500Name-is-in',
183'urn:oasis:names:tc:xacml:1.0:function:x500Name-bag',
184'urn:oasis:names:tc:xacml:1.0:function:rfc822Name-one-and-only',
185'urn:oasis:names:tc:xacml:1.0:function:rfc822Name-bag-size',
186'urn:oasis:names:tc:xacml:1.0:function:rfc822Name-is-in',
187'urn:oasis:names:tc:xacml:1.0:function:rfc822Name-bag',
188'urn:oasis:names:tc:xacml:2.0:function:string-concatenate',
189'urn:oasis:names:tc:xacml:2.0:function:uri-string-concatenate',
190'urn:oasis:names:tc:xacml:1.0:function:any-of',
191'urn:oasis:names:tc:xacml:1.0:function:all-of',
192'urn:oasis:names:tc:xacml:1.0:function:any-of-any',
193'urn:oasis:names:tc:xacml:1.0:function:all-of-any',
194'urn:oasis:names:tc:xacml:1.0:function:any-of-all',
195'urn:oasis:names:tc:xacml:1.0:function:all-of-all',
196'urn:oasis:names:tc:xacml:1.0:function:map',
197'urn:oasis:names:tc:xacml:1.0:function:x500Name-match',
198'urn:oasis:names:tc:xacml:1.0:function:rfc822Name-match',
199'urn:oasis:names:tc:xacml:1.0:function:string-regexp-match',
200'urn:oasis:names:tc:xacml:2.0:function:anyURI-regexp-match',
201'urn:oasis:names:tc:xacml:2.0:function:ipAddress-regexp-match',
202'urn:oasis:names:tc:xacml:2.0:function:dnsName-regexp-match',
203'urn:oasis:names:tc:xacml:2.0:function:rfc822Name-regexp-match',
204'urn:oasis:names:tc:xacml:2.0:function:x500Name-regexp-match',
205'urn:oasis:names:tc:xacml:1.0:function:xpath-node-count',
206'urn:oasis:names:tc:xacml:1.0:function:xpath-node-equal',
207'urn:oasis:names:tc:xacml:1.0:function:xpath-node-match',
208'urn:oasis:names:tc:xacml:1.0:function:string-intersection',
209'urn:oasis:names:tc:xacml:1.0:function:string-at-least-one-member-of',
210'urn:oasis:names:tc:xacml:1.0:function:string-union',
211'urn:oasis:names:tc:xacml:1.0:function:string-subset',
212'urn:oasis:names:tc:xacml:1.0:function:string-set-equals',
213'urn:oasis:names:tc:xacml:1.0:function:boolean-intersection',
214'urn:oasis:names:tc:xacml:1.0:function:boolean-at-least-one-member-of',
215'urn:oasis:names:tc:xacml:1.0:function:boolean-union',
216'urn:oasis:names:tc:xacml:1.0:function:boolean-subset',
217'urn:oasis:names:tc:xacml:1.0:function:boolean-set-equals',
218'urn:oasis:names:tc:xacml:1.0:function:integer-intersection',
219'urn:oasis:names:tc:xacml:1.0:function:integer-at-least-one-member-of',
220'urn:oasis:names:tc:xacml:1.0:function:integer-union',
221'urn:oasis:names:tc:xacml:1.0:function:integer-subset',
222'urn:oasis:names:tc:xacml:1.0:function:integer-set-equals',
223'urn:oasis:names:tc:xacml:1.0:function:double-intersection',
224'urn:oasis:names:tc:xacml:1.0:function:double-at-least-one-member-of',
225'urn:oasis:names:tc:xacml:1.0:function:double-union',
226'urn:oasis:names:tc:xacml:1.0:function:double-subset',
227'urn:oasis:names:tc:xacml:1.0:function:double-set-equals',
228'urn:oasis:names:tc:xacml:1.0:function:time-intersection',
229'urn:oasis:names:tc:xacml:1.0:function:time-at-least-one-member-of',
230'urn:oasis:names:tc:xacml:1.0:function:time-union',
231'urn:oasis:names:tc:xacml:1.0:function:time-subset',
232'urn:oasis:names:tc:xacml:1.0:function:time-set-equals',
233'urn:oasis:names:tc:xacml:1.0:function:date-intersection',
234'urn:oasis:names:tc:xacml:1.0:function:date-at-least-one-member-of',
235'urn:oasis:names:tc:xacml:1.0:function:date-union',
236'urn:oasis:names:tc:xacml:1.0:function:date-subset',
237'urn:oasis:names:tc:xacml:1.0:function:date-set-equals',
238'urn:oasis:names:tc:xacml:1.0:function:dateTime-intersection',
239'urn:oasis:names:tc:xacml:1.0:function:dateTime-at-least-one-member-of',
240'urn:oasis:names:tc:xacml:1.0:function:dateTime-union',
241'urn:oasis:names:tc:xacml:1.0:function:dateTime-subset',
242'urn:oasis:names:tc:xacml:1.0:function:dateTime-set-equals',
243'urn:oasis:names:tc:xacml:1.0:function:anyURI-intersection',
244'urn:oasis:names:tc:xacml:1.0:function:anyURI-at-least-one-member-of',
245'urn:oasis:names:tc:xacml:1.0:function:anyURI-union',
246'urn:oasis:names:tc:xacml:1.0:function:anyURI-subset',
247'urn:oasis:names:tc:xacml:1.0:function:anyURI-set-equals',
248'urn:oasis:names:tc:xacml:1.0:function:hexBinary-intersection',
249'urn:oasis:names:tc:xacml:1.0:function:hexBinary-at-least-one-member-of',
250'urn:oasis:names:tc:xacml:1.0:function:hexBinary-union',
251'urn:oasis:names:tc:xacml:1.0:function:hexBinary-subset',
252'urn:oasis:names:tc:xacml:1.0:function:hexBinary-set-equals',
253'urn:oasis:names:tc:xacml:1.0:function:base64Binary-intersection',
254'urn:oasis:names:tc:xacml:1.0:function:base64Binary-at-least-one-member-of',
255'urn:oasis:names:tc:xacml:1.0:function:base64Binary-union',
256'urn:oasis:names:tc:xacml:1.0:function:base64Binary-subset',
257'urn:oasis:names:tc:xacml:1.0:function:base64Binary-set-equals',
258'urn:oasis:names:tc:xacml:1.0:function:dayTimeDuration-intersection',
259'urn:oasis:names:tc:xacml:1.0:function:dayTimeDuration-at-least-one-member-of',
260'urn:oasis:names:tc:xacml:1.0:function:dayTimeDuration-union',
261'urn:oasis:names:tc:xacml:1.0:function:dayTimeDuration-subset',
262'urn:oasis:names:tc:xacml:1.0:function:dayTimeDuration-set-equals',
263'urn:oasis:names:tc:xacml:1.0:function:yearMonthDuration-intersection',
264'urn:oasis:names:tc:xacml:1.0:function:yearMonthDuration-at-least-one-member-of',
265'urn:oasis:names:tc:xacml:1.0:function:yearMonthDuration-union',
266'urn:oasis:names:tc:xacml:1.0:function:yearMonthDuration-subset',
267'urn:oasis:names:tc:xacml:1.0:function:yearMonthDuration-set-equals',
268'urn:oasis:names:tc:xacml:1.0:function:x500Name-intersection',
269'urn:oasis:names:tc:xacml:1.0:function:x500Name-at-least-one-member-of',
270'urn:oasis:names:tc:xacml:1.0:function:x500Name-union',
271'urn:oasis:names:tc:xacml:1.0:function:x500Name-subset',
272'urn:oasis:names:tc:xacml:1.0:function:x500Name-set-equals',
273'urn:oasis:names:tc:xacml:1.0:function:rfc822Name-intersection',
274'urn:oasis:names:tc:xacml:1.0:function:rfc822Name-at-least-one-member-of',
275'urn:oasis:names:tc:xacml:1.0:function:rfc822Name-union',
276'urn:oasis:names:tc:xacml:1.0:function:rfc822Name-subset',
277'urn:oasis:names:tc:xacml:1.0:function:rfc822Name-set-equals',
278    )
279
280from ndg.xacml import XacmlError
281
282
283class UnsupportedFunctionError(XacmlError):
284    """Encountered a function type that is not recognised as part of the XACML
285    specification and is not supported in this implementation"""
286
287 
288class UnsupportedStdFunctionError(UnsupportedFunctionError): 
289    """Encountered a function type that is not supported even though it is
290    part of the XACML specification"""
291   
292   
293def unsupportedFunctionErrorFactory(identifier, msg=None):
294    """Factory function to return an unsupported function exception based on
295    the function identifier passed in
296   
297    @param identifier: XACML function namespace to check
298    @type identifier: basestring
299   
300    @return: unsupported function exception instance
301    @rtype: UnsupportedFunctionError or UnsupportedStdFunctionError depending
302    on the identifier passed
303    """
304    if identifier in XacmlFunctionNames.FUNCTION_NAMES:
305        if msg is None:
306            msg = "%s: %s" % (UnsupportedStdFunctionError.__doc__, identifier)
307           
308        raise UnsupportedStdFunctionError(msg)
309    else:
310        if msg is None:
311            msg = "%s: %s" % (UnsupportedFunctionError.__doc__, identifier)
312           
313        raise UnsupportedFunctionError(msg)
314   
315   
316class FunctionClassFactoryInterface(object):
317    """Interface class for function module class factory class
318    """
319    __meta__ = ABCMeta
320   
321    @abstractmethod
322    def __call__(self, identifier):
323        '''Create class for the given XACML function identifier
324       
325        @param identifier: XACML function identifier
326        @type identifier: basestring
327        @return: at least one member of class corresponding to the given input
328        identifier
329        @rtype: AbstractFunction derived type or NoneType if no match is found
330        '''
331        return None
332   
333
334class FunctionClassFactoryBase(FunctionClassFactoryInterface):
335    """Base implementation for XACML Function Class Factory.  There should be
336    one derived type for each function family implemented in sub-modules of
337    ndg.xacml.core.functions
338   
339    e.g.
340   
341    for urn:oasis:names:tc:xacml:1.0:function:<type>-at-least-one-member-of a
342    class factory should exist,
343   
344    ndg.xacml.core.functions.v1.at_least_one_member_of.FunctionClassFactory
345   
346    which will be capable of returning a type derived from AbstractFunction:
347   
348    <type>AtLeastOneMemberOf   
349   
350    e.g. StringAtLeastOneMemberOf, BooleanAtLeastOneMemberOf.
351   
352    This class is for convenience only some function factories are better
353    derived directly from FunctionClassFactoryInterface
354   
355    Derived classes MUST define these class variables:
356   
357    @cvar FUNCTION_NAMES: list of function identifiers that this factory can
358    produce classes for e.g.:
359   
360    ('urn:oasis:names:tc:xacml:1.0:function:string-at-least-one-member-of', ...)
361   
362    @type FUNCTION_NAMES: NoneType (but list in derived class)
363   
364    @cvar FUNCTION_NS_SUFFIX: urn suffix for the family of function to define
365    e.g. -at-least-one-member-of is the suffix for the URN:
366   
367    urn:oasis:names:tc:xacml:1.0:function:string-at-least-one-member-of
368    @type FUNCTION_NS_SUFFIX: NoneType (but basestring in derived class)
369   
370    @cvar FUNCTION_BASE_CLASS: base class for this family of functions e.g for
371    urn:oasis:names:tc:xacml:1.0:function:string-at-least-one-member-of,
372    ndg.xacml.core.functions.v1.at_least_one_member_of.AtLeastOneMemberOfBase
373    @type FUNCTION_BASE_CLASS: NoneType (but AbstractFunction derived type in
374    derived function factory class)
375    """
376   
377    FUNCTION_NS_SUFFIX = None
378    FUNCTION_NAMES = None
379    FUNCTION_BASE_CLASS = None
380   
381    URN_SEP = ':'
382    FUNCTION_NAME_SEP = '-'
383    __slots__ = ('__map', 'attributeValueClassFactory', 'functionSuffix')
384   
385    def __init__(self):
386        '''This class is in fact abstract - derived types must define the
387        FUNCTION_NS_SUFFIX and FUNCTION_BASE_CLASS class variables
388        '''
389        if None in (self.__class__.FUNCTION_NS_SUFFIX, 
390                    self.__class__.FUNCTION_BASE_CLASS):
391            raise TypeError('"FUNCTION_NS_SUFFIX" and "FUNCTION_BASE_CLASS" '
392                            'must be defined in a derived implementation of '
393                            'FunctionClassFactoryBase.  See '
394                            'FunctionClassFactoryBase.__doc__ contents')
395       
396        if not _isIterable(self.__class__.FUNCTION_NAMES):
397            raise TypeError('"FUNCTION_NAMES" class variable must be an '
398                            'iterable of string type function identifiers; got '
399                            '%r' % self.__class__.FUNCTION_NAMES)
400
401        self.__map = {}   
402       
403        # Enables creation of matching attribute types to relevant to the
404        # function classes   
405        self.attributeValueClassFactory = AttributeValueClassFactory()
406           
407       
408        functionSuffixParts = self.__class__.FUNCTION_NS_SUFFIX.split(
409                                            self.__class__.FUNCTION_NAME_SEP)
410        self.functionSuffix = ''.join([n[0].upper() + n[1:] 
411                                  for n in functionSuffixParts if n])
412       
413    def initAllFunctionClasses(self):
414        """Create classes for all functions for a data type e.g. a derived class
415        could implement a factory for <type>-at-least-one-member-of functions:
416        string-at-least-one-member-of, boolean-at-least-one-member-of, etc.
417       
418        Function classes are placed in a look-up table __map for the __call__()
419        method to access
420       
421        In practice, there shouldn't be a need to load all the functions in
422        one go.  The __call__ method loads functions and caches them as needed.
423        """       
424        for identifier in self.__class__.FUNCTION_NAMES:
425            self.loadFunction(identifier)       
426
427    def loadFunction(self, identifier):
428        """Create a class for the given function namespace and cache it in the
429        function class look-up table for future requests.  Note that this call
430        overwrites any existing entry in the cache whereas __call__ will try
431        to use an entry in the cache if it already exists
432       
433        @param identifier: XACML function namespace
434        @type identifier: basestring
435        """
436
437        # str.capitalize doesn't do what's required: need to capitalize the
438        # first letter of the word BUT retain camel case for the rest of it
439        _capitalize = lambda s: s[0].upper() + s[1:]
440       
441        # Extract the function name and the type portion of the function
442        # name in order to make an implementation of a class to handle it
443        functionName = identifier.split(self.__class__.URN_SEP)[-1]
444        typePart = functionName.split(self.__class__.FUNCTION_NS_SUFFIX)[0]
445       
446        # Attempt to infer from the function name the associated type
447        typeName = _capitalize(typePart)
448       
449        # Remove any hyphens converting to camel case
450        if '-' in typeName:
451            typeName = ''.join([_capitalize(i) for i in typeName.split('-')])
452           
453        typeURI = AttributeValue.TYPE_URI_MAP.get(typeName)
454        if typeURI is None:
455            # Ugly hack to allow for XPath node functions
456            if typePart == 'xpath-node':
457                typeURI = AttributeValue.TYPE_URI_MAP['String']
458            else:
459                raise TypeError('No AttributeValue.TYPE_URI_MAP entry for '
460                                '%r type' % typePart) 
461           
462        _type = self.attributeValueClassFactory(typeURI)
463        if _type is None:
464            raise TypeError('No AttributeValue.TYPE_MAP entry for %r type' %
465                            typeName)
466         
467        className = typeName + self.functionSuffix
468        classVars = {
469            'TYPE': _type,
470            'FUNCTION_NS': identifier
471        }
472       
473        functionClass = type(className, 
474                             (self.__class__.FUNCTION_BASE_CLASS, ), 
475                             classVars)
476       
477        self.__map[identifier] = functionClass
478           
479    def __call__(self, identifier):
480        """Return the class for the given XACML type function identifier
481       
482        @param identifier: XACML *-at-least-one-member-of type function
483        identifier
484        @type identifier: basestring
485        @return: at least one member of class corresponding to the given input
486        identifier
487        @rtype: AtLeastOneMemberOfBase derived type or None if no match is
488        found
489        """
490        # Check the cache first
491        functionClass = self.__map.get(identifier)
492        if functionClass is None:
493            # No class set in the cache - try loading the new class and updating
494            # the cache.
495            self.loadFunction(identifier)
496           
497        # This should result in a safe retrieval from the cache because of the
498        # above check - None return would result otherwise.
499        return self.__map.get(identifier)
500       
501   
502class FunctionMapError(Exception):
503    """Generic Error exception class for FunctionMap"""
504   
505   
506class FunctionMapConfigError(FunctionMapError):
507    """Configuration related exception for FunctionMap"""
508       
509       
510class FunctionMap(VettedDict):
511    """Map function IDs to their class implementations in the various function
512    sub-modules.  It provide a layer over the various
513    FunctionClassFactoryInterface implementations so that a function class can
514    be obtained directly from a given XACML function URN. 
515   
516    @cvar FUNCTION_PKG_PREFIX: python package path for functions package
517    @type FUNCTION_PKG_PREFIX: string
518   
519    @cvar V1_0_PKG_PREFIX: python package path for XACML 1.0 functions package
520    @type V1_0_PKG_PREFIX: string
521   
522    @cvar V2_0_PKG_PREFIX: python package path for XACML 2.0 functions package
523    @type V2_0_PKG_PREFIX: string
524   
525    @cvar SUPPORTED_NSS: mapping of function URN prefix to Python package
526    @type SUPPORTED_NSS: dict
527   
528    @cvar FUNCTION_CLASS_FACTORY_CLASSNAME: standard name for class factory
529    which should be present in each generic function module.  This factory is
530    invoked to create the function class for any given function URN related to
531    that module
532    @type FUNCTION_CLASS_FACTORY_CLASSNAME: string
533    """
534    FUNCTION_PKG_PREFIX = 'ndg.xacml.core.functions.'
535   
536    V1_0_PKG_PREFIX = FUNCTION_PKG_PREFIX + 'v1.'
537    V2_0_PKG_PREFIX = FUNCTION_PKG_PREFIX + 'v2.'
538   
539    SUPPORTED_NSS = {
540        AbstractFunction.V1_0_FUNCTION_NS: V1_0_PKG_PREFIX,
541        AbstractFunction.V2_0_FUNCTION_NS: V2_0_PKG_PREFIX
542    }
543   
544    # Each function module is expected to have a class factory for obtaining
545    # a class for the given function identifier associated with that module
546    FUNCTION_CLASS_FACTORY_CLASSNAME = 'FunctionClassFactory'
547   
548    def __init__(self):
549        """Force type for dictionary key value pairs: function values must be
550        of AbstractFunction derived type and ID keys string type
551        """       
552        # Filters are defined as staticmethods but reference via self here to
553        # enable derived class to override them as standard methods without
554        # needing to redefine this __init__ method           
555        VettedDict.__init__(self, self.keyFilter, self.valueFilter)
556       
557        # This classes maintains a list of XACML function URN -> Function class
558        # mappings.  This additional dict enables caching of class factories
559        # used to obtain the function classes.  There is one class factory per
560        # function module e.g. ndg.xacml.core.functions.v1.equal contains a
561        # class factory which creates the various
562        # urn:oasis:names:tc:xacml:1.0:function:<type>-equal function classes
563        self.__classFactoryMap = {}
564       
565    @staticmethod
566    def keyFilter(key):
567        """Enforce string type keys
568       
569        @param key: function URN
570        @type key: basestring
571        @return: True for valid key type
572        @rtype: bool
573        @raise TypeError: invalid key type
574        """
575        if not isinstance(key, basestring):
576            raise TypeError('Expecting %r type for key; got %r' % 
577                            (basestring, type(key))) 
578           
579        return True 
580   
581    @staticmethod
582    def valueFilter(value):
583        """Enforce AbstractFunction derived types for match functions
584       
585        @param value: function URN
586        @type value: ndg.xacml.core.functions.AbstractFunction / NotImplemented
587        @return: True for valid function type
588        @rtype: bool
589        @raise TypeError: invlaid key type
590        """
591        if value is NotImplemented:
592            return True
593       
594        elif not issubclass(value, AbstractFunction):
595            raise TypeError('Expecting %r derived type for value; got %r' % 
596                            (AbstractFunction, value)) 
597           
598        return True 
599           
600    def loadAll(self):
601        """Load function map with implementations from the relevant function
602        package"""
603       
604        for functionNs in XacmlFunctionNames.FUNCTION_NAMES:
605            self.loadFunction(functionNs)
606           
607    def loadFunction(self, functionNs):
608        """Get package to retrieve function class for the given XACML function
609        namespace
610       
611        @param functionNs: XACML function namespace
612        @type functionNs: basestring
613        """
614        functionFactory = self.__classFactoryMap.get(functionNs)
615        if functionFactory is not None:
616            # Get function class from previously cached factory
617            self[functionNs] = functionFactory(functionNs)
618            return
619           
620        # No Factory has been cached for this function yet
621        cls = FunctionMap
622        classPath = None
623       
624        for namespacePrefix, pkgNamePrefix in cls.SUPPORTED_NSS.items():
625            if functionNs.startswith(namespacePrefix):
626                # Namespace is recognised - translate into a path to a
627                # function class in the right functions package
628                functionName = functionNs.split(namespacePrefix)[-1]
629                functionNameParts = functionName.split('-')
630               
631                if len(functionNameParts) == 1:
632                    moduleName = functionNameParts[0]
633                   
634                elif functionName.startswith('xpath-node'):
635                    # Ugly hack for xpath-node functions
636                    moduleName = functionNameParts[-1].lower()
637                else:
638                    moduleName = '_'.join(functionNameParts[1:]).lower()
639                   
640                classPath = pkgNamePrefix + moduleName + '.' + \
641                            cls.FUNCTION_CLASS_FACTORY_CLASSNAME
642                break
643
644        if classPath is None:
645            raise FunctionMapConfigError('Namespace for function not '
646                                         'recognised: %r' % functionNs) 
647                       
648        # Try instantiating the function class and loading it into the map
649        try:
650            functionFactory = callModuleObject(classPath)
651                     
652        except (ImportError, AttributeError), e:
653            log.error("Error importing function factory class %r for function "
654                      "identifier %r: %s", classPath, functionNs, str(e))
655           
656            # No implementation exists - default to Abstract function
657            self[functionNs] = NotImplemented
658        else:
659            function = functionFactory(functionNs)
660            if function is None:
661                raise unsupportedFunctionErrorFactory(functionNs)
662               
663            self[functionNs] = function
664            self.__classFactoryMap[functionNs] = functionFactory
665                       
666    def __getitem__(self, key):
667        """Override base class implementation to load and cache function classes
668        if they don't otherwise exist
669       
670        @param key: function URN
671        @type key: basestring
672        @return: function class
673        @rtype: ndg.xacml.core.functions.AbstractFunction / NotImplemented
674        """
675        functionClass = VettedDict.get(self, key)
676        if functionClass is None:
677            self.loadFunction(key)
678           
679        return VettedDict.__getitem__(self, key)
680       
681    def get(self, key, *arg):
682        """Likewise to __getitem__, enable loading and caching of function
683        classes if they don't otherwise exist
684       
685        @param key: XACML function URN
686        @type key: basestring
687        @param arg: set a single additional argument if required which is
688        used as the default value should the key not be found in the map
689        @type arg: tuple
690        @return: function class
691        @rtype: ndg.xacml.core.functions.AbstractFunction / NotImplemented
692        """
693        functionClass = VettedDict.get(self, key, *arg)
694        if functionClass is None:
695            self.loadFunction(key)
696            return VettedDict.get(self, key, *arg)   
697        else:
698            return functionClass
699
700
701       
Note: See TracBrowser for help on using the repository browser.