[6803] | 1 | """NDG XACML package for functions |
---|
[6777] | 2 | |
---|
[7087] | 3 | NERC DataGrid |
---|
[6777] | 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" |
---|
[7064] | 11 | __revision__ = "$Id$" |
---|
[6777] | 12 | from abc import ABCMeta, abstractmethod |
---|
[6803] | 13 | from datetime import datetime, timedelta |
---|
| 14 | import traceback |
---|
| 15 | import logging |
---|
| 16 | log = logging.getLogger(__name__) |
---|
[6777] | 17 | |
---|
[6805] | 18 | from ndg.xacml.core.attributevalue import (AttributeValue, |
---|
| 19 | AttributeValueClassFactory) |
---|
[6804] | 20 | from ndg.xacml.utils import VettedDict, _isIterable |
---|
[6777] | 21 | from ndg.xacml.utils.factory import callModuleObject |
---|
| 22 | |
---|
| 23 | |
---|
| 24 | class AbstractFunction(object): |
---|
[7088] | 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) |
---|
[6777] | 28 | |
---|
[7088] | 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 | """ |
---|
[6777] | 35 | __metaclass__ = ABCMeta |
---|
[7088] | 36 | |
---|
[6777] | 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): |
---|
[7088] | 42 | """ |
---|
| 43 | @raise TypeError: if FUNCTION_NS not set correctly |
---|
| 44 | """ |
---|
[6777] | 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 |
---|
[7088] | 52 | |
---|
[6777] | 53 | @param inputs: input arguments need to evaluate the function |
---|
| 54 | @type inputs: tuple |
---|
[7088] | 55 | @return: derived type should return True for match, False otherwise |
---|
| 56 | @rtype: bool (derived type), NoneType for THIS implementation |
---|
[6777] | 57 | """ |
---|
| 58 | |
---|
| 59 | class XacmlFunctionNames(object): |
---|
[7088] | 60 | """XACML standard match function names |
---|
| 61 | |
---|
| 62 | @cvar FUNCTION_NAMES: list of all the XACML function URNs |
---|
| 63 | @type FUNCTION_NAMES: tuple |
---|
| 64 | """ |
---|
[6777] | 65 | FUNCTION_NAMES = ( |
---|
[6804] | 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', |
---|
[6777] | 278 | ) |
---|
| 279 | |
---|
[7351] | 280 | from ndg.xacml import XacmlError |
---|
[6777] | 281 | |
---|
[7351] | 282 | |
---|
| 283 | class 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 | |
---|
| 288 | class UnsupportedStdFunctionError(UnsupportedFunctionError): |
---|
| 289 | """Encountered a function type that is not supported even though it is |
---|
| 290 | part of the XACML specification""" |
---|
| 291 | |
---|
| 292 | |
---|
| 293 | def 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 | |
---|
[6804] | 316 | class FunctionClassFactoryInterface(object): |
---|
[6803] | 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 | |
---|
[6804] | 325 | @param identifier: XACML function identifier |
---|
[6803] | 326 | @type identifier: basestring |
---|
| 327 | @return: at least one member of class corresponding to the given input |
---|
| 328 | identifier |
---|
[7088] | 329 | @rtype: AbstractFunction derived type or NoneType if no match is found |
---|
[6803] | 330 | ''' |
---|
| 331 | return None |
---|
| 332 | |
---|
| 333 | |
---|
[6804] | 334 | class FunctionClassFactoryBase(FunctionClassFactoryInterface): |
---|
[7072] | 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 |
---|
[6804] | 338 | |
---|
| 339 | e.g. |
---|
| 340 | |
---|
[7072] | 341 | for urn:oasis:names:tc:xacml:1.0:function:<type>-at-least-one-member-of a |
---|
[6803] | 342 | class factory should exist, |
---|
| 343 | |
---|
| 344 | ndg.xacml.core.functions.v1.at_least_one_member_of.FunctionClassFactory |
---|
| 345 | |
---|
[7072] | 346 | which will be capable of returning a type derived from AbstractFunction: |
---|
[6804] | 347 | |
---|
[7072] | 348 | <type>AtLeastOneMemberOf |
---|
[6803] | 349 | |
---|
[7072] | 350 | e.g. StringAtLeastOneMemberOf, BooleanAtLeastOneMemberOf. |
---|
| 351 | |
---|
[6804] | 352 | This class is for convenience only some function factories are better |
---|
| 353 | derived directly from FunctionClassFactoryInterface |
---|
[6803] | 354 | |
---|
[6804] | 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 | |
---|
[6803] | 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 |
---|
[7072] | 374 | derived function factory class) |
---|
[6803] | 375 | """ |
---|
| 376 | |
---|
| 377 | FUNCTION_NS_SUFFIX = None |
---|
[6804] | 378 | FUNCTION_NAMES = None |
---|
[6803] | 379 | FUNCTION_BASE_CLASS = None |
---|
| 380 | |
---|
| 381 | URN_SEP = ':' |
---|
| 382 | FUNCTION_NAME_SEP = '-' |
---|
[7072] | 383 | __slots__ = ('__map', 'attributeValueClassFactory', 'functionSuffix') |
---|
| 384 | |
---|
[6803] | 385 | def __init__(self): |
---|
[7072] | 386 | '''This class is in fact abstract - derived types must define the |
---|
| 387 | FUNCTION_NS_SUFFIX and FUNCTION_BASE_CLASS class variables |
---|
| 388 | ''' |
---|
[6803] | 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 ' |
---|
[6804] | 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) |
---|
[7072] | 400 | |
---|
| 401 | self.__map = {} |
---|
| 402 | |
---|
| 403 | # Enables creation of matching attribute types to relevant to the |
---|
| 404 | # function classes |
---|
| 405 | self.attributeValueClassFactory = AttributeValueClassFactory() |
---|
[6803] | 406 | |
---|
[7072] | 407 | |
---|
[6803] | 408 | functionSuffixParts = self.__class__.FUNCTION_NS_SUFFIX.split( |
---|
| 409 | self.__class__.FUNCTION_NAME_SEP) |
---|
[7072] | 410 | self.functionSuffix = ''.join([n[0].upper() + n[1:] |
---|
[6803] | 411 | for n in functionSuffixParts if n]) |
---|
| 412 | |
---|
[7072] | 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. |
---|
[6805] | 417 | |
---|
[7072] | 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('-')]) |
---|
[6803] | 452 | |
---|
[7072] | 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) |
---|
[7064] | 461 | |
---|
[7072] | 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 |
---|
[6803] | 478 | |
---|
| 479 | def __call__(self, identifier): |
---|
[7351] | 480 | """Return the class for the given XACML type function identifier |
---|
| 481 | |
---|
[6803] | 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 | """ |
---|
[7072] | 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. |
---|
[6803] | 499 | return self.__map.get(identifier) |
---|
| 500 | |
---|
| 501 | |
---|
[6777] | 502 | class FunctionMapError(Exception): |
---|
| 503 | """Generic Error exception class for FunctionMap""" |
---|
| 504 | |
---|
| 505 | |
---|
| 506 | class FunctionMapConfigError(FunctionMapError): |
---|
| 507 | """Configuration related exception for FunctionMap""" |
---|
| 508 | |
---|
| 509 | |
---|
| 510 | class FunctionMap(VettedDict): |
---|
[7072] | 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. |
---|
[7088] | 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 |
---|
[7072] | 533 | """ |
---|
[6777] | 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 | |
---|
[6803] | 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 | |
---|
[6777] | 548 | def __init__(self): |
---|
[7072] | 549 | """Force type for dictionary key value pairs: function values must be |
---|
| 550 | of AbstractFunction derived type and ID keys string type |
---|
[6790] | 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 |
---|
[7072] | 555 | VettedDict.__init__(self, self.keyFilter, self.valueFilter) |
---|
[6777] | 556 | |
---|
[7072] | 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 | |
---|
[6790] | 565 | @staticmethod |
---|
| 566 | def keyFilter(key): |
---|
[7088] | 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 | """ |
---|
[6790] | 575 | if not isinstance(key, basestring): |
---|
| 576 | raise TypeError('Expecting %r type for key; got %r' % |
---|
| 577 | (basestring, type(key))) |
---|
[6803] | 578 | |
---|
[6790] | 579 | return True |
---|
| 580 | |
---|
| 581 | @staticmethod |
---|
| 582 | def valueFilter(value): |
---|
[7088] | 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 | """ |
---|
[6803] | 591 | if value is NotImplemented: |
---|
| 592 | return True |
---|
| 593 | |
---|
| 594 | elif not issubclass(value, AbstractFunction): |
---|
[6790] | 595 | raise TypeError('Expecting %r derived type for value; got %r' % |
---|
[6803] | 596 | (AbstractFunction, value)) |
---|
| 597 | |
---|
[6790] | 598 | return True |
---|
| 599 | |
---|
[7072] | 600 | def loadAll(self): |
---|
[6777] | 601 | """Load function map with implementations from the relevant function |
---|
| 602 | package""" |
---|
| 603 | |
---|
| 604 | for functionNs in XacmlFunctionNames.FUNCTION_NAMES: |
---|
[7072] | 605 | self.loadFunction(functionNs) |
---|
[6777] | 606 | |
---|
[7072] | 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 |
---|
[6777] | 619 | |
---|
[7072] | 620 | # No Factory has been cached for this function yet |
---|
[6777] | 621 | cls = FunctionMap |
---|
| 622 | classPath = None |
---|
[6802] | 623 | |
---|
[6777] | 624 | for namespacePrefix, pkgNamePrefix in cls.SUPPORTED_NSS.items(): |
---|
| 625 | if functionNs.startswith(namespacePrefix): |
---|
[7072] | 626 | # Namespace is recognised - translate into a path to a |
---|
| 627 | # function class in the right functions package |
---|
[6777] | 628 | functionName = functionNs.split(namespacePrefix)[-1] |
---|
| 629 | functionNameParts = functionName.split('-') |
---|
| 630 | |
---|
[6796] | 631 | if len(functionNameParts) == 1: |
---|
| 632 | moduleName = functionNameParts[0] |
---|
[7064] | 633 | |
---|
| 634 | elif functionName.startswith('xpath-node'): |
---|
| 635 | # Ugly hack for xpath-node functions |
---|
| 636 | moduleName = functionNameParts[-1].lower() |
---|
[6796] | 637 | else: |
---|
| 638 | moduleName = '_'.join(functionNameParts[1:]).lower() |
---|
| 639 | |
---|
[6803] | 640 | classPath = pkgNamePrefix + moduleName + '.' + \ |
---|
| 641 | cls.FUNCTION_CLASS_FACTORY_CLASSNAME |
---|
[6777] | 642 | break |
---|
| 643 | |
---|
| 644 | if classPath is None: |
---|
| 645 | raise FunctionMapConfigError('Namespace for function not ' |
---|
| 646 | 'recognised: %r' % functionNs) |
---|
| 647 | |
---|
[7072] | 648 | # Try instantiating the function class and loading it into the map |
---|
[6777] | 649 | try: |
---|
[6803] | 650 | functionFactory = callModuleObject(classPath) |
---|
[6804] | 651 | |
---|
[7072] | 652 | except (ImportError, AttributeError), e: |
---|
[6803] | 653 | log.error("Error importing function factory class %r for function " |
---|
[7072] | 654 | "identifier %r: %s", classPath, functionNs, str(e)) |
---|
[6803] | 655 | |
---|
[6777] | 656 | # No implementation exists - default to Abstract function |
---|
| 657 | self[functionNs] = NotImplemented |
---|
[6804] | 658 | else: |
---|
[7351] | 659 | function = functionFactory(functionNs) |
---|
| 660 | if function is None: |
---|
| 661 | raise unsupportedFunctionErrorFactory(functionNs) |
---|
| 662 | |
---|
| 663 | self[functionNs] = function |
---|
[7072] | 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 |
---|
[7088] | 669 | |
---|
| 670 | @param key: function URN |
---|
| 671 | @type key: basestring |
---|
| 672 | @return: function class |
---|
| 673 | @rtype: ndg.xacml.core.functions.AbstractFunction / NotImplemented |
---|
[7072] | 674 | """ |
---|
| 675 | functionClass = VettedDict.get(self, key) |
---|
| 676 | if functionClass is None: |
---|
| 677 | self.loadFunction(key) |
---|
[6777] | 678 | |
---|
[7072] | 679 | return VettedDict.__getitem__(self, key) |
---|
[6804] | 680 | |
---|
[7072] | 681 | def get(self, key, *arg): |
---|
| 682 | """Likewise to __getitem__, enable loading and caching of function |
---|
| 683 | classes if they don't otherwise exist |
---|
[7088] | 684 | |
---|
| 685 | @param key: XACML function URN |
---|
| 686 | @type key: basestring |
---|
[7109] | 687 | @param arg: set a single additional argument if required which is |
---|
[7088] | 688 | used as the default value should the key not be found in the map |
---|
[7109] | 689 | @type arg: tuple |
---|
[7088] | 690 | @return: function class |
---|
| 691 | @rtype: ndg.xacml.core.functions.AbstractFunction / NotImplemented |
---|
[7072] | 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 |
---|
[6777] | 699 | |
---|
| 700 | |
---|
| 701 | |
---|