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.
--- 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):
--- 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
--- /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)
--- 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):
--- 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 #########################################################
--- 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):
--- 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)
--- 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')
--- 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'),
])
--- 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')
--- 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