13 from logging import getLogger |
13 from logging import getLogger |
14 from warnings import warn |
14 from warnings import warn |
15 |
15 |
16 from logilab.common.decorators import cached, clear_cache, monkeypatch |
16 from logilab.common.decorators import cached, clear_cache, monkeypatch |
17 from logilab.common.logging_ext import set_log_methods |
17 from logilab.common.logging_ext import set_log_methods |
18 from logilab.common.deprecation import deprecated |
18 from logilab.common.deprecation import deprecated, class_moved |
19 from logilab.common.graph import get_cycles |
19 from logilab.common.graph import get_cycles |
20 from logilab.common.compat import any |
20 from logilab.common.compat import any |
21 |
21 |
22 from yams import BadSchemaDefinition, buildobjs as ybo |
22 from yams import BadSchemaDefinition, buildobjs as ybo |
23 from yams.schema import Schema, ERSchema, EntitySchema, RelationSchema |
23 from yams.schema import Schema, ERSchema, EntitySchema, RelationSchema, \ |
24 from yams.constraints import (BaseConstraint, StaticVocabularyConstraint, |
24 RelationDefinitionSchema, PermissionMixIn |
25 FormatConstraint) |
25 from yams.constraints import BaseConstraint, FormatConstraint |
26 from yams.reader import (CONSTRAINTS, PyFileReader, SchemaLoader, |
26 from yams.reader import (CONSTRAINTS, PyFileReader, SchemaLoader, |
27 obsolete as yobsolete, cleanup_sys_modules) |
27 obsolete as yobsolete, cleanup_sys_modules) |
28 |
28 |
29 from rql import parse, nodes, RQLSyntaxError, TypeResolverException |
29 from rql import parse, nodes, RQLSyntaxError, TypeResolverException |
30 |
30 |
31 import cubicweb |
31 import cubicweb |
32 from cubicweb import ETYPE_NAME_MAP, ValidationError, Unauthorized |
32 from cubicweb import ETYPE_NAME_MAP, ValidationError, Unauthorized |
33 |
|
34 # XXX <3.2 bw compat |
|
35 from yams import schema |
|
36 schema.use_py_datetime() |
|
37 nodes.use_py_datetime() |
|
38 |
33 |
39 PURE_VIRTUAL_RTYPES = set(('identity', 'has_text',)) |
34 PURE_VIRTUAL_RTYPES = set(('identity', 'has_text',)) |
40 VIRTUAL_RTYPES = set(('eid', 'identity', 'has_text',)) |
35 VIRTUAL_RTYPES = set(('eid', 'identity', 'has_text',)) |
41 |
36 |
42 # set of meta-relations available for every entity types |
37 # set of meta-relations available for every entity types |
52 'CWConstraint', 'CWConstraintType', 'RQLExpression', |
47 'CWConstraint', 'CWConstraintType', 'RQLExpression', |
53 'relation_type', 'from_entity', 'to_entity', |
48 'relation_type', 'from_entity', 'to_entity', |
54 'constrained_by', 'cstrtype', |
49 'constrained_by', 'cstrtype', |
55 )) |
50 )) |
56 |
51 |
|
52 WORKFLOW_TYPES = set(('Transition', 'State', 'TrInfo', 'Workflow', |
|
53 'WorkflowTransition', 'BaseTransition', |
|
54 'SubWorkflowExitPoint')) |
|
55 |
|
56 INTERNAL_TYPES = set(('CWProperty', 'CWPermission', 'CWCache', 'ExternalUri')) |
|
57 |
|
58 |
57 _LOGGER = getLogger('cubicweb.schemaloader') |
59 _LOGGER = getLogger('cubicweb.schemaloader') |
58 |
60 |
59 # schema entities created from serialized schema have an eid rproperty |
61 # schema entities created from serialized schema have an eid rproperty |
60 ybo.ETYPE_PROPERTIES += ('eid',) |
62 ybo.ETYPE_PROPERTIES += ('eid',) |
61 ybo.RTYPE_PROPERTIES += ('eid',) |
63 ybo.RTYPE_PROPERTIES += ('eid',) |
62 ybo.RDEF_PROPERTIES += ('eid',) |
64 ybo.RDEF_PROPERTIES += ('eid',) |
63 |
65 |
|
66 |
|
67 PUB_SYSTEM_ENTITY_PERMS = { |
|
68 'read': ('managers', 'users', 'guests',), |
|
69 'add': ('managers',), |
|
70 'delete': ('managers',), |
|
71 'update': ('managers',), |
|
72 } |
|
73 PUB_SYSTEM_REL_PERMS = { |
|
74 'read': ('managers', 'users', 'guests',), |
|
75 'add': ('managers',), |
|
76 'delete': ('managers',), |
|
77 } |
|
78 PUB_SYSTEM_ATTR_PERMS = { |
|
79 'read': ('managers', 'users', 'guests',), |
|
80 'update': ('managers',), |
|
81 } |
|
82 RO_REL_PERMS = { |
|
83 'read': ('managers', 'users', 'guests',), |
|
84 'add': (), |
|
85 'delete': (), |
|
86 } |
|
87 RO_ATTR_PERMS = { |
|
88 'read': ('managers', 'users', 'guests',), |
|
89 'update': (), |
|
90 } |
64 |
91 |
65 # XXX same algorithm as in reorder_cubes and probably other place, |
92 # XXX same algorithm as in reorder_cubes and probably other place, |
66 # may probably extract a generic function |
93 # may probably extract a generic function |
67 def order_eschemas(eschemas): |
94 def order_eschemas(eschemas): |
68 """return entity schemas ordered such that entity types which specializes an |
95 """return entity schemas ordered such that entity types which specializes an |
170 :return: names of the groups with the given permission |
197 :return: names of the groups with the given permission |
171 """ |
198 """ |
172 assert action in self.ACTIONS, action |
199 assert action in self.ACTIONS, action |
173 #assert action in self._groups, '%s %s' % (self, action) |
200 #assert action in self._groups, '%s %s' % (self, action) |
174 try: |
201 try: |
175 return frozenset(g for g in self._groups[action] if isinstance(g, basestring)) |
202 return frozenset(g for g in self.permissions[action] if isinstance(g, basestring)) |
176 except KeyError: |
203 except KeyError: |
177 return () |
204 return () |
178 ERSchema.get_groups = ERSchema_get_groups |
205 PermissionMixIn.get_groups = get_groups |
179 |
|
180 def ERSchema_set_groups(self, action, groups): |
|
181 """set the groups allowed to perform <action> on entities of this type. Don't |
|
182 change rql expressions for the same action. |
|
183 |
|
184 :type action: str |
|
185 :param action: the name of a permission |
|
186 |
|
187 :type groups: list or tuple |
|
188 :param groups: names of the groups granted to do the given action |
|
189 """ |
|
190 assert action in self.ACTIONS, action |
|
191 clear_cache(self, 'ERSchema_get_groups') |
|
192 self._groups[action] = tuple(groups) + self.get_rqlexprs(action) |
|
193 ERSchema.set_groups = ERSchema_set_groups |
|
194 |
206 |
195 @cached |
207 @cached |
196 def ERSchema_get_rqlexprs(self, action): |
208 def get_rqlexprs(self, action): |
197 """return the rql expressions representing queries to check the user is allowed |
209 """return the rql expressions representing queries to check the user is allowed |
198 to perform <action> on entities of this type |
210 to perform <action> on entities of this type |
199 |
211 |
200 :type action: str |
212 :type action: str |
201 :param action: the name of a permission |
213 :param action: the name of a permission |
204 :return: the rql expressions with the given permission |
216 :return: the rql expressions with the given permission |
205 """ |
217 """ |
206 assert action in self.ACTIONS, action |
218 assert action in self.ACTIONS, action |
207 #assert action in self._rqlexprs, '%s %s' % (self, action) |
219 #assert action in self._rqlexprs, '%s %s' % (self, action) |
208 try: |
220 try: |
209 return tuple(g for g in self._groups[action] if not isinstance(g, basestring)) |
221 return tuple(g for g in self.permissions[action] if not isinstance(g, basestring)) |
210 except KeyError: |
222 except KeyError: |
211 return () |
223 return () |
212 ERSchema.get_rqlexprs = ERSchema_get_rqlexprs |
224 PermissionMixIn.get_rqlexprs = get_rqlexprs |
213 |
225 |
214 def ERSchema_set_rqlexprs(self, action, rqlexprs): |
226 orig_set_action_permissions = PermissionMixIn.set_action_permissions |
215 """set the rql expression allowing to perform <action> on entities of this type. Don't |
227 def set_action_permissions(self, action, permissions): |
216 change groups for the same action. |
228 """set the groups and rql expressions allowing to perform <action> on |
|
229 entities of this type |
217 |
230 |
218 :type action: str |
231 :type action: str |
219 :param action: the name of a permission |
232 :param action: the name of a permission |
220 |
233 |
221 :type rqlexprs: list or tuple |
|
222 :param rqlexprs: the rql expressions allowing the given action |
|
223 """ |
|
224 assert action in self.ACTIONS, action |
|
225 clear_cache(self, 'ERSchema_get_rqlexprs') |
|
226 self._groups[action] = tuple(self.get_groups(action)) + tuple(rqlexprs) |
|
227 ERSchema.set_rqlexprs = ERSchema_set_rqlexprs |
|
228 |
|
229 def ERSchema_set_permissions(self, action, permissions): |
|
230 """set the groups and rql expressions allowing to perform <action> on |
|
231 entities of this type |
|
232 |
|
233 :type action: str |
|
234 :param action: the name of a permission |
|
235 |
|
236 :type permissions: tuple |
234 :type permissions: tuple |
237 :param permissions: the groups and rql expressions allowing the given action |
235 :param permissions: the groups and rql expressions allowing the given action |
238 """ |
236 """ |
239 assert action in self.ACTIONS, action |
237 orig_set_action_permissions(self, action, tuple(permissions)) |
240 clear_cache(self, 'ERSchema_get_rqlexprs') |
238 clear_cache(self, 'get_rqlexprs') |
241 clear_cache(self, 'ERSchema_get_groups') |
239 clear_cache(self, 'get_groups') |
242 self._groups[action] = tuple(permissions) |
240 PermissionMixIn.set_action_permissions = set_action_permissions |
243 ERSchema.set_permissions = ERSchema_set_permissions |
241 |
244 |
242 def has_local_role(self, action): |
245 def ERSchema_has_perm(self, session, action, *args, **kwargs): |
|
246 """return true if the action is granted globaly or localy""" |
|
247 try: |
|
248 self.check_perm(session, action, *args, **kwargs) |
|
249 return True |
|
250 except Unauthorized: |
|
251 return False |
|
252 ERSchema.has_perm = ERSchema_has_perm |
|
253 |
|
254 def ERSchema_has_local_role(self, action): |
|
255 """return true if the action *may* be granted localy (eg either rql |
243 """return true if the action *may* be granted localy (eg either rql |
256 expressions or the owners group are used in security definition) |
244 expressions or the owners group are used in security definition) |
257 |
245 |
258 XXX this method is only there since we don't know well how to deal with |
246 XXX this method is only there since we don't know well how to deal with |
259 'add' action checking. Also find a better name would be nice. |
247 'add' action checking. Also find a better name would be nice. |
260 """ |
248 """ |
261 assert action in self.ACTIONS, action |
249 assert action in self.ACTIONS, action |
262 if self.get_rqlexprs(action): |
250 if self.get_rqlexprs(action): |
263 return True |
251 return True |
264 if action in ('update', 'delete'): |
252 if action in ('update', 'delete'): |
265 return self.has_group(action, 'owners') |
253 return 'owners' in self.get_groups(action) |
266 return False |
254 return False |
267 ERSchema.has_local_role = ERSchema_has_local_role |
255 PermissionMixIn.has_local_role = has_local_role |
|
256 |
|
257 def may_have_permission(self, action, req): |
|
258 if action != 'read' and not (self.has_local_role('read') or |
|
259 self.has_perm(req, 'read')): |
|
260 return False |
|
261 return self.has_local_role(action) or self.has_perm(req, action) |
|
262 PermissionMixIn.may_have_permission = may_have_permission |
|
263 |
|
264 def has_perm(self, session, action, **kwargs): |
|
265 """return true if the action is granted globaly or localy""" |
|
266 try: |
|
267 self.check_perm(session, action, **kwargs) |
|
268 return True |
|
269 except Unauthorized: |
|
270 return False |
|
271 PermissionMixIn.has_perm = has_perm |
|
272 |
|
273 def check_perm(self, session, action, **kwargs): |
|
274 # NB: session may be a server session or a request object check user is |
|
275 # in an allowed group, if so that's enough internal sessions should |
|
276 # always stop there |
|
277 groups = self.get_groups(action) |
|
278 if session.user.matching_groups(groups): |
|
279 return |
|
280 # if 'owners' in allowed groups, check if the user actually owns this |
|
281 # object, if so that's enough |
|
282 if 'owners' in groups and ( |
|
283 kwargs.get('creating') |
|
284 or ('eid' in kwargs and session.user.owns(kwargs['eid']))): |
|
285 return |
|
286 # else if there is some rql expressions, check them |
|
287 if any(rqlexpr.check(session, **kwargs) |
|
288 for rqlexpr in self.get_rqlexprs(action)): |
|
289 return |
|
290 raise Unauthorized(action, str(self)) |
|
291 PermissionMixIn.check_perm = check_perm |
|
292 |
|
293 |
|
294 RelationDefinitionSchema._RPROPERTIES['eid'] = None |
|
295 |
|
296 def rql_expression(self, expression, mainvars=None, eid=None): |
|
297 """rql expression factory""" |
|
298 if self.rtype.final: |
|
299 return ERQLExpression(expression, mainvars, eid) |
|
300 return RRQLExpression(expression, mainvars, eid) |
|
301 RelationDefinitionSchema.rql_expression = rql_expression |
|
302 |
|
303 orig_check_permission_definitions = RelationDefinitionSchema.check_permission_definitions |
|
304 def check_permission_definitions(self): |
|
305 orig_check_permission_definitions(self) |
|
306 schema = self.subject.schema |
|
307 for action, groups in self.permissions.iteritems(): |
|
308 for group_or_rqlexpr in groups: |
|
309 if action == 'read' and \ |
|
310 isinstance(group_or_rqlexpr, RQLExpression): |
|
311 msg = "can't use rql expression for read permission of %s" |
|
312 raise BadSchemaDefinition(msg % self) |
|
313 if self.final and isinstance(group_or_rqlexpr, RRQLExpression): |
|
314 msg = "can't use RRQLExpression on %s, use an ERQLExpression" |
|
315 raise BadSchemaDefinition(msg % self) |
|
316 if not self.final and isinstance(group_or_rqlexpr, ERQLExpression): |
|
317 msg = "can't use ERQLExpression on %s, use a RRQLExpression" |
|
318 raise BadSchemaDefinition(msg % self) |
|
319 RelationDefinitionSchema.check_permission_definitions = check_permission_definitions |
268 |
320 |
269 |
321 |
270 class CubicWebEntitySchema(EntitySchema): |
322 class CubicWebEntitySchema(EntitySchema): |
271 """a entity has a type, a set of subject and or object relations |
323 """a entity has a type, a set of subject and or object relations |
272 the entity schema defines the possible relations for a given type and some |
324 the entity schema defines the possible relations for a given type and some |
275 def __init__(self, schema=None, edef=None, eid=None, **kwargs): |
327 def __init__(self, schema=None, edef=None, eid=None, **kwargs): |
276 super(CubicWebEntitySchema, self).__init__(schema, edef, **kwargs) |
328 super(CubicWebEntitySchema, self).__init__(schema, edef, **kwargs) |
277 if eid is None and edef is not None: |
329 if eid is None and edef is not None: |
278 eid = getattr(edef, 'eid', None) |
330 eid = getattr(edef, 'eid', None) |
279 self.eid = eid |
331 self.eid = eid |
280 # take care: no _groups attribute when deep-copying |
332 |
281 if getattr(self, '_groups', None): |
333 def check_permission_definitions(self): |
282 for groups in self._groups.itervalues(): |
334 super(CubicWebEntitySchema, self).check_permission_definitions() |
283 for group_or_rqlexpr in groups: |
335 for groups in self.permissions.itervalues(): |
284 if isinstance(group_or_rqlexpr, RRQLExpression): |
336 for group_or_rqlexpr in groups: |
285 msg = "can't use RRQLExpression on an entity type, use an ERQLExpression (%s)" |
337 if isinstance(group_or_rqlexpr, RRQLExpression): |
286 raise BadSchemaDefinition(msg % self.type) |
338 msg = "can't use RRQLExpression on %s, use an ERQLExpression" |
|
339 raise BadSchemaDefinition(msg % self.type) |
287 |
340 |
288 def attribute_definitions(self): |
341 def attribute_definitions(self): |
289 """return an iterator on attribute definitions |
342 """return an iterator on attribute definitions |
290 |
343 |
291 attribute relations are a subset of subject relations where the |
344 attribute relations are a subset of subject relations where the |
340 else: |
393 else: |
341 need_has_text = False |
394 need_has_text = False |
342 if need_has_text is None: |
395 if need_has_text is None: |
343 need_has_text = may_need_has_text |
396 need_has_text = may_need_has_text |
344 if need_has_text and not has_has_text and not deletion: |
397 if need_has_text and not has_has_text and not deletion: |
345 rdef = ybo.RelationDefinition(self.type, 'has_text', 'String') |
398 rdef = ybo.RelationDefinition(self.type, 'has_text', 'String', |
|
399 __permissions__=RO_ATTR_PERMS) |
346 self.schema.add_relation_def(rdef) |
400 self.schema.add_relation_def(rdef) |
347 elif not need_has_text and has_has_text: |
401 elif not need_has_text and has_has_text: |
348 self.schema.del_relation_def(self.type, 'has_text', 'String') |
402 self.schema.del_relation_def(self.type, 'has_text', 'String') |
349 |
403 |
350 def schema_entity(self): |
404 def schema_entity(self): |
351 """return True if this entity type is used to build the schema""" |
405 """return True if this entity type is used to build the schema""" |
352 return self.type in SCHEMA_TYPES |
406 return self.type in SCHEMA_TYPES |
353 |
407 |
354 def check_perm(self, session, action, eid=None): |
|
355 # NB: session may be a server session or a request object |
|
356 user = session.user |
|
357 # check user is in an allowed group, if so that's enough |
|
358 # internal sessions should always stop there |
|
359 if user.matching_groups(self.get_groups(action)): |
|
360 return |
|
361 # if 'owners' in allowed groups, check if the user actually owns this |
|
362 # object, if so that's enough |
|
363 if eid is not None and 'owners' in self.get_groups(action) and \ |
|
364 user.owns(eid): |
|
365 return |
|
366 # else if there is some rql expressions, check them |
|
367 if any(rqlexpr.check(session, eid) |
|
368 for rqlexpr in self.get_rqlexprs(action)): |
|
369 return |
|
370 raise Unauthorized(action, str(self)) |
|
371 |
|
372 def rql_expression(self, expression, mainvars=None, eid=None): |
408 def rql_expression(self, expression, mainvars=None, eid=None): |
373 """rql expression factory""" |
409 """rql expression factory""" |
374 return ERQLExpression(expression, mainvars, eid) |
410 return ERQLExpression(expression, mainvars, eid) |
375 |
411 |
376 |
412 |
377 class CubicWebRelationSchema(RelationSchema): |
413 class CubicWebRelationSchema(RelationSchema): |
378 RelationSchema._RPROPERTIES['eid'] = None |
|
379 _perms_checked = False |
|
380 |
414 |
381 def __init__(self, schema=None, rdef=None, eid=None, **kwargs): |
415 def __init__(self, schema=None, rdef=None, eid=None, **kwargs): |
382 if rdef is not None: |
416 if rdef is not None: |
383 # if this relation is inlined |
417 # if this relation is inlined |
384 self.inlined = rdef.inlined |
418 self.inlined = rdef.inlined |
389 |
423 |
390 @property |
424 @property |
391 def meta(self): |
425 def meta(self): |
392 return self.type in META_RTYPES |
426 return self.type in META_RTYPES |
393 |
427 |
394 def update(self, subjschema, objschema, rdef): |
|
395 super(CubicWebRelationSchema, self).update(subjschema, objschema, rdef) |
|
396 if not self._perms_checked and self._groups: |
|
397 for action, groups in self._groups.iteritems(): |
|
398 for group_or_rqlexpr in groups: |
|
399 if action == 'read' and \ |
|
400 isinstance(group_or_rqlexpr, RQLExpression): |
|
401 msg = "can't use rql expression for read permission of "\ |
|
402 "a relation type (%s)" |
|
403 raise BadSchemaDefinition(msg % self.type) |
|
404 elif self.final and isinstance(group_or_rqlexpr, RRQLExpression): |
|
405 if self.schema.reading_from_database: |
|
406 # we didn't have final relation earlier, so turn |
|
407 # RRQLExpression into ERQLExpression now |
|
408 rqlexpr = group_or_rqlexpr |
|
409 newrqlexprs = [x for x in self.get_rqlexprs(action) if not x is rqlexpr] |
|
410 newrqlexprs.append(ERQLExpression(rqlexpr.expression, |
|
411 rqlexpr.mainvars, |
|
412 rqlexpr.eid)) |
|
413 self.set_rqlexprs(action, newrqlexprs) |
|
414 else: |
|
415 msg = "can't use RRQLExpression on a final relation "\ |
|
416 "type (eg attribute relation), use an ERQLExpression (%s)" |
|
417 raise BadSchemaDefinition(msg % self.type) |
|
418 elif not self.final and \ |
|
419 isinstance(group_or_rqlexpr, ERQLExpression): |
|
420 msg = "can't use ERQLExpression on a relation type, use "\ |
|
421 "a RRQLExpression (%s)" |
|
422 raise BadSchemaDefinition(msg % self.type) |
|
423 self._perms_checked = True |
|
424 |
|
425 def cardinality(self, subjtype, objtype, target): |
|
426 card = self.rproperty(subjtype, objtype, 'cardinality') |
|
427 return (target == 'subject' and card[0]) or \ |
|
428 (target == 'object' and card[1]) |
|
429 |
|
430 def schema_relation(self): |
428 def schema_relation(self): |
431 """return True if this relation type is used to build the schema""" |
429 """return True if this relation type is used to build the schema""" |
432 return self.type in SCHEMA_TYPES |
430 return self.type in SCHEMA_TYPES |
433 |
431 |
434 def physical_mode(self): |
432 def may_have_permission(self, action, req, eschema=None, role=None): |
435 """return an appropriate mode for physical storage of this relation type: |
433 if eschema is not None: |
436 * 'subjectinline' if every possible subject cardinalities are 1 or ? |
434 for tschema in self.targets(eschema, role): |
437 * 'objectinline' if 'subjectinline' mode is not possible but every |
435 rdef = self.role_rdef(eschema, tschema, role) |
438 possible object cardinalities are 1 or ? |
436 if rdef.may_have_permission(action, req): |
439 * None if neither 'subjectinline' and 'objectinline' |
437 return True |
440 """ |
438 else: |
441 assert not self.final |
439 for rdef in self.rdefs.itervalues(): |
442 return self.inlined and 'subjectinline' or None |
440 if rdef.may_have_permission(action, req): |
443 |
441 return True |
444 def check_perm(self, session, action, *args, **kwargs): |
442 return False |
445 # NB: session may be a server session or a request object check user is |
443 |
446 # in an allowed group, if so that's enough internal sessions should |
444 def has_perm(self, session, action, **kwargs): |
447 # always stop there |
445 """return true if the action is granted globaly or localy""" |
448 if session.user.matching_groups(self.get_groups(action)): |
|
449 return |
|
450 # else if there is some rql expressions, check them |
|
451 if any(rqlexpr.check(session, *args, **kwargs) |
|
452 for rqlexpr in self.get_rqlexprs(action)): |
|
453 return |
|
454 raise Unauthorized(action, str(self)) |
|
455 |
|
456 def rql_expression(self, expression, mainvars=None, eid=None): |
|
457 """rql expression factory""" |
|
458 if self.final: |
446 if self.final: |
459 return ERQLExpression(expression, mainvars, eid) |
447 assert not ('fromeid' in kwargs or 'toeid' in kwargs), kwargs |
460 return RRQLExpression(expression, mainvars, eid) |
448 assert action in ('read', 'update') |
|
449 if 'eid' in kwargs: |
|
450 subjtype = session.describe(kwargs['eid'])[0] |
|
451 else: |
|
452 subjtype = objtype = None |
|
453 else: |
|
454 assert not 'eid' in kwargs, kwargs |
|
455 assert action in ('read', 'add', 'delete') |
|
456 if 'fromeid' in kwargs: |
|
457 subjtype = session.describe(kwargs['fromeid'])[0] |
|
458 else: |
|
459 subjtype = None |
|
460 if 'toeid' in kwargs: |
|
461 objtype = session.describe(kwargs['toeid'])[0] |
|
462 else: |
|
463 objtype = None |
|
464 if objtype and subjtype: |
|
465 return self.rdef(subjtype, objtype).has_perm(session, action, **kwargs) |
|
466 elif subjtype: |
|
467 for tschema in self.targets(subjtype, 'subject'): |
|
468 rdef = self.rdef(subjtype, tschema) |
|
469 if not rdef.has_perm(session, action, **kwargs): |
|
470 return False |
|
471 elif objtype: |
|
472 for tschema in self.targets(objtype, 'object'): |
|
473 rdef = self.rdef(tschema, objtype) |
|
474 if not rdef.has_perm(session, action, **kwargs): |
|
475 return False |
|
476 else: |
|
477 for rdef in self.rdefs.itervalues(): |
|
478 if not rdef.has_perm(session, action, **kwargs): |
|
479 return False |
|
480 return True |
|
481 |
|
482 @deprecated('use .rdef(subjtype, objtype).role_cardinality(role)') |
|
483 def cardinality(self, subjtype, objtype, target): |
|
484 return self.rdef(subjtype, objtype).role_cardinality(target) |
461 |
485 |
462 |
486 |
463 class CubicWebSchema(Schema): |
487 class CubicWebSchema(Schema): |
464 """set of entities and relations schema defining the possible data sets |
488 """set of entities and relations schema defining the possible data sets |
465 used in an application |
489 used in an application |
466 |
|
467 |
490 |
468 :type name: str |
491 :type name: str |
469 :ivar name: name of the schema, usually the instance identifier |
492 :ivar name: name of the schema, usually the instance identifier |
470 |
493 |
471 :type base: str |
494 :type base: str |
480 self._eid_index = {} |
503 self._eid_index = {} |
481 super(CubicWebSchema, self).__init__(*args, **kwargs) |
504 super(CubicWebSchema, self).__init__(*args, **kwargs) |
482 ybo.register_base_types(self) |
505 ybo.register_base_types(self) |
483 rschema = self.add_relation_type(ybo.RelationType('eid')) |
506 rschema = self.add_relation_type(ybo.RelationType('eid')) |
484 rschema.final = True |
507 rschema.final = True |
485 rschema.set_default_groups() |
|
486 rschema = self.add_relation_type(ybo.RelationType('has_text')) |
508 rschema = self.add_relation_type(ybo.RelationType('has_text')) |
487 rschema.final = True |
509 rschema.final = True |
488 rschema.set_default_groups() |
|
489 rschema = self.add_relation_type(ybo.RelationType('identity')) |
510 rschema = self.add_relation_type(ybo.RelationType('identity')) |
490 rschema.final = False |
511 rschema.final = False |
491 rschema.set_default_groups() |
|
492 |
512 |
493 def add_entity_type(self, edef): |
513 def add_entity_type(self, edef): |
494 edef.name = edef.name.encode() |
514 edef.name = edef.name.encode() |
495 edef.name = bw_normalize_etype(edef.name) |
515 edef.name = bw_normalize_etype(edef.name) |
496 assert re.match(r'[A-Z][A-Za-z0-9]*[a-z]+[0-9]*$', edef.name), repr(edef.name) |
516 assert re.match(r'[A-Z][A-Za-z0-9]*[a-z]+[0-9]*$', edef.name), repr(edef.name) |
497 eschema = super(CubicWebSchema, self).add_entity_type(edef) |
517 eschema = super(CubicWebSchema, self).add_entity_type(edef) |
498 if not eschema.final: |
518 if not eschema.final: |
499 # automatically add the eid relation to non final entity types |
519 # automatically add the eid relation to non final entity types |
500 rdef = ybo.RelationDefinition(eschema.type, 'eid', 'Int', |
520 rdef = ybo.RelationDefinition(eschema.type, 'eid', 'Int', |
501 cardinality='11', uid=True) |
521 cardinality='11', uid=True, |
|
522 __permissions__=RO_ATTR_PERMS) |
502 self.add_relation_def(rdef) |
523 self.add_relation_def(rdef) |
503 rdef = ybo.RelationDefinition(eschema.type, 'identity', eschema.type) |
524 rdef = ybo.RelationDefinition(eschema.type, 'identity', eschema.type, |
|
525 __permissions__=RO_REL_PERMS) |
504 self.add_relation_def(rdef) |
526 self.add_relation_def(rdef) |
505 self._eid_index[eschema.eid] = eschema |
527 self._eid_index[eschema.eid] = eschema |
506 return eschema |
528 return eschema |
507 |
529 |
508 def add_relation_type(self, rdef): |
530 def add_relation_type(self, rdef): |
528 :param: the newly created or just completed relation schema |
550 :param: the newly created or just completed relation schema |
529 """ |
551 """ |
530 rdef.name = rdef.name.lower() |
552 rdef.name = rdef.name.lower() |
531 rdef.subject = bw_normalize_etype(rdef.subject) |
553 rdef.subject = bw_normalize_etype(rdef.subject) |
532 rdef.object = bw_normalize_etype(rdef.object) |
554 rdef.object = bw_normalize_etype(rdef.object) |
533 if super(CubicWebSchema, self).add_relation_def(rdef): |
555 rdefs = super(CubicWebSchema, self).add_relation_def(rdef) |
|
556 if rdefs: |
534 try: |
557 try: |
535 self._eid_index[rdef.eid] = (self.eschema(rdef.subject), |
558 self._eid_index[rdef.eid] = rdefs |
536 self.rschema(rdef.name), |
|
537 self.eschema(rdef.object)) |
|
538 except AttributeError: |
559 except AttributeError: |
539 pass # not a serialized schema |
560 pass # not a serialized schema |
|
561 return rdefs |
540 |
562 |
541 def del_relation_type(self, rtype): |
563 def del_relation_type(self, rtype): |
542 rschema = self.rschema(rtype) |
564 rschema = self.rschema(rtype) |
543 self._eid_index.pop(rschema.eid, None) |
565 self._eid_index.pop(rschema.eid, None) |
544 super(CubicWebSchema, self).del_relation_type(rtype) |
566 super(CubicWebSchema, self).del_relation_type(rtype) |
545 |
567 |
546 def del_relation_def(self, subjtype, rtype, objtype): |
568 def del_relation_def(self, subjtype, rtype, objtype): |
547 for k, v in self._eid_index.items(): |
569 for k, v in self._eid_index.items(): |
548 if v == (subjtype, rtype, objtype): |
570 if not isinstance(v, RelationDefinitionSchema): |
|
571 continue |
|
572 if v.subject == subjtype and v.rtype == rtype and v.object == objtype: |
549 del self._eid_index[k] |
573 del self._eid_index[k] |
550 break |
574 break |
551 super(CubicWebSchema, self).del_relation_def(subjtype, rtype, objtype) |
575 super(CubicWebSchema, self).del_relation_def(subjtype, rtype, objtype) |
552 |
576 |
553 def del_entity_type(self, etype): |
577 def del_entity_type(self, etype): |
785 """return True if the rql expression is matching the given relation |
814 """return True if the rql expression is matching the given relation |
786 between fromeid and toeid |
815 between fromeid and toeid |
787 |
816 |
788 session may actually be a request as well |
817 session may actually be a request as well |
789 """ |
818 """ |
790 if self.eid is not None: |
819 creating = kwargs.get('creating') |
|
820 if not creating and self.eid is not None: |
791 key = (self.eid, tuple(sorted(kwargs.iteritems()))) |
821 key = (self.eid, tuple(sorted(kwargs.iteritems()))) |
792 try: |
822 try: |
793 return session.local_perm_cache[key] |
823 return session.local_perm_cache[key] |
794 except KeyError: |
824 except KeyError: |
795 pass |
825 pass |
796 rql, has_perm_defs, keyarg = self.transform_has_permission() |
826 rql, has_perm_defs, keyarg = self.transform_has_permission() |
|
827 if creating: |
|
828 # when creating an entity, consider has_*_permission satisfied |
|
829 if has_perm_defs: |
|
830 return True |
|
831 return False |
797 if keyarg is None: |
832 if keyarg is None: |
798 # on the server side, use unsafe_execute, but this is not available |
833 # on the server side, use unsafe_execute, but this is not available |
799 # on the client side (session is actually a request) |
834 # on the client side (session is actually a request) |
800 execute = getattr(session, 'unsafe_execute', session.execute) |
835 execute = getattr(session, 'unsafe_execute', session.execute) |
801 # XXX what if 'u' in kwargs |
836 kwargs.setdefault('u', session.user.eid) |
802 cachekey = kwargs.keys() |
837 cachekey = kwargs.keys() |
803 kwargs['u'] = session.user.eid |
|
804 try: |
838 try: |
805 rset = execute(rql, kwargs, cachekey, build_descr=True) |
839 rset = execute(rql, kwargs, cachekey, build_descr=True) |
806 except NotImplementedError: |
840 except NotImplementedError: |
807 self.critical('cant check rql expression, unsupported rql %s', rql) |
841 self.critical('cant check rql expression, unsupported rql %s', rql) |
808 if self.eid is not None: |
842 if self.eid is not None: |