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

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

Incomplete - task 2: XACML-Security Integration

  • updating epydoc ready for release.
  • 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
280
281class FunctionClassFactoryInterface(object):
282    """Interface class for function module class factory class
283    """
284    __meta__ = ABCMeta
285   
286    @abstractmethod
287    def __call__(self, identifier):
288        '''Create class for the given XACML function identifier
289       
290        @param identifier: XACML function identifier
291        @type identifier: basestring
292        @return: at least one member of class corresponding to the given input
293        identifier
294        @rtype: AbstractFunction derived type or NoneType if no match is found
295        '''
296        return None
297   
298
299class FunctionClassFactoryBase(FunctionClassFactoryInterface):
300    """Base implementation for XACML Function Class Factory.  There should be
301    one derived type for each function family implemented in sub-modules of
302    ndg.xacml.core.functions
303   
304    e.g.
305   
306    for urn:oasis:names:tc:xacml:1.0:function:<type>-at-least-one-member-of a
307    class factory should exist,
308   
309    ndg.xacml.core.functions.v1.at_least_one_member_of.FunctionClassFactory
310   
311    which will be capable of returning a type derived from AbstractFunction:
312   
313    <type>AtLeastOneMemberOf   
314   
315    e.g. StringAtLeastOneMemberOf, BooleanAtLeastOneMemberOf.
316   
317    This class is for convenience only some function factories are better
318    derived directly from FunctionClassFactoryInterface
319   
320    Derived classes MUST define these class variables:
321   
322    @cvar FUNCTION_NAMES: list of function identifiers that this factory can
323    produce classes for e.g.:
324   
325    ('urn:oasis:names:tc:xacml:1.0:function:string-at-least-one-member-of', ...)
326   
327    @type FUNCTION_NAMES: NoneType (but list in derived class)
328   
329    @cvar FUNCTION_NS_SUFFIX: urn suffix for the family of function to define
330    e.g. -at-least-one-member-of is the suffix for the URN:
331   
332    urn:oasis:names:tc:xacml:1.0:function:string-at-least-one-member-of
333    @type FUNCTION_NS_SUFFIX: NoneType (but basestring in derived class)
334   
335    @cvar FUNCTION_BASE_CLASS: base class for this family of functions e.g for
336    urn:oasis:names:tc:xacml:1.0:function:string-at-least-one-member-of,
337    ndg.xacml.core.functions.v1.at_least_one_member_of.AtLeastOneMemberOfBase
338    @type FUNCTION_BASE_CLASS: NoneType (but AbstractFunction derived type in
339    derived function factory class)
340    """
341   
342    FUNCTION_NS_SUFFIX = None
343    FUNCTION_NAMES = None
344    FUNCTION_BASE_CLASS = None
345   
346    URN_SEP = ':'
347    FUNCTION_NAME_SEP = '-'
348    __slots__ = ('__map', 'attributeValueClassFactory', 'functionSuffix')
349   
350    def __init__(self):
351        '''This class is in fact abstract - derived types must define the
352        FUNCTION_NS_SUFFIX and FUNCTION_BASE_CLASS class variables
353        '''
354        if None in (self.__class__.FUNCTION_NS_SUFFIX, 
355                    self.__class__.FUNCTION_BASE_CLASS):
356            raise TypeError('"FUNCTION_NS_SUFFIX" and "FUNCTION_BASE_CLASS" '
357                            'must be defined in a derived implementation of '
358                            'FunctionClassFactoryBase.  See '
359                            'FunctionClassFactoryBase.__doc__ contents')
360       
361        if not _isIterable(self.__class__.FUNCTION_NAMES):
362            raise TypeError('"FUNCTION_NAMES" class variable must be an '
363                            'iterable of string type function identifiers; got '
364                            '%r' % self.__class__.FUNCTION_NAMES)
365
366        self.__map = {}   
367       
368        # Enables creation of matching attribute types to relevant to the
369        # function classes   
370        self.attributeValueClassFactory = AttributeValueClassFactory()
371           
372       
373        functionSuffixParts = self.__class__.FUNCTION_NS_SUFFIX.split(
374                                            self.__class__.FUNCTION_NAME_SEP)
375        self.functionSuffix = ''.join([n[0].upper() + n[1:] 
376                                  for n in functionSuffixParts if n])
377       
378    def initAllFunctionClasses(self):
379        """Create classes for all functions for a data type e.g. a derived class
380        could implement a factory for <type>-at-least-one-member-of functions:
381        string-at-least-one-member-of, boolean-at-least-one-member-of, etc.
382       
383        Function classes are placed in a look-up table __map for the __call__()
384        method to access
385       
386        In practice, there shouldn't be a need to load all the functions in
387        one go.  The __call__ method loads functions and caches them as needed.
388        """       
389        for identifier in self.__class__.FUNCTION_NAMES:
390            self.loadFunction(identifier)       
391
392    def loadFunction(self, identifier):
393        """Create a class for the given function namespace and cache it in the
394        function class look-up table for future requests.  Note that this call
395        overwrites any existing entry in the cache whereas __call__ will try
396        to use an entry in the cache if it already exists
397       
398        @param identifier: XACML function namespace
399        @type identifier: basestring
400        """
401
402        # str.capitalize doesn't do what's required: need to capitalize the
403        # first letter of the word BUT retain camel case for the rest of it
404        _capitalize = lambda s: s[0].upper() + s[1:]
405       
406        # Extract the function name and the type portion of the function
407        # name in order to make an implementation of a class to handle it
408        functionName = identifier.split(self.__class__.URN_SEP)[-1]
409        typePart = functionName.split(self.__class__.FUNCTION_NS_SUFFIX)[0]
410       
411        # Attempt to infer from the function name the associated type
412        typeName = _capitalize(typePart)
413       
414        # Remove any hyphens converting to camel case
415        if '-' in typeName:
416            typeName = ''.join([_capitalize(i) for i in typeName.split('-')])
417           
418        typeURI = AttributeValue.TYPE_URI_MAP.get(typeName)
419        if typeURI is None:
420            # Ugly hack to allow for XPath node functions
421            if typePart == 'xpath-node':
422                typeURI = AttributeValue.TYPE_URI_MAP['String']
423            else:
424                raise TypeError('No AttributeValue.TYPE_URI_MAP entry for '
425                                '%r type' % typePart) 
426           
427        _type = self.attributeValueClassFactory(typeURI)
428        if _type is None:
429            raise TypeError('No AttributeValue.TYPE_MAP entry for %r type' %
430                            typeName)
431         
432        className = typeName + self.functionSuffix
433        classVars = {
434            'TYPE': _type,
435            'FUNCTION_NS': identifier
436        }
437       
438        functionClass = type(className, 
439                             (self.__class__.FUNCTION_BASE_CLASS, ), 
440                             classVars)
441       
442        self.__map[identifier] = functionClass
443           
444    def __call__(self, identifier):
445        """Return the class for the given XACML *-at-least-one-member-of type
446        function identifier
447        @param identifier: XACML *-at-least-one-member-of type function
448        identifier
449        @type identifier: basestring
450        @return: at least one member of class corresponding to the given input
451        identifier
452        @rtype: AtLeastOneMemberOfBase derived type or None if no match is
453        found
454        """
455        # Check the cache first
456        functionClass = self.__map.get(identifier)
457        if functionClass is None:
458            # No class set in the cache - try loading the new class and updating
459            # the cache.
460            self.loadFunction(identifier)
461           
462        # This should result in a safe retrieval from the cache because of the
463        # above check - None return would result otherwise.
464        return self.__map.get(identifier)
465       
466   
467class FunctionMapError(Exception):
468    """Generic Error exception class for FunctionMap"""
469   
470   
471class FunctionMapConfigError(FunctionMapError):
472    """Configuration related exception for FunctionMap"""
473       
474       
475class FunctionMap(VettedDict):
476    """Map function IDs to their class implementations in the various function
477    sub-modules.  It provide a layer over the various
478    FunctionClassFactoryInterface implementations so that a function class can
479    be obtained directly from a given XACML function URN. 
480   
481    @cvar FUNCTION_PKG_PREFIX: python package path for functions package
482    @type FUNCTION_PKG_PREFIX: string
483   
484    @cvar V1_0_PKG_PREFIX: python package path for XACML 1.0 functions package
485    @type V1_0_PKG_PREFIX: string
486   
487    @cvar V2_0_PKG_PREFIX: python package path for XACML 2.0 functions package
488    @type V2_0_PKG_PREFIX: string
489   
490    @cvar SUPPORTED_NSS: mapping of function URN prefix to Python package
491    @type SUPPORTED_NSS: dict
492   
493    @cvar FUNCTION_CLASS_FACTORY_CLASSNAME: standard name for class factory
494    which should be present in each generic function module.  This factory is
495    invoked to create the function class for any given function URN related to
496    that module
497    @type FUNCTION_CLASS_FACTORY_CLASSNAME: string
498    """
499    FUNCTION_PKG_PREFIX = 'ndg.xacml.core.functions.'
500   
501    V1_0_PKG_PREFIX = FUNCTION_PKG_PREFIX + 'v1.'
502    V2_0_PKG_PREFIX = FUNCTION_PKG_PREFIX + 'v2.'
503   
504    SUPPORTED_NSS = {
505        AbstractFunction.V1_0_FUNCTION_NS: V1_0_PKG_PREFIX,
506        AbstractFunction.V2_0_FUNCTION_NS: V2_0_PKG_PREFIX
507    }
508   
509    # Each function module is expected to have a class factory for obtaining
510    # a class for the given function identifier associated with that module
511    FUNCTION_CLASS_FACTORY_CLASSNAME = 'FunctionClassFactory'
512   
513    def __init__(self):
514        """Force type for dictionary key value pairs: function values must be
515        of AbstractFunction derived type and ID keys string type
516        """       
517        # Filters are defined as staticmethods but reference via self here to
518        # enable derived class to override them as standard methods without
519        # needing to redefine this __init__ method           
520        VettedDict.__init__(self, self.keyFilter, self.valueFilter)
521       
522        # This classes maintains a list of XACML function URN -> Function class
523        # mappings.  This additional dict enables caching of class factories
524        # used to obtain the function classes.  There is one class factory per
525        # function module e.g. ndg.xacml.core.functions.v1.equal contains a
526        # class factory which creates the various
527        # urn:oasis:names:tc:xacml:1.0:function:<type>-equal function classes
528        self.__classFactoryMap = {}
529       
530    @staticmethod
531    def keyFilter(key):
532        """Enforce string type keys
533       
534        @param key: function URN
535        @type key: basestring
536        @return: True for valid key type
537        @rtype: bool
538        @raise TypeError: invalid key type
539        """
540        if not isinstance(key, basestring):
541            raise TypeError('Expecting %r type for key; got %r' % 
542                            (basestring, type(key))) 
543           
544        return True 
545   
546    @staticmethod
547    def valueFilter(value):
548        """Enforce AbstractFunction derived types for match functions
549       
550        @param value: function URN
551        @type value: ndg.xacml.core.functions.AbstractFunction / NotImplemented
552        @return: True for valid function type
553        @rtype: bool
554        @raise TypeError: invlaid key type
555        """
556        if value is NotImplemented:
557            return True
558       
559        elif not issubclass(value, AbstractFunction):
560            raise TypeError('Expecting %r derived type for value; got %r' % 
561                            (AbstractFunction, value)) 
562           
563        return True 
564           
565    def loadAll(self):
566        """Load function map with implementations from the relevant function
567        package"""
568       
569        for functionNs in XacmlFunctionNames.FUNCTION_NAMES:
570            self.loadFunction(functionNs)
571           
572    def loadFunction(self, functionNs):
573        """Get package to retrieve function class for the given XACML function
574        namespace
575       
576        @param functionNs: XACML function namespace
577        @type functionNs: basestring
578        """
579        functionFactory = self.__classFactoryMap.get(functionNs)
580        if functionFactory is not None:
581            # Get function class from previously cached factory
582            self[functionNs] = functionFactory(functionNs)
583            return
584           
585        # No Factory has been cached for this function yet
586        cls = FunctionMap
587        classPath = None
588       
589        for namespacePrefix, pkgNamePrefix in cls.SUPPORTED_NSS.items():
590            if functionNs.startswith(namespacePrefix):
591                # Namespace is recognised - translate into a path to a
592                # function class in the right functions package
593                functionName = functionNs.split(namespacePrefix)[-1]
594                functionNameParts = functionName.split('-')
595               
596                if len(functionNameParts) == 1:
597                    moduleName = functionNameParts[0]
598                   
599                elif functionName.startswith('xpath-node'):
600                    # Ugly hack for xpath-node functions
601                    moduleName = functionNameParts[-1].lower()
602                else:
603                    moduleName = '_'.join(functionNameParts[1:]).lower()
604                   
605                classPath = pkgNamePrefix + moduleName + '.' + \
606                            cls.FUNCTION_CLASS_FACTORY_CLASSNAME
607                break
608
609        if classPath is None:
610            raise FunctionMapConfigError('Namespace for function not '
611                                         'recognised: %r' % functionNs) 
612                       
613        # Try instantiating the function class and loading it into the map
614        try:
615            functionFactory = callModuleObject(classPath)
616                     
617        except (ImportError, AttributeError), e:
618            log.error("Error importing function factory class %r for function "
619                      "identifier %r: %s", classPath, functionNs, str(e))
620           
621            # No implementation exists - default to Abstract function
622            self[functionNs] = NotImplemented
623        else:
624            self[functionNs] = functionFactory(functionNs)
625            self.__classFactoryMap[functionNs] = functionFactory
626                       
627    def __getitem__(self, key):
628        """Override base class implementation to load and cache function classes
629        if they don't otherwise exist
630       
631        @param key: function URN
632        @type key: basestring
633        @return: function class
634        @rtype: ndg.xacml.core.functions.AbstractFunction / NotImplemented
635        """
636        functionClass = VettedDict.get(self, key)
637        if functionClass is None:
638            self.loadFunction(key)
639           
640        return VettedDict.__getitem__(self, key)
641       
642    def get(self, key, *arg):
643        """Likewise to __getitem__, enable loading and caching of function
644        classes if they don't otherwise exist
645       
646        @param key: XACML function URN
647        @type key: basestring
648        @param *arg: set a single additional argument if required which is
649        used as the default value should the key not be found in the map
650        @type *arg: tuple
651        @return: function class
652        @rtype: ndg.xacml.core.functions.AbstractFunction / NotImplemented
653        """
654        functionClass = VettedDict.get(self, key, *arg)
655        if functionClass is None:
656            self.loadFunction(key)
657            return VettedDict.get(self, key, *arg)   
658        else:
659            return functionClass
660
661
662       
Note: See TracBrowser for help on using the repository browser.