# HG changeset patch # User Julien Cristau # Date 1438068326 -7200 # Node ID 1d824df4f2bdb335ce9aed9c5c955bcbf192091e # Parent 0e7fab5043052227736ea9874ab105970fabfd73 Fix (de)serialization of ComputedRelation read permissions For normal relation types, permissions don't need to be stored since they're just default values for the relation definitions. However, computed relations are serialized (as CWComputedRType), while their relation definitions are added at schema finalization time, and are only in memory. So add the 'read_permission' relation to CWComputedRType, and the appropriate hooks to save and restore those permissions. To avoid having to touch yams, we drop the 'add' and 'delete' permissions from the default computed relation permissions; this should probably be backported there. The actual permissions (set on the relation definitions) are hardcoded in finalize_computed_relations anyway. In deserialize_schema, the CWComputedRType handling needs to be delayed a little bit, until after we've called deserialize_ertype_permissions. The rql2sql test is adjusted because CWComputedRType has a 'name' attribute and the 'read_permission' relation, which generates ambiguity vs CWEType. We add an explicit CubicWebRelationSchema.check_permission_definitions, since we need to check both that computed and non-computed rtypes are defined properly. Based on report and initial patch from Christophe de Vienne (thanks!). Closes #5706307 diff -r 0e7fab504305 -r 1d824df4f2bd misc/migration/3.21.1_Any.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/misc/migration/3.21.1_Any.py Tue Jul 28 09:25:26 2015 +0200 @@ -0,0 +1,4 @@ +# re-read ComputedRelation permissions from schema.py now that we're +# able to serialize them +for computedrtype in schema.iter_computed_relations(): + sync_schema_props_perms(computedrtype.type) diff -r 0e7fab504305 -r 1d824df4f2bd misc/migration/bootstrapmigration_repository.py --- a/misc/migration/bootstrapmigration_repository.py Fri Jul 17 16:48:43 2015 +0200 +++ b/misc/migration/bootstrapmigration_repository.py Tue Jul 28 09:25:26 2015 +0200 @@ -434,6 +434,12 @@ if applcubicwebversion < (3, 2, 0) and cubicwebversion >= (3, 2, 0): add_cube('card', update_database=False) + +if applcubicwebversion < (3, 21, 1) and cubicwebversion >= (3, 21, 1): + add_relation_definition('CWComputedRType', 'read_permission', 'CWGroup') + add_relation_definition('CWComputedRType', 'read_permission', 'RQLExpression') + + def sync_constraint_types(): """Make sure the repository knows about all constraint types defined in the code""" from cubicweb.schema import CONSTRAINTS diff -r 0e7fab504305 -r 1d824df4f2bd schema.py --- a/schema.py Fri Jul 17 16:48:43 2015 +0200 +++ b/schema.py Tue Jul 28 09:25:26 2015 +0200 @@ -32,6 +32,7 @@ from logilab.common.textutils import splitstrip from logilab.common.graph import get_cycles +import yams from yams import BadSchemaDefinition, buildobjs as ybo from yams.schema import Schema, ERSchema, EntitySchema, RelationSchema, \ RelationDefinitionSchema, PermissionMixIn, role_name @@ -462,6 +463,13 @@ ybo.DEFAULT_ATTRPERMS['update'] = ('managers', ERQLExpression('U has_update_permission X')) ybo.DEFAULT_ATTRPERMS['add'] = ('managers', ERQLExpression('U has_add_permission X')) +# we don't want 'add' or 'delete' permissions on computed relation types +# (they're hardcoded to '()' on computed relation definitions) +if 'add' in yams.DEFAULT_COMPUTED_RELPERMS: + del yams.DEFAULT_COMPUTED_RELPERMS['add'] +if 'delete' in yams.DEFAULT_COMPUTED_RELPERMS: + del yams.DEFAULT_COMPUTED_RELPERMS['delete'] + PUB_SYSTEM_ENTITY_PERMS = { 'read': ('managers', 'users', 'guests',), @@ -859,7 +867,9 @@ return ERQLExpression(expression, mainvars, eid) -class CubicWebRelationSchema(RelationSchema): +class CubicWebRelationSchema(PermissionMixIn, RelationSchema): + permissions = {} + ACTIONS = () def __init__(self, schema=None, rdef=None, eid=None, **kwargs): if rdef is not None: @@ -870,6 +880,17 @@ eid = getattr(rdef, 'eid', None) self.eid = eid + def init_computed_relation(self, rdef): + self.ACTIONS = ('read',) + super(CubicWebRelationSchema, self).init_computed_relation(rdef) + + def advertise_new_add_permission(self): + pass + + def check_permission_definitions(self): + RelationSchema.check_permission_definitions(self) + PermissionMixIn.check_permission_definitions(self) + @property def meta(self): return self.type in META_RTYPES diff -r 0e7fab504305 -r 1d824df4f2bd schemas/bootstrap.py --- a/schemas/bootstrap.py Fri Jul 17 16:48:43 2015 +0200 +++ b/schemas/bootstrap.py Tue Jul 28 09:25:26 2015 +0200 @@ -239,7 +239,7 @@ """groups allowed to read entities/relations of this type""" __permissions__ = PUB_SYSTEM_REL_PERMS name = 'read_permission' - subject = ('CWEType', 'CWAttribute', 'CWRelation') + subject = ('CWEType', 'CWAttribute', 'CWRelation', 'CWComputedRType') object = 'CWGroup' cardinality = '**' @@ -271,7 +271,7 @@ """rql expression allowing to read entities/relations of this type""" __permissions__ = PUB_SYSTEM_REL_PERMS name = 'read_permission' - subject = ('CWEType', 'CWAttribute', 'CWRelation') + subject = ('CWEType', 'CWAttribute', 'CWRelation', 'CWComputedRType') object = 'RQLExpression' cardinality = '*?' composite = 'subject' diff -r 0e7fab504305 -r 1d824df4f2bd server/migractions.py --- a/server/migractions.py Fri Jul 17 16:48:43 2015 +0200 +++ b/server/migractions.py Tue Jul 28 09:25:26 2015 +0200 @@ -1020,7 +1020,8 @@ print 'warning: relation type %s is already known, skip addition' % ( rtype) elif rschema.rule: - ss.execschemarql(execute, rschema, ss.crschema2rql(rschema)) + gmap = self.group_mapping() + ss.execschemarql(execute, rschema, ss.crschema2rql(rschema, gmap)) else: # register the relation into CWRType and insert necessary relation # definitions diff -r 0e7fab504305 -r 1d824df4f2bd server/schemaserial.py --- a/server/schemaserial.py Fri Jul 17 16:48:43 2015 +0200 +++ b/server/schemaserial.py Tue Jul 28 09:25:26 2015 +0200 @@ -92,14 +92,6 @@ with cnx.ensure_cnx_set: tables = set(t.lower() for t in dbhelper.list_tables(cnx.cnxset.cu)) has_computed_relations = 'cw_cwcomputedrtype' in tables - if has_computed_relations: - rset = cnx.execute( - 'Any X, N, R, D WHERE X is CWComputedRType, X name N, ' - 'X rule R, X description D') - for eid, rule_name, rule, description in rset.rows: - rtype = ybo.ComputedRelation(name=rule_name, rule=rule, eid=eid, - description=description) - schema.add_relation_type(rtype) # computed attribute try: cnx.system_sql("SELECT cw_formula FROM cw_CWAttribute") @@ -177,6 +169,15 @@ stype = ETYPE_NAME_MAP.get(stype, stype) schema.eschema(etype)._specialized_type = stype schema.eschema(stype)._specialized_by.append(etype) + if has_computed_relations: + rset = cnx.execute( + 'Any X, N, R, D WHERE X is CWComputedRType, X name N, ' + 'X rule R, X description D') + for eid, rule_name, rule, description in rset.rows: + rtype = ybo.ComputedRelation(name=rule_name, rule=rule, eid=eid, + description=description) + rschema = schema.add_relation_type(rtype) + set_perms(rschema, permsidx) # load every relation types for eid, rtype, desc, sym, il, ftc in cnx.execute( 'Any X,N,D,S,I,FTC WHERE X is CWRType, X name N, X description D, ' @@ -375,7 +376,7 @@ pb.update() continue if rschema.rule: - execschemarql(execute, rschema, crschema2rql(rschema)) + execschemarql(execute, rschema, crschema2rql(rschema, groupmap)) pb.update() continue execschemarql(execute, rschema, rschema2rql(rschema, addrdef=False)) @@ -525,9 +526,12 @@ relations = ['X %s %%(%s)s' % (attr, attr) for attr in sorted(values)] return relations, values -def crschema2rql(crschema): +def crschema2rql(crschema, groupmap): relations, values = crschema_relations_values(crschema) yield 'INSERT CWComputedRType X: %s' % ','.join(relations), values + if groupmap: + for rql, args in _erperms2rql(crschema, groupmap): + yield rql, args def crschema_relations_values(crschema): values = _ervalues(crschema) diff -r 0e7fab504305 -r 1d824df4f2bd server/test/data-cwep002/schema.py --- a/server/test/data-cwep002/schema.py Fri Jul 17 16:48:43 2015 +0200 +++ b/server/test/data-cwep002/schema.py Tue Jul 28 09:25:26 2015 +0200 @@ -32,4 +32,5 @@ class has_employee(ComputedRelation): rule = 'O works_for S' + __permissions__ = {'read': ('managers',)} diff -r 0e7fab504305 -r 1d824df4f2bd server/test/unittest_rql2sql.py --- a/server/test/unittest_rql2sql.py Fri Jul 17 16:48:43 2015 +0200 +++ b/server/test/unittest_rql2sql.py Tue Jul 28 09:25:26 2015 +0200 @@ -396,13 +396,13 @@ ORDER BY 1'''), # DISTINCT, can use relation under exists scope as principal - ('DISTINCT Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), EXISTS(X read_permission Y)', + ('DISTINCT Any X,Y WHERE X name "CWGroup", X is CWEType, Y eid IN(1, 2, 3), EXISTS(X read_permission Y)', '''SELECT DISTINCT _X.cw_eid, rel_read_permission0.eid_to FROM cw_CWEType AS _X, read_permission_relation AS rel_read_permission0 WHERE _X.cw_name=CWGroup AND rel_read_permission0.eid_to IN(1, 2, 3) AND EXISTS(SELECT 1 WHERE rel_read_permission0.eid_from=_X.cw_eid)'''), # no distinct, Y can't be invariant - ('Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), EXISTS(X read_permission Y)', + ('Any X,Y WHERE X name "CWGroup", X is CWEType, Y eid IN(1, 2, 3), EXISTS(X read_permission Y)', '''SELECT _X.cw_eid, _Y.cw_eid FROM cw_CWEType AS _X, cw_CWGroup AS _Y WHERE _X.cw_name=CWGroup AND _Y.cw_eid IN(1, 2, 3) AND EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=_X.cw_eid AND rel_read_permission0.eid_to=_Y.cw_eid) @@ -412,7 +412,7 @@ WHERE _X.cw_name=CWGroup AND _Y.cw_eid IN(1, 2, 3) AND EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=_X.cw_eid AND rel_read_permission0.eid_to=_Y.cw_eid)'''), # DISTINCT but NEGED exists, can't be invariant - ('DISTINCT Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), NOT EXISTS(X read_permission Y)', + ('DISTINCT Any X,Y WHERE X name "CWGroup", X is CWEType, Y eid IN(1, 2, 3), NOT EXISTS(X read_permission Y)', '''SELECT DISTINCT _X.cw_eid, _Y.cw_eid FROM cw_CWEType AS _X, cw_CWGroup AS _Y WHERE _X.cw_name=CWGroup AND _Y.cw_eid IN(1, 2, 3) AND NOT (EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=_X.cw_eid AND rel_read_permission0.eid_to=_Y.cw_eid)) @@ -422,7 +422,7 @@ WHERE _X.cw_name=CWGroup AND _Y.cw_eid IN(1, 2, 3) AND NOT (EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=_X.cw_eid AND rel_read_permission0.eid_to=_Y.cw_eid))'''), # should generate the same query as above - ('DISTINCT Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), NOT X read_permission Y', + ('DISTINCT Any X,Y WHERE X name "CWGroup", X is CWEType, Y eid IN(1, 2, 3), NOT X read_permission Y', '''SELECT DISTINCT _X.cw_eid, _Y.cw_eid FROM cw_CWEType AS _X, cw_CWGroup AS _Y WHERE _X.cw_name=CWGroup AND _Y.cw_eid IN(1, 2, 3) AND NOT (EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=_X.cw_eid AND rel_read_permission0.eid_to=_Y.cw_eid)) @@ -432,7 +432,7 @@ WHERE _X.cw_name=CWGroup AND _Y.cw_eid IN(1, 2, 3) AND NOT (EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=_X.cw_eid AND rel_read_permission0.eid_to=_Y.cw_eid))'''), # neged relation, can't be inveriant - ('Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), NOT X read_permission Y', + ('Any X,Y WHERE X name "CWGroup", X is CWEType, Y eid IN(1, 2, 3), NOT X read_permission Y', '''SELECT _X.cw_eid, _Y.cw_eid FROM cw_CWEType AS _X, cw_CWGroup AS _Y WHERE _X.cw_name=CWGroup AND _Y.cw_eid IN(1, 2, 3) AND NOT (EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=_X.cw_eid AND rel_read_permission0.eid_to=_Y.cw_eid)) diff -r 0e7fab504305 -r 1d824df4f2bd server/test/unittest_schemaserial.py --- a/server/test/unittest_schemaserial.py Fri Jul 17 16:48:43 2015 +0200 +++ b/server/test/unittest_schemaserial.py Tue Jul 28 09:25:26 2015 +0200 @@ -434,6 +434,8 @@ self.repo.set_schema(self.repo.deserialize_schema(), resetvreg=False) schema = self.repo.schema self.assertEqual([('Company', 'Person')], list(schema['has_employee'].rdefs)) + self.assertEqual(schema['has_employee'].rdef('Company', 'Person').permissions['read'], + (u'managers',)) self.assertEqual('O works_for S', schema['has_employee'].rule) self.assertEqual([('Company', 'Int')], list(schema['total_salary'].rdefs)) diff -r 0e7fab504305 -r 1d824df4f2bd test/unittest_schema.py --- a/test/unittest_schema.py Fri Jul 17 16:48:43 2015 +0200 +++ b/test/unittest_schema.py Tue Jul 28 09:25:26 2015 +0200 @@ -482,6 +482,7 @@ ('cw_schema', 'CWSourceSchemaConfig', 'CWRelation', 'object'), ('delete_permission', 'CWRelation', 'RQLExpression', 'subject'), ('read_permission', 'CWRelation', 'RQLExpression', 'subject')], + 'CWComputedRType': [('read_permission', 'CWComputedRType', 'RQLExpression', 'subject')], 'CWSource': [('cw_for_source', 'CWSourceSchemaConfig', 'CWSource', 'object'), ('cw_host_config_of', 'CWSourceHostConfig', 'CWSource', 'object'), ('cw_import_of', 'CWDataImport', 'CWSource', 'object'),