# HG changeset patch # User Adrien Di Mascio # Date 1260209689 -3600 # Node ID 394f853bb653dc216ef627f76f4e70b05596726d # Parent b2d0b14a365ddab036ea180050af897f2723f9f6 [migration] write migration instructions for permissions handling on relation definition diff -r b2d0b14a365d -r 394f853bb653 __pkginfo__.py --- a/__pkginfo__.py Mon Dec 07 17:57:19 2009 +0100 +++ b/__pkginfo__.py Mon Dec 07 19:14:49 2009 +0100 @@ -7,7 +7,7 @@ distname = "cubicweb" modname = "cubicweb" -numversion = (3, 5, 10) +numversion = (3, 6, 0) version = '.'.join(str(num) for num in numversion) license = 'LGPL' diff -r b2d0b14a365d -r 394f853bb653 hooks/syncschema.py --- a/hooks/syncschema.py Mon Dec 07 17:57:19 2009 +0100 +++ b/hooks/syncschema.py Mon Dec 07 19:14:49 2009 +0100 @@ -12,7 +12,7 @@ """ __docformat__ = "restructuredtext en" -from yams.schema import BASE_TYPES +from yams.schema import BASE_TYPES, RelationSchema from yams.buildobjs import EntityType, RelationType, RelationDefinition from yams.schema2sql import eschema2sql, rschema2sql, type_from_constraints @@ -483,7 +483,8 @@ # so there is nothing to do here if session.added_in_transaction(rdef.eid): return - subjtype, rtype, objtype = session.vreg.schema.schema_by_eid(rdef.eid) + rdefschema = session.vreg.schema.schema_by_eid(rdef.eid) + subjtype, rtype, objtype = rdefschema.as_triple() cstrtype = self.entity.type oldcstr = rtype.rdef(subjtype, objtype).constraint_by_type(cstrtype) newcstr = CONSTRAINTS[cstrtype].deserialize(self.entity.value) @@ -603,7 +604,7 @@ def commit_event(self): # structure should be clean, not need to remove entity's relations # at this point - self.rschema.rdef[self.kobj].update(self.values) + self.rschema.rdefs[self.kobj].update(self.values) class MemSchemaRDefDel(MemSchemaOperation): @@ -632,7 +633,8 @@ if self.session.added_in_transaction(rdef.eid): self.cancelled = True return - subjtype, rtype, objtype = self.session.vreg.schema.schema_by_eid(rdef.eid) + rdef = self.session.vreg.schema.schema_by_eid(rdef.eid) + subjtype, rtype, objtype = rdef.as_triple() self.prepare_constraints(subjtype, rtype, objtype) cstrtype = self.entity.type self.cstr = rtype.rdef(subjtype, objtype).constraint_by_type(cstrtype) @@ -668,13 +670,13 @@ def commit_event(self): """the observed connections pool has been commited""" try: - erschema = self.session.vreg.schema[self.name] + erschema = self.session.vreg.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) + self.error('no schema for %s', self.eid) return perms = list(erschema.action_permissions(self.action)) - if hasattr(self, group_eid): + if hasattr(self, 'group_eid'): perm = self.session.entity_from_eid(self.group_eid).name else: perm = erschema.rql_expression(self.expr) @@ -695,18 +697,20 @@ def commit_event(self): """the observed connections pool has been commited""" try: - erschema = self.session.vreg.schema[self.name] + erschema = self.session.vreg.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) + self.error('no schema for %s', self.eid) + return + if isinstance(erschema, RelationSchema): # XXX 3.6 migration return perms = list(erschema.action_permissions(self.action)) - if hasattr(self, group_eid): + if hasattr(self, 'group_eid'): perm = self.session.entity_from_eid(self.group_eid).name else: perm = erschema.rql_expression(self.expr) try: - perms.remove(self.group) + perms.remove(perm) erschema.set_action_permissions(self.action, perms) except ValueError: self.error('can\'t remove permission %s for %s on %s', @@ -916,7 +920,7 @@ # don't use getattr(entity, attr), we would get the modified value if any for attr in ro_attrs: if attr in entity.edited_attributes: - origval, newval = entity_oldnewvalue(entity, attr) + origval, newval = hook.entity_oldnewvalue(entity, attr) if newval != origval: errors[attr] = session._("can't change the %s attribute") % \ display_name(session, attr) @@ -940,8 +944,8 @@ def __call__(self): session = self._cw - subjschema, rschema, objschema = session.vreg.schema.schema_by_eid(self.eidfrom) - subjschema, rschema, objschema = session.schema.schema_by_eid(rdefeid) + rdef = session.vreg.schema.schema_by_eid(self.eidfrom) + subjschema, rschema, objschema = rdef.as_triple() pendings = session.transaction_data.get('pendingeids', ()) pendingrdefs = session.transaction_data.setdefault('pendingrdefs', set()) # first delete existing relation if necessary @@ -956,7 +960,7 @@ % (rschema, subjschema, objschema)) execute = session.unsafe_execute rset = execute('Any COUNT(X) WHERE X is %s, X relation_type R,' - 'R eid %%(x)s' % rdeftype, {'x': rteid}) + 'R eid %%(x)s' % rdeftype, {'x': self.eidto}) lastrel = rset[0][0] == 0 # we have to update physical schema systematically for final and inlined # relations, but only if it's the last instance for this relation type @@ -965,17 +969,17 @@ if (rschema.final or rschema.inlined): rset = execute('Any COUNT(X) WHERE X is %s, X relation_type R, ' 'R eid %%(x)s, X from_entity E, E name %%(name)s' - % rdeftype, {'x': rteid, 'name': str(subjschema)}) + % rdeftype, {'x': self.eidto, 'name': str(subjschema)}) if rset[0][0] == 0 and not subjschema.eid in pendings: ptypes = session.transaction_data.setdefault('pendingrtypes', set()) ptypes.add(rschema.type) DropColumn(session, table=SQL_PREFIX + subjschema.type, - column=SQL_PREFIX + rschema.type) + column=SQL_PREFIX + rschema.type) elif lastrel: DropRelationTable(session, rschema.type) # if this is the last instance, drop associated relation type if lastrel and not rteid in pendings: - execute('DELETE CWRType X WHERE X eid %(x)s', {'x': rteid}, 'x') + execute('DELETE CWRType X WHERE X eid %(x)s', {'x': self.eidto}, 'x') MemSchemaRDefDel(session, (subjschema, rschema, objschema)) @@ -1087,7 +1091,7 @@ group_eid=self.eidto) else: # RQLExpression expr = self._cw.entity_from_eid(self.eidto).expression - MemSchemaPermissionAdd(session, action=action, eid=self.eidfrom, + MemSchemaPermissionAdd(self._cw, action=action, eid=self.eidfrom, expr=expr) diff -r b2d0b14a365d -r 394f853bb653 misc/migration/bootstrapmigration_repository.py --- a/misc/migration/bootstrapmigration_repository.py Mon Dec 07 17:57:19 2009 +0100 +++ b/misc/migration/bootstrapmigration_repository.py Mon Dec 07 19:14:49 2009 +0100 @@ -10,6 +10,45 @@ applcubicwebversion, cubicwebversion = versions_map['cubicweb'] +if applcubicwebversion < (3, 6, 0) and cubicwebversion >= (3, 6, 0): + from cubicweb.server import schemaserial as ss + 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) + checkpoint(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'): + actperms = [] + for something in rpermsdict.get(action, ()): + if isinstance(something, tuple): + actperms.append(rdef.rql_expression(*something)) + else: # group name + actperms.append(something) + rdef.set_action_permissions(action, actperms) + 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') + _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), ' + '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 relation_type RT, RT %s_permission Y2, Y2 exprtype YET, ' + 'Y2 mainvars YMV, Y2 expression YEX' % (action, action)) + drop_relation_definition('CWRType', '%s_permission' % action, 'CWGroup', commit=False) + drop_relation_definition('CWRType', '%s_permission' % action, 'RQLExpression') + config.disabled_hooks_categories.add('integrity') + if applcubicwebversion < (3, 4, 0) and cubicwebversion >= (3, 4, 0): session.set_shared_data('do-not-insert-cwuri', True) diff -r b2d0b14a365d -r 394f853bb653 server/hook.py --- a/server/hook.py Mon Dec 07 17:57:19 2009 +0100 +++ b/server/hook.py Mon Dec 07 19:14:49 2009 +0100 @@ -87,6 +87,19 @@ VRegistry.REGISTRY_FACTORY['hooks'] = HooksRegistry +def entity_oldnewvalue(entity, attr): + """returns the couple (old attr value, new attr value) + NOTE: will only work in a before_update_entity hook + """ + # get new value and remove from local dict to force a db query to + # fetch old value + newvalue = entity.pop(attr, None) + oldvalue = getattr(entity, attr) + if newvalue is not None: + entity[attr] = newvalue + return oldvalue, newvalue + + # some hook specific selectors ################################################# @objectify_selector diff -r b2d0b14a365d -r 394f853bb653 server/hookhelper.py --- a/server/hookhelper.py Mon Dec 07 17:57:19 2009 +0100 +++ b/server/hookhelper.py Mon Dec 07 19:14:49 2009 +0100 @@ -10,18 +10,12 @@ from logilab.common.deprecation import deprecated, class_moved from cubicweb import RepositoryError +from cubicweb.server import hook +@deprecated('[3.6] entity_oldnewvalue should be imported from cw.server.hook') def entity_oldnewvalue(entity, attr): - """returns the couple (old attr value, new attr value) - NOTE: will only work in a before_update_entity hook - """ - # get new value and remove from local dict to force a db query to - # fetch old value - newvalue = entity.pop(attr, None) - oldvalue = getattr(entity, attr) - if newvalue is not None: - entity[attr] = newvalue - return oldvalue, newvalue + """return the "name" attribute of the entity with the given eid""" + return hook.entity_oldnewvalue(entity, attr) @deprecated('[3.6] entity_name is deprecated, use entity.name') def entity_name(session, eid): @@ -32,5 +26,4 @@ def rproperty(session, rtype, eidfrom, eidto, rprop): return session.rproperty(rtype, eidfrom, eidto, rprop) -from cubicweb.server.hook import SendMailOp -SendMailOp = class_moved(SendMailOp) +SendMailOp = class_moved(hook.SendMailOp) diff -r b2d0b14a365d -r 394f853bb653 server/migractions.py --- a/server/migractions.py Mon Dec 07 17:57:19 2009 +0100 +++ b/server/migractions.py Mon Dec 07 19:14:49 2009 +0100 @@ -521,9 +521,9 @@ # base actions ############################################################ - def checkpoint(self): + def checkpoint(self, ask_confirm=True): """checkpoint action""" - if self.confirm('commit now ?', shell=False): + if not ask_confirm or self.confirm('commit now ?', shell=False): self.commit() def cmd_add_cube(self, cube, update_database=True): @@ -686,12 +686,9 @@ eschema = self.fs_schema.eschema(etype) confirm = self.verbosity >= 2 # register the entity into CWEType - self.rqlexecall(ss.eschema2rql(eschema), ask_confirm=confirm) + self.rqlexecall(ss.eschema2rql(eschema, self.group_mapping()), ask_confirm=confirm) # add specializes relation if needed self.rqlexecall(ss.eschemaspecialize2rql(eschema), ask_confirm=confirm) - # register groups / permissions for the entity - self.rqlexecall(ss.erperms2rql(eschema, self.group_mapping()), - ask_confirm=confirm) # register entity's attributes for rschema, attrschema in eschema.attribute_definitions(): # ignore those meta relations, they will be automatically added @@ -828,12 +825,9 @@ # definitions self.rqlexecall(ss.rschema2rql(rschema, addrdef=False), ask_confirm=self.verbosity>=2) - # register groups / permissions for the relation - self.rqlexecall(ss.erperms2rql(rschema, self.group_mapping()), - ask_confirm=self.verbosity>=2) if addrdef: self.commit() - self.rqlexecall(ss.rdef2rql(rschema), + self.rqlexecall(ss.rdef2rql(rschema, groupmap=self.group_mapping()), ask_confirm=self.verbosity>=2) if rtype in META_RTYPES: # if the relation is in META_RTYPES, ensure we're adding it for @@ -880,7 +874,7 @@ rschema = self.fs_schema.rschema(rtype) if not rtype in self.repo.schema: self.cmd_add_relation_type(rtype, addrdef=False, commit=True) - self.rqlexecall(ss.rdef2rql(rschema, subjtype, objtype), + self.rqlexecall(ss.rdef2rql(rschema, subjtype, objtype, groupmap=self.group_mapping()), ask_confirm=self.verbosity>=2) if commit: self.commit() diff -r b2d0b14a365d -r 394f853bb653 server/schemaserial.py --- a/server/schemaserial.py Mon Dec 07 17:57:19 2009 +0100 +++ b/server/schemaserial.py Mon Dec 07 19:14:49 2009 +0100 @@ -308,14 +308,10 @@ if pb is not None: pb.update() continue - for rql, kwargs in erschema2rql(schema[ertype]): + for rql, kwargs in erschema2rql(schema[ertype], groupmap): if verbose: print rql % kwargs cursor.execute(rql, kwargs) - for rql, kwargs in erperms2rql(schema[ertype], groupmap): - if verbose: - print rql - cursor.execute(rql, kwargs) if pb is not None: pb.update() for rql, kwargs in specialize2rql(schema): @@ -399,7 +395,7 @@ return relations, values -def __rdef2rql(genmap, rschema, subjtype=None, objtype=None, props=None): +def __rdef2rql(genmap, rschema, subjtype=None, objtype=None, props=None, groupmap=None): if subjtype is None: assert objtype is None assert props is None @@ -407,9 +403,14 @@ else: assert not objtype is None targets = [(subjtype, objtype)] + # relation schema + if rschema.final: + etype = 'CWAttribute' + else: + etype = 'CWRelation' for subjtype, objtype in targets: if props is None: - _props = rschema.rproperties(subjtype, objtype) + _props = rschema.rdef(subjtype, objtype) else: _props = props # don't serialize infered relations @@ -418,6 +419,15 @@ gen = genmap[rschema.final] for rql, values in gen(rschema, subjtype, objtype, _props): yield rql, values + # no groupmap means "no security insertion" + if groupmap: + for rql, args in _erperms2rql(_props, groupmap): + args['st'] = str(subjtype) + args['rt'] = str(rschema) + args['ot'] = str(objtype) + 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 def schema2rql(schema, skip=None, allow=None): @@ -433,12 +443,12 @@ return chain(*[erschema2rql(schema[t]) for t in all if t in allow]) return chain(*[erschema2rql(schema[t]) for t in all]) -def erschema2rql(erschema): +def erschema2rql(erschema, groupmap): if isinstance(erschema, schemamod.EntitySchema): - return eschema2rql(erschema) + return eschema2rql(erschema, groupmap) return rschema2rql(erschema) -def eschema2rql(eschema): +def eschema2rql(eschema, groupmap): """return a list of rql insert statements to enter an entity schema in the database as an CWEType entity """ @@ -446,6 +456,10 @@ # NOTE: 'specializes' relation can't be inserted here since there's no # way to make sure the parent type is inserted before the child type yield 'INSERT CWEType X: %s' % ','.join(relations) , values + # entity schema + for rql, args in _erperms2rql(eschema, groupmap): + args['name'] = str(eschema) + yield rql + 'X is CWEType, X name %(name)s', args def specialize2rql(schema): for eschema in schema.entities(): @@ -458,7 +472,7 @@ values = {'x': eschema.type, 'et': specialized_type.type} yield 'SET X specializes ET WHERE X name %(x)s, ET name %(et)s', values -def rschema2rql(rschema, addrdef=True): +def rschema2rql(rschema, addrdef=True, groupmap=None): """return a list of rql insert statements to enter a relation schema in the database as an CWRType entity """ @@ -467,12 +481,12 @@ relations, values = rschema_relations_values(rschema) yield 'INSERT CWRType X: %s' % ','.join(relations), values if addrdef: - for rql, values in rdef2rql(rschema): + for rql, values in rdef2rql(rschema, groupmap=groupmap): yield rql, values -def rdef2rql(rschema, subjtype=None, objtype=None, props=None): +def rdef2rql(rschema, subjtype=None, objtype=None, props=None, groupmap=None): genmap = {True: frdef2rql, False: nfrdef2rql} - return __rdef2rql(genmap, rschema, subjtype, objtype, props) + return __rdef2rql(genmap, rschema, subjtype, objtype, props, groupmap) _LOCATE_RDEF_RQL0 = 'X relation_type ER,X from_entity SE,X to_entity OE' @@ -508,43 +522,8 @@ CT name %(ctname)s, EDEF relation_type ER, EDEF from_entity SE, EDEF to_entity OE, \ ER name %(rt)s, SE name %(se)s, OE name %(oe)s', values -def perms2rql(schema, groupmapping): - """return rql insert statements to enter the schema's permissions in - the database as [read|add|delete|update]_permission relations between - CWEType/CWRType and CWGroup entities - groupmapping is a dictionnary mapping standard group names to - eids - """ - for etype in sorted(schema.entities()): - yield erperms2rql(schema[etype], groupmapping) - for rtype in sorted(schema.relations()): - 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(erschema) - yield rql + 'X is CWEType, X name %(name)s', args - -def _erperms2rql(erschema, groupmapping): +def _erperms2rql(erschema, groupmap): """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 @@ -556,7 +535,7 @@ # group try: yield ('SET X %s_permission Y WHERE Y eid %%(g)s, ' % action, - {'g': groupmapping[group_or_rqlexpr]}) + {'g': groupmap[group_or_rqlexpr]}) except KeyError: continue else: