# HG changeset patch # User Sylvain Thénault # Date 1266243025 -3600 # Node ID ede247bbbf62f3b43a0e603e4eef8211eecea41d # Parent 1acd90d0cb5980f83b4a7a49a33276bc8697c9c1 follow yams api change: attributes permissions are now defined for an 'update' action, no more 'add' / 'delete' which makes no sense in such case. fix afs.relations_by_section permissions checking of object relation on the way. diff -r 1acd90d0cb59 -r ede247bbbf62 hooks/security.py --- a/hooks/security.py Mon Feb 15 15:05:15 2010 +0100 +++ b/hooks/security.py Mon Feb 15 15:10:25 2010 +0100 @@ -28,7 +28,7 @@ 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' ?) - rdef.check_perm(session, 'add', eid=eid) + rdef.check_perm(session, 'update', eid=eid) class _CheckEntityPermissionOp(hook.LateOperation): diff -r 1acd90d0cb59 -r ede247bbbf62 hooks/syncschema.py --- a/hooks/syncschema.py Mon Feb 15 15:05:15 2010 +0100 +++ b/hooks/syncschema.py Mon Feb 15 15:10:25 2010 +0100 @@ -716,6 +716,9 @@ return if isinstance(erschema, RelationSchema): # XXX 3.6 migration return + if isinstance(erschema, RelationDefinitionSchema) and \ + self.action in ('delete', 'add'): # XXX 3.6.1 migration + return perms = list(erschema.action_permissions(self.action)) if hasattr(self, 'group_eid'): perm = self.session.entity_from_eid(self.group_eid).name diff -r 1acd90d0cb59 -r ede247bbbf62 misc/migration/3.6.1_Any.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/misc/migration/3.6.1_Any.py Mon Feb 15 15:10:25 2010 +0100 @@ -0,0 +1,1 @@ +sync_schema_props_perms(syncprops=False) diff -r 1acd90d0cb59 -r ede247bbbf62 misc/migration/bootstrapmigration_repository.py --- a/misc/migration/bootstrapmigration_repository.py Mon Feb 15 15:05:15 2010 +0100 +++ b/misc/migration/bootstrapmigration_repository.py Mon Feb 15 15:10:25 2010 +0100 @@ -10,24 +10,35 @@ applcubicwebversion, cubicwebversion = versions_map['cubicweb'] -if applcubicwebversion < (3, 6, 0) and cubicwebversion >= (3, 6, 0): - from cubicweb.server import schemaserial as ss +from cubicweb.server import schemaserial as ss +def _add_relation_definition_no_perms(subjtype, rtype, objtype): + rschema = fsschema.rschema(rtype) + for query, args in ss.rdef2rql(rschema, subjtype, objtype, groupmap=None): + rql(query, args, ask_confirm=False) + commit(ask_confirm=False) + +if applcubicwebversion == (3, 6, 0) and cubicwebversion >= (3, 6, 0): + _add_relation_definition_no_perms('CWAttribute', 'update_permission', 'CWGroup') + _add_relation_definition_no_perms('CWAttribute', 'update_permission', 'RQLExpression') + session.set_pool() + session.unsafe_execute('SET X update_permission Y WHERE X is CWAttribute, X add_permission Y') + drop_relation_definition('CWAttribute', 'add_permission', 'CWGroup') + drop_relation_definition('CWAttribute', 'add_permission', 'RQLExpression') + drop_relation_definition('CWAttribute', 'delete_permission', 'CWGroup') + drop_relation_definition('CWAttribute', 'delete_permission', 'RQLExpression') + +elif applcubicwebversion < (3, 6, 0) and cubicwebversion >= (3, 6, 0): session.set_pool() session.execute = session.unsafe_execute permsdict = ss.deserialize_ertype_permissions(session) - def _add_relation_definition_no_perms(subjtype, rtype, objtype): - rschema = fsschema.rschema(rtype) - for query, args in ss.rdef2rql(rschema, subjtype, objtype, groupmap=None): - rql(query, args, ask_confirm=False) - commit(ask_confirm=False) config.disabled_hooks_categories.add('integrity') for rschema in repo.schema.relations(): rpermsdict = permsdict.get(rschema.eid, {}) for rdef in rschema.rdefs.values(): - for action in ('read', 'add', 'delete'): + for action in rdef.ACTIONS: actperms = [] - for something in rpermsdict.get(action, ()): + for something in rpermsdict.get(action == 'update' and 'add' or action, ()): if isinstance(something, tuple): actperms.append(rdef.rql_expression(*something)) else: # group name @@ -36,18 +47,32 @@ for action in ('read', 'add', 'delete'): _add_relation_definition_no_perms('CWRelation', '%s_permission' % action, 'CWGroup') _add_relation_definition_no_perms('CWRelation', '%s_permission' % action, 'RQLExpression') + for action in ('read', 'update'): _add_relation_definition_no_perms('CWAttribute', '%s_permission' % action, 'CWGroup') _add_relation_definition_no_perms('CWAttribute', '%s_permission' % action, 'RQLExpression') for action in ('read', 'add', 'delete'): - rql('SET X %s_permission Y WHERE X is IN (CWAttribute, CWRelation), ' + rql('SET X %s_permission Y WHERE X is CWRelation, ' 'RT %s_permission Y, X relation_type RT, Y is CWGroup' % (action, action)) rql('INSERT RQLExpression Y: Y exprtype YET, Y mainvars YMV, Y expression YEX, ' - 'X %s_permission Y WHERE X is IN (CWAttribute, CWRelation), ' + 'X %s_permission Y WHERE X is CWRelation, ' 'X relation_type RT, RT %s_permission Y2, Y2 exprtype YET, ' 'Y2 mainvars YMV, Y2 expression YEX' % (action, action)) + rql('SET X read_permission Y WHERE X is CWAttribute, ' + 'RT read_permission Y, X relation_type RT, Y is CWGroup') + rql('INSERT RQLExpression Y: Y exprtype YET, Y mainvars YMV, Y expression YEX, ' + 'X read_permission Y WHERE X is CWAttribute, ' + 'X relation_type RT, RT read_permission Y2, Y2 exprtype YET, ' + 'Y2 mainvars YMV, Y2 expression YEX') + rql('SET X update_permission Y WHERE X is CWAttribute, ' + 'RT add_permission Y, X relation_type RT, Y is CWGroup') + rql('INSERT RQLExpression Y: Y exprtype YET, Y mainvars YMV, Y expression YEX, ' + 'X update_permission Y WHERE X is CWAttribute, ' + 'X relation_type RT, RT add_permission Y2, Y2 exprtype YET, ' + 'Y2 mainvars YMV, Y2 expression YEX') + for action in ('read', 'add', 'delete'): drop_relation_definition('CWRType', '%s_permission' % action, 'CWGroup', commit=False) drop_relation_definition('CWRType', '%s_permission' % action, 'RQLExpression') - config.disabled_hooks_categories.add('integrity') + config.disabled_hooks_categories.remove('integrity') if applcubicwebversion < (3, 4, 0) and cubicwebversion >= (3, 4, 0): diff -r 1acd90d0cb59 -r ede247bbbf62 schema.py --- a/schema.py Mon Feb 15 15:05:15 2010 +0100 +++ b/schema.py Mon Feb 15 15:10:25 2010 +0100 @@ -919,6 +919,11 @@ kwargs['o'] = toeid return self._check(session, **kwargs) +# in yams, default 'update' perm for attributes granted to managers and owners. +# Within cw, we want to default to users who may edit the entity holding the +# attribute. +ybo._DEFAULT_ATTRPERMS['update'] = ( + 'managers', ERQLExpression('U has_update_permission X')) # workflow extensions ######################################################### diff -r 1acd90d0cb59 -r ede247bbbf62 schemas/bootstrap.py --- a/schemas/bootstrap.py Mon Feb 15 15:05:15 2010 +0100 +++ b/schemas/bootstrap.py Mon Feb 15 15:10:25 2010 +0100 @@ -9,7 +9,7 @@ _ = unicode from yams.buildobjs import (EntityType, RelationType, SubjectRelation, - ObjectRelation, RichString, String, Boolean, Int) + RichString, String, Boolean, Int) from cubicweb.schema import RQLConstraint from cubicweb.schemas import META_ETYPE_PERMS, META_RTYPE_PERMS @@ -131,15 +131,6 @@ 'relation\'subject, object and to ' 'the request user. ')) - read_permission = ObjectRelation(('CWEType', 'CWAttribute', 'CWRelation'), cardinality='*?', composite='subject', - description=_('rql expression allowing to read entities/relations of this type')) - add_permission = ObjectRelation(('CWEType', 'CWAttribute', 'CWRelation'), cardinality='*?', composite='subject', - description=_('rql expression allowing to add entities/relations of this type')) - 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')) - class CWConstraint(EntityType): """define a schema constraint""" @@ -162,16 +153,6 @@ name = String(required=True, indexed=True, internationalizable=True, unique=True, maxsize=64) - read_permission = ObjectRelation(('CWEType', 'CWAttribute', 'CWRelation'), cardinality='**', - description=_('groups allowed to read entities/relations of this type')) - add_permission = ObjectRelation(('CWEType', 'CWAttribute', 'CWRelation'), - description=_('groups allowed to add entities/relations of this type')) - 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')) - - class CWProperty(EntityType): """used for cubicweb configuration. Once a property has been created you @@ -215,27 +196,44 @@ inlined = True class read_permission(RelationType): - """core relation giving to a group the permission to read an entity or - relation type + """grant permission to read entity or relation through a group or rql + expression """ __permissions__ = META_RTYPE_PERMS + subject = ('CWEType', 'CWAttribute', 'CWRelation') + object = ('CWGroup', 'RQLExpression') + cardinality = '*?' + composite = 'subject' class add_permission(RelationType): - """core relation giving to a group the permission to add an entity or - relation type + """grant permission to add entity or relation through a group or rql + expression """ __permissions__ = META_RTYPE_PERMS + subject = ('CWEType', 'CWRelation') + object = ('CWGroup', 'RQLExpression') + cardinality = '*?' + composite = 'subject' class delete_permission(RelationType): - """core relation giving to a group the permission to delete an entity or - relation type + """grant permission to delete entity or relation through a group or rql + expression """ __permissions__ = META_RTYPE_PERMS + subject = ('CWEType', 'CWRelation') + object = ('CWGroup', 'RQLExpression') + cardinality = '*?' + composite = 'subject' class update_permission(RelationType): - """core relation giving to a group the permission to update an entity type + """grant permission to update entity or attribute through a group or rql + expression """ __permissions__ = META_RTYPE_PERMS + subject = ('CWEType', 'CWAttribute') + object = ('CWGroup', 'RQLExpression') + cardinality = '*?' + composite = 'subject' class is_(RelationType): diff -r 1acd90d0cb59 -r ede247bbbf62 server/schemaserial.py --- a/server/schemaserial.py Mon Feb 15 15:05:15 2010 +0100 +++ b/server/schemaserial.py Mon Feb 15 15:10:25 2010 +0100 @@ -189,13 +189,17 @@ definition dictionary as built by deserialize_ertype_permissions for a given erschema's eid """ + # reset erschema permissions here to avoid getting yams default anyway + erschema.permissions = dict((action, ()) for action in erschema.ACTIONS) try: thispermsdict = permsdict[erschema.eid] except KeyError: return - permissions = erschema.permissions for action, somethings in thispermsdict.iteritems(): - permissions[action] = tuple( + # XXX cw < 3.6.1 bw compat + if isinstance(erschema, schemamod.RelationDefinitionSchema) and erschema.final and action == 'add': + action = 'update' + erschema.permissions[action] = tuple( isinstance(p, tuple) and erschema.rql_expression(*p) or p for p in somethings) diff -r 1acd90d0cb59 -r ede247bbbf62 server/test/data/schema.py --- a/server/test/data/schema.py Mon Feb 15 15:05:15 2010 +0100 +++ b/server/test/data/schema.py Mon Feb 15 15:10:25 2010 +0100 @@ -137,14 +137,13 @@ class para(RelationType): __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"')), + 'update': ('managers', ERQLExpression('X in_state S, S name "todo"')), } class test(RelationType): __permissions__ = {'read': ('managers', 'users', 'guests'), - 'delete': ('managers',), - 'add': ('managers',)} + 'update': ('managers',), + } class multisource_rel(RelationDefinition): subject = ('Card', 'Note') diff -r 1acd90d0cb59 -r ede247bbbf62 web/test/unittest_views_editforms.py --- a/web/test/unittest_views_editforms.py Mon Feb 15 15:05:15 2010 +0100 +++ b/web/test/unittest_views_editforms.py Mon Feb 15 15:10:25 2010 +0100 @@ -16,7 +16,11 @@ AFS = uicfg.autoform_section def rbc(entity, formtype, section): - return [(rschema.type, x) for rschema, tschemas, x in AFS.relations_by_section(entity, formtype, section)] + if section in ('attributes', 'metadata', 'hidden'): + permission = 'update' + else: + permission = 'add' + return [(rschema.type, x) for rschema, tschemas, x in AFS.relations_by_section(entity, formtype, section, permission)] class AutomaticEntityFormTC(CubicWebTC): @@ -69,19 +73,16 @@ [('use_email', 'subject'), ]) # owned_by is defined both as subject and object relations on CWUser - self.assertListEquals(rbc(e, 'main', 'hidden'), - [('in_state', 'subject'), - ('is', 'subject'), - ('is_instance_of', 'subject'), - ('has_text', 'subject'), - ('identity', 'subject'), - ('tags', 'object'), - ('for_user', 'object'), - ('created_by', 'object'), - ('wf_info_for', 'object'), - ('owned_by', 'object'), - ('identity', 'object'), - ]) + self.assertListEquals(sorted(rbc(e, 'main', 'hidden')), + sorted([('has_text', 'subject'), + ('identity', 'subject'), + ('tags', 'object'), + ('for_user', 'object'), + ('created_by', 'object'), + ('wf_info_for', 'object'), + ('owned_by', 'object'), + ('identity', 'object'), + ])) def test_inlined_view(self): self.failUnless('main_inlined' in AFS.etype_get('CWUser', 'use_email', 'subject', 'EmailAddress')) @@ -122,10 +123,8 @@ ('connait', 'object') ]) self.assertListEquals(rbc(e, 'main', 'hidden'), - [('is', 'subject'), - ('has_text', 'subject'), + [('has_text', 'subject'), ('identity', 'subject'), - ('is_instance_of', 'subject'), ('identity', 'object'), ]) diff -r 1acd90d0cb59 -r ede247bbbf62 web/uicfg.py --- a/web/uicfg.py Mon Feb 15 15:05:15 2010 +0100 +++ b/web/uicfg.py Mon Feb 15 15:10:25 2010 +0100 @@ -347,8 +347,8 @@ # overriden to avoid recomputing done in parent classes return self._tagdefs.get(key, ()) - def relations_by_section(self, entity, formtype, section, - permission=None, strict=False): + def relations_by_section(self, entity, formtype, section, permission, + strict=False): """return a list of (relation schema, target schemas, role) for the given entity matching categories and permission. @@ -363,52 +363,60 @@ else: eid = None strict = False + if permission == 'update': + assert section in ('attributes', 'metadata', 'hidden') + relpermission = 'add' + else: + assert section not in ('attributes', 'metadata', 'hidden') + relpermission = permission cw = entity._cw for rschema, targetschemas, role in eschema.relation_definitions(True): - # check category first, potentially lower cost than checking - # permission which may imply rql queries _targetschemas = [] for tschema in targetschemas: + # check section's tag first, potentially lower cost than + # checking permission which may imply rql queries if not tag in self.etype_get(eschema, rschema, role, tschema): continue rdef = rschema.role_rdef(eschema, tschema, role) - if permission is not None and \ - not ((not strict and rdef.has_local_role(permission)) or - rdef.has_perm(cw, permission, fromeid=eid)): - continue + if rschema.final: + if not rdef.has_perm(cw, permission, eid=eid): + continue + elif strict or not rdef.has_local_role(relpermission): + if role == 'subject': + if not rdef.has_perm(cw, relpermission, fromeid=eid): + continue + elif role == 'object': + if not rdef.has_perm(cw, relpermission, toeid=eid): + continue _targetschemas.append(tschema) if not _targetschemas: continue targetschemas = _targetschemas - if permission is not None: - rdef = eschema.rdef(rschema, role=role, targettype=targetschemas[0]) - # tag allowing to hijack the permission machinery when - # permission is not verifiable until the entity is actually - # created... - if eid is None and '%s_on_new' % permission in permsoverrides.etype_get(eschema, rschema, role): - yield (rschema, targetschemas, role) + rdef = eschema.rdef(rschema, role=role, targettype=targetschemas[0]) + # XXX tag allowing to hijack the permission machinery when + # permission is not verifiable until the entity is actually + # created... + if eid is None and '%s_on_new' % permission in permsoverrides.etype_get(eschema, rschema, role): + yield (rschema, targetschemas, role) + continue + if not rschema.final and role == 'subject': + # on relation with cardinality 1 or ?, we need delete perm as well + # if the relation is already set + if (relpermission == 'add' + and rdef.role_cardinality(role) in '1?' + and eid and entity.related(rschema.type, role) + and not rdef.has_perm(cw, 'delete', fromeid=eid, + toeid=entity.related(rschema.type, role)[0][0])): continue - if rschema.final: - if not rdef.has_perm(cw, permission, fromeid=eid): - continue - elif role == 'subject': - # on relation with cardinality 1 or ?, we need delete perm as well - # if the relation is already set - if (permission == 'add' - and rdef.role_cardinality(role) in '1?' - and eid and entity.related(rschema.type, role) - and not rdef.has_perm(cw, 'delete', fromeid=eid, - toeid=entity.related(rschema.type, role)[0][0])): - continue - elif role == 'object': - # on relation with cardinality 1 or ?, we need delete perm as well - # if the relation is already set - if (permission == 'add' - and rdef.role_cardinality(role) in '1?' - and eid and entity.related(rschema.type, role) - and not rdef.has_perm(cw, 'delete', toeid=eid, - fromeid=entity.related(rschema.type, role)[0][0])): - continue + elif role == 'object': + # on relation with cardinality 1 or ?, we need delete perm as well + # if the relation is already set + if (relpermission == 'add' + and rdef.role_cardinality(role) in '1?' + and eid and entity.related(rschema.type, role) + and not rdef.has_perm(cw, 'delete', toeid=eid, + fromeid=entity.related(rschema.type, role)[0][0])): + continue yield (rschema, targetschemas, role) autoform_section = AutoformSectionRelationTags('autoform_section') diff -r 1acd90d0cb59 -r ede247bbbf62 web/views/autoform.py --- a/web/views/autoform.py Mon Feb 15 15:05:15 2010 +0100 +++ b/web/views/autoform.py Mon Feb 15 15:10:25 2010 +0100 @@ -686,7 +686,7 @@ return self.display_fields # XXX we should simply put eid in the generated section, no? return [(rtype, role) for rtype, _, role in self._relations_by_section( - 'attributes', strict=strict) if rtype != 'eid'] + 'attributes', 'update', strict) if rtype != 'eid'] def editable_relations(self): """return a sorted list of (relation's label, relation'schema, role) for