# HG changeset patch # User Sylvain Thénault # Date 1269449885 -3600 # Node ID cb27485ef5ae61cf85615714ade2c95b0db71af1 # Parent 6cb91be7707f3b4be5bd16110a15beb3b7a0bc16# Parent 0ce27e435b3af6fa846ca5f5d3873450030190d8 backport stable diff -r 6cb91be7707f -r cb27485ef5ae cwvreg.py --- a/cwvreg.py Wed Mar 24 08:42:49 2010 +0100 +++ b/cwvreg.py Wed Mar 24 17:58:05 2010 +0100 @@ -22,12 +22,6 @@ from cubicweb.rtags import RTAGS -@onevent('before-registry-reload') -def clear_rtag_objects(): - for rtag in RTAGS: - rtag.clear() - - def use_interfaces(obj): """return interfaces used by the given object by searching for implements selectors, with a bw compat fallback to accepts_interfaces attribute @@ -265,6 +259,13 @@ self.schema = None self.initialized = False self.reset() + if self.config.mode != 'test': + # don't clear rtags during test, this may cause breakage with + # manually imported appobject modules + @onevent('before-registry-reload') + def clear_rtag_objects(): + for rtag in RTAGS: + rtag.clear() def setdefault(self, regid): try: diff -r 6cb91be7707f -r cb27485ef5ae doc/book/en/admin/additional-tips.rst --- a/doc/book/en/admin/additional-tips.rst Wed Mar 24 08:42:49 2010 +0100 +++ b/doc/book/en/admin/additional-tips.rst Wed Mar 24 17:58:05 2010 +0100 @@ -9,12 +9,10 @@ Backup, backup, backup `````````````````````` -It is always a good idea to backup. If your system does not do that, -you should set it up. Note that whenever you do an upgrade, -`cubicweb-ctl` offers you to backup your database. - -There are a number of ways for doing backups. Before you go ahead, -make sure the following permissions are correct :: +It is always a good idea to backup. If your system does not do that, you should +set it up. Note that whenever you do an upgrade, `cubicweb-ctl` offers you to +backup your database. There are a number of ways for doing backups. Before you +go ahead, make sure the following permissions are correct :: # chgrp postgres /var/lib/cubicweb/backup @@ -24,31 +22,36 @@ # chmod g+r /etc/cubicweb.d/**/sources -**Classic way** +**Classic way on PostgreSQL server** -Simply use the pg_dump in a cron :: +Simply use the pg_dump in a cron installed for `postgres` user on the database server:: - su -c "pg_dump -Fc --username=cubicweb --no-owner" postgres > -$(date '+%Y-%m-%d_%H:%M:%S').dump + # m h dom mon dow command + 0 2 * * * pg_dump -Fc --username=cubicweb --no-owner > /var/backups/-$(date '+%Y-%m-%d_%H:%M:%S').dump **CubicWeb way** -The CubicWeb way is to use the `db-dump` command. For that, you have to put your passwords in a user-only-readable file at the -root of the postgres user. The file is `.pgpass` (`chmod 0600`), in this case for a socket run connection to postgres :: +The CubicWeb way is to use the `db-dump` command. For that, you have to put +your passwords in a user-only-readable file at the home directory of root user. +The file is `.pgpass` (`chmod 0600`), in this case for a socket run connection +to PostgreSQL :: - /var/run/postgresql:5432::cubicweb: + /var/run/postgresql:5432::: The postgres documentation for the `.pgpass` format can be found `here`_ -Then add the following command to the crontab of the postgres user (`su posgres 'crontab -e'`):: +Then add the following command to the crontab of the user (`crontab -e`):: # m h dom mon dow command 0 2 * * * cubicweb-ctl db-dump **The automated sysadmin way** -You can use a combination `backup-ninja`_ (which has a postgres script in the example directory), `backuppc`)_ (for versionning). +You can use a combination `backup-ninja`_ (which has a postgres script in the +example directory), `backuppc`)_ (for versionning). -Please note that in the *CubicWeb way* it adds a second location for your password which is error-prone. +Please note that in the *CubicWeb way* it adds a second location for your +password which is error-prone. .. _`here` : http://www.postgresql.org/docs/current/static/libpq-pgpass.html .. _`backup-ninja` : https://labs.riseup.net/code/projects/show/backupninja/ diff -r 6cb91be7707f -r cb27485ef5ae entity.py --- a/entity.py Wed Mar 24 08:42:49 2010 +0100 +++ b/entity.py Wed Mar 24 17:58:05 2010 +0100 @@ -235,11 +235,13 @@ def __setitem__(self, attr, value): """override __setitem__ to update self.edited_attributes. - Typically, a before_update_hook could do:: + Typically, a before_[update|add]_hook could do:: entity['generated_attr'] = generated_value - and this way, edited_attributes will be updated accordingly + and this way, edited_attributes will be updated accordingly. Also, add + the attribute to skip_security since we don't want to check security + for such attributes set by hooks. """ if attr == 'eid': warn('[3.7] entity["eid"] = value is deprecated, use entity.eid = value instead', @@ -247,14 +249,40 @@ self.eid = value else: super(Entity, self).__setitem__(attr, value) - if hasattr(self, 'edited_attributes'): + # don't add attribute into skip_security if already in edited + # attributes, else we may accidentaly skip a desired security check + if hasattr(self, 'edited_attributes') and \ + attr not in self.edited_attributes: self.edited_attributes.add(attr) self.skip_security_attributes.add(attr) + def __delitem__(self, attr): + """override __delitem__ to update self.edited_attributes on cleanup of + undesired changes introduced in the entity's dict. For example, see the + code snippet below from the `forge` cube: + + .. sourcecode:: python + + edited = self.entity.edited_attributes + has_load_left = 'load_left' in edited + if 'load' in edited and self.entity.load_left is None: + self.entity.load_left = self.entity['load'] + elif not has_load_left and edited: + # cleanup, this may cause undesired changes + del self.entity['load_left'] + + """ + super(Entity, self).__delitem__(attr) + if hasattr(self, 'edited_attributes'): + self.edited_attributes.remove(attr) + def setdefault(self, attr, default): """override setdefault to update self.edited_attributes""" super(Entity, self).setdefault(attr, default) - if hasattr(self, 'edited_attributes'): + # don't add attribute into skip_security if already in edited + # attributes, else we may accidentaly skip a desired security check + if hasattr(self, 'edited_attributes') and \ + attr not in self.edited_attributes: self.edited_attributes.add(attr) self.skip_security_attributes.add(attr) @@ -861,8 +889,8 @@ else: restr = 'X %s Y' % attr if values is None: - execute('DELETE %s WHERE X eid %%(x)s' % restr, - {'x': self.eid}, 'x') + self._cw.execute('DELETE %s WHERE X eid %%(x)s' % restr, + {'x': self.eid}, 'x') continue if not isinstance(values, (tuple, list, set, frozenset)): values = (values,) @@ -901,13 +929,16 @@ _ = unicode else: _ = self._cw._ - if creation or not hasattr(self, 'edited_attributes'): + if creation: # on creations, we want to check all relations, especially # required attributes - relations = None - else: + relations = [rschema for rschema in self.e_schema.subject_relations() + if rschema.final and rschema.type != 'eid'] + elif hasattr(self, 'edited_attributes'): relations = [self._cw.vreg.schema.rschema(rtype) for rtype in self.edited_attributes] + else: + relations = None self.e_schema.check(self, creation=creation, _=_, relations=relations) diff -r 6cb91be7707f -r cb27485ef5ae hooks/notification.py --- a/hooks/notification.py Wed Mar 24 08:42:49 2010 +0100 +++ b/hooks/notification.py Wed Mar 24 17:58:05 2010 +0100 @@ -112,12 +112,12 @@ if session.added_in_transaction(self.entity.eid): return # entity is being created # then compute changes - changes = session.transaction_data.setdefault('changes', {}) - thisentitychanges = changes.setdefault(self.entity.eid, set()) attrs = [k for k in self.entity.edited_attributes if not k in self.skip_attrs] if not attrs: return + changes = session.transaction_data.setdefault('changes', {}) + thisentitychanges = changes.setdefault(self.entity.eid, set()) rqlsel, rqlrestr = [], ['X eid %(x)s'] for i, attr in enumerate(attrs): var = chr(65+i) diff -r 6cb91be7707f -r cb27485ef5ae hooks/security.py --- a/hooks/security.py Wed Mar 24 08:42:49 2010 +0100 +++ b/hooks/security.py Wed Mar 24 17:58:05 2010 +0100 @@ -25,15 +25,16 @@ except AttributeError: editedattrs = entity # XXX unexpected for attr in editedattrs: - try: - dontcheck.remove(attr) + if attr in dontcheck: continue - except KeyError: - pass 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, 'update', eid=eid) + # don't update dontcheck until everything went fine: see usage in + # after_update_entity, where if we got an Unauthorized at hook time, we will + # retry and commit time + dontcheck |= frozenset(editedattrs) class _CheckEntityPermissionOp(hook.LateOperation): diff -r 6cb91be7707f -r cb27485ef5ae server/session.py --- a/server/session.py Wed Mar 24 08:42:49 2010 +0100 +++ b/server/session.py Wed Mar 24 17:58:05 2010 +0100 @@ -155,6 +155,9 @@ session = Session(user, self.repo) threaddata = session._threaddata threaddata.pool = self.pool + # share pending_operations, else operation added in the hi-jacked + # session such as SendMailOp won't ever be processed + threaddata.pending_operations = self.pending_operations # everything in transaction_data should be copied back but the entity # type cache we don't want to avoid security pb threaddata.transaction_data = self.transaction_data.copy() @@ -885,13 +888,15 @@ # deprecated ############################################################### - @deprecated("[3.7] control security with session.[read|write]_security") + @deprecated("[3.7] execute is now unsafe by default in hooks/operation. You" + " can also control security with session.[read|write]_security") def unsafe_execute(self, rql, kwargs=None, eid_key=None, build_descr=True, propagate=False): """like .execute but with security checking disabled (this method is internal to the server, it's not part of the db-api) """ - return self.execute(rql, kwargs, eid_key, build_descr) + with security_enabled(self, read=False, write=False): + return self.execute(rql, kwargs, eid_key, build_descr) @property @deprecated("[3.7] is_super_session is deprecated, test " diff -r 6cb91be7707f -r cb27485ef5ae server/test/unittest_rql2sql.py --- a/server/test/unittest_rql2sql.py Wed Mar 24 08:42:49 2010 +0100 +++ b/server/test/unittest_rql2sql.py Wed Mar 24 17:58:05 2010 +0100 @@ -543,13 +543,10 @@ ORDER BY 4 DESC'''), - ("Any X WHERE X eid 0, X eid 0", - '''SELECT 0'''), - - ("Any X WHERE X eid 0, X eid 0, X test TRUE", + ("Any X WHERE X eid 0, X test TRUE", '''SELECT _X.cw_eid FROM cw_Personne AS _X -WHERE _X.cw_eid=0 AND _X.cw_eid=0 AND _X.cw_test=TRUE'''), +WHERE _X.cw_eid=0 AND _X.cw_test=TRUE'''), ("Any X,GROUP_CONCAT(TN) GROUPBY X ORDERBY XN WHERE T tags X, X name XN, T name TN, X is CWGroup", '''SELECT _X.cw_eid, GROUP_CONCAT(_T.cw_name) diff -r 6cb91be7707f -r cb27485ef5ae web/application.py --- a/web/application.py Wed Mar 24 08:42:49 2010 +0100 +++ b/web/application.py Wed Mar 24 17:58:05 2010 +0100 @@ -123,7 +123,11 @@ SESSION_MANAGER = self.session_manager if not 'last_login_time' in self.vreg.schema: self._update_last_login_time = lambda x: None - CW_EVENT_MANAGER.bind('after-registry-reload', self.reset_session_manager) + if self.vreg.config.mode != 'test': + # don't try to reset session manager during test, this leads to + # weird failures when running multiple tests + CW_EVENT_MANAGER.bind('after-registry-reload', + self.reset_session_manager) def reset_session_manager(self): data = self.session_manager.dump_data() diff -r 6cb91be7707f -r cb27485ef5ae web/test/unittest_uicfg.py --- a/web/test/unittest_uicfg.py Wed Mar 24 08:42:49 2010 +0100 +++ b/web/test/unittest_uicfg.py Wed Mar 24 17:58:05 2010 +0100 @@ -1,10 +1,12 @@ from cubicweb.devtools.testlib import CubicWebTC from cubicweb.web import uicfg +abaa = uicfg.actionbox_appearsin_addmenu + class UICFGTC(CubicWebTC): - def test(self): - self.skip('write some tests') + def test_default_actionbox_appearsin_addmenu_config(self): + self.failIf(abaa.etype_get('TrInfo', 'wf_info_for', 'object', 'CWUser')) if __name__ == '__main__': from logilab.common.testlib import unittest_main diff -r 6cb91be7707f -r cb27485ef5ae web/views/actions.py --- a/web/views/actions.py Wed Mar 24 08:42:49 2010 +0100 +++ b/web/views/actions.py Wed Mar 24 17:58:05 2010 +0100 @@ -404,16 +404,8 @@ addmenu = uicfg.actionbox_appearsin_addmenu addmenu.tag_subject_of(('*', 'require_permission', '*'), False) -addmenu.tag_subject_of(('*', 'wf_info_for', '*'), False) -addmenu.tag_object_of(('*', 'wf_info_for', '*'), False) -addmenu.tag_object_of(('*', 'state_of', 'CWEType'), True) -addmenu.tag_object_of(('*', 'transition_of', 'CWEType'), True) addmenu.tag_object_of(('*', 'relation_type', 'CWRType'), True) addmenu.tag_object_of(('*', 'from_entity', 'CWEType'), False) addmenu.tag_object_of(('*', 'to_entity', 'CWEType'), False) addmenu.tag_object_of(('*', 'in_group', 'CWGroup'), True) addmenu.tag_object_of(('*', 'bookmarked_by', 'CWUser'), True) -addmenu.tag_subject_of(('Transition', 'destination_state', '*'), True) -addmenu.tag_object_of(('*', 'allowed_transition', 'Transition'), True) -addmenu.tag_object_of(('*', 'destination_state', 'State'), True) -addmenu.tag_subject_of(('State', 'allowed_transition', '*'), True) diff -r 6cb91be7707f -r cb27485ef5ae web/views/workflow.py --- a/web/views/workflow.py Wed Mar 24 08:42:49 2010 +0100 +++ b/web/views/workflow.py Wed Mar 24 17:58:05 2010 +0100 @@ -35,6 +35,15 @@ _abaa.tag_subject_of(('State', 'allowed_transition', 'BaseTransition'), False) _abaa.tag_object_of(('SubWorkflowExitPoint', 'destination_state', 'State'), False) +_abaa.tag_subject_of(('*', 'wf_info_for', '*'), False) +_abaa.tag_object_of(('*', 'wf_info_for', '*'), False) + +_abaa.tag_object_of(('*', 'state_of', 'CWEType'), True) +_abaa.tag_object_of(('*', 'transition_of', 'CWEType'), True) +_abaa.tag_subject_of(('Transition', 'destination_state', '*'), True) +_abaa.tag_object_of(('*', 'allowed_transition', 'Transition'), True) +_abaa.tag_object_of(('*', 'destination_state', 'State'), True) +_abaa.tag_subject_of(('State', 'allowed_transition', '*'), True) _abaa.tag_object_of(('State', 'state_of', 'Workflow'), True) _abaa.tag_object_of(('Transition', 'transition_of', 'Workflow'), True) _abaa.tag_object_of(('WorkflowTransition', 'transition_of', 'Workflow'), True) diff -r 6cb91be7707f -r cb27485ef5ae web/views/xmlrss.py --- a/web/views/xmlrss.py Wed Mar 24 08:42:49 2010 +0100 +++ b/web/views/xmlrss.py Wed Mar 24 17:58:05 2010 +0100 @@ -50,11 +50,14 @@ self.w(u'<%s>\n' % (entity.e_schema)) for rschema, attrschema in entity.e_schema.attribute_definitions(): attr = rschema.type - try: - value = entity[attr] - except KeyError: - # Bytes - continue + if attr == 'eid': + value = entity.eid + else: + try: + value = entity[attr] + except KeyError: + # Bytes + continue if value is not None: if attrschema == 'Bytes': from base64 import b64encode