Commit 75ee462c authored by Sylvain Thénault's avatar Sylvain Thénault
Browse files

[repo] enhanced security handling: deprecates unsafe_execute, in favor of...

[repo] enhanced security handling: deprecates unsafe_execute, in favor of explicit read/write security control using the `enabled_security` context manager. Also code executed on the repository side is now unsafe by default.
parent b718626a0e60
......@@ -116,17 +116,6 @@ class FakeRequest(CubicWebRequestBase):
def validate_cache(self):
pass
# session compatibility (in some test are using this class to test server
# side views...)
def actual_session(self):
"""return the original parent session if any, else self"""
return self
def unsafe_execute(self, *args, **kwargs):
"""return the original parent session if any, else self"""
kwargs.pop('propagate', None)
return self.execute(*args, **kwargs)
class FakeUser(object):
login = 'toto'
......@@ -136,18 +125,19 @@ class FakeUser(object):
class FakeSession(RequestSessionBase):
read_security = write_security = True
set_read_security = set_write_security = lambda *args, **kwargs: None
def __init__(self, repo=None, user=None):
self.repo = repo
self.vreg = getattr(self.repo, 'vreg', CubicWebVRegistry(FakeConfig(), initlog=False))
self.pool = FakePool()
self.user = user or FakeUser()
self.is_internal_session = False
self.is_super_session = self.user.eid == -1
self.transaction_data = {}
def execute(self, *args):
def execute(self, *args, **kwargs):
pass
unsafe_execute = execute
def commit(self, *args):
self.transaction_data.clear()
......
......@@ -178,9 +178,10 @@ class BaseQuerierTC(TestCase):
self._dumb_sessions = []
def get_max_eid(self):
return self.session.unsafe_execute('Any MAX(X)')[0][0]
return self.session.execute('Any MAX(X)')[0][0]
def cleanup(self):
self.session.unsafe_execute('DELETE Any X WHERE X eid > %s' % self.maxeid)
self.session.set_pool()
self.session.execute('DELETE Any X WHERE X eid > %s' % self.maxeid)
def tearDown(self):
undo_monkey_patch()
......
......@@ -228,7 +228,9 @@ class CubicWebTC(TestCase):
@property
def session(self):
"""return current server side session (using default manager account)"""
return self.repo._sessions[self.cnx.sessionid]
session = self.repo._sessions[self.cnx.sessionid]
session.set_pool()
return session
@property
def adminsession(self):
......
......@@ -93,15 +93,10 @@ class CWUser(AnyEntity):
return self.groups == frozenset(('guests', ))
def owns(self, eid):
if hasattr(self._cw, 'unsafe_execute'):
# use unsafe_execute on the repository side, in case
# session's user doesn't have access to CWUser
execute = self._cw.unsafe_execute
else:
execute = self._cw.execute
try:
return execute('Any X WHERE X eid %(x)s, X owned_by U, U eid %(u)s',
{'x': eid, 'u': self.eid}, 'x')
return self._cw.execute(
'Any X WHERE X eid %(x)s, X owned_by U, U eid %(u)s',
{'x': eid, 'u': self.eid}, 'x')
except Unauthorized:
return False
owns = cached(owns, keyarg=1)
......
from __future__ import with_statement
from cubicweb.devtools.testlib import CubicWebTC
from cubicweb import ValidationError
from cubicweb.server.session import security_enabled
def add_wf(self, etype, name=None, default=False):
if name is None:
......@@ -126,10 +128,11 @@ class WorkflowTC(CubicWebTC):
wf = add_wf(self, 'CWUser')
s = wf.add_state(u'foo', initial=True)
self.commit()
ex = self.assertRaises(ValidationError, self.session.unsafe_execute,
with security_enabled(self.session, write=False):
ex = self.assertRaises(ValidationError, self.session.execute,
'SET X in_state S WHERE X eid %(x)s, S eid %(s)s',
{'x': self.user().eid, 's': s.eid}, 'x')
self.assertEquals(ex.errors, {'in_state': "state doesn't belong to entity's workflow. "
self.assertEquals(ex.errors, {'in_state': "state doesn't belong to entity's workflow. "
"You may want to set a custom workflow for this entity first."})
def test_fire_transition(self):
......@@ -505,7 +508,7 @@ class WorkflowHooksTC(CubicWebTC):
{'wf': self.wf.eid})
self.commit()
# XXX currently, we've to rely on hooks to set initial state, or to use unsafe_execute
# XXX currently, we've to rely on hooks to set initial state, or to use execute
# def test_initial_state(self):
# cnx = self.login('stduser')
# cu = cnx.cursor()
......
......@@ -158,7 +158,7 @@ class Workflow(AnyEntity):
todelstate = self.state_by_name(todelstate)
if not hasattr(replacement, 'eid'):
replacement = self.state_by_name(replacement)
execute = self._cw.unsafe_execute
execute = self._cw.execute
execute('SET X in_state S WHERE S eid %(s)s', {'s': todelstate.eid}, 's')
execute('SET X from_state NS WHERE X to_state OS, OS eid %(os)s, NS eid %(ns)s',
{'os': todelstate.eid, 'ns': replacement.eid}, 's')
......
......@@ -20,6 +20,7 @@ from cubicweb import Unauthorized, typed_eid
from cubicweb.rset import ResultSet
from cubicweb.selectors import yes
from cubicweb.appobject import AppObject
from cubicweb.req import _check_cw_unsafe
from cubicweb.schema import RQLVocabularyConstraint, RQLConstraint
from cubicweb.rqlrewrite import RQLRewriter
......@@ -531,8 +532,8 @@ class Entity(AppObject, dict):
# if some outer join are included to fetch inlined relations
rql = 'Any %s,%s %s' % (V, ','.join(var for attr, var in selected),
','.join(rql))
execute = getattr(self._cw, 'unsafe_execute', self._cw.execute)
rset = execute(rql, {'x': self.eid}, 'x', build_descr=False)[0]
rset = self._cw.execute(rql, {'x': self.eid}, 'x',
build_descr=False)[0]
# handle attributes
for i in xrange(1, lastattr):
self[str(selected[i-1][0])] = rset[i]
......@@ -560,11 +561,8 @@ class Entity(AppObject, dict):
if not self.is_saved():
return None
rql = "Any A WHERE X eid %%(x)s, X %s A" % name
# XXX should we really use unsafe_execute here? I think so (syt),
# see #344874
execute = getattr(self._cw, 'unsafe_execute', self._cw.execute)
try:
rset = execute(rql, {'x': self.eid}, 'x')
rset = self._cw.execute(rql, {'x': self.eid}, 'x')
except Unauthorized:
self[name] = value = None
else:
......@@ -595,10 +593,7 @@ class Entity(AppObject, dict):
pass
assert self.has_eid()
rql = self.related_rql(rtype, role)
# XXX should we really use unsafe_execute here? I think so (syt),
# see #344874
execute = getattr(self._cw, 'unsafe_execute', self._cw.execute)
rset = execute(rql, {'x': self.eid}, 'x')
rset = self._cw.execute(rql, {'x': self.eid}, 'x')
self.set_related_cache(rtype, role, rset)
return self.related(rtype, role, limit, entities)
......@@ -800,8 +795,9 @@ class Entity(AppObject, dict):
# raw edition utilities ###################################################
def set_attributes(self, _cw_unsafe=False, **kwargs):
def set_attributes(self, **kwargs):
assert kwargs
_check_cw_unsafe(kwargs)
relations = []
for key in kwargs:
relations.append('X %s %%(%s)s' % (key, key))
......@@ -809,25 +805,18 @@ class Entity(AppObject, dict):
self.update(kwargs)
# and now update the database
kwargs['x'] = self.eid
if _cw_unsafe:
self._cw.unsafe_execute(
'SET %s WHERE X eid %%(x)s' % ','.join(relations), kwargs, 'x')
else:
self._cw.execute('SET %s WHERE X eid %%(x)s' % ','.join(relations),
kwargs, 'x')
self._cw.execute('SET %s WHERE X eid %%(x)s' % ','.join(relations),
kwargs, 'x')
def set_relations(self, _cw_unsafe=False, **kwargs):
def set_relations(self, **kwargs):
"""add relations to the given object. To set a relation where this entity
is the object of the relation, use 'reverse_'<relation> as argument name.
Values may be an entity, a list of entity, or None (meaning that all
relations of the given type from or to this object should be deleted).
"""
if _cw_unsafe:
execute = self._cw.unsafe_execute
else:
execute = self._cw.execute
# XXX update cache
_check_cw_unsafe(kwargs)
for attr, values in kwargs.iteritems():
if attr.startswith('reverse_'):
restr = 'Y %s X' % attr[len('reverse_'):]
......@@ -839,14 +828,14 @@ class Entity(AppObject, dict):
continue
if not isinstance(values, (tuple, list, set, frozenset)):
values = (values,)
execute('SET %s WHERE X eid %%(x)s, Y eid IN (%s)' % (
self._cw.execute('SET %s WHERE X eid %%(x)s, Y eid IN (%s)' % (
restr, ','.join(str(r.eid) for r in values)),
{'x': self.eid}, 'x')
{'x': self.eid}, 'x')
def delete(self):
def delete(self, **kwargs):
assert self.has_eid(), self.eid
self._cw.execute('DELETE %s X WHERE X eid %%(x)s' % self.e_schema,
{'x': self.eid})
{'x': self.eid}, **kwargs)
# server side utilities ###################################################
......
......@@ -172,7 +172,7 @@ class ContentClear(StartupView):
skip_etypes = ('CWGroup', 'CWUser')
def call(self):
# XXX should use unsafe_execute with all hooks deactivated
# XXX should use unsafe execute with all hooks deactivated
# XXX step by catching datastore errors?
for eschema in self.schema.entities():
if eschema.final or eschema in self.skip_etypes:
......
......@@ -84,7 +84,7 @@ def fix_entities(schema):
Put(gaeentity)
def init_persistent_schema(ssession, schema):
execute = ssession.unsafe_execute
execute = ssession.execute
rql = ('INSERT CWEType X: X name %(name)s, X description %(descr)s,'
'X final FALSE')
eschema = schema.eschema('CWEType')
......@@ -96,7 +96,7 @@ def init_persistent_schema(ssession, schema):
'descr': unicode(eschema.description)})
def insert_versions(ssession, config):
execute = ssession.unsafe_execute
execute = ssession.execute
# insert versions
execute('INSERT CWProperty X: X pkey %(pk)s, X value%(v)s',
{'pk': u'system.version.cubicweb',
......
......@@ -26,7 +26,7 @@ class SetUseEmailRelationOp(hook.Operation):
def precommit_event(self):
if self.condition():
self.session.unsafe_execute(
self.session.execute(
'SET X %s Y WHERE X eid %%(x)s, Y eid %%(y)s' % self.rtype,
{'x': self.entity.eid, 'y': self.email.eid}, 'x')
......
......@@ -35,13 +35,12 @@ def _acquire_unique_cstr_lock(session):
RQLUniqueConstraint in two different transactions, as explained in
http://intranet.logilab.fr/jpl/ticket/36564
"""
asession = session.actual_session()
if 'uniquecstrholder' in asession.transaction_data:
if 'uniquecstrholder' in session.transaction_data:
return
_UNIQUE_CONSTRAINTS_LOCK.acquire()
asession.transaction_data['uniquecstrholder'] = True
session.transaction_data['uniquecstrholder'] = True
# register operation responsible to release the lock on commit/rollback
_ReleaseUniqueConstraintsOperation(asession)
_ReleaseUniqueConstraintsOperation(session)
def _release_unique_cstr_lock(session):
if 'uniquecstrholder' in session.transaction_data:
......@@ -69,7 +68,7 @@ class _CheckRequiredRelationOperation(hook.LateOperation):
return
if self.rtype in self.session.transaction_data.get('pendingrtypes', ()):
return
if self.session.unsafe_execute(*self._rql()).rowcount < 1:
if self.session.execute(*self._rql()).rowcount < 1:
etype = self.session.describe(self.eid)[0]
_ = self.session._
msg = _('at least one relation %(rtype)s is required on %(etype)s (%(eid)s)')
......@@ -99,12 +98,8 @@ class IntegrityHook(hook.Hook):
__abstract__ = True
category = 'integrity'
class UserIntegrityHook(IntegrityHook):
__abstract__ = True
__select__ = IntegrityHook.__select__ & hook.regular_session()
class CheckCardinalityHook(UserIntegrityHook):
class CheckCardinalityHook(IntegrityHook):
"""check cardinalities are satisfied"""
__regid__ = 'checkcard'
events = ('after_add_entity', 'before_delete_relation')
......@@ -176,7 +171,7 @@ class _CheckConstraintsOp(hook.LateOperation):
pass
class CheckConstraintHook(UserIntegrityHook):
class CheckConstraintHook(IntegrityHook):
"""check the relation satisfy its constraints
this is delayed to a precommit time operation since other relation which
......@@ -194,7 +189,7 @@ class CheckConstraintHook(UserIntegrityHook):
rdef=(self.eidfrom, self.rtype, self.eidto))
class CheckAttributeConstraintHook(UserIntegrityHook):
class CheckAttributeConstraintHook(IntegrityHook):
"""check the attribute relation satisfy its constraints
this is delayed to a precommit time operation since other relation which
......@@ -214,7 +209,7 @@ class CheckAttributeConstraintHook(UserIntegrityHook):
rdef=(self.entity.eid, attr, None))
class CheckUniqueHook(UserIntegrityHook):
class CheckUniqueHook(IntegrityHook):
__regid__ = 'checkunique'
events = ('before_add_entity', 'before_update_entity')
......@@ -227,7 +222,7 @@ class CheckUniqueHook(UserIntegrityHook):
if val is None:
continue
rql = '%s X WHERE X %s %%(val)s' % (entity.e_schema, attr)
rset = self._cw.unsafe_execute(rql, {'val': val})
rset = self._cw.execute(rql, {'val': val})
if rset and rset[0][0] != entity.eid:
msg = self._cw._('the value "%s" is already used, use another one')
raise ValidationError(entity.eid, {attr: msg % val})
......@@ -244,9 +239,9 @@ class _DelayedDeleteOp(hook.Operation):
if not (session.deleted_in_transaction(self.eid) or
session.added_in_transaction(self.eid)):
etype = session.describe(self.eid)[0]
session.unsafe_execute('DELETE %s X WHERE X eid %%(x)s, NOT %s'
% (etype, self.relation),
{'x': self.eid}, 'x')
session.execute('DELETE %s X WHERE X eid %%(x)s, NOT %s'
% (etype, self.relation),
{'x': self.eid}, 'x')
class DeleteCompositeOrphanHook(IntegrityHook):
......@@ -290,7 +285,7 @@ class DontRemoveOwnersGroupHook(IntegrityHook):
self.entity['name'] = newname
class TidyHtmlFields(UserIntegrityHook):
class TidyHtmlFields(IntegrityHook):
"""tidy HTML in rich text strings"""
__regid__ = 'htmltidy'
events = ('before_add_entity', 'before_update_entity')
......
......@@ -19,7 +19,7 @@ def eschema_eid(session, eschema):
# eschema.eid is None if schema has been readen from the filesystem, not
# from the database (eg during tests)
if eschema.eid is None:
eschema.eid = session.unsafe_execute(
eschema.eid = session.execute(
'Any X WHERE X is CWEType, X name %(name)s',
{'name': str(eschema)})[0][0]
return eschema.eid
......@@ -103,18 +103,17 @@ class SetOwnershipHook(MetaDataHook):
events = ('after_add_entity',)
def __call__(self):
asession = self._cw.actual_session()
if not asession.is_internal_session:
self._cw.add_relation(self.entity.eid, 'owned_by', asession.user.eid)
_SetCreatorOp(asession, entity=self.entity)
if not self._cw.is_internal_session:
self._cw.add_relation(self.entity.eid, 'owned_by', self._cw.user.eid)
_SetCreatorOp(self._cw, entity=self.entity)
class _SyncOwnersOp(hook.Operation):
def precommit_event(self):
self.session.unsafe_execute('SET X owned_by U WHERE C owned_by U, C eid %(c)s,'
'NOT EXISTS(X owned_by U, X eid %(x)s)',
{'c': self.compositeeid, 'x': self.composedeid},
('c', 'x'))
self.session.execute('SET X owned_by U WHERE C owned_by U, C eid %(c)s,'
'NOT EXISTS(X owned_by U, X eid %(x)s)',
{'c': self.compositeeid, 'x': self.composedeid},
('c', 'x'))
class SyncCompositeOwner(MetaDataHook):
......
......@@ -103,7 +103,7 @@ class EntityUpdatedNotificationOp(hook.SingleLastOperation):
class EntityUpdateHook(NotificationHook):
__regid__ = 'notifentityupdated'
__abstract__ = True # do not register by default
__select__ = NotificationHook.__select__ & hook.from_dbapi_query()
events = ('before_update_entity',)
skip_attrs = set()
......@@ -111,8 +111,6 @@ class EntityUpdateHook(NotificationHook):
session = self._cw
if self.entity.eid in session.transaction_data.get('neweids', ()):
return # entity is being created
if session.is_super_session:
return # ignore changes triggered by hooks
# then compute changes
changes = session.transaction_data.setdefault('changes', {})
thisentitychanges = changes.setdefault(self.entity.eid, set())
......@@ -125,7 +123,7 @@ class EntityUpdateHook(NotificationHook):
rqlsel.append(var)
rqlrestr.append('X %s %s' % (attr, var))
rql = 'Any %s WHERE %s' % (','.join(rqlsel), ','.join(rqlrestr))
rset = session.unsafe_execute(rql, {'x': self.entity.eid}, 'x')
rset = session.execute(rql, {'x': self.entity.eid}, 'x')
for i, attr in enumerate(attrs):
oldvalue = rset[0][i]
newvalue = self.entity[attr]
......@@ -139,13 +137,11 @@ class EntityUpdateHook(NotificationHook):
class SomethingChangedHook(NotificationHook):
__regid__ = 'supervising'
__select__ = NotificationHook.__select__ & hook.from_dbapi_query()
events = ('before_add_relation', 'before_delete_relation',
'after_add_entity', 'before_update_entity')
def __call__(self):
# XXX use proper selectors
if self._cw.is_super_session or self._cw.repo.config.repairing:
return # ignore changes triggered by hooks or maintainance shell
dest = self._cw.vreg.config['supervising-addrs']
if not dest: # no supervisors, don't do this for nothing...
return
......
......@@ -9,6 +9,7 @@ the user connected to a session
__docformat__ = "restructuredtext en"
from cubicweb import Unauthorized
from cubicweb.selectors import objectify_selector, lltrace
from cubicweb.server import BEFORE_ADD_RELATIONS, ON_COMMIT_ADD_RELATIONS, hook
......@@ -53,10 +54,17 @@ class _CheckRelationPermissionOp(hook.LateOperation):
pass
@objectify_selector
@lltrace
def write_security_enabled(cls, req, **kwargs):
if req is None or not req.write_security:
return 0
return 1
class SecurityHook(hook.Hook):
__abstract__ = True
category = 'security'
__select__ = hook.Hook.__select__ & hook.regular_session()
__select__ = hook.Hook.__select__ & write_security_enabled()
class AfterAddEntitySecurityHook(SecurityHook):
......
......@@ -801,7 +801,7 @@ class DelCWETypeHook(SyncSchemaHook):
if name in CORE_ETYPES:
raise ValidationError(self.entity.eid, {None: self._cw._('can\'t be deleted')})
# delete every entities of this type
self._cw.unsafe_execute('DELETE %s X' % name)
self._cw.execute('DELETE %s X' % name)
DropTable(self._cw, table=SQL_PREFIX + name)
MemSchemaCWETypeDel(self._cw, name)
......@@ -986,7 +986,7 @@ class AfterDelRelationTypeHook(SyncSchemaHook):
if not (subjschema.eid in pendings or objschema.eid in pendings):
session.execute('DELETE X %s Y WHERE X is %s, Y is %s'
% (rschema, subjschema, objschema))
execute = session.unsafe_execute
execute = session.execute
rset = execute('Any COUNT(X) WHERE X is %s, X relation_type R,'
'R eid %%(x)s' % rdeftype, {'x': self.eidto})
lastrel = rset[0][0] == 0
......
......@@ -19,8 +19,8 @@ def _change_state(session, x, oldstate, newstate):
nocheck = session.transaction_data.setdefault('skip-security', set())
nocheck.add((x, 'in_state', oldstate))
nocheck.add((x, 'in_state', newstate))
# delete previous state first in case we're using a super session,
# unless in_state isn't stored in the system source
# delete previous state first unless in_state isn't stored in the system
# source
fromsource = session.describe(x)[1]
if fromsource == 'system' or \
not session.repo.sources_by_uri[fromsource].support_relation('in_state'):
......@@ -42,9 +42,7 @@ class _SetInitialStateOp(hook.Operation):
and entity.current_workflow:
state = entity.current_workflow.initial
if state:
# use super session to by-pass security checks
session.super_session.add_relation(entity.eid, 'in_state',
state.eid)
session.add_relation(entity.eid, 'in_state', state.eid)
class _FireAutotransitionOp(hook.Operation):
......@@ -122,14 +120,7 @@ class _SubWorkflowExitOp(hook.Operation):
msg = session._('exiting from subworkflow %s')
msg %= session._(forentity.current_workflow.name)
session.transaction_data[(forentity.eid, 'subwfentrytr')] = True
# XXX iirk
req = forentity._cw
forentity._cw = session.super_session
try:
trinfo = forentity.change_state(tostate, msg, u'text/plain',
tr=wftr)
finally:
forentity._cw = req
forentity.change_state(tostate, msg, u'text/plain', tr=wftr)
# hooks ########################################################################
......@@ -195,7 +186,8 @@ class FireTransitionHook(WorkflowHook):
raise ValidationError(entity.eid, {None: msg})
# True if we are coming back from subworkflow
swtr = session.transaction_data.pop((forentity.eid, 'subwfentrytr'), None)
cowpowers = session.is_super_session or 'managers' in session.user.groups
cowpowers = ('managers' in session.user.groups
or not session.write_security)
# no investigate the requested state change...
try:
treid = entity['by_transition']
......@@ -266,7 +258,7 @@ class FiredTransitionHook(WorkflowHook):
class CheckInStateChangeAllowed(WorkflowHook):
"""check state apply, in case of direct in_state change using unsafe_execute
"""check state apply, in case of direct in_state change using unsafe execute
"""
__regid__ = 'wfcheckinstate'
__select__ = WorkflowHook.__select__ & hook.match_rtype('in_state')
......@@ -307,8 +299,7 @@ class SetModificationDateOnStateChange(WorkflowHook):
return
entity = self._cw.entity_from_eid(self.eidfrom)
try:
entity.set_attributes(modification_date=datetime.now(),
_cw_unsafe=True)
entity.set_attributes(modification_date=datetime.now())
except RepositoryError, ex:
# usually occurs if entity is coming from a read-only source
# (eg ldap user)
......
......@@ -215,16 +215,9 @@ class NotificationView(EntityView):
"""return a list of either 2-uple (email, language) or user entity to
who this email should be sent
"""
# use super_session when available, we don't want to consider security
# when selecting recipients_finder
try:
req = self._cw.super_session
except AttributeError:
req = self._cw
finder = self._cw.vreg['components'].select('recipients_finder', req,
rset=self.cw_rset,
row=self.cw_row or 0,
col=self.cw_col or 0)
finder = self._cw.vreg['components'].select(
'recipients_finder', self._cw, rset=self.cw_rset,
row=self.cw_row or 0, col=self.cw_col or 0)
return finder.recipients()
def send_now(self, recipients, msg):
......
......@@ -20,8 +20,7 @@ def _add_relation_definition_no_perms(subjtype, rtype, objtype):
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')
rql('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')
......@@ -29,7 +28,6 @@ if applcubicwebversion == (3, 6, 0) and cubicwebversion >= (3, 6, 0):
elif applcubicwebversion < (3, 6, 0) and cubicwebversion >= (3, 6, 0):
session.set_pool()
session.execute = session.unsafe_execute
permsdict = ss.deserialize_ertype_permissions(session)
changes = session.disable_hooks_category.add('integrity')
......@@ -81,13 +79,11 @@ if applcubicwebversion < (3, 4, 0) and cubicwebversion >= (3, 4, 0):
deactivate_verification_hooks()
add_relation_type('cwuri')
base_url = session.base_url()
# use an internal session since some entity might forbid modifications to admin
isession = repo.internal_session()
for eid, in rql('Any X', ask_confirm=False):
type, source, extid = session.describe(eid)
if source == 'system':
isession.execute('SET X cwuri %(u)s WHERE X eid %(x)s',
{'x': eid, 'u': base_url + u'eid/%s' % eid})
rql('SET X cwuri %(u)s WHERE X eid %(x)s',
{'x': eid, 'u': base_url + u'eid/%s' % eid})
isession.commit()
reactivate_verification_hooks()
session.set_shared_data('do-not-insert-cwuri', False)
......
......@@ -42,8 +42,8 @@ if hasattr(config, 'anonymous_user'):
# need this since we already have at least one user in the database (the default admin)
for user in rql('Any X WHERE X is CWUser').entities():