Package ndg :: Package xacml :: Package core :: Package functions
[hide private]

Source Code for Package ndg.xacml.core.functions

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