# HG changeset patch # User Sylvain Thénault # Date 1258631747 -3600 # Node ID 7ca53fc72a0a6d5b7f66bd1e3fb21192a821c430 # Parent 1169d3154be6f044309d81b94eab4727e0bec0f4 reldefsecurity branch : * follow yams default branch api changes * now consider permissions on relation definitions, not relation types. This is still experimental. diff -r 1169d3154be6 -r 7ca53fc72a0a devtools/_apptest.py --- a/devtools/_apptest.py Wed Nov 18 09:16:38 2009 +0100 +++ b/devtools/_apptest.py Thu Nov 19 12:55:47 2009 +0100 @@ -92,7 +92,7 @@ schema = self.vreg.schema # else we may run into problems since email address are ususally share in app tests # XXX should not be necessary anymore - schema.rschema('primary_email').set_rproperty('CWUser', 'EmailAddress', 'composite', False) + schema.rschema('primary_email').rdef('CWUser', 'EmailAddress').composite = False self.deletable_entities = unprotected_entities(schema) def restore_database(self): diff -r 1169d3154be6 -r 7ca53fc72a0a entity.py --- a/entity.py Wed Nov 18 09:16:38 2009 +0100 +++ b/entity.py Thu Nov 19 12:55:47 2009 +0100 @@ -34,7 +34,7 @@ def greater_card(rschema, subjtypes, objtypes, index): for subjtype in subjtypes: for objtype in objtypes: - card = rschema.rproperty(subjtype, objtype, 'cardinality')[index] + card = rschema.rdef(subjtype, objtype).cardinality[index] if card in '+*': return card return '1' @@ -243,7 +243,8 @@ cls.warning('skipping fetch_attr %s defined in %s (not found in schema)', attr, cls.id) continue - if not user.matching_groups(rschema.get_groups('read')): + rdef = eschema.rdef(attr) + if not user.matching_groups(rdef.get_groups('read')): continue var = varmaker.next() selection.append(var) @@ -252,7 +253,7 @@ if not rschema.final: # XXX this does not handle several destination types desttype = rschema.objects(eschema.type)[0] - card = rschema.rproperty(eschema, desttype, 'cardinality')[0] + card = rdef.cardinality[0] if card not in '?1': cls.warning('bad relation %s specified in fetch attrs for %s', attr, cls) @@ -362,10 +363,10 @@ self.req.local_perm_cache.pop((rqlexpr.eid, (('x', self.eid),)), None) def check_perm(self, action): - self.e_schema.check_perm(self.req, action, self.eid) + self.e_schema.check_perm(self.req, action, eid=self.eid) def has_perm(self, action): - return self.e_schema.has_perm(self.req, action, self.eid) + return self.e_schema.has_perm(self.req, action, eid=self.eid) def view(self, vid, __registry='views', **kwargs): """shortcut to apply a view on this entity""" @@ -443,11 +444,11 @@ return u'' if attrtype is None: attrtype = self.e_schema.destination(attr) - props = self.e_schema.rproperties(attr) + props = self.e_schema.rdef(attr) if attrtype == 'String': # internalinalized *and* formatted string such as schema # description... - if props.get('internationalizable'): + if props.internationalizable: value = self.req._(value) attrformat = self.attr_metadata(attr, 'format') if attrformat: @@ -495,11 +496,12 @@ if rschema.type in self.skip_copy_for: continue # skip composite relation - if self.e_schema.subjrproperty(rschema, 'composite'): + rdef = self.e_schema.rdef(rschema) + if rdef.composite: continue # skip relation with card in ?1 else we either change the copied # object (inlined relation) or inserting some inconsistency - if self.e_schema.subjrproperty(rschema, 'cardinality')[1] in '?1': + if rdef.cardinality[1] in '?1': continue rql = 'SET X %s V WHERE X eid %%(x)s, Y eid %%(y)s, Y %s V' % ( rschema.type, rschema.type) @@ -509,14 +511,15 @@ if rschema.meta: continue # skip already defined relations - if getattr(self, 'reverse_%s' % rschema.type): + if self.related(rschema.type, 'object'): continue + rdef = self.e_schema.rdef(rschema, 'object') # skip composite relation - if self.e_schema.objrproperty(rschema, 'composite'): + if rdef.composite: continue # skip relation with card in ?1 else we either change the copied # object (inlined relation) or inserting some inconsistency - if self.e_schema.objrproperty(rschema, 'cardinality')[0] in '?1': + if rdef.cardinality[0] in '?1': continue rql = 'SET V %s X WHERE X eid %%(x)s, Y eid %%(y)s, V %s Y' % ( rschema.type, rschema.type) @@ -537,15 +540,16 @@ for rschema in self.e_schema.subject_relations(): if rschema.final: continue - if len(rschema.objects(self.e_schema)) > 1: + targets = rschema.objects(self.e_schema) + if len(targets) > 1: # ambigous relations, the querier doesn't handle # outer join correctly in this case continue if rschema.inlined: + rdef = rschema.rdef(self.e_schema, targets[0]) matching_groups = self.req.user.matching_groups - if matching_groups(rschema.get_groups('read')) and \ - all(matching_groups(es.get_groups('read')) - for es in rschema.objects(self.e_schema)): + if matching_groups(rdef.get_groups('read')) and \ + all(matching_groups(e.get_groups('read')) for e in targets): yield rschema, 'subject' def to_complete_attributes(self, skip_bytes=True): @@ -557,7 +561,8 @@ if attr == 'eid': continue # password retreival is blocked at the repository server level - if not self.req.user.matching_groups(rschema.get_groups('read')) \ + rdef = rschema.rdef(self.e_schema, attrschema) + if not self.req.user.matching_groups(rdef.get_groups('read')) \ or attrschema.type == 'Password': self[attr] = None continue @@ -593,24 +598,21 @@ if self.relation_cached(rtype, role): continue var = varmaker.next() + targettype = rschema.targets(self.e_schema, role)[0] + rdef = rschema.role_rdef(self.e_schema, targettype, role) + card = rdef.role_cardinality(role) + assert card in '1?', '%s %s %s %s' % (self.e_schema, rtype, + role, card) if role == 'subject': - targettype = rschema.objects(self.e_schema)[0] - card = rschema.rproperty(self.e_schema, targettype, - 'cardinality')[0] if card == '1': rql.append('%s %s %s' % (V, rtype, var)) - else: # '?" + else: rql.append('%s %s %s?' % (V, rtype, var)) else: - targettype = rschema.subjects(self.e_schema)[1] - card = rschema.rproperty(self.e_schema, targettype, - 'cardinality')[1] if card == '1': rql.append('%s %s %s' % (var, rtype, V)) - else: # '?" + else: rql.append('%s? %s %s' % (var, rtype, V)) - assert card in '1?', '%s %s %s %s' % (self.e_schema, rtype, - role, card) selected.append(((rtype, role), var)) if selected: # select V, we need it as the left most selected variable @@ -756,16 +758,16 @@ restriction = [] args = {} securitycheck_args = {} - insertsecurity = (rtype.has_local_role('add') and not - rtype.has_perm(self.req, 'add', **securitycheck_args)) - constraints = rtype.rproperty(subjtype, objtype, 'constraints') + rdef = rtype.role_rdef(self.e_schema, targettype, role) + insertsecurity = (rdef.has_local_role('add') and not + rdef.has_perm(self.req, 'add', **securitycheck_args)) if vocabconstraints: # RQLConstraint is a subclass for RQLVocabularyConstraint, so they # will be included as well - restriction += [cstr.restriction for cstr in constraints + restriction += [cstr.restriction for cstr in rdef.constraints if isinstance(cstr, RQLVocabularyConstraint)] else: - restriction += [cstr.restriction for cstr in constraints + restriction += [cstr.restriction for cstr in rdef.constraints if isinstance(cstr, RQLConstraint)] etypecls = self.vreg['etypes'].etype_class(targettype) rql = etypecls.fetch_rql(self.req.user, restriction, @@ -775,7 +777,7 @@ before, after = rql.split(' WHERE ', 1) rql = '%s ORDERBY %s WHERE %s' % (before, searchedvar, after) if insertsecurity: - rqlexprs = rtype.get_rqlexprs('add') + rqlexprs = rdef.get_rqlexprs('add') rewriter = RQLRewriter(self.req) rqlst = self.req.vreg.parse(self.req, rql, args) if not self.has_eid(): @@ -827,12 +829,10 @@ related = tuple(rset.entities(col)) rschema = self.schema.rschema(rtype) if role == 'subject': - rcard = rschema.rproperty(self.e_schema, related[0].e_schema, - 'cardinality')[1] + rcard = rschema.rdef(self.e_schema, related[0].e_schema).cardinality[1] target = 'object' else: - rcard = rschema.rproperty(related[0].e_schema, self.e_schema, - 'cardinality')[0] + rcard = rschema.rdef(related[0].e_schema, self.e_schema).cardinality[0] target = 'subject' if rcard in '?1': for rentity in related: diff -r 1169d3154be6 -r 7ca53fc72a0a rqlrewrite.py --- a/rqlrewrite.py Wed Nov 18 09:16:38 2009 +0100 +++ b/rqlrewrite.py Thu Nov 19 12:55:47 2009 +0100 @@ -402,12 +402,12 @@ orel = self.varinfo['lhs_rels'][sniprel.r_type] cardindex = 0 ttypes_func = rschema.objects - rprop = rschema.rproperty + rdef = rschema.rdef else: # target == 'subject': orel = self.varinfo['rhs_rels'][sniprel.r_type] cardindex = 1 ttypes_func = rschema.subjects - rprop = lambda x, y, z: rschema.rproperty(y, x, z) + rdef = lambda x, y: rschema.rdef(y, x) except KeyError, ex: # may be raised by self.varinfo['xhs_rels'][sniprel.r_type] return None @@ -419,7 +419,7 @@ # variable from the original query for etype in self.varinfo['stinfo']['possibletypes']: for ttype in ttypes_func(etype): - if rprop(etype, ttype, 'cardinality')[cardindex] in '+*': + if rdef(etype, ttype).cardinality[cardindex] in '+*': return None return orel diff -r 1169d3154be6 -r 7ca53fc72a0a rset.py --- a/rset.py Wed Nov 18 09:16:38 2009 +0100 +++ b/rset.py Thu Nov 19 12:55:47 2009 +0100 @@ -403,25 +403,22 @@ select = rqlst # take care, due to outer join support, we may find None # values for non final relation - for i, attr, x in attr_desc_iterator(select, col): + for i, attr, role in attr_desc_iterator(select, col): outerselidx = rqlst.subquery_selection_index(select, i) if outerselidx is None: continue - if x == 'subject': + if role == 'subject': rschema = eschema.subjrels[attr] if rschema.final: entity[attr] = rowvalues[outerselidx] continue - tetype = rschema.objects(etype)[0] - card = rschema.rproperty(etype, tetype, 'cardinality')[0] else: rschema = eschema.objrels[attr] - tetype = rschema.subjects(etype)[0] - card = rschema.rproperty(tetype, etype, 'cardinality')[1] + rdef = eschema.rdef(attr, role) # only keep value if it can't be multivalued - if card in '1?': + if rdef.role_cardinality(role) in '1?': if rowvalues[outerselidx] is None: - if x == 'subject': + if role == 'subject': rql = 'Any Y WHERE X %s Y, X eid %s' else: rql = 'Any Y WHERE Y %s X, X eid %s' @@ -429,7 +426,7 @@ req.decorate_rset(rrset) else: rrset = self._build_entity(row, outerselidx).as_rset() - entity.set_related_cache(attr, x, rrset) + entity.set_related_cache(attr, role, rrset) return entity @cached diff -r 1169d3154be6 -r 7ca53fc72a0a schema.py --- a/schema.py Wed Nov 18 09:16:38 2009 +0100 +++ b/schema.py Thu Nov 19 12:55:47 2009 +0100 @@ -20,7 +20,8 @@ from logilab.common.compat import any from yams import BadSchemaDefinition, buildobjs as ybo -from yams.schema import Schema, ERSchema, EntitySchema, RelationSchema +from yams.schema import Schema, ERSchema, EntitySchema, RelationSchema, \ + RelationDefinitionSchema, PermissionMixIn from yams.constraints import (BaseConstraint, StaticVocabularyConstraint, FormatConstraint) from yams.reader import (CONSTRAINTS, PyFileReader, SchemaLoader, @@ -127,7 +128,7 @@ ERSchema.display_name = ERSchema_display_name @cached -def ERSchema_get_groups(self, action): +def get_groups(self, action): """return the groups authorized to perform on entities of this type @@ -140,28 +141,13 @@ assert action in self.ACTIONS, action #assert action in self._groups, '%s %s' % (self, action) try: - return frozenset(g for g in self._groups[action] if isinstance(g, basestring)) + return frozenset(g for g in self.permissions[action] if isinstance(g, basestring)) except KeyError: return () -ERSchema.get_groups = ERSchema_get_groups - -def ERSchema_set_groups(self, action, groups): - """set the groups allowed to perform on entities of this type. Don't - change rql expressions for the same action. - - :type action: str - :param action: the name of a permission - - :type groups: list or tuple - :param groups: names of the groups granted to do the given action - """ - assert action in self.ACTIONS, action - clear_cache(self, 'ERSchema_get_groups') - self._groups[action] = tuple(groups) + self.get_rqlexprs(action) -ERSchema.set_groups = ERSchema_set_groups +PermissionMixIn.get_groups = get_groups @cached -def ERSchema_get_rqlexprs(self, action): +def get_rqlexprs(self, action): """return the rql expressions representing queries to check the user is allowed to perform on entities of this type @@ -174,27 +160,13 @@ assert action in self.ACTIONS, action #assert action in self._rqlexprs, '%s %s' % (self, action) try: - return tuple(g for g in self._groups[action] if not isinstance(g, basestring)) + return tuple(g for g in self.permissions[action] if not isinstance(g, basestring)) except KeyError: return () -ERSchema.get_rqlexprs = ERSchema_get_rqlexprs - -def ERSchema_set_rqlexprs(self, action, rqlexprs): - """set the rql expression allowing to perform on entities of this type. Don't - change groups for the same action. - - :type action: str - :param action: the name of a permission +PermissionMixIn.get_rqlexprs = get_rqlexprs - :type rqlexprs: list or tuple - :param rqlexprs: the rql expressions allowing the given action - """ - assert action in self.ACTIONS, action - clear_cache(self, 'ERSchema_get_rqlexprs') - self._groups[action] = tuple(self.get_groups(action)) + tuple(rqlexprs) -ERSchema.set_rqlexprs = ERSchema_set_rqlexprs - -def ERSchema_set_permissions(self, action, permissions): +orig_set_action_permissions = PermissionMixIn.set_action_permissions +def set_action_permissions(self, action, permissions): """set the groups and rql expressions allowing to perform on entities of this type @@ -204,22 +176,12 @@ :type permissions: tuple :param permissions: the groups and rql expressions allowing the given action """ - assert action in self.ACTIONS, action - clear_cache(self, 'ERSchema_get_rqlexprs') - clear_cache(self, 'ERSchema_get_groups') - self._groups[action] = tuple(permissions) -ERSchema.set_permissions = ERSchema_set_permissions + orig_set_action_permissions(self, action, tuple(permissions)) + clear_cache(self, 'get_rqlexprs') + clear_cache(self, 'get_groups') +PermissionMixIn.set_action_permissions = set_action_permissions -def ERSchema_has_perm(self, session, action, *args, **kwargs): - """return true if the action is granted globaly or localy""" - try: - self.check_perm(session, action, *args, **kwargs) - return True - except Unauthorized: - return False -ERSchema.has_perm = ERSchema_has_perm - -def ERSchema_has_local_role(self, action): +def has_local_role(self, action): """return true if the action *may* be granted localy (eg either rql expressions or the owners group are used in security definition) @@ -230,9 +192,83 @@ if self.get_rqlexprs(action): return True if action in ('update', 'delete'): - return self.has_group(action, 'owners') + return 'owners' in self.get_groups(action) return False -ERSchema.has_local_role = ERSchema_has_local_role +PermissionMixIn.has_local_role = has_local_role + +def may_have_permission(self, action, req): + if action != 'read' and not (self.has_local_role('read') or + self.has_perm(req, 'read')): + return False + return self.has_local_role(action) or self.has_perm(req, action) +PermissionMixIn.may_have_permission = may_have_permission + +def has_perm(self, session, action, **kwargs): + """return true if the action is granted globaly or localy""" + try: + self.check_perm(session, action, **kwargs) + return True + except Unauthorized: + return False +PermissionMixIn.has_perm = has_perm + +def check_perm(self, session, action, **kwargs): + # NB: session may be a server session or a request object check user is + # in an allowed group, if so that's enough internal sessions should + # always stop there + groups = self.get_groups(action) + if session.user.matching_groups(groups): + return + # if 'owners' in allowed groups, check if the user actually owns this + # object, if so that's enough + if 'owners' in groups and 'eid' in kwargs and session.user.owns(kwargs['eid']): + return + # else if there is some rql expressions, check them + if any(rqlexpr.check(session, **kwargs) + for rqlexpr in self.get_rqlexprs(action)): + return + raise Unauthorized(action, str(self)) +PermissionMixIn.check_perm = check_perm + + +RelationDefinitionSchema._RPROPERTIES['eid'] = None + +def rql_expression(self, expression, mainvars=None, eid=None): + """rql expression factory""" + if self.rtype.final: + return ERQLExpression(expression, mainvars, eid) + return RRQLExpression(expression, mainvars, eid) +RelationDefinitionSchema.rql_expression = rql_expression + +orig_check_permission_definitions = RelationDefinitionSchema.check_permission_definitions +def check_permission_definitions(self): + orig_check_permission_definitions(self) + schema = self.subject.schema + for action, groups in self.permissions.iteritems(): + for group_or_rqlexpr in groups: + if action == 'read' and \ + isinstance(group_or_rqlexpr, RQLExpression): + msg = "can't use rql expression for read permission of %s" + raise BadSchemaDefinition(msg % self) + elif self.final and isinstance(group_or_rqlexpr, RRQLExpression): + if schema.reading_from_database: + # we didn't have final relation earlier, so turn + # RRQLExpression into ERQLExpression now + rqlexpr = group_or_rqlexpr + newrqlexprs = [x for x in self.get_rqlexprs(action) + if not x is rqlexpr] + newrqlexprs.append(ERQLExpression(rqlexpr.expression, + rqlexpr.mainvars, + rqlexpr.eid)) + self.set_rqlexprs(action, newrqlexprs) + else: + msg = "can't use RRQLExpression on %s, use an ERQLExpression" + raise BadSchemaDefinition(msg % self) + elif not self.final and \ + isinstance(group_or_rqlexpr, ERQLExpression): + msg = "can't use ERQLExpression on %s, use a RRQLExpression" + raise BadSchemaDefinition(msg % self) +RelationDefinitionSchema.check_permission_definitions = check_permission_definitions def system_etypes(schema): @@ -256,8 +292,8 @@ eid = getattr(edef, 'eid', None) self.eid = eid # take care: no _groups attribute when deep-copying - if getattr(self, '_groups', None): - for groups in self._groups.itervalues(): + if getattr(self, 'permissions', None): + for groups in self.permissions.itervalues(): for group_or_rqlexpr in groups: if isinstance(group_or_rqlexpr, RRQLExpression): msg = "can't use RRQLExpression on an entity type, use an ERQLExpression (%s)" @@ -304,7 +340,7 @@ if rschema.final: if rschema == 'has_text': has_has_text = True - elif self.rproperty(rschema, 'fulltextindexed'): + elif self.rdef(rschema).get('fulltextindexed'): may_need_has_text = True elif rschema.fulltext_container: if rschema.fulltext_container == 'subject': @@ -329,32 +365,12 @@ """return True if this entity type is used to build the schema""" return self.type in SCHEMA_TYPES - def check_perm(self, session, action, eid=None): - # NB: session may be a server session or a request object - user = session.user - # check user is in an allowed group, if so that's enough - # internal sessions should always stop there - if user.matching_groups(self.get_groups(action)): - return - # if 'owners' in allowed groups, check if the user actually owns this - # object, if so that's enough - if eid is not None and 'owners' in self.get_groups(action) and \ - user.owns(eid): - return - # else if there is some rql expressions, check them - if any(rqlexpr.check(session, eid) - for rqlexpr in self.get_rqlexprs(action)): - return - raise Unauthorized(action, str(self)) - def rql_expression(self, expression, mainvars=None, eid=None): """rql expression factory""" return ERQLExpression(expression, mainvars, eid) class CubicWebRelationSchema(RelationSchema): - RelationSchema._RPROPERTIES['eid'] = None - _perms_checked = False def __init__(self, schema=None, rdef=None, eid=None, **kwargs): if rdef is not None: @@ -369,73 +385,52 @@ def meta(self): return self.type in META_RTYPES - def update(self, subjschema, objschema, rdef): - super(CubicWebRelationSchema, self).update(subjschema, objschema, rdef) - if not self._perms_checked and self._groups: - for action, groups in self._groups.iteritems(): - for group_or_rqlexpr in groups: - if action == 'read' and \ - isinstance(group_or_rqlexpr, RQLExpression): - msg = "can't use rql expression for read permission of "\ - "a relation type (%s)" - raise BadSchemaDefinition(msg % self.type) - elif self.final and isinstance(group_or_rqlexpr, RRQLExpression): - if self.schema.reading_from_database: - # we didn't have final relation earlier, so turn - # RRQLExpression into ERQLExpression now - rqlexpr = group_or_rqlexpr - newrqlexprs = [x for x in self.get_rqlexprs(action) if not x is rqlexpr] - newrqlexprs.append(ERQLExpression(rqlexpr.expression, - rqlexpr.mainvars, - rqlexpr.eid)) - self.set_rqlexprs(action, newrqlexprs) - else: - msg = "can't use RRQLExpression on a final relation "\ - "type (eg attribute relation), use an ERQLExpression (%s)" - raise BadSchemaDefinition(msg % self.type) - elif not self.final and \ - isinstance(group_or_rqlexpr, ERQLExpression): - msg = "can't use ERQLExpression on a relation type, use "\ - "a RRQLExpression (%s)" - raise BadSchemaDefinition(msg % self.type) - self._perms_checked = True - - def cardinality(self, subjtype, objtype, target): - card = self.rproperty(subjtype, objtype, 'cardinality') - return (target == 'subject' and card[0]) or \ - (target == 'object' and card[1]) - def schema_relation(self): """return True if this relation type is used to build the schema""" return self.type in SCHEMA_TYPES - def physical_mode(self): - """return an appropriate mode for physical storage of this relation type: - * 'subjectinline' if every possible subject cardinalities are 1 or ? - * 'objectinline' if 'subjectinline' mode is not possible but every - possible object cardinalities are 1 or ? - * None if neither 'subjectinline' and 'objectinline' - """ - assert not self.final - return self.inlined and 'subjectinline' or None + def may_have_permission(self, action, req, eschema=None, role=None): + if eschema is not None: + for tschema in rschema.targets(eschema, role): + rdef = rschema.role_rdef(eschema, tschema, role) + if rdef.may_have_permission(action, req): + return True + else: + for rdef in self.rdefs.itervalues(): + if rdef.may_have_permission(action, req): + return True + return False - def check_perm(self, session, action, *args, **kwargs): - # NB: session may be a server session or a request object check user is - # in an allowed group, if so that's enough internal sessions should - # always stop there - if session.user.matching_groups(self.get_groups(action)): - return - # else if there is some rql expressions, check them - if any(rqlexpr.check(session, *args, **kwargs) - for rqlexpr in self.get_rqlexprs(action)): - return - raise Unauthorized(action, str(self)) + def has_perm(self, session, action, **kwargs): + """return true if the action is granted globaly or localy""" + if 'fromeid' in kwargs: + subjtype = session.describe(kwargs['fromeid']) + else: + subjtype = None + if 'toeid' in kwargs: + objtype = session.describe(kwargs['toeid']) + else: + objtype = Nono + if objtype and subjtype: + return self.rdef(subjtype, objtype).has_perm(session, action, **kwargs) + elif subjtype: + for tschema in rschema.targets(subjtype, 'subject'): + rdef = rschema.rdef(subjtype, tschema) + if not rdef.has_perm(action, req, **kwargs): + return False + elif objtype: + for tschema in rschema.targets(objtype, 'object'): + rdef = rschema.rdef(tschema, objtype) + if not rdef.has_perm(action, req, **kwargs): + return False + else: + for rdef in self.rdefs.itervalues(): + if not rdef.has_perm(action, req, **kwargs): + return False - def rql_expression(self, expression, mainvars=None, eid=None): - """rql expression factory""" - if self.final: - return ERQLExpression(expression, mainvars, eid) - return RRQLExpression(expression, mainvars, eid) + @deprecated('use .rdef(subjtype, objtype).role_cardinality(role)') + def cardinality(self, subjtype, objtype, target): + return self.rdef(subjtype, objtype).role_cardinality(target) class CubicWebSchema(Schema): @@ -460,13 +455,10 @@ ybo.register_base_types(self) rschema = self.add_relation_type(ybo.RelationType('eid')) rschema.final = True - rschema.set_default_groups() rschema = self.add_relation_type(ybo.RelationType('has_text')) rschema.final = True - rschema.set_default_groups() rschema = self.add_relation_type(ybo.RelationType('identity')) rschema.final = False - rschema.set_default_groups() def add_entity_type(self, edef): edef.name = edef.name.encode() @@ -508,11 +500,10 @@ rdef.name = rdef.name.lower() rdef.subject = bw_normalize_etype(rdef.subject) rdef.object = bw_normalize_etype(rdef.object) - if super(CubicWebSchema, self).add_relation_def(rdef): + rdefs = super(CubicWebSchema, self).add_relation_def(rdef) + if rdefs: try: - self._eid_index[rdef.eid] = (self.eschema(rdef.subject), - self.rschema(rdef.name), - self.eschema(rdef.object)) + self._eid_index[rdef.eid] = rdefs except AttributeError: pass # not a serialized schema @@ -523,7 +514,9 @@ def del_relation_def(self, subjtype, rtype, objtype): for k, v in self._eid_index.items(): - if v == (subjtype, rtype, objtype): + if not isinstance(v, RelationDefinitionSchema): + continue + if v.subject == subjtype and v.rtype == rtype and v.object == objtype: del self._eid_index[k] break super(CubicWebSchema, self).del_relation_def(subjtype, rtype, objtype) @@ -654,6 +647,11 @@ def __repr__(self): return '%s(%s)' % (self.__class__.__name__, self.full_rql) + def __cmp__(self, other): + if hasattr(other, 'expression'): + return cmp(other.expression, self.expression) + return False + def __deepcopy__(self, memo): return self.__class__(self.expression, self.mainvars) def __getstate__(self): @@ -755,7 +753,7 @@ for eaction, var, col in has_perm_defs: for i in xrange(len(rset)): eschema = get_eschema(rset.description[i][col]) - eschema.check_perm(session, eaction, rset[i][col]) + eschema.check_perm(session, eaction, eid=rset[i][col]) if self.eid is not None: session.local_perm_cache[key] = True return True diff -r 1169d3154be6 -r 7ca53fc72a0a schemas/Bookmark.py --- a/schemas/Bookmark.py Wed Nov 18 09:16:38 2009 +0100 +++ b/schemas/Bookmark.py Thu Nov 19 12:55:47 2009 +0100 @@ -13,7 +13,7 @@ class Bookmark(EntityType): """bookmarks are used to have user's specific internal links""" - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests',), 'add': ('managers', 'users',), 'delete': ('managers', 'owners',), @@ -29,7 +29,7 @@ class bookmarked_by(RelationType): - permissions = {'read': ('managers', 'users', 'guests',), + __permissions__ = {'read': ('managers', 'users', 'guests',), # test user in users group to avoid granting permission to anonymous user 'add': ('managers', RRQLExpression('O identity U, U in_group G, G name "users"')), 'delete': ('managers', RRQLExpression('O identity U, U in_group G, G name "users"')), diff -r 1169d3154be6 -r 7ca53fc72a0a schemas/base.py --- a/schemas/base.py Wed Nov 18 09:16:38 2009 +0100 +++ b/schemas/base.py Thu Nov 19 12:55:47 2009 +0100 @@ -16,7 +16,7 @@ class CWUser(WorkflowableEntityType): """define a CubicWeb user""" - permissions = { + __permissions__ = { 'read': ('managers', 'users', ERQLExpression('X identity U')), 'add': ('managers',), 'delete': ('managers',), @@ -37,12 +37,12 @@ in_group = SubjectRelation('CWGroup', cardinality='+*', constraints=[RQLConstraint('NOT O name "owners"')], - description=_('groups grant permissions to the user')) + description=_('groups grant __permissions__ to the user')) class EmailAddress(EntityType): """an electronic mail address associated to a short alias""" - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests',), # XXX if P use_email X, U has_read_permission P 'add': ('managers', 'users',), 'delete': ('managers', 'owners', ERQLExpression('P use_email X, U has_update_permission P')), @@ -59,7 +59,7 @@ class use_email(RelationType): """ """ - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests',), 'add': ('managers', RRQLExpression('U has_update_permission S'),), 'delete': ('managers', RRQLExpression('U has_update_permission S'),), @@ -68,12 +68,12 @@ class primary_email(RelationType): """the prefered email""" - permissions = use_email.permissions + __permissions__ = use_email.__permissions__ class prefered_form(RelationType): - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests',), - # XXX should have update permissions on both subject and object, + # XXX should have update __permissions__ on both subject and object, # though by doing this we will probably have no way to add # this relation in the web ui. The easiest way to acheive this # is probably to be able to have "U has_update_permission O" as @@ -85,13 +85,13 @@ class in_group(RelationType): """core relation indicating a user's groups""" - permissions = META_RTYPE_PERMS + __permissions__ = META_RTYPE_PERMS class owned_by(RelationType): """core relation indicating owners of an entity. This relation implicitly put the owner into the owners group for the entity """ - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests'), 'add': ('managers', RRQLExpression('S owned_by U'),), 'delete': ('managers', RRQLExpression('S owned_by U'),), @@ -104,7 +104,7 @@ class created_by(RelationType): """core relation indicating the original creator of an entity""" - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests'), 'add': ('managers',), 'delete': ('managers',), @@ -139,7 +139,7 @@ """used for cubicweb configuration. Once a property has been created you can't change the key. """ - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests'), 'add': ('managers', 'users',), 'update': ('managers', 'owners',), @@ -163,7 +163,7 @@ """link a property to the user which want this property customization. Unless you're a site manager, this relation will be handled automatically. """ - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests'), 'add': ('managers',), 'delete': ('managers',), @@ -174,7 +174,7 @@ class CWPermission(EntityType): """entity type that may be used to construct some advanced security configuration """ - permissions = META_ETYPE_PERMS + __permissions__ = META_ETYPE_PERMS name = String(required=True, indexed=True, internationalizable=True, maxsize=100, description=_('name or identifier of the permission')) @@ -189,7 +189,7 @@ """link a permission to the entity. This permission should be used in the security definition of the entity's type to be useful. """ - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests'), 'add': ('managers',), 'delete': ('managers',), @@ -197,7 +197,7 @@ class require_group(RelationType): """used to grant a permission to a group""" - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests'), 'add': ('managers',), 'delete': ('managers',), @@ -217,7 +217,7 @@ NOTE: You'll have to explicitly declare which entity types can have a same_as relation """ - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests',), 'add': ('managers', 'users'), 'delete': ('managers', 'owners'), @@ -237,7 +237,7 @@ Also, checkout the AppObject.get_cache() method. """ - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests'), 'add': ('managers',), 'update': ('managers', 'users',), # XXX @@ -254,9 +254,9 @@ class identical_to(RelationType): """identical to""" symetric = True - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests',), - # XXX should have update permissions on both subject and object, + # XXX should have update __permissions__ on both subject and object, # though by doing this we will probably have no way to add # this relation in the web ui. The easiest way to acheive this # is probably to be able to have "U has_update_permission O" as @@ -269,7 +269,7 @@ class see_also(RelationType): """generic relation to link one entity to another""" symetric = True - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests',), 'add': ('managers', RRQLExpression('U has_update_permission S'),), 'delete': ('managers', RRQLExpression('U has_update_permission S'),), diff -r 1169d3154be6 -r 7ca53fc72a0a schemas/bootstrap.py --- a/schemas/bootstrap.py Wed Nov 18 09:16:38 2009 +0100 +++ b/schemas/bootstrap.py Thu Nov 19 12:55:47 2009 +0100 @@ -17,7 +17,7 @@ # access to this class CWEType(EntityType): """define an entity type, used to build the instance schema""" - permissions = META_ETYPE_PERMS + __permissions__ = META_ETYPE_PERMS name = String(required=True, indexed=True, internationalizable=True, unique=True, maxsize=64) description = RichString(internationalizable=True, @@ -28,7 +28,7 @@ class CWRType(EntityType): """define a relation type, used to build the instance schema""" - permissions = META_ETYPE_PERMS + __permissions__ = META_ETYPE_PERMS name = String(required=True, indexed=True, internationalizable=True, unique=True, maxsize=64) description = RichString(internationalizable=True, @@ -48,7 +48,7 @@ used to build the instance schema """ - permissions = META_ETYPE_PERMS + __permissions__ = META_ETYPE_PERMS relation_type = SubjectRelation('CWRType', cardinality='1*', constraints=[RQLConstraint('O final TRUE')], composite='object') @@ -85,7 +85,7 @@ used to build the instance schema """ - permissions = META_ETYPE_PERMS + __permissions__ = META_ETYPE_PERMS relation_type = SubjectRelation('CWRType', cardinality='1*', constraints=[RQLConstraint('O final FALSE')], composite='object') @@ -115,8 +115,8 @@ # not restricted since it has to be read when checking allowed transitions class RQLExpression(EntityType): - """define a rql expression used to define permissions""" - permissions = META_ETYPE_PERMS + """define a rql expression used to define __permissions__""" + __permissions__ = META_ETYPE_PERMS exprtype = String(required=True, vocabulary=['ERQLExpression', 'RRQLExpression']) mainvars = String(maxsize=8, description=_('name of the main variables which should be ' @@ -131,11 +131,11 @@ 'relation\'subject, object and to ' 'the request user. ')) - read_permission = ObjectRelation(('CWEType', 'CWRType'), cardinality='+?', composite='subject', + read_permission = ObjectRelation(('CWEType', 'CWAttribute', 'CWRelation'), cardinality='+?', composite='subject', description=_('rql expression allowing to read entities/relations of this type')) - add_permission = ObjectRelation(('CWEType', 'CWRType'), cardinality='*?', composite='subject', + add_permission = ObjectRelation(('CWEType', 'CWAttribute', 'CWRelation'), cardinality='*?', composite='subject', description=_('rql expression allowing to add entities/relations of this type')) - delete_permission = ObjectRelation(('CWEType', 'CWRType'), cardinality='*?', composite='subject', + delete_permission = ObjectRelation(('CWEType', 'CWAttribute', 'CWRelation'), cardinality='*?', composite='subject', description=_('rql expression allowing to delete entities/relations of this type')) update_permission = ObjectRelation('CWEType', cardinality='*?', composite='subject', description=_('rql expression allowing to update entities of this type')) @@ -143,14 +143,14 @@ class CWConstraint(EntityType): """define a schema constraint""" - permissions = META_ETYPE_PERMS + __permissions__ = META_ETYPE_PERMS cstrtype = SubjectRelation('CWConstraintType', cardinality='1*') value = String(description=_('depends on the constraint type')) class CWConstraintType(EntityType): """define a schema constraint type""" - permissions = META_ETYPE_PERMS + __permissions__ = META_ETYPE_PERMS name = String(required=True, indexed=True, internationalizable=True, unique=True, maxsize=64) @@ -158,15 +158,15 @@ # not restricted since it has to be read when checking allowed transitions class CWGroup(EntityType): """define a CubicWeb users group""" - permissions = META_ETYPE_PERMS + __permissions__ = META_ETYPE_PERMS name = String(required=True, indexed=True, internationalizable=True, unique=True, maxsize=64) - read_permission = ObjectRelation(('CWEType', 'CWRType'), cardinality='+*', + read_permission = ObjectRelation(('CWEType', 'CWAttribute', 'CWRelation'), cardinality='+*', description=_('groups allowed to read entities/relations of this type')) - add_permission = ObjectRelation(('CWEType', 'CWRType'), + add_permission = ObjectRelation(('CWEType', 'CWAttribute', 'CWRelation'), description=_('groups allowed to add entities/relations of this type')) - delete_permission = ObjectRelation(('CWEType', 'CWRType'), + delete_permission = ObjectRelation(('CWEType', 'CWAttribute', 'CWRelation'), description=_('groups allowed to delete entities/relations of this type')) update_permission = ObjectRelation('CWEType', description=_('groups allowed to update entities of this type')) @@ -175,50 +175,50 @@ class relation_type(RelationType): """link a relation definition to its relation type""" - permissions = META_RTYPE_PERMS + __permissions__ = META_RTYPE_PERMS inlined = True class from_entity(RelationType): """link a relation definition to its subject entity type""" - permissions = META_RTYPE_PERMS + __permissions__ = META_RTYPE_PERMS inlined = True class to_entity(RelationType): """link a relation definition to its object entity type""" - permissions = META_RTYPE_PERMS + __permissions__ = META_RTYPE_PERMS inlined = True class constrained_by(RelationType): """constraints applying on this relation""" - permissions = META_RTYPE_PERMS + __permissions__ = META_RTYPE_PERMS class cstrtype(RelationType): """constraint factory""" - permissions = META_RTYPE_PERMS + __permissions__ = META_RTYPE_PERMS inlined = True class read_permission(RelationType): """core relation giving to a group the permission to read an entity or relation type """ - permissions = META_RTYPE_PERMS + __permissions__ = META_RTYPE_PERMS class add_permission(RelationType): """core relation giving to a group the permission to add an entity or relation type """ - permissions = META_RTYPE_PERMS + __permissions__ = META_RTYPE_PERMS class delete_permission(RelationType): """core relation giving to a group the permission to delete an entity or relation type """ - permissions = META_RTYPE_PERMS + __permissions__ = META_RTYPE_PERMS class update_permission(RelationType): """core relation giving to a group the permission to update an entity type """ - permissions = META_RTYPE_PERMS + __permissions__ = META_RTYPE_PERMS class is_(RelationType): @@ -227,7 +227,7 @@ name = 'is' # don't explicitly set composite here, this is handled anyway #composite = 'object' - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests'), 'add': (), 'delete': (), @@ -242,7 +242,7 @@ """ # don't explicitly set composite here, this is handled anyway #composite = 'object' - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests'), 'add': (), 'delete': (), @@ -253,7 +253,7 @@ class specializes(RelationType): name = 'specializes' - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests'), 'add': ('managers',), 'delete': ('managers',), diff -r 1169d3154be6 -r 7ca53fc72a0a schemas/workflow.py --- a/schemas/workflow.py Wed Nov 18 09:16:38 2009 +0100 +++ b/schemas/workflow.py Thu Nov 19 12:55:47 2009 +0100 @@ -15,7 +15,7 @@ HOOKS_RTYPE_PERMS) class Workflow(EntityType): - permissions = META_ETYPE_PERMS + __permissions__ = META_ETYPE_PERMS name = String(required=True, indexed=True, internationalizable=True, maxsize=256) @@ -33,7 +33,7 @@ class default_workflow(RelationType): """default workflow for an entity type""" - permissions = META_RTYPE_PERMS + __permissions__ = META_RTYPE_PERMS subject = 'CWEType' object = 'Workflow' @@ -45,7 +45,7 @@ """used to associate simple states to an entity type and/or to define workflows """ - permissions = META_ETYPE_PERMS + __permissions__ = META_ETYPE_PERMS name = String(required=True, indexed=True, internationalizable=True, maxsize=256) @@ -64,7 +64,7 @@ class BaseTransition(EntityType): """abstract base class for transitions""" - permissions = META_ETYPE_PERMS + __permissions__ = META_ETYPE_PERMS name = String(required=True, indexed=True, internationalizable=True, maxsize=256) @@ -126,7 +126,7 @@ class TrInfo(EntityType): """workflow history item""" # 'add' security actually done by hooks - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests',), # XXX U has_read_permission O ? 'add': ('managers', 'users', 'guests',), 'delete': (), # XXX should we allow managers to delete TrInfo? @@ -142,11 +142,11 @@ # get actor and date time using owned_by and creation_date class from_state(RelationType): - permissions = HOOKS_RTYPE_PERMS.copy() + __permissions__ = HOOKS_RTYPE_PERMS.copy() inlined = True class to_state(RelationType): - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests',), 'add': ('managers',), 'delete': (), @@ -155,7 +155,7 @@ class by_transition(RelationType): # 'add' security actually done by hooks - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests',), 'add': ('managers', 'users', 'guests',), 'delete': (), @@ -164,52 +164,52 @@ class workflow_of(RelationType): """link a workflow to one or more entity type""" - permissions = META_RTYPE_PERMS + __permissions__ = META_RTYPE_PERMS class state_of(RelationType): """link a state to one or more workflow""" - permissions = META_RTYPE_PERMS + __permissions__ = META_RTYPE_PERMS class transition_of(RelationType): """link a transition to one or more workflow""" - permissions = META_RTYPE_PERMS + __permissions__ = META_RTYPE_PERMS class subworkflow(RelationType): """link a transition to one or more workflow""" - permissions = META_RTYPE_PERMS + __permissions__ = META_RTYPE_PERMS inlined = True class exit_point(RelationType): """link a transition to one or more workflow""" - permissions = META_RTYPE_PERMS + __permissions__ = META_RTYPE_PERMS class subworkflow_state(RelationType): """link a transition to one or more workflow""" - permissions = META_RTYPE_PERMS + __permissions__ = META_RTYPE_PERMS inlined = True class initial_state(RelationType): """indicate which state should be used by default when an entity using states is created """ - permissions = META_RTYPE_PERMS + __permissions__ = META_RTYPE_PERMS inlined = True class destination_state(RelationType): """destination state of a transition""" - permissions = META_RTYPE_PERMS + __permissions__ = META_RTYPE_PERMS inlined = True class allowed_transition(RelationType): """allowed transition from this state""" - permissions = META_RTYPE_PERMS + __permissions__ = META_RTYPE_PERMS # "abstract" relations, set by WorkflowableEntityType ########################## class custom_workflow(RelationType): """allow to set a specific workflow for an entity""" - permissions = META_RTYPE_PERMS + __permissions__ = META_RTYPE_PERMS cardinality = '?*' constraints = [RQLConstraint('S is ET, O workflow_of ET')] @@ -219,7 +219,7 @@ class wf_info_for(RelationType): """link a transition information to its object""" # 'add' security actually done by hooks - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests',), 'add': ('managers', 'users', 'guests',), 'delete': (), @@ -234,7 +234,7 @@ class in_state(RelationType): """indicate the current state of an entity""" - permissions = HOOKS_RTYPE_PERMS + __permissions__ = HOOKS_RTYPE_PERMS # not inlined intentionnaly since when using ldap sources, user'state # has to be stored outside the CWUser table diff -r 1169d3154be6 -r 7ca53fc72a0a schemaviewer.py --- a/schemaviewer.py Wed Nov 18 09:16:38 2009 +0100 +++ b/schemaviewer.py Thu Nov 19 12:55:47 2009 +0100 @@ -13,6 +13,7 @@ I18NSTRINGS = [_('read'), _('add'), _('delete'), _('update'), _('order')] + class SchemaViewer(object): """return an ureport layout for some part of a schema""" def __init__(self, req=None, encoding=None): @@ -68,7 +69,8 @@ _ = self.req._ data = [_('attribute'), _('type'), _('default'), _('constraints')] for rschema, aschema in eschema.attribute_definitions(): - if not (rschema.has_local_role('read') or rschema.has_perm(self.req, 'read')): + rdef = eschema.rdef(rschema) + if not rdef.may_have_permission('read', self.req): continue aname = rschema.type if aname == 'eid': @@ -78,7 +80,7 @@ defaultval = eschema.default(aname) if defaultval is not None: default = self.to_string(defaultval) - elif eschema.rproperty(rschema, 'cardinality')[0] == '1': + elif rdef.cardinality[0] == '1': default = _('required field') else: default = '' @@ -119,20 +121,23 @@ t_vars = [] rels = [] first = True - for rschema, targetschemas, x in eschema.relation_definitions(): + for rschema, targetschemas, role in eschema.relation_definitions(): if rschema.type in skiptypes: continue - if not (rschema.has_local_role('read') or rschema.has_perm(self.req, 'read')): - continue rschemaurl = self.rschema_link_url(rschema) for oeschema in targetschemas: + rdef = rschema.role_rdef(eschema, oeschema, role) + if not rdef.may_have_permission('read', self.req): + continue label = rschema.type - if x == 'subject': + if role == 'subject': cards = rschema.rproperty(eschema, oeschema, 'cardinality') else: cards = rschema.rproperty(oeschema, eschema, 'cardinality') cards = cards[::-1] - label = '%s %s (%s) %s' % (CARD_MAP[cards[1]], label, display_name(self.req, label, x), CARD_MAP[cards[0]]) + label = '%s %s (%s) %s' % (CARD_MAP[cards[1]], label, + display_name(self.req, label, role), + CARD_MAP[cards[0]]) rlink = Link(rschemaurl, label) elink = Link(self.eschema_link_url(oeschema), oeschema.type) if first: diff -r 1169d3154be6 -r 7ca53fc72a0a selectors.py --- a/selectors.py Wed Nov 18 09:16:38 2009 +0100 +++ b/selectors.py Thu Nov 19 12:55:47 2009 +0100 @@ -665,19 +665,6 @@ self.target_etype = target_etype self.action = action - @lltrace - def __call__(self, cls, req, *args, **kwargs): - rschema = cls.schema.rschema(self.rtype) - if not (rschema.has_perm(req, self.action) - or rschema.has_local_role(self.action)): - return 0 - if self.action != 'read': - if not (rschema.has_perm(req, 'read') - or rschema.has_local_role('read')): - return 0 - score = super(relation_possible, self).__call__(cls, req, *args, **kwargs) - return score - def score_class(self, eclass, req): eschema = eclass.e_schema try: @@ -689,12 +676,13 @@ return 0 if self.target_etype is not None: try: - if self.role == 'subject': - return int(self.target_etype in rschema.objects(eschema)) - else: - return int(self.target_etype in rschema.subjects(eschema)) + rdef = rschema.role_rdef(eschema, self.target_etype, self.role) + if not rdef.may_have_permission(self.action, req): + return 0 except KeyError: return 0 + else: + return rschema.may_have_permission(self.action, req, eschema, self.role) return 1 @@ -1070,11 +1058,11 @@ perm = getattr(cls, 'require_permission', 'read') if hasattr(cls, 'etype'): eschema = schema.eschema(cls.etype) - if not (eschema.has_perm(req, perm) or eschema.has_local_role(perm)): + if not eschema.may_have_permission(perm, req): return 0 if hasattr(cls, 'rtype'): rschema = schema.rschema(cls.rtype) - if not (rschema.has_perm(req, perm) or rschema.has_local_role(perm)): + if not rschema.may_have_permission(perm, req): return 0 return 1 etype_rtype_selector = deprecated()(etype_rtype_selector) diff -r 1169d3154be6 -r 7ca53fc72a0a server/hookhelper.py --- a/server/hookhelper.py Wed Nov 18 09:16:38 2009 +0100 +++ b/server/hookhelper.py Thu Nov 19 12:55:47 2009 +0100 @@ -27,7 +27,7 @@ rschema = session.repo.schema[rtype] subjtype = session.describe(eidfrom)[0] objtype = session.describe(eidto)[0] - return rschema.rproperty(subjtype, objtype, rprop) + return getattr(rschema.rdef(subjtype, objtype), rprop) def check_internal_entity(session, eid, internal_names): """check that the entity's name is not in the internal_names list. diff -r 1169d3154be6 -r 7ca53fc72a0a server/hooks.py --- a/server/hooks.py Wed Nov 18 09:16:38 2009 +0100 +++ b/server/hooks.py Thu Nov 19 12:55:47 2009 +0100 @@ -260,7 +260,7 @@ schema = session.vreg.schema for attr in entity.edited_attributes: if schema.rschema(attr).final: - constraints = [c for c in entity.e_schema.constraints(attr) + constraints = [c for c in entity.e_schema.rdef(attr).constraints if isinstance(c, RQLVocabularyConstraint)] if constraints: CheckConstraintsOperation(session, rdef=(entity.eid, attr, None), @@ -317,22 +317,20 @@ if session.is_super_session: return eid = entity.eid - for rschema, targetschemas, x in entity.e_schema.relation_definitions(): + for rschema, targetschemas, role in entity.e_schema.relation_definitions(): # skip automatically handled relations if rschema.type in DONT_CHECK_RTYPES_ON_ADD: continue - if x == 'subject': + if role == 'subject': subjtype = entity.e_schema objtype = targetschemas[0].type - cardindex = 0 opcls = CheckSRelationOp else: subjtype = targetschemas[0].type objtype = entity.e_schema - cardindex = 1 opcls = CheckORelationOp - card = rschema.rproperty(subjtype, objtype, 'cardinality') - if card[cardindex] in '1+': + card = rschema.rdef(subjtype, objtype).role_cardinality(role) + if card in '1+': checkrel_if_necessary(session, opcls, rschema.type, eid) def cardinalitycheck_before_del_relation(session, eidfrom, rtype, eidto): diff -r 1169d3154be6 -r 7ca53fc72a0a server/migractions.py --- a/server/migractions.py Wed Nov 18 09:16:38 2009 +0100 +++ b/server/migractions.py Thu Nov 19 12:55:47 2009 +0100 @@ -305,35 +305,33 @@ # schema synchronization internals ######################################## - def _synchronize_permissions(self, ertype): + def _synchronize_permissions(self, erschema, teid): """permission synchronization for an entity or relation type""" - if ertype in VIRTUAL_RTYPES: + if erschema in VIRTUAL_RTYPES: return - newrschema = self.fs_schema[ertype] - teid = self.repo.schema[ertype].eid - if 'update' in newrschema.ACTIONS or newrschema.final: + assert teid, erschema + if 'update' in erschema.ACTIONS or erschema.final: # entity type exprtype = u'ERQLExpression' else: # relation type exprtype = u'RRQLExpression' - assert teid, ertype gm = self.group_mapping() confirm = self.verbosity >= 2 # * remove possibly deprecated permission (eg in the persistent schema # but not in the new schema) # * synchronize existing expressions # * add new groups/expressions - for action in newrschema.ACTIONS: + for action in erschema.ACTIONS: perm = '%s_permission' % action # handle groups - newgroups = list(newrschema.get_groups(action)) + newgroups = list(erschema.get_groups(action)) for geid, gname in self.rqlexec('Any G, GN WHERE T %s G, G name GN, ' 'T eid %%(x)s' % perm, {'x': teid}, 'x', ask_confirm=False): if not gname in newgroups: if not confirm or self.confirm('remove %s permission of %s to %s?' - % (action, ertype, gname)): + % (action, erschema, gname)): self.rqlexec('DELETE T %s G WHERE G eid %%(x)s, T eid %s' % (perm, teid), {'x': geid}, 'x', ask_confirm=False) @@ -341,18 +339,18 @@ newgroups.remove(gname) for gname in newgroups: if not confirm or self.confirm('grant %s permission of %s to %s?' - % (action, ertype, gname)): + % (action, erschema, gname)): self.rqlexec('SET T %s G WHERE G eid %%(x)s, T eid %s' % (perm, teid), {'x': gm[gname]}, 'x', ask_confirm=False) # handle rql expressions - newexprs = dict((expr.expression, expr) for expr in newrschema.get_rqlexprs(action)) + newexprs = dict((expr.expression, expr) for expr in erschema.get_rqlexprs(action)) for expreid, expression in self.rqlexec('Any E, EX WHERE T %s E, E expression EX, ' 'T eid %s' % (perm, teid), ask_confirm=False): if not expression in newexprs: if not confirm or self.confirm('remove %s expression for %s permission of %s?' - % (expression, action, ertype)): + % (expression, action, erschema)): # deleting the relation will delete the expression entity self.rqlexec('DELETE T %s E WHERE E eid %%(x)s, T eid %s' % (perm, teid), @@ -362,7 +360,7 @@ for expression in newexprs.values(): expr = expression.expression if not confirm or self.confirm('add %s expression for %s permission of %s?' - % (expr, action, ertype)): + % (expr, action, erschema)): self.rqlexec('INSERT RQLExpression X: X exprtype %%(exprtype)s, ' 'X expression %%(expr)s, X mainvars %%(vars)s, T %s X ' 'WHERE T eid %%(x)s' % perm, @@ -394,9 +392,7 @@ for subj, obj in rschema.iter_rdefs(): if not reporschema.has_rdef(subj, obj): continue - self._synchronize_rdef_schema(subj, rschema, obj) - if syncperms: - self._synchronize_permissions(rtype) + self._synchronize_rdef_schema(subj, rschema, obj, syncperms=syncperms) def _synchronize_eschema(self, etype, syncperms=True): """synchronize properties of the persistent entity schema against @@ -446,9 +442,9 @@ continue self._synchronize_rdef_schema(subj, rschema, obj) if syncperms: - self._synchronize_permissions(etype) + self._synchronize_permissions(eschema, repoeschema.eid) - def _synchronize_rdef_schema(self, subjtype, rtype, objtype): + def _synchronize_rdef_schema(self, subjtype, rtype, objtype, syncperms=True): """synchronize properties of the persistent relation definition schema against its current definition: * order and other properties @@ -467,12 +463,14 @@ self.rqlexecall(ss.updaterdef2rql(rschema, subjtype, objtype), ask_confirm=confirm) # constraints - newconstraints = list(rschema.rproperty(subjtype, objtype, 'constraints')) + rdef = rschema.rdef(subjtype, objtype) + repordef = reporschema.rdef(subjtype, objtype) + newconstraints = list(rdef.constraints) # 1. remove old constraints and update constraints of the same type # NOTE: don't use rschema.constraint_by_type because it may be # out of sync with newconstraints when multiple # constraints of the same type are used - for cstr in reporschema.rproperty(subjtype, objtype, 'constraints'): + for cstr in repordef.constraints: for newcstr in newconstraints: if newcstr.type() == cstr.type(): break @@ -496,6 +494,8 @@ self.rqlexecall(ss.constraint2rql(rschema, subjtype, objtype, newcstr), ask_confirm=confirm) + if syncperms: + self._synchronize_permissions(rdef, repordef.eid) # base actions ############################################################ @@ -888,7 +888,8 @@ if isinstance(ertype, (tuple, list)): assert len(ertype) == 3, 'not a relation definition' assert syncprops, 'can\'t update permission for a relation definition' - self._synchronize_rdef_schema(*ertype) + self._synchronize_rdef_schema(ertype[0], ertype[1], ertype[2], + syncperms=syncperms) elif syncprops: erschema = self.repo.schema[ertype] if isinstance(erschema, CubicWebRelationSchema): @@ -897,13 +898,14 @@ else: self._synchronize_eschema(erschema, syncperms=syncperms) else: - self._synchronize_permissions(ertype) + erschema = self.repo.schema[ertype] + self._synchronize_permissions(self.fs_schema[ertype], erschema.eid) else: for etype in self.repo.schema.entities(): if syncprops: self._synchronize_eschema(etype, syncperms=syncperms) else: - self._synchronize_permissions(etype) + self._synchronize_permissions(self.fs_schema[etype], erschema.eid) if commit: self.commit() diff -r 1169d3154be6 -r 7ca53fc72a0a server/querier.py --- a/server/querier.py Wed Nov 18 09:16:38 2009 +0100 +++ b/server/querier.py Thu Nov 19 12:55:47 2009 +0100 @@ -71,14 +71,23 @@ # XXX has_text may have specific perm ? if rel.r_type in READ_ONLY_RTYPES: continue - if not schema.rschema(rel.r_type).has_access(user, 'read'): + rschema = schema.rschema(rel.r_type) + if rschema.final: + eschema = schema.eschema(solution[rel.children[0].name]) + rdef = eschema.rdef(rschema) + else: + rdef = rschema.rdef(solution[rel.children[0].name], + solution[rel.children[1].children[0].name]) + if not user.matching_groups(rdef.get_groups('read')): raise Unauthorized('read', rel.r_type) localchecks = {} # iterate on defined_vars and not on solutions to ignore column aliases for varname in rqlst.defined_vars: etype = solution[varname] eschema = schema.eschema(etype) - if not eschema.has_access(user, 'read'): + if eschema.final: + continue + if not user.matching_groups(eschema.get_groups('read')): erqlexprs = eschema.get_rqlexprs('read') if not erqlexprs: ex = Unauthorized('read', etype) diff -r 1169d3154be6 -r 7ca53fc72a0a server/repository.py --- a/server/repository.py Wed Nov 18 09:16:38 2009 +0100 +++ b/server/repository.py Thu Nov 19 12:55:47 2009 +0100 @@ -1087,7 +1087,7 @@ continue rschema = eschema.subjrels[attr] if rschema.final: - if eschema.rproperty(attr, 'fulltextindexed'): + if getattr(eschema.rdef(attr), 'fulltextindexed', False): need_fti_update = True only_inline_rels = False else: diff -r 1169d3154be6 -r 7ca53fc72a0a server/schemahooks.py --- a/server/schemahooks.py Wed Nov 18 09:16:38 2009 +0100 +++ b/server/schemahooks.py Thu Nov 19 12:55:47 2009 +0100 @@ -180,18 +180,6 @@ return i + 1 -class MemSchemaPermissionOperation(MemSchemaOperation): - """base class to synchronize schema permission definitions""" - def __init__(self, session, perm, etype_eid): - self.perm = perm - try: - self.name = session.entity_from_eid(etype_eid).name - except IndexError: - self.error('changing permission of a no more existant type #%s', - etype_eid) - else: - Operation.__init__(self, session) - # operations for high-level source database alteration ######################## @@ -487,7 +475,7 @@ return subjtype, rtype, objtype = session.schema.schema_by_eid(rdef.eid) cstrtype = self.entity.type - oldcstr = rtype.constraint_by_type(subjtype, objtype, cstrtype) + oldcstr = rtype.rdef(subjtype, objtype).constraint_by_type(cstrtype) newcstr = CONSTRAINTS[cstrtype].deserialize(self.entity.value) table = SQL_PREFIX + str(subjtype) column = SQL_PREFIX + str(rtype) @@ -567,8 +555,7 @@ """actually add the relation type to the instance's schema""" eid = None # make pylint happy def commit_event(self): - rschema = self.schema.add_relation_type(self.kobj) - rschema.set_default_groups() + self.schema.add_relation_type(self.kobj) class MemSchemaCWRTypeUpdate(MemSchemaOperation): @@ -606,7 +593,7 @@ def commit_event(self): # structure should be clean, not need to remove entity's relations # at this point - self.rschema._rproperties[self.kobj].update(self.values) + self.rschema.rdef[self.kobj].update(self.values) class MemSchemaRDefDel(MemSchemaOperation): @@ -638,7 +625,7 @@ subjtype, rtype, objtype = self.session.schema.schema_by_eid(rdef.eid) self.prepare_constraints(subjtype, rtype, objtype) cstrtype = self.entity.type - self.cstr = rtype.constraint_by_type(subjtype, objtype, cstrtype) + self.cstr = rtype.rdef(subjtype, objtype).constraint_by_type(cstrtype) self.newcstr = CONSTRAINTS[cstrtype].deserialize(self.entity.value) self.newcstr.eid = self.entity.eid @@ -664,33 +651,33 @@ self.constraints.remove(self.cstr) -class MemSchemaPermissionCWGroupAdd(MemSchemaPermissionOperation): +class MemSchemaPermissionAdd(MemSchemaOperation): """synchronize schema when a *_permission relation has been added on a group """ - def __init__(self, session, perm, etype_eid, group_eid): - self.group = session.entity_from_eid(group_eid).name - super(MemSchemaPermissionCWGroupAdd, self).__init__( - session, perm, etype_eid) def commit_event(self): """the observed connections pool has been commited""" try: - erschema = self.schema[self.name] + erschema = self.schema.schema_by_eid(self.eid) except KeyError: # duh, schema not found, log error and skip operation self.error('no schema for %s', self.name) return - groups = list(erschema.get_groups(self.perm)) + perms = list(erschema.action_permissions(self.action)) + if hasattr(self, group_eid): + perm = self.session.entity_from_eid(self.group_eid).name + else: + perm = erschema.rql_expression(self.expr) try: - groups.index(self.group) - self.warning('group %s already have permission %s on %s', - self.group, self.perm, erschema.type) + perms.index(perm) + self.warning('%s already in permissions for %s on %s', + perm, self.action, erschema) except ValueError: - groups.append(self.group) - erschema.set_groups(self.perm, groups) + perms.append(perm) + erschema.set_action_permissions(self.action, perms) -class MemSchemaPermissionCWGroupDel(MemSchemaPermissionCWGroupAdd): +class MemSchemaPermissionDel(MemSchemaPermissionAdd): """synchronize schema when a *_permission relation has been deleted from a group """ @@ -703,60 +690,17 @@ # duh, schema not found, log error and skip operation self.error('no schema for %s', self.name) return - groups = list(erschema.get_groups(self.perm)) - try: - groups.remove(self.group) - erschema.set_groups(self.perm, groups) - except ValueError: - self.error('can\'t remove permission %s on %s to group %s', - self.perm, erschema.type, self.group) - - -class MemSchemaPermissionRQLExpressionAdd(MemSchemaPermissionOperation): - """synchronize schema when a *_permission relation has been added on a rql - expression - """ - def __init__(self, session, perm, etype_eid, expression): - self.expr = expression - super(MemSchemaPermissionRQLExpressionAdd, self).__init__( - session, perm, etype_eid) - - def commit_event(self): - """the observed connections pool has been commited""" + perms = list(erschema.action_permissions(self.action)) + if hasattr(self, group_eid): + perm = self.session.entity_from_eid(self.group_eid).name + else: + perm = erschema.rql_expression(self.expr) try: - erschema = self.schema[self.name] - except KeyError: - # duh, schema not found, log error and skip operation - self.error('no schema for %s', self.name) - return - exprs = list(erschema.get_rqlexprs(self.perm)) - exprs.append(erschema.rql_expression(self.expr)) - erschema.set_rqlexprs(self.perm, exprs) - - -class MemSchemaPermissionRQLExpressionDel(MemSchemaPermissionRQLExpressionAdd): - """synchronize schema when a *_permission relation has been deleted from an - rql expression - """ - - def commit_event(self): - """the observed connections pool has been commited""" - try: - erschema = self.schema[self.name] - except KeyError: - # duh, schema not found, log error and skip operation - self.error('no schema for %s', self.name) - return - rqlexprs = list(erschema.get_rqlexprs(self.perm)) - for i, rqlexpr in enumerate(rqlexprs): - if rqlexpr.expression == self.expr: - rqlexprs.pop(i) - break - else: - self.error('can\'t remove permission %s on %s for expression %s', - self.perm, erschema.type, self.expr) - return - erschema.set_rqlexprs(self.perm, rqlexprs) + perms.remove(self.group) + erschema.set_action_permissions(self.action, perms) + except ValueError: + self.error('can\'t remove permission %s for %s on %s', + perm, self.action, erschema) class MemSchemaSpecializesAdd(MemSchemaOperation): @@ -894,7 +838,6 @@ # but remove it before doing anything more dangerous... schema = session.schema eschema = schema.add_entity_type(etype) - eschema.set_default_groups() # generate table sql and rql to add metadata tablesql = eschema2sql(session.pool.source('system').dbhelper, eschema, prefix=SQL_PREFIX) @@ -1033,8 +976,8 @@ entity = session.entity_from_eid(toeid) subjtype, rtype, objtype = schema.schema_by_eid(fromeid) try: - cstr = rtype.constraint_by_type(subjtype, objtype, - entity.cstrtype[0].name) + cstr = rtype.rdef(subjtype, objtype).constraint_by_type( + entity.cstrtype[0].name) except IndexError: session.critical('constraint type no more accessible') else: @@ -1053,13 +996,14 @@ def after_add_permission(session, subject, rtype, object): """added entity/relation *_permission, need to update schema""" - perm = rtype.split('_', 1)[0] + action = rtype.split('_', 1)[0] if session.describe(object)[0] == 'CWGroup': - MemSchemaPermissionCWGroupAdd(session, perm, subject, object) + MemSchemaPermissionAdd(session, action=action, eid=subject, + group_eid=object) else: # RQLExpression - expr = session.execute('Any EXPR WHERE X eid %(x)s, X expression EXPR', - {'x': object}, 'x')[0][0] - MemSchemaPermissionRQLExpressionAdd(session, perm, subject, expr) + expr = session.entity_from_eid(object).expression + MemSchemaPermissionAdd(session, action=action, eid=subject, + expr=expr) def before_del_permission(session, subject, rtype, object): @@ -1069,13 +1013,12 @@ """ if subject in session.transaction_data.get('pendingeids', ()): return - perm = rtype.split('_', 1)[0] + action = rtype.split('_', 1)[0] if session.describe(object)[0] == 'CWGroup': - MemSchemaPermissionCWGroupDel(session, perm, subject, object) + MemSchemaPermissionDel(session, action=action, eid=subject, group_eid=object) else: # RQLExpression - expr = session.execute('Any EXPR WHERE X eid %(x)s, X expression EXPR', - {'x': object}, 'x')[0][0] - MemSchemaPermissionRQLExpressionDel(session, perm, subject, expr) + expr = session.entity_from_eid(object).expression + MemSchemaPermissionDel(session, action=action, eid=subject, expr=expr) def after_add_specializes(session, subject, rtype, object): diff -r 1169d3154be6 -r 7ca53fc72a0a server/schemaserial.py --- a/server/schemaserial.py Wed Nov 18 09:16:38 2009 +0100 +++ b/server/schemaserial.py Thu Nov 19 12:55:47 2009 +0100 @@ -256,7 +256,7 @@ actperms.append(erschema.rql_expression(*something)) else: # group name actperms.append(something) - erschema.set_permissions(action, actperms) + erschema.set_action_permissions(action, actperms) def deserialize_rdef_constraints(session): @@ -513,25 +513,51 @@ yield erperms2rql(schema[rtype], groupmapping) def erperms2rql(erschema, groupmapping): + if hasattr(erschema, 'iter_rdefs'): + # relation schema + if erschema.final: + etype = 'CWAttribute' + else: + etype = 'CWRelation' + for subject, object in erschema.iter_rdefs(): + permissions = erschema.rproperty(subject, object, 'permissions') + for rql, args in _erperms2rql(erschema.rproperties(subject, object), + groupmapping): + args['st'] = str(subject) + args['rt'] = str(erschema) + args['ot'] = str(object) + yield rql + 'X is %s, X from_entity ST, X to_entity OT, '\ + 'X relation_type RT, RT name %%(rt)s, ST name %%(st)s, '\ + 'OT name %%(ot)s' % etype, args + else: + # entity schema + for rql, args in _erperms2rql(erschema, groupmapping): + args['name'] = str(eschema) + yield rql + 'X is CWEType, X name %(name)s', args + +def _erperms2rql(erschema, groupmapping): """return rql insert statements to enter the entity or relation schema's permissions in the database as [read|add|delete|update]_permission relations between CWEType/CWRType and CWGroup entities """ - etype = isinstance(erschema, schemamod.EntitySchema) and 'CWEType' or 'CWRType' for action in erschema.ACTIONS: - for group in sorted(erschema.get_groups(action)): - try: - yield ('SET X %s_permission Y WHERE X is %s, X name %%(name)s, Y eid %s' - % (action, etype, groupmapping[group]), {'name': str(erschema)}) - except KeyError: - continue - for rqlexpr in sorted(erschema.get_rqlexprs(action)): - yield ('INSERT RQLExpression E: E expression %%(e)s, E exprtype %%(t)s, ' - 'E mainvars %%(v)s, X %s_permission E ' - 'WHERE X is %s, X name "%s"' % (action, etype, erschema), - {'e': unicode(rqlexpr.expression), 'v': unicode(rqlexpr.mainvars), - 't': unicode(rqlexpr.__class__.__name__)}) + for group_or_rqlexpr in erschema.action_permissions(action): + if isinstance(group_or_rqlexpr, basestring): + # group + try: + yield ('SET X %s_permission Y WHERE Y eid %%(g)s' % action, + {'g': groupmapping[group_or_rqlexpr]}) + except KeyError: + continue + else: + # rqlexpr + rqlexpr = group_or_rqlexpr + yield ('INSERT RQLExpression E: E expression %%(e)s, E exprtype %%(t)s, ' + 'E mainvars %%(v)s, X %s_permission E WHERE ' % action, + {'e': unicode(rqlexpr.expression), + 'v': unicode(rqlexpr.mainvars), + 't': unicode(rqlexpr.__class__.__name__)}) def updateeschema2rql(eschema): diff -r 1169d3154be6 -r 7ca53fc72a0a server/securityhooks.py --- a/server/securityhooks.py Wed Nov 18 09:16:38 2009 +0100 +++ b/server/securityhooks.py Thu Nov 19 12:55:47 2009 +0100 @@ -25,10 +25,10 @@ for attr in editedattrs: if attr in defaults: continue - rschema = eschema.subjrels[attr] - if rschema.final: # non final relation are checked by other hooks + rdef = eschema.rdef(attr) + if rdef.final: # non final relation are checked by other hooks # add/delete should be equivalent (XXX: unify them into 'update' ?) - rschema.check_perm(session, 'add', eid) + rdef.check_perm(session, 'add', eid=eid) class CheckEntityPermissionOp(LateOperation): @@ -43,7 +43,10 @@ class CheckRelationPermissionOp(LateOperation): def precommit_event(self): - self.rschema.check_perm(self.session, self.action, self.fromeid, self.toeid) + rdef = self.rschema.rdef(self.session.describe(self.fromeid)[0], + self.session.describe(self.toeid)[0]) + rdef.check_perm(self.session, self.action, + fromeid=self.fromeid, toeid=self.toeid) def commit_event(self): pass @@ -65,7 +68,7 @@ def before_del_entity(session, eid): if not session.is_super_session: eschema = session.repo.schema[session.describe(eid)[0]] - eschema.check_perm(session, 'delete', eid) + eschema.check_perm(session, 'delete', eid=eid) def before_add_relation(session, fromeid, rtype, toeid): @@ -74,26 +77,33 @@ if (fromeid, rtype, toeid) in nocheck: return rschema = session.repo.schema[rtype] - rschema.check_perm(session, 'add', fromeid, toeid) + rdef = rschema.rdef(session.describe(fromeid)[0], + session.describe(toeid)[0]) + rdef.check_perm(session, 'add', fromeid=fromeid, toeid=toeid) def after_add_relation(session, fromeid, rtype, toeid): if not rtype in BEFORE_ADD_RELATIONS and not session.is_super_session: nocheck = session.transaction_data.get('skip-security', ()) if (fromeid, rtype, toeid) in nocheck: return - rschema = session.repo.schema[rtype] + rschema = session.repo.schema.rschema(rtype) if rtype in ON_COMMIT_ADD_RELATIONS: CheckRelationPermissionOp(session, action='add', rschema=rschema, fromeid=fromeid, toeid=toeid) else: - rschema.check_perm(session, 'add', fromeid, toeid) + rdef = rschema.rdef(session.describe(fromeid)[0], + session.describe(toeid)[0]) + rdef.check_perm(session, 'add', fromeid=fromeid, toeid=toeid) def before_del_relation(session, fromeid, rtype, toeid): if not session.is_super_session: nocheck = session.transaction_data.get('skip-security', ()) if (fromeid, rtype, toeid) in nocheck: return - session.repo.schema[rtype].check_perm(session, 'delete', fromeid, toeid) + rschema = session.vreg.schema.rschema(rtype) + rdef = rschema.rdef(session.describe(fromeid)[0], + session.describe(toeid)[0]) + rdef.check_perm(session, 'delete', fromeid=fromeid, toeid=toeid) def register_security_hooks(hm): """register meta-data related hooks on the hooks manager""" diff -r 1169d3154be6 -r 7ca53fc72a0a server/test/data/migratedapp/schema.py --- a/server/test/data/migratedapp/schema.py Wed Nov 18 09:16:38 2009 +0100 +++ b/server/test/data/migratedapp/schema.py Thu Nov 19 12:55:47 2009 +0100 @@ -13,7 +13,7 @@ ERQLExpression, RRQLExpression) class Affaire(EntityType): - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests'), 'add': ('managers', ERQLExpression('X concerne S, S owned_by U')), 'update': ('managers', 'owners', ERQLExpression('X concerne S, S owned_by U')), @@ -27,7 +27,7 @@ concerne = SubjectRelation('Societe') class concerne(RelationType): - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests'), 'add': ('managers', RRQLExpression('U has_update_permission S')), 'delete': ('managers', RRQLExpression('O owned_by U')), @@ -42,7 +42,7 @@ class Note(Para): __specializes_schema__ = True - permissions = {'read': ('managers', 'users', 'guests',), + __permissions__ = {'read': ('managers', 'users', 'guests',), 'update': ('managers', 'owners',), 'delete': ('managers', ), 'add': ('managers', @@ -63,7 +63,7 @@ summary = String(maxsize=512) class ecrit_par(RelationType): - permissions = {'read': ('managers', 'users', 'guests',), + __permissions__ = {'read': ('managers', 'users', 'guests',), 'delete': ('managers', ), 'add': ('managers', RRQLExpression('O require_permission P, P name "add_note", ' @@ -105,7 +105,7 @@ connait = SubjectRelation('Personne', symetric=True) class Societe(WorkflowableEntityType): - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests'), 'update': ('managers', 'owners'), 'delete': ('managers', 'owners'), diff -r 1169d3154be6 -r 7ca53fc72a0a server/test/data/schema.py --- a/server/test/data/schema.py Wed Nov 18 09:16:38 2009 +0100 +++ b/server/test/data/schema.py Thu Nov 19 12:55:47 2009 +0100 @@ -13,7 +13,7 @@ ERQLExpression, RRQLExpression) class Affaire(WorkflowableEntityType): - permissions = { + __permissions__ = { 'read': ('managers', ERQLExpression('X owned_by U'), ERQLExpression('X concerne S?, S owned_by U')), 'add': ('managers', ERQLExpression('X concerne S, S owned_by U')), @@ -39,7 +39,7 @@ class Societe(EntityType): - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests'), 'update': ('managers', 'owners', ERQLExpression('U login L, X nom L')), 'delete': ('managers', 'owners', ERQLExpression('U login L, X nom L')), @@ -121,28 +121,28 @@ symetric = True class concerne(RelationType): - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests'), 'add': ('managers', RRQLExpression('U has_update_permission S')), 'delete': ('managers', RRQLExpression('O owned_by U')), } class travaille(RelationType): - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests'), 'add': ('managers', RRQLExpression('U has_update_permission S')), 'delete': ('managers', RRQLExpression('O owned_by U')), } class para(RelationType): - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests'), 'add': ('managers', ERQLExpression('X in_state S, S name "todo"')), 'delete': ('managers', ERQLExpression('X in_state S, S name "todo"')), } class test(RelationType): - permissions = {'read': ('managers', 'users', 'guests'), + __permissions__ = {'read': ('managers', 'users', 'guests'), 'delete': ('managers',), 'add': ('managers',)} diff -r 1169d3154be6 -r 7ca53fc72a0a server/test/unittest_security.py --- a/server/test/unittest_security.py Wed Nov 18 09:16:38 2009 +0100 +++ b/server/test/unittest_security.py Thu Nov 19 12:55:47 2009 +0100 @@ -14,13 +14,13 @@ def setUp(self): RepositoryBasedTC.setUp(self) self.create_user('iaminusersgrouponly') - self.readoriggroups = self.schema['Personne'].get_groups('read') - self.addoriggroups = self.schema['Personne'].get_groups('add') + self.readoriggroups = self.schema['Personne'].permissions['read'] + self.addoriggroups = self.schema['Personne'].permissions['add'] def tearDown(self): RepositoryBasedTC.tearDown(self) - self.schema['Personne'].set_groups('read', self.readoriggroups) - self.schema['Personne'].set_groups('add', self.addoriggroups) + self.schema['Personne'].set_action_permissions('read', self.readoriggroups) + self.schema['Personne'].set_action_permissions('add', self.addoriggroups) class LowLevelSecurityFunctionTC(BaseSecurityTC): @@ -29,7 +29,7 @@ rql = u'Personne U where U nom "managers"' rqlst = self.repo.vreg.rqlhelper.parse(rql).children[0] origgroups = self.schema['Personne'].get_groups('read') - self.schema['Personne'].set_groups('read', ('users', 'managers')) + self.schema['Personne'].set_action_permissions('read', ('users', 'managers')) self.repo.vreg.rqlhelper.compute_solutions(rqlst) solution = rqlst.solutions[0] check_read_access(self.schema, self.session.user, rqlst, solution) @@ -96,8 +96,8 @@ def test_update_security_2(self): cnx = self.login('anon') cu = cnx.cursor() - self.repo.schema['Personne'].set_groups('read', ('users', 'managers')) - self.repo.schema['Personne'].set_groups('add', ('guests', 'users', 'managers')) + self.repo.schema['Personne'].set_action_permissions('read', ('users', 'managers')) + self.repo.schema['Personne'].set_action_permissions('add', ('guests', 'users', 'managers')) self.assertRaises(Unauthorized, cu.execute, "SET X nom 'bidulechouette' WHERE X is Personne") #self.assertRaises(Unauthorized, cnx.commit) # test nothing has actually been inserted @@ -177,7 +177,7 @@ ent = rset.get_entity(0, 0) session.set_pool() # necessary self.assertRaises(Unauthorized, - ent.e_schema.check_perm, session, 'update', ent.eid) + ent.e_schema.check_perm, session, 'update', eid=ent.eid) self.assertRaises(Unauthorized, cu.execute, "SET P travaille S WHERE P is Personne, S is Societe") # test nothing has actually been inserted: @@ -230,7 +230,7 @@ # read security test def test_read_base(self): - self.schema['Personne'].set_groups('read', ('users', 'managers')) + self.schema['Personne'].set_action_permissions('read', ('users', 'managers')) cnx = self.login('anon') cu = cnx.cursor() self.assertRaises(Unauthorized, @@ -281,7 +281,7 @@ self.execute("INSERT Personne X: X nom 'bidule'") self.execute("INSERT Societe X: X nom 'bidule'") self.commit() - self.schema['Personne'].set_groups('read', ('managers',)) + self.schema['Personne'].set_action_permissions('read', ('managers',)) cnx = self.login('iaminusersgrouponly') cu = cnx.cursor() rset = cu.execute('Any N WHERE N has_text "bidule"') @@ -293,7 +293,7 @@ self.execute("INSERT Personne X: X nom 'bidule'") self.execute("INSERT Societe X: X nom 'bidule'") self.commit() - self.schema['Personne'].set_groups('read', ('managers',)) + self.schema['Personne'].set_action_permissions('read', ('managers',)) cnx = self.login('anon') cu = cnx.cursor() rset = cu.execute('Any N,U WHERE N has_text "bidule", N owned_by U?') @@ -371,8 +371,8 @@ def test_attribute_read_security(self): # anon not allowed to see users'login, but they can see users - self.repo.schema['CWUser'].set_groups('read', ('guests', 'users', 'managers')) - self.repo.schema['login'].set_groups('read', ('users', 'managers')) + self.repo.schema['CWUser'].set_action_permissions('read', ('guests', 'users', 'managers')) + self.repo.schema['CWUser'].rdef('login').set_action_permissions('read', ('users', 'managers')) cnx = self.login('anon') cu = cnx.cursor() rset = cu.execute('CWUser X') @@ -494,11 +494,11 @@ # needed to avoid check_perm error session.set_pool() # needed to remove rql expr granting update perm to the user - self.schema['Affaire'].set_rqlexprs('update', ()) + self.schema['Affaire'].set_action_permissions('update', self.schema['Affaire'].get_groups('update')) self.assertRaises(Unauthorized, - self.schema['Affaire'].check_perm, session, 'update', eid) + self.schema['Affaire'].check_perm, session, 'update', eid=eid) cu = cnx.cursor() - self.schema['Affaire'].set_groups('read', ('users',)) + self.schema['Affaire'].set_action_permissions('read', ('users',)) try: aff = cu.execute('Any X WHERE X ref "ARCT01"').get_entity(0, 0) aff.fire_transition('abort') @@ -510,7 +510,7 @@ # from the current state but Unauthorized if it exists but user can't pass it self.assertRaises(ValidationError, user.fire_transition, 'deactivate') finally: - self.schema['Affaire'].set_groups('read', ('managers',)) + self.schema['Affaire'].set_action_permissions('read', ('managers',)) def test_trinfo_security(self): aff = self.execute('INSERT Affaire X: X ref "ARCT01"').get_entity(0, 0) diff -r 1169d3154be6 -r 7ca53fc72a0a sobjects/notification.py --- a/sobjects/notification.py Wed Nov 18 09:16:38 2009 +0100 +++ b/sobjects/notification.py Thu Nov 19 12:55:47 2009 +0100 @@ -269,14 +269,16 @@ changes = self.req.transaction_data['changes'][self.rset[0][0]] _ = self.req._ formatted_changes = [] + entity = self.entity(self.row or 0, self.col or 0) for attr, oldvalue, newvalue in sorted(changes): # check current user has permission to see the attribute rschema = self.vreg.schema[attr] if rschema.final: - if not rschema.has_perm(self.req, 'read', eid=self.rset[0][0]): + rdef = entity.e_schema.rdef(rschema) + if not rdef.has_perm(self.req, 'read', eid=self.rset[0][0]): continue # XXX suppose it's a subject relation... - elif not rschema.has_perm(self.req, 'read', fromeid=self.rset[0][0]): + elif not rschema.has_perm(self.req, 'read', fromeid=self.rset[0][0]): # XXX toeid continue if attr in self.no_detailed_change_attrs: msg = _('%s updated') % _(attr) diff -r 1169d3154be6 -r 7ca53fc72a0a test/data/erqlexpr_on_ertype.py --- a/test/data/erqlexpr_on_ertype.py Wed Nov 18 09:16:38 2009 +0100 +++ b/test/data/erqlexpr_on_ertype.py Thu Nov 19 12:55:47 2009 +0100 @@ -9,7 +9,7 @@ from cubicweb.schema import ERQLExpression class ToTo(EntityType): - permissions = { + __permissions__ = { 'read': ('managers',), 'add': ('managers',), 'update': ('managers',), @@ -18,7 +18,7 @@ toto = SubjectRelation('TuTu') class TuTu(EntityType): - permissions = { + __permissions__ = { 'read': ('managers',), 'add': ('managers',), 'update': ('managers',), @@ -26,7 +26,7 @@ } class toto(RelationType): - permissions = { + __permissions__ = { 'read': ('managers', ), 'add': ('managers', ERQLExpression('S bla Y'),), 'delete': ('managers',), diff -r 1169d3154be6 -r 7ca53fc72a0a test/data/rewrite/schema.py --- a/test/data/rewrite/schema.py Wed Nov 18 09:16:38 2009 +0100 +++ b/test/data/rewrite/schema.py Thu Nov 19 12:55:47 2009 +0100 @@ -2,7 +2,7 @@ from cubicweb.schema import ERQLExpression class Affaire(EntityType): - permissions = { + __permissions__ = { 'read': ('managers', ERQLExpression('X owned_by U'), ERQLExpression('X concerne S?, S owned_by U')), 'add': ('managers', ERQLExpression('X concerne S, S owned_by U')), @@ -15,7 +15,7 @@ class Societe(EntityType): - permissions = { + __permissions__ = { 'read': ('managers', 'users', 'guests'), 'update': ('managers', 'owners', ERQLExpression('U login L, X nom L')), 'delete': ('managers', 'owners', ERQLExpression('U login L, X nom L')), diff -r 1169d3154be6 -r 7ca53fc72a0a test/data/rqlexpr_on_ertype_read.py --- a/test/data/rqlexpr_on_ertype_read.py Wed Nov 18 09:16:38 2009 +0100 +++ b/test/data/rqlexpr_on_ertype_read.py Thu Nov 19 12:55:47 2009 +0100 @@ -9,7 +9,7 @@ from cubicweb.schema import RRQLExpression class ToTo(EntityType): - permissions = { + __permissions__ = { 'read': ('managers',), 'add': ('managers',), 'update': ('managers',), @@ -18,7 +18,7 @@ toto = SubjectRelation('TuTu') class TuTu(EntityType): - permissions = { + __permissions__ = { 'read': ('managers',), 'add': ('managers',), 'update': ('managers',), @@ -26,7 +26,7 @@ } class toto(RelationType): - permissions = { + __permissions__ = { 'read': ('managers', RRQLExpression('S bla Y'), ), 'add': ('managers',), 'delete': ('managers',), diff -r 1169d3154be6 -r 7ca53fc72a0a test/data/rrqlexpr_on_attr.py --- a/test/data/rrqlexpr_on_attr.py Wed Nov 18 09:16:38 2009 +0100 +++ b/test/data/rrqlexpr_on_attr.py Thu Nov 19 12:55:47 2009 +0100 @@ -9,7 +9,7 @@ from cubicweb.schema import RRQLExpression class ToTo(EntityType): - permissions = { + __permissions__ = { 'read': ('managers',), 'add': ('managers',), 'update': ('managers',), @@ -18,7 +18,7 @@ attr = String() class attr(RelationType): - permissions = { + __permissions__ = { 'read': ('managers', ), 'add': ('managers', RRQLExpression('S bla Y'),), 'delete': ('managers',), diff -r 1169d3154be6 -r 7ca53fc72a0a test/data/rrqlexpr_on_eetype.py --- a/test/data/rrqlexpr_on_eetype.py Wed Nov 18 09:16:38 2009 +0100 +++ b/test/data/rrqlexpr_on_eetype.py Thu Nov 19 12:55:47 2009 +0100 @@ -9,7 +9,7 @@ from cubicweb.schema import RRQLExpression class ToTo(EntityType): - permissions = { + __permissions__ = { 'read': ('managers', RRQLExpression('S bla Y'),), 'add': ('managers',), 'update': ('managers',), diff -r 1169d3154be6 -r 7ca53fc72a0a test/unittest_entity.py --- a/test/unittest_entity.py Wed Nov 18 09:16:38 2009 +0100 +++ b/test/unittest_entity.py Thu Nov 19 12:55:47 2009 +0100 @@ -63,7 +63,7 @@ def test_copy_with_nonmeta_composite_inlined(self): p = self.add_entity('Personne', nom=u'toto') oe = self.add_entity('Note', type=u'x') - self.schema['ecrit_par'].set_rproperty('Note', 'Personne', 'composite', 'subject') + self.schema['ecrit_par'].rdef('Note', 'Personne').composite = 'subject' self.execute('SET T ecrit_par U WHERE T eid %(t)s, U eid %(u)s', {'t': oe.eid, 'u': p.eid}, ('t','u')) e = self.add_entity('Note', type=u'z') @@ -127,10 +127,10 @@ Note = self.vreg['etypes'].etype_class('Note') peschema = Personne.e_schema seschema = Societe.e_schema - peschema.subjrels['travaille'].set_rproperty(peschema, seschema, 'cardinality', '1*') - peschema.subjrels['connait'].set_rproperty(peschema, peschema, 'cardinality', '11') - peschema.subjrels['evaluee'].set_rproperty(peschema, Note.e_schema, 'cardinality', '1*') - seschema.subjrels['evaluee'].set_rproperty(seschema, Note.e_schema, 'cardinality', '1*') + peschema.subjrels['travaille'].rdef(peschema, seschema).cardinality = '1*' + peschema.subjrels['connait'].rdef(peschema, peschema).cardinality = '11' + peschema.subjrels['evaluee'].rdef(peschema, Note.e_schema).cardinality = '1*' + seschema.subjrels['evaluee'].rdef(seschema, Note.e_schema).cardinality = '1*' # testing basic fetch_attrs attribute self.assertEquals(Personne.fetch_rql(user), 'Any X,AA,AB,AC ORDERBY AA ASC ' @@ -165,13 +165,13 @@ self.assertEquals(Personne.fetch_rql(user), 'Any X,AA,AB ORDERBY AA ASC ' 'WHERE X is Personne, X nom AA, X connait AB?') # testing optional relation - peschema.subjrels['travaille'].set_rproperty(peschema, seschema, 'cardinality', '?*') + peschema.subjrels['travaille'].rdef(peschema, seschema).cardinality = '?*' Personne.fetch_attrs = ('nom', 'prenom', 'travaille') Societe.fetch_attrs = ('nom',) self.assertEquals(Personne.fetch_rql(user), 'Any X,AA,AB,AC,AD ORDERBY AA ASC WHERE X is Personne, X nom AA, X prenom AB, X travaille AC?, AC nom AD') # testing relation with cardinality > 1 - peschema.subjrels['travaille'].set_rproperty(peschema, seschema, 'cardinality', '**') + peschema.subjrels['travaille'].rdef(peschema, seschema).cardinality = '**' self.assertEquals(Personne.fetch_rql(user), 'Any X,AA,AB ORDERBY AA ASC WHERE X is Personne, X nom AA, X prenom AB') # XXX test unauthorized attribute diff -r 1169d3154be6 -r 7ca53fc72a0a test/unittest_rqlrewrite.py --- a/test/unittest_rqlrewrite.py Wed Nov 18 09:16:38 2009 +0100 +++ b/test/unittest_rqlrewrite.py Thu Nov 19 12:55:47 2009 +0100 @@ -18,7 +18,8 @@ config = TestServerConfiguration('data/rewrite') config.bootstrap_cubes() schema = config.load_schema() -schema.add_relation_def(mock_object(subject='Card', name='in_state', object='State', cardinality='1*')) +from yams.buildobjs import RelationDefinition +schema.add_relation_def(RelationDefinition(subject='Card', name='in_state', object='State', cardinality='1*')) rqlhelper = RQLHelper(schema, special_relations={'eid': 'uid', 'has_text': 'fti'}) diff -r 1169d3154be6 -r 7ca53fc72a0a test/unittest_schema.py --- a/test/unittest_schema.py Wed Nov 18 09:16:38 2009 +0100 +++ b/test/unittest_schema.py Thu Nov 19 12:55:47 2009 +0100 @@ -19,7 +19,7 @@ from yams.reader import PyFileReader from cubicweb.schema import CubicWebSchema, CubicWebEntitySchema, \ - RQLConstraint, CubicWebSchemaLoader, ERQLExpression, RRQLExpression, \ + RQLConstraint, CubicWebSchemaLoader, RQLExpression, ERQLExpression, RRQLExpression, \ normalize_expression, order_eschemas from cubicweb.devtools import TestServerConfiguration as TestConfiguration @@ -44,7 +44,7 @@ schema = CubicWebSchema('Test Schema') enote = schema.add_entity_type(EntityType('Note')) eaffaire = schema.add_entity_type(EntityType('Affaire')) -eperson = schema.add_entity_type(EntityType('Personne', permissions=PERSONNE_PERMISSIONS)) +eperson = schema.add_entity_type(EntityType('Personne', __permissions__=PERSONNE_PERMISSIONS)) esociete = schema.add_entity_type(EntityType('Societe')) RELS = ( @@ -73,12 +73,13 @@ for rel in RELS: _from, _type, _to = rel.split() if not _type.lower() in done: - if _type == 'concerne': - schema.add_relation_type(RelationType(_type, permissions=CONCERNE_PERMISSIONS)) - else: - schema.add_relation_type(RelationType(_type)) + schema.add_relation_type(RelationType(_type)) done[_type.lower()] = True - schema.add_relation_def(RelationDefinition(_from, _type, _to)) + if _type == 'concerne': + schema.add_relation_def(RelationDefinition(_from, _type, _to, + __permissions__=CONCERNE_PERMISSIONS)) + else: + schema.add_relation_def(RelationDefinition(_from, _type, _to)) class CubicWebSchemaTC(TestCase): @@ -94,26 +95,24 @@ self.assertEqual(schema.rschema('concerne').type, 'concerne') def test_entity_perms(self): - eperson.set_default_groups() self.assertEqual(eperson.get_groups('read'), set(('managers', 'users', 'guests'))) self.assertEqual(eperson.get_groups('update'), set(('managers', 'owners',))) self.assertEqual(eperson.get_groups('delete'), set(('managers', 'owners'))) self.assertEqual(eperson.get_groups('add'), set(('managers',))) self.assertEqual([str(e) for e in eperson.get_rqlexprs('add')], ['Any X WHERE X travaille S, S owned_by U, X eid %(x)s, U eid %(u)s']) - eperson.set_groups('read', ('managers',)) + eperson.set_action_permissions('read', ('managers',)) self.assertEqual(eperson.get_groups('read'), set(('managers',))) def test_relation_perms(self): - rconcerne = schema.rschema('concerne') - rconcerne.set_default_groups() + rconcerne = schema.rschema('concerne').rdef('Personne', 'Societe') self.assertEqual(rconcerne.get_groups('read'), set(('managers', 'users', 'guests'))) self.assertEqual(rconcerne.get_groups('delete'), set(('managers',))) self.assertEqual(rconcerne.get_groups('add'), set(('managers', ))) - rconcerne.set_groups('read', ('managers',)) + rconcerne.set_action_permissions('read', ('managers',)) self.assertEqual(rconcerne.get_groups('read'), set(('managers',))) self.assertEqual([str(e) for e in rconcerne.get_rqlexprs('add')], - ['Any S WHERE U has_update_permission S, S eid %(s)s, U eid %(u)s']) + ['Any S,U WHERE U has_update_permission S, S eid %(s)s, U eid %(u)s']) def test_erqlexpression(self): self.assertRaises(RQLSyntaxError, ERQLExpression, '1') @@ -124,7 +123,7 @@ self.assertRaises(Exception, RRQLExpression, '1') self.assertRaises(RQLSyntaxError, RRQLExpression, 'O X Y') expr = RRQLExpression('U has_update_permission O') - self.assertEquals(str(expr), 'Any O WHERE U has_update_permission O, O eid %(o)s, U eid %(u)s') + self.assertEquals(str(expr), 'Any O,U WHERE U has_update_permission O, O eid %(o)s, U eid %(u)s') loader = CubicWebSchemaLoader() config = TestConfiguration('data') @@ -215,9 +214,9 @@ self.assertListEquals(rels, ['bookmarked_by', 'created_by', 'for_user', 'identity', 'owned_by', 'wf_info_for']) rschema = schema.rschema('relation_type') - properties = rschema.rproperties('CWAttribute', 'CWRType') - self.assertEquals(properties['cardinality'], '1*') - constraints = properties['constraints'] + properties = rschema.rdef('CWAttribute', 'CWRType') + self.assertEquals(properties.cardinality, '1*') + constraints = properties.constraints self.failUnlessEqual(len(constraints), 1, constraints) constraint = constraints[0] self.failUnless(isinstance(constraint, RQLConstraint)) @@ -246,13 +245,13 @@ self._test('rrqlexpr_on_eetype.py', "can't use RRQLExpression on an entity type, use an ERQLExpression (ToTo)") def test_erqlexpr_on_rtype(self): - self._test('erqlexpr_on_ertype.py', "can't use ERQLExpression on a relation type, use a RRQLExpression (toto)") + self._test('erqlexpr_on_ertype.py', "can't use ERQLExpression on relation ToTo toto TuTu, use a RRQLExpression") def test_rqlexpr_on_rtype_read(self): - self._test('rqlexpr_on_ertype_read.py', "can't use rql expression for read permission of a relation type (toto)") + self._test('rqlexpr_on_ertype_read.py', "can't use rql expression for read permission of relation ToTo toto TuTu") def test_rrqlexpr_on_attr(self): - self._test('rrqlexpr_on_attr.py', "can't use RRQLExpression on a final relation type (eg attribute relation), use an ERQLExpression (attr)") + self._test('rrqlexpr_on_attr.py', "can't use RRQLExpression on attribute ToTo.attr[String], use an ERQLExpression") class NormalizeExpressionTC(TestCase): @@ -261,5 +260,9 @@ self.assertEquals(normalize_expression('X bla Y,Y blur Z , Z zigoulou X '), 'X bla Y, Y blur Z, Z zigoulou X') +class RQLExpressionTC(TestCase): + def test_comparison(self): + self.assertEquals(ERQLExpression('X is CWUser', 'X', 0), ERQLExpression('X is CWUser', 'X', 0)) + self.assertNotEquals(ERQLExpression('X is CWUser', 'X', 0), ERQLExpression('X is CWGroup', 'X', 0)) if __name__ == '__main__': unittest_main() diff -r 1169d3154be6 -r 7ca53fc72a0a web/uicfg.py --- a/web/uicfg.py Wed Nov 18 09:16:38 2009 +0100 +++ b/web/uicfg.py Thu Nov 19 12:55:47 2009 +0100 @@ -73,30 +73,24 @@ from cubicweb.web import formwidgets -def card_from_role(card, role): - if role == 'subject': - return card[0] - assert role in ('object', 'sobject'), repr(role) - return card[1] - # primary view configuration ################################################## def init_primaryview_section(rtag, sschema, rschema, oschema, role): if rtag.get(sschema, rschema, oschema, role) is None: - card = card_from_role(rschema.rproperty(sschema, oschema, 'cardinality'), role) - composed = rschema.rproperty(sschema, oschema, 'composite') == neg_role(role) + rdef = rschema.rdef(sschema, oschema) if rschema.final: if rschema.meta or sschema.is_metadata(rschema) \ or oschema.type in ('Password', 'Bytes'): section = 'hidden' else: section = 'attributes' - elif card in '1+': - section = 'attributes' - elif composed: - section = 'relations' else: - section = 'sideboxes' + if rdef.role_cardinality(role) in '1+': + section = 'attributes' + elif rdef.composite == neg_role(role): + section = 'relations' + else: + section = 'sideboxes' rtag.tag_relation((sschema, rschema, oschema, role), section) primaryview_section = RelationTags('primaryview_section', @@ -177,13 +171,8 @@ elif sschema.is_metadata(rschema): section = 'metadata' else: - if role == 'subject': - card = rschema.rproperty(sschema, oschema, 'cardinality')[0] - composed = rschema.rproperty(sschema, oschema, 'composite') == 'object' - else: - card = rschema.rproperty(sschema, oschema, 'cardinality')[1] - composed = rschema.rproperty(sschema, oschema, 'composite') == 'subject' - if card in '1+': + rdef = rschema.rdef(sschema, oschema) + if rdef.role_cardinality(role) in '1+': section = 'primary' elif rschema.final: section = 'secondary' @@ -217,9 +206,8 @@ # 'link' / 'create' relation tags, used to control the "add entity" submenu def init_actionbox_appearsin_addmenu(rtag, sschema, rschema, oschema, role): if rtag.get(sschema, rschema, oschema, role) is None: - card = rschema.rproperty(sschema, oschema, 'cardinality')[role == 'object'] - if not card in '?1' and \ - rschema.rproperty(sschema, oschema, 'composite') == role: + rdef = rschema.rdef(sschema, oschema) + if not rdef.role_cardinality(role) in '?1' and rdef.composite == role: rtag.tag_relation((sschema, rschema, oschema, role), True) actionbox_appearsin_addmenu = RelationTagsBool('actionbox_appearsin_addmenu', diff -r 1169d3154be6 -r 7ca53fc72a0a web/views/actions.py --- a/web/views/actions.py Wed Nov 18 09:16:38 2009 +0100 +++ b/web/views/actions.py Thu Nov 19 12:55:47 2009 +0100 @@ -268,20 +268,21 @@ for rschema in rschemas: if rschema.final: continue - # check the relation can be added as well - # XXX consider autoform_permissions_overrides? - if role == 'subject'and not rschema.has_perm(req, 'add', - fromeid=entity.eid): - continue - if role == 'object'and not rschema.has_perm(req, 'add', - toeid=entity.eid): - continue - # check the target types can be added as well for teschema in rschema.targets(eschema, role): if not appearsin_addmenu.etype_get(eschema, rschema, role, teschema): continue - if teschema.has_local_role('add') or teschema.has_perm(req, 'add'): + rdef = rschema.role_rdef(eschema, teschema, role) + # check the relation can be added + # XXX consider autoform_permissions_overrides? + if role == 'subject'and not rdef.has_perm( + req, 'add', fromeid=entity.eid): + continue + if role == 'object'and not rdef.has_perm( + req, 'add', toeid=entity.eid): + continue + # check the target types can be added as well + if teschema.may_have_permission('add', req): yield rschema, teschema, role def linkto_url(self, entity, rtype, etype, target): diff -r 1169d3154be6 -r 7ca53fc72a0a web/views/autoform.py --- a/web/views/autoform.py Wed Nov 18 09:16:38 2009 +0100 +++ b/web/views/autoform.py Thu Nov 19 12:55:47 2009 +0100 @@ -75,10 +75,18 @@ # check category first, potentially lower cost than checking # permission which may imply rql queries if categories is not None: - targetschemas = [tschema for tschema in targetschemas - if rtags.etype_get(eschema, rschema, role, tschema) in categories] - if not targetschemas: + _targetschemas = [] + for tschema in targetschemas: + if not rtags.etype_get(eschema, rschema, role, tschema) in categories: + continue + rdef = rschema.role_rdef(eschema, tschema, role) + if not ((not strict and rdef.has_local_role(permission)) or + rdef.has_perm(entity.req, permission, fromeid=eid)): + continue + _targetschemas.append(tschema) + if not _targetschemas: continue + targetschemas = _targetschemas if permission is not None: # tag allowing to hijack the permission machinery when # permission is not verifiable until the entity is actually @@ -87,28 +95,22 @@ yield (rschema, targetschemas, role) continue if rschema.final: - if not rschema.has_perm(entity.req, permission, eid): + if not eschema.rdef(rschema).has_perm(entity.req, permission, eid=eid): continue elif role == 'subject': - if not ((not strict and rschema.has_local_role(permission)) or - rschema.has_perm(entity.req, permission, fromeid=eid)): - continue # on relation with cardinality 1 or ?, we need delete perm as well # if the relation is already set if (permission == 'add' - and rschema.cardinality(eschema, targetschemas[0], role) in '1?' + and rschema.rdef(eschema, targetschemas[0]).role_cardinality(role) in '1?' and eid and entity.related(rschema.type, role) and not rschema.has_perm(entity.req, 'delete', fromeid=eid, toeid=entity.related(rschema.type, role)[0][0])): continue elif role == 'object': - if not ((not strict and rschema.has_local_role(permission)) or - rschema.has_perm(entity.req, permission, toeid=eid)): - continue # on relation with cardinality 1 or ?, we need delete perm as well # if the relation is already set if (permission == 'add' - and rschema.cardinality(targetschemas[0], eschema, role) in '1?' + and rschema.rdef(targetschemas[0], eschema).role_cardinality(role) in '1?' and eid and entity.related(rschema.type, role) and not rschema.has_perm(entity.req, 'delete', toeid=eid, fromeid=entity.related(rschema.type, role)[0][0])): diff -r 1169d3154be6 -r 7ca53fc72a0a web/views/owl.py --- a/web/views/owl.py Wed Nov 18 09:16:38 2009 +0100 +++ b/web/views/owl.py Thu Nov 19 12:55:47 2009 +0100 @@ -71,10 +71,8 @@ if writeprefix: self.w(OWL_CLOSING_ROOT) - def should_display_rschema(self, rschema): - return not rschema in self.skiptypes and ( - rschema.has_local_role('read') or - rschema.has_perm(self.req, 'read')) + def should_display_rschema(self, eschema, rschema, tschemas, role): + return rschema.may_have_permissions('read', self.req, eschema, role) def visit_schema(self, skiptypes): """get a layout for a whole schema""" @@ -94,7 +92,7 @@ self.w(u''% eschema) self.w(u'') for rschema, targetschemas, role in eschema.relation_definitions(): - if not self.should_display_rschema(rschema): + if not self.should_display_rschema(eschema, rschema, targetschemas, role): continue for oeschema in targetschemas: if role == 'subject': @@ -112,7 +110,7 @@ self.w(u'') for rschema, aschema in eschema.attribute_definitions(): - if not self.should_display_rschema(rschema): + if not self.should_display_rschema(eschema, rschema, (aschema,), 'subject'): continue self.w(u''' @@ -125,7 +123,7 @@ def visit_property_schema(self, eschema): """get a layout for property entity OWL schema""" for rschema, targetschemas, role in eschema.relation_definitions(): - if not self.should_display_rschema(rschema): + if not self.should_display_rschema(eschema, rschema, targetschemas, role): continue for oeschema in targetschemas: self.w(u''' @@ -135,7 +133,7 @@ def visit_property_object_schema(self, eschema): for rschema, aschema in eschema.attribute_definitions(): - if not self.should_display_rschema(rschema): + if not self.should_display_rschema(eschema, rschema, (aschema,), 'subject'): continue self.w(u''' @@ -174,7 +172,8 @@ for rschema, aschema in eschema.attribute_definitions(): if rschema.meta: continue - if not (rschema.has_local_role('read') or rschema.has_perm(self.req, 'read')): + rdef = rschema.rdef(eschema, aschema) + if not rdef.may_have_permission('read', self.req): continue aname = rschema.type if aname == 'eid': @@ -189,8 +188,10 @@ for rschema, targetschemas, role in eschema.relation_definitions(): if rschema.meta: continue - if not (rschema.has_local_role('read') or rschema.has_perm(self.req, 'read')): - continue + for tschema in targetschemas: + rdef = rschema.role_rdef(eschema, tschema, role) + if not rdef.may_have_permission('read', self.req): + continue if role == 'object': attr = 'reverse_%s' % rschema.type else: diff -r 1169d3154be6 -r 7ca53fc72a0a web/views/schema.py --- a/web/views/schema.py Wed Nov 18 09:16:38 2009 +0100 +++ b/web/views/schema.py Thu Nov 19 12:55:47 2009 +0100 @@ -357,13 +357,11 @@ def should_display_schema(self, rschema): return (super(RestrictedSchemaVisitorMixIn, self).should_display_schema(rschema) - and (rschema.has_local_role('read') - or rschema.has_perm(self.req, 'read'))) + and rschema.may_have_permission('read', self.req)) - def should_display_attr(self, rschema): + def should_display_attr(self, eschema, rschema): return (super(RestrictedSchemaVisitorMixIn, self).should_display_attr(rschema) - and (rschema.has_local_role('read') - or rschema.has_perm(self.req, 'read'))) + and eschema.rdef(rschema).may_have_permission('read')) class FullSchemaVisitor(RestrictedSchemaVisitorMixIn, s2d.FullSchemaVisitor): diff -r 1169d3154be6 -r 7ca53fc72a0a web/views/startup.py --- a/web/views/startup.py Wed Nov 18 09:16:38 2009 +0100 +++ b/web/views/startup.py Thu Nov 19 12:55:47 2009 +0100 @@ -26,6 +26,8 @@ @classmethod def vreg_initialization_completed(cls): for eschema in cls.schema.entities(): + if eschema.final: + continue if eschema.schema_entity(): uicfg.indexview_etype_section.setdefault(eschema, 'schema') elif eschema.is_subobject(strict=True): @@ -140,8 +142,7 @@ """ req = self.req for eschema in eschemas: - if eschema.final or (not eschema.has_perm(req, 'read') and - not eschema.has_local_role('read')): + if eschema.final or not eschema.may_have_permission('read', req): continue etype = eschema.type label = display_name(req, etype, 'plural')