1 | """NDG XACML attribute type definition |
---|
2 | |
---|
3 | NERC DataGrid |
---|
4 | """ |
---|
5 | __author__ = "P J Kershaw" |
---|
6 | __date__ = "25/02/10" |
---|
7 | __copyright__ = "(C) 2010 Science and Technology Facilities Council" |
---|
8 | __contact__ = "Philip.Kershaw@stfc.ac.uk" |
---|
9 | __license__ = "BSD - see LICENSE file in top-level directory" |
---|
10 | __contact__ = "Philip.Kershaw@stfc.ac.uk" |
---|
11 | __revision__ = "$Id$" |
---|
12 | from datetime import datetime, timedelta |
---|
13 | |
---|
14 | from ndg.xacml.utils import VettedDict |
---|
15 | from ndg.xacml.core.expression import Expression |
---|
16 | |
---|
17 | |
---|
18 | class AttributeValue(Expression): |
---|
19 | """XACML Attribute Value type |
---|
20 | |
---|
21 | @cvar ELEMENT_LOCAL_NAME: XML local name for this element |
---|
22 | @type ELEMENT_LOCAL_NAME: string |
---|
23 | @cvar CLASS_NAME_SUFFIX: all attribute value classes end with this suffix |
---|
24 | @type CLASS_NAME_SUFFIX: string |
---|
25 | @cvar IDENTIFIER_PREFIX: geberic prefix for attribute value URNs |
---|
26 | @type IDENTIFIER_PREFIX: string |
---|
27 | @cvar IDENTIFIER: URN for attribute value in derived class |
---|
28 | @type IDENTIFIER: NoneType - derived classes should set to appropriate |
---|
29 | string |
---|
30 | @cvar TYPE_URIS: URIs for all the different supported types |
---|
31 | @type TYPE_URIS: tuple |
---|
32 | @cvar TYPE_NAMES: corresponding short names for all the types |
---|
33 | @type TYPE_NAMES: tuple |
---|
34 | @cvar NATIVE_TYPES: equivalent python types as implemented |
---|
35 | @cvar TYPE_MAP: mapping from type names to python types |
---|
36 | @type TYPE_MAP: dict |
---|
37 | @cvar TYPE_URI_MAP: mapping from type names to type URIs |
---|
38 | @type TYPE_URI_MAP: dict |
---|
39 | @cvar TYPE: type name for derived type - set to None in this parent class |
---|
40 | @type TYPE: NoneType / string in derived type |
---|
41 | |
---|
42 | @ivar __value: setting for this attribute value |
---|
43 | @type __value: any - constrained in derived classes |
---|
44 | """ |
---|
45 | ELEMENT_LOCAL_NAME = 'AttributeValue' |
---|
46 | CLASS_NAME_SUFFIX = 'AttributeValue' |
---|
47 | IDENTIFIER_PREFIX = 'http://www.w3.org/2001/XMLSchema#' |
---|
48 | |
---|
49 | IDENTIFIER = None |
---|
50 | |
---|
51 | STRING_TYPE_URI = IDENTIFIER_PREFIX + 'string' |
---|
52 | ANY_TYPE_URI = IDENTIFIER_PREFIX + 'anyURI' |
---|
53 | INTEGER_TYPE_URI = IDENTIFIER_PREFIX + 'integer' |
---|
54 | BOOLEAN_TYPE_URI = IDENTIFIER_PREFIX + 'boolean' |
---|
55 | DOUBLE_TYPE_URI = IDENTIFIER_PREFIX + 'double' |
---|
56 | DATE_TYPE_URI = IDENTIFIER_PREFIX + 'date' |
---|
57 | DATETIME_TYPE_URI = IDENTIFIER_PREFIX + 'dateTime' |
---|
58 | TIME_TYPE_URI = IDENTIFIER_PREFIX + 'time' |
---|
59 | DAYTIMEDURATION_TYPE_URI = \ |
---|
60 | 'http://www.w3.org/TR/2002/WD-xquery-operators-20020816#dayTimeDuration' |
---|
61 | YEARMONTHDURATION_TYPE_URI = \ |
---|
62 | 'http://www.w3.org/TR/2002/WD-xquery-operators-20020816#yearMonthDuration' |
---|
63 | X500NAME_TYPE_URI = 'urn:oasis:names:tc:xacml:1.0:data-type:x500Name' |
---|
64 | RFC822NAME_TYPE_URI = 'urn:oasis:names:tc:xacml:1.0:data-type:rfc822Name' |
---|
65 | HEXBINARY_TYPE_URI = IDENTIFIER_PREFIX + 'hexBinary' |
---|
66 | BASE64BINARY_TYPE_URI = IDENTIFIER_PREFIX + 'base64Binary' |
---|
67 | IPADDRESS_TYPE_URI = 'urn:oasis:names:tc:xacml:2.0:data-type:ipAddress' |
---|
68 | DNSNAME_TYPE_URI = 'urn:oasis:names:tc:xacml:2.0:data-type:dnsName' |
---|
69 | |
---|
70 | TYPE_URIS = ( |
---|
71 | STRING_TYPE_URI, |
---|
72 | ANY_TYPE_URI, |
---|
73 | INTEGER_TYPE_URI, |
---|
74 | BOOLEAN_TYPE_URI, |
---|
75 | DOUBLE_TYPE_URI, |
---|
76 | DATE_TYPE_URI, |
---|
77 | DATETIME_TYPE_URI, |
---|
78 | TIME_TYPE_URI, |
---|
79 | DAYTIMEDURATION_TYPE_URI, |
---|
80 | YEARMONTHDURATION_TYPE_URI, |
---|
81 | X500NAME_TYPE_URI, |
---|
82 | RFC822NAME_TYPE_URI, |
---|
83 | HEXBINARY_TYPE_URI, |
---|
84 | BASE64BINARY_TYPE_URI, |
---|
85 | IPADDRESS_TYPE_URI, |
---|
86 | DNSNAME_TYPE_URI |
---|
87 | ) |
---|
88 | TYPE_NAMES = ( |
---|
89 | 'String', |
---|
90 | 'AnyURI', |
---|
91 | 'Integer', |
---|
92 | 'Boolean', |
---|
93 | 'Double', |
---|
94 | 'Date', |
---|
95 | 'DateTime', |
---|
96 | 'Time', |
---|
97 | 'DayTimeDuration', |
---|
98 | 'YearMonthDuration', |
---|
99 | 'X500Name', |
---|
100 | 'Rfc822Name', |
---|
101 | 'HexBinary', |
---|
102 | 'Base64Binary', |
---|
103 | 'IpAddress', |
---|
104 | 'DnsName', |
---|
105 | ) |
---|
106 | NATIVE_TYPES = ( |
---|
107 | basestring, |
---|
108 | basestring, |
---|
109 | int, |
---|
110 | bool, |
---|
111 | float, |
---|
112 | datetime, |
---|
113 | datetime, |
---|
114 | datetime, |
---|
115 | timedelta, |
---|
116 | timedelta, |
---|
117 | basestring, |
---|
118 | basestring, |
---|
119 | int, |
---|
120 | NotImplemented, |
---|
121 | basestring, |
---|
122 | basestring |
---|
123 | ) |
---|
124 | TYPE_MAP = dict(zip(TYPE_NAMES, NATIVE_TYPES)) |
---|
125 | TYPE_URI_MAP = dict(zip(TYPE_NAMES, TYPE_URIS)) |
---|
126 | TYPE = None |
---|
127 | |
---|
128 | __slots__ = ('__value',) |
---|
129 | |
---|
130 | def __init__(self, value=None): |
---|
131 | """Derived classes must override setting TYPE class variable |
---|
132 | |
---|
133 | @param value: initialise the attribute value by setting this keyword |
---|
134 | @type value: (set by self.__class__.TYPE) |
---|
135 | """ |
---|
136 | |
---|
137 | super(AttributeValue, self).__init__() |
---|
138 | if self.__class__.TYPE is None: |
---|
139 | raise NotImplementedError('TYPE class variable must be set to a ' |
---|
140 | 'valid type in a derived class') |
---|
141 | |
---|
142 | self.__value = None |
---|
143 | |
---|
144 | # Allow derived classes to make an implicit data type setting |
---|
145 | self.dataType = self.__class__.IDENTIFIER |
---|
146 | |
---|
147 | if value is not None: |
---|
148 | self.value = value |
---|
149 | |
---|
150 | def __repr__(self): |
---|
151 | return "%s = %r " % (super(AttributeValue, self).__repr__(), |
---|
152 | self.__value) |
---|
153 | |
---|
154 | def __eq__(self, attrVal): |
---|
155 | """Check for equality by comparing the value attributes""" |
---|
156 | if not isinstance(attrVal, self.__class__): |
---|
157 | raise TypeError('Expecting %r type for "value" ' |
---|
158 | 'attribute; got %r' % (self.__class__.TYPE, |
---|
159 | type(attrVal))) |
---|
160 | |
---|
161 | return self.__value == attrVal.value |
---|
162 | |
---|
163 | def _get_value(self): |
---|
164 | """Get value |
---|
165 | @return: setting for this attribute value |
---|
166 | @rtype: any - constrained in derived classes |
---|
167 | """ |
---|
168 | return self.__value |
---|
169 | |
---|
170 | def _set_value(self, value): |
---|
171 | """Set value |
---|
172 | |
---|
173 | @param value: setting for this attribute value |
---|
174 | @type value: any - constrained in derived classes |
---|
175 | @raise TypeError: if type doesn't match TYPE class variable. Derived |
---|
176 | classes should set this |
---|
177 | """ |
---|
178 | if not isinstance(value, self.__class__.TYPE): |
---|
179 | raise TypeError('Expecting %r type for "value" ' |
---|
180 | 'attribute; got %r' % (self.__class__.TYPE, |
---|
181 | type(value))) |
---|
182 | |
---|
183 | self.__value = value |
---|
184 | |
---|
185 | value = property(_get_value, _set_value, None, "expression value") |
---|
186 | |
---|
187 | def evaluate(self, context): |
---|
188 | """Evaluate the result of the expression in a condition. In the case of |
---|
189 | an attribute value it's simply itself |
---|
190 | |
---|
191 | @param context: the request context |
---|
192 | @type context: ndg.xacml.core.context.request.Request |
---|
193 | @return: this attribute value |
---|
194 | @rtype: AttributeValue |
---|
195 | """ |
---|
196 | return self |
---|
197 | |
---|
198 | |
---|
199 | class AttributeValueClassMap(VettedDict): |
---|
200 | """Specialised dictionary to hold mappings of XML attribute type URIs to |
---|
201 | their equivalent classes |
---|
202 | """ |
---|
203 | |
---|
204 | def __init__(self): |
---|
205 | """Force entries to derive from AttributeValue and IDs to |
---|
206 | be string type |
---|
207 | """ |
---|
208 | # Filters are defined as staticmethods but reference via self here to |
---|
209 | # enable derived class to override them as standard methods without |
---|
210 | # needing to redefine this __init__ method |
---|
211 | VettedDict.__init__(self, self.keyFilter, self.valueFilter) |
---|
212 | |
---|
213 | @staticmethod |
---|
214 | def keyFilter(key): |
---|
215 | """Enforce string type keys |
---|
216 | |
---|
217 | @param key: URN for attribute |
---|
218 | @type key: basestring |
---|
219 | @return: boolean True indicating key is OK |
---|
220 | @rtype: bool |
---|
221 | @raise TypeError: incorrect input type |
---|
222 | """ |
---|
223 | if not isinstance(key, basestring): |
---|
224 | raise TypeError('Expecting %r type for key; got %r' % |
---|
225 | (basestring, type(key))) |
---|
226 | return True |
---|
227 | |
---|
228 | @staticmethod |
---|
229 | def valueFilter(value): |
---|
230 | """Enforce AttributeValue derived types for values |
---|
231 | @param value: attribute value |
---|
232 | @type value: ndg.xacml.core.attributevalue.AttributeValue derived type |
---|
233 | @return: boolean True indicating attribute value is correct type |
---|
234 | @rtype: bool |
---|
235 | @raise TypeError: incorrect input type |
---|
236 | """ |
---|
237 | if not issubclass(value, AttributeValue): |
---|
238 | raise TypeError('Expecting %r derived type for value; got %r' % |
---|
239 | (AttributeValue, type(value))) |
---|
240 | return True |
---|
241 | |
---|
242 | |
---|
243 | # Dynamically Create classes based on AttributeValue for all the XACML primitive |
---|
244 | # types |
---|
245 | _IDENTIFIER2CLASS_MAP = AttributeValueClassMap() |
---|
246 | |
---|
247 | for typeName, _type in AttributeValue.TYPE_MAP.items(): |
---|
248 | identifier = AttributeValue.TYPE_URI_MAP[typeName] |
---|
249 | |
---|
250 | className = typeName + AttributeValue.CLASS_NAME_SUFFIX |
---|
251 | classVars = {'TYPE': _type, 'IDENTIFIER': identifier} |
---|
252 | |
---|
253 | attributeValueClass = type(className, (AttributeValue, ), classVars) |
---|
254 | AttributeValue.register(attributeValueClass) |
---|
255 | _IDENTIFIER2CLASS_MAP[identifier] = attributeValueClass |
---|
256 | |
---|
257 | |
---|
258 | class AttributeValueClassFactory(object): |
---|
259 | """Create AttributeValue types based on the XML namespace identifier |
---|
260 | |
---|
261 | Convenience wrapper for _IDENTIFIER2CLASS_MAP instance of |
---|
262 | AttributeValueClassMap |
---|
263 | |
---|
264 | @ivar __classMap: mapping object to map attribute value URIs to their |
---|
265 | implementations as classes |
---|
266 | @type __classMap: ndg.xacml.core.attributevalue.AttributeValueClassMap |
---|
267 | """ |
---|
268 | __slots__ = ('__classMap',) |
---|
269 | |
---|
270 | def __init__(self, classMap=None): |
---|
271 | """Set a mapping object to map attribute value URIs to their |
---|
272 | implementations as classes |
---|
273 | |
---|
274 | @param classMap: input an alternative to the default class mapping |
---|
275 | object _IDENTIFIER2CLASS_MAP, if None, it will default to this setting |
---|
276 | @type classMap: ndg.xacml.core.attributevalue.AttributeValueClassMap |
---|
277 | """ |
---|
278 | if classMap is None: |
---|
279 | self.__classMap = _IDENTIFIER2CLASS_MAP |
---|
280 | elif isinstance(classMap, AttributeValueClassMap): |
---|
281 | self.__classMap = classMap |
---|
282 | else: |
---|
283 | raise TypeError('Expecting %r derived type for "map" input; got %r' |
---|
284 | % (AttributeValueClassMap, type(map))) |
---|
285 | |
---|
286 | def __call__(self, identifier): |
---|
287 | """Return <type>AttributeValue class for given identifier URI or None |
---|
288 | if no match is found |
---|
289 | |
---|
290 | @return: attribute value class |
---|
291 | @rtype: NoneType / ndg.xacml.core.attributevalue.AttributeValue derived |
---|
292 | type |
---|
293 | """ |
---|
294 | return self.__classMap.get(identifier) |
---|
295 | |
---|