[migration] write migration instructions for permissions handling on relation definition
--- 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'
--- 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)
--- 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)
--- 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
--- 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)
--- 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()
--- 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: