# HG changeset patch # User Nicolas Chauvat # Date 1354489383 -3600 # Node ID 112a04c0473dcff4fbb9ed9738b3a1545c570196 # Parent bd4f5052a532fc8350bcc83b7a8fce483f2f858e# Parent 1a87ccdf12a337920d10c8aa7a6e5ed925b4d2ee merge fixes from stable diff -r bd4f5052a532 -r 112a04c0473d .hgtags --- a/.hgtags Fri Nov 16 11:53:17 2012 +0100 +++ b/.hgtags Mon Dec 03 00:03:03 2012 +0100 @@ -268,3 +268,5 @@ 70cb36c826df86de465f9b69647cef7096dcf12c cubicweb-debian-version-3.15.4-1 b0e086f451b7213fe63141438edc91a6b2da9072 cubicweb-version-3.15.5 19e115ae5442c427c0adbda8b9d8ceccf2931b5c cubicweb-debian-version-3.15.5-1 +0163bd9f4880d5531e433c1500f9298a0adef6b7 cubicweb-version-3.15.6 +b05e156b8fe720494293b08e7060ba43ad57a5c8 cubicweb-debian-version-3.15.6-1 diff -r bd4f5052a532 -r 112a04c0473d __pkginfo__.py --- a/__pkginfo__.py Fri Nov 16 11:53:17 2012 +0100 +++ b/__pkginfo__.py Mon Dec 03 00:03:03 2012 +0100 @@ -22,7 +22,7 @@ modname = distname = "cubicweb" -numversion = (3, 15, 5) +numversion = (3, 15, 6) version = '.'.join(str(num) for num in numversion) description = "a repository of entities / relations for knowledge management" diff -r bd4f5052a532 -r 112a04c0473d _exceptions.py --- a/_exceptions.py Fri Nov 16 11:53:17 2012 +0100 +++ b/_exceptions.py Mon Dec 03 00:03:03 2012 +0100 @@ -103,6 +103,10 @@ except Exception, ex: return str(ex) +class Forbidden(SecurityError): + """raised when a user tries to perform a forbidden action + """ + # source exceptions ########################################################### class EidNotInSource(SourceException): diff -r bd4f5052a532 -r 112a04c0473d debian/changelog --- a/debian/changelog Fri Nov 16 11:53:17 2012 +0100 +++ b/debian/changelog Mon Dec 03 00:03:03 2012 +0100 @@ -1,3 +1,9 @@ +cubicweb (3.15.6-1) squeeze; urgency=low + + * New upstream release + + -- David Douard Fri, 30 Nov 2012 19:25:20 +0100 + cubicweb (3.15.5-1) unstable; urgency=low * New upstream release diff -r bd4f5052a532 -r 112a04c0473d doc/book/en/admin/cubicweb-ctl.rst --- a/doc/book/en/admin/cubicweb-ctl.rst Fri Nov 16 11:53:17 2012 +0100 +++ b/doc/book/en/admin/cubicweb-ctl.rst Mon Dec 03 00:03:03 2012 +0100 @@ -44,7 +44,7 @@ Create an instance ------------------- -You must ensure `~/cubicweb.d/` exists prior to this. On windows, the +You must ensure `~/etc/cubicweb.d/` exists prior to this. On windows, the '~' part will probably expand to 'Documents and Settings/user'. To create an instance from an existing cube, execute the following diff -r bd4f5052a532 -r 112a04c0473d doc/book/en/admin/setup.rst --- a/doc/book/en/admin/setup.rst Fri Nov 16 11:53:17 2012 +0100 +++ b/doc/book/en/admin/setup.rst Mon Dec 03 00:03:03 2012 +0100 @@ -51,7 +51,7 @@ Depending on the distribution you are using, add the appropriate line to your `list of sources` (for example by editing ``/etc/apt/sources.list``). -For Debian Squeeze (stable):: +For Debian 6.0 Squeeze (stable):: deb http://download.logilab.org/production/ squeeze/ @@ -59,13 +59,9 @@ deb http://download.logilab.org/production/ sid/ -For Ubuntu Lucid (Long Term Support) and newer:: - - deb http://download.logilab.org/production/ lucid/ +For Ubuntu 12.04 Precise Pangolin (Long Term Support) and newer:: -Note that for Ubuntu Maverick and newer, you shall use the `lucid` -repository and install the ``libgecode19`` package from `lucid -universe `_. + deb http://download.logilab.org/production/ precise/ The repositories are signed with the `Logilab's gnupg key`_. You can download and register the key to avoid warnings:: diff -r bd4f5052a532 -r 112a04c0473d i18n/de.po --- a/i18n/de.po Fri Nov 16 11:53:17 2012 +0100 +++ b/i18n/de.po Mon Dec 03 00:03:03 2012 +0100 @@ -869,6 +869,11 @@ msgid "This WorkflowTransition" msgstr "Dieser Workflow-Übergang" +msgid "" +"This action is forbidden. If you think it should be allowed, please contact " +"the site administrator." +msgstr "" + msgid "This entity type permissions:" msgstr "Berechtigungen für diesen Entitätstyp" diff -r bd4f5052a532 -r 112a04c0473d i18n/en.po --- a/i18n/en.po Fri Nov 16 11:53:17 2012 +0100 +++ b/i18n/en.po Mon Dec 03 00:03:03 2012 +0100 @@ -845,6 +845,11 @@ msgid "This WorkflowTransition" msgstr "This workflow-transition" +msgid "" +"This action is forbidden. If you think it should be allowed, please contact " +"the site administrator." +msgstr "" + msgid "This entity type permissions:" msgstr "" diff -r bd4f5052a532 -r 112a04c0473d i18n/es.po --- a/i18n/es.po Fri Nov 16 11:53:17 2012 +0100 +++ b/i18n/es.po Mon Dec 03 00:03:03 2012 +0100 @@ -870,6 +870,11 @@ msgid "This WorkflowTransition" msgstr "Esta transición de Workflow" +msgid "" +"This action is forbidden. If you think it should be allowed, please contact " +"the site administrator." +msgstr "" + msgid "This entity type permissions:" msgstr "Permisos para este tipo de entidad:" diff -r bd4f5052a532 -r 112a04c0473d i18n/fr.po --- a/i18n/fr.po Fri Nov 16 11:53:17 2012 +0100 +++ b/i18n/fr.po Mon Dec 03 00:03:03 2012 +0100 @@ -871,6 +871,13 @@ msgid "This WorkflowTransition" msgstr "Cette transition workflow" +msgid "" +"This action is forbidden. If you think it should be allowed, please contact " +"the site administrator." +msgstr "" +"Cette action est interdite. Si toutefois vous pensez qu'elle devrait être " +"autorisée, veuillez contacter l'administrateur du site." + msgid "This entity type permissions:" msgstr "Permissions pour ce type d'entité" diff -r bd4f5052a532 -r 112a04c0473d mail.py --- a/mail.py Fri Nov 16 11:53:17 2012 +0100 +++ b/mail.py Mon Dec 03 00:03:03 2012 +0100 @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU Lesser General Public License along # with CubicWeb. If not, see . -"""Common utilies to format / semd emails.""" +"""Common utilies to format / send emails.""" __docformat__ = "restructuredtext en" diff -r bd4f5052a532 -r 112a04c0473d web/application.py --- a/web/application.py Fri Nov 16 11:53:17 2012 +0100 +++ b/web/application.py Mon Dec 03 00:03:03 2012 +0100 @@ -34,7 +34,8 @@ from cubicweb import set_log_methods, cwvreg from cubicweb import ( - ValidationError, Unauthorized, AuthenticationError, NoSelectableObject, + ValidationError, Unauthorized, Forbidden, + AuthenticationError, NoSelectableObject, BadConnectionId, CW_EVENT_MANAGER) from cubicweb.dbapi import DBAPISession, anonymous_session from cubicweb.web import LOGGER, component @@ -469,6 +470,11 @@ 'If you think you should, please contact the site administrator.') req.status_out = httplib.UNAUTHORIZED result = self.error_handler(req, ex, tb=False) + except Forbidden, ex: + req.data['errmsg'] = req._('This action is forbidden. ' + 'If you think it should be allowed, please contact the site administrator.') + req.status_out = httplib.FORBIDDEN + result = self.error_handler(req, ex, tb=False) except (BadRQLQuery, RequestError), ex: result = self.error_handler(req, ex, tb=False) ### pass through exception diff -r bd4f5052a532 -r 112a04c0473d web/facet.py --- a/web/facet.py Fri Nov 16 11:53:17 2012 +0100 +++ b/web/facet.py Mon Dec 03 00:03:03 2012 +0100 @@ -1388,11 +1388,16 @@ return if isinstance(value, list): value = reduce(lambda x, y: int(x) | int(y), value) + else: + value = int(value) attr_var = self.select.make_variable() self.select.add_relation(self.filtered_variable, self.rtype, attr_var) comp = nodes.Comparison('=', nodes.Constant(value, 'Int')) - comp.append(nodes.MathExpression('&', nodes.variable_ref(attr_var), - nodes.Constant(value, 'Int'))) + if value == 0: + comp.append(nodes.variable_ref(attr_var)) + else: + comp.append(nodes.MathExpression('&', nodes.variable_ref(attr_var), + nodes.Constant(value, 'Int'))) having = self.select.having if having: self.select.replace(having[0], nodes.And(having[0], comp)) @@ -1402,7 +1407,7 @@ def rset_vocabulary(self, rset): mask = reduce(lambda x, y: x | (y[0] or 0), rset, 0) return sorted([(self._cw._(label), val) for label, val in self.choices - if val & mask]) + if not val or val & mask]) def possible_values(self): return [unicode(val) for label, val in self.vocabulary()] diff -r bd4f5052a532 -r 112a04c0473d web/request.py --- a/web/request.py Fri Nov 16 11:53:17 2012 +0100 +++ b/web/request.py Mon Dec 03 00:03:03 2012 +0100 @@ -22,6 +22,7 @@ import time import random import base64 +import urllib from hashlib import sha1 # pylint: disable=E0611 from Cookie import SimpleCookie from calendar import timegm @@ -38,7 +39,6 @@ from logilab.mtconverter import xml_escape from cubicweb.dbapi import DBAPIRequest -from cubicweb.mail import header from cubicweb.uilib import remove_html_tags, js from cubicweb.utils import SizeConstrainedList, HTMLHead, make_uid from cubicweb.view import STRICT_DOCTYPE, TRANSITIONAL_DOCTYPE_NOEXT @@ -608,10 +608,17 @@ content_type += ';charset=' + (encoding or self.encoding) self.set_header('content-type', content_type) if filename: - if isinstance(filename, unicode): - filename = header(filename).encode() - self.set_header('content-disposition', 'inline; filename=%s' - % filename) + header = ['attachment'] + try: + filename = filename.encode('ascii') + header.append('filename=' + filename) + except UnicodeEncodeError: + # fallback filename for very old browser + header.append('filename=' + filename.encode('ascii', 'ignore')) + # encoded filename according RFC5987 + filename = urllib.quote(filename.encode('utf-8'), '') + header.append("filename*=utf-8''" + filename) + self.set_header('content-disposition', ';'.join(header)) # high level methods for HTML headers management ########################## diff -r bd4f5052a532 -r 112a04c0473d web/test/unittest_facet.py --- a/web/test/unittest_facet.py Fri Nov 16 11:53:17 2012 +0100 +++ b/web/test/unittest_facet.py Mon Dec 03 00:03:03 2012 +0100 @@ -221,6 +221,25 @@ self.assertEqual(f.select.as_string(), "DISTINCT Any WHERE X ordernum XO, X is CWAttribute, X ordernum C HAVING 3 = (C & 3)") + def test_bitfield_0_value(self): + req, rset, rqlst, filtered_variable = self.prepare_rqlst( + 'CWAttribute X WHERE X ordernum XO', + expected_baserql='Any X WHERE X ordernum XO, X is CWAttribute', + expected_preparedrql='DISTINCT Any WHERE X ordernum XO, X is CWAttribute') + f = facet.BitFieldFacet(req, rset=rset, + select=rqlst.children[0], + filtered_variable=filtered_variable) + f.choices = [('zero', 0,), ('un', 1,), ('deux', 2,)] + f.rtype = 'ordernum' + self.assertEqual(f.vocabulary(), + [(u'deux', 2), (u'un', 1), (u'zero', 0)]) + self.assertEqual(f.possible_values(), + ['2', '1', '0']) + req.form[f.__regid__] = '0' + f.add_rql_restrictions() + self.assertEqual(f.select.as_string(), + "DISTINCT Any WHERE X ordernum XO, X is CWAttribute, X ordernum C HAVING 0 = C") + def test_rql_path_eid(self): req, rset, rqlst, filtered_variable = self.prepare_rqlst() class RPF(facet.RQLPathFacet): diff -r bd4f5052a532 -r 112a04c0473d web/test/unittest_idownloadable.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/test/unittest_idownloadable.py Mon Dec 03 00:03:03 2012 +0100 @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +# copyright 2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of CubicWeb. +# +# CubicWeb is free software: you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 2.1 of the License, or (at your option) +# any later version. +# +# CubicWeb is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with CubicWeb. If not, see . +from __future__ import with_statement + +from functools import partial + +from logilab.common.testlib import unittest_main + +from cubicweb.devtools.testlib import CubicWebTC +from cubicweb import view +from cubicweb.predicates import is_instance + + +class IDownloadableTC(CubicWebTC): + + def setUp(self): + super(IDownloadableTC, self).setUp() + class IDownloadableUser(view.EntityAdapter): + __regid__ = 'IDownloadable' + __select__ = is_instance('CWUser') + + def download_content_type(self): + """return MIME type of the downloadable content""" + return 'text/plain' + + def download_encoding(self): + """return encoding of the downloadable content""" + return 'ascii' + + def download_file_name(self): + """return file name of the downloadable content""" + return self.entity.name() + '.txt' + + def download_data(self): + return 'Babar is not dead!' + self.vreg.register(IDownloadableUser) + self.addCleanup(partial(self.vreg.unregister, IDownloadableUser)) + + def test_header_simple_case(self): + req = self.request() + req.form['vid'] = 'download' + req.form['eid'] = str(req.user.eid) + data = self.ctrl_publish(req,'view') + get = req.headers_out.getRawHeaders + self.assertEqual(['attachment;filename=admin.txt'], + get('content-disposition')) + self.assertEqual(['text/plain;charset=ascii'], + get('content-type')) + self.assertEqual('Babar is not dead!', data) + + def test_header_unicode_filename(self): + req = self.request() + self.create_user(req, login=u'cécilia', password='babar') + self.commit() + with self.login(u'cécilia', password='babar'): + req = self.request() + req.form['vid'] = 'download' + req.form['eid'] = str(req.user.eid) + self.ctrl_publish(req,'view') + get = req.headers_out.getRawHeaders + self.assertEqual(["attachment;filename=ccilia.txt;filename*=utf-8''c%C3%A9cilia.txt"], + get('content-disposition')) + + def test_header_unicode_long_filename(self): + req = self.request() + name = u'Bèrte_hô_grand_nôm_ça_va_totallement_déborder_de_la_limite_là' + self.create_user(req, login=name, password='babar') + self.commit() + with self.login(name, password='babar'): + req = self.request() + req.form['vid'] = 'download' + req.form['eid'] = str(req.user.eid) + self.ctrl_publish(req,'view') + get = req.headers_out.getRawHeaders + self.assertEqual(["attachment;filename=Brte_h_grand_nm_a_va_totallement_dborder_de_la_limite_l.txt;filename*=utf-8''B%C3%A8rte_h%C3%B4_grand_n%C3%B4m_%C3%A7a_va_totallement_d%C3%A9border_de_la_limite_l%C3%A0.txt"], + get('content-disposition')) + +if __name__ == '__main__': + unittest_main() diff -r bd4f5052a532 -r 112a04c0473d web/test/unittest_views_errorform.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/test/unittest_views_errorform.py Mon Dec 03 00:03:03 2012 +0100 @@ -0,0 +1,101 @@ +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of CubicWeb. +# +# CubicWeb is free software: you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 2.1 of the License, or (at your option) +# any later version. +# +# CubicWeb is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with CubicWeb. If not, see . + +from __future__ import with_statement + +from logilab.common.testlib import unittest_main +from logilab.mtconverter import html_unescape + +from cubicweb import Forbidden, ValidationError +from cubicweb.devtools.testlib import CubicWebTC +from cubicweb.utils import json +from cubicweb.view import StartupView, TRANSITIONAL_DOCTYPE_NOEXT +from cubicweb.web import Redirect +from cubicweb.web.htmlwidgets import TableWidget +from cubicweb.web.views import vid_from_rset + +import re +import hmac + +class ErrorViewTC(CubicWebTC): + def setUp(self): + super(ErrorViewTC, self).setUp() + self.req = self.request() + self.vreg.config['submit-mail'] = "test@logilab.fr" + self.vreg.config['print-traceback'] = "yes" + + def test_error_generation(self): + """ + tests + """ + + class MyWrongView(StartupView): + __regid__ = 'my-view' + def call(self): + raise ValueError('This is wrong') + + with self.temporary_appobjects(MyWrongView): + try: + self.view('my-view') + except Exception, e: + import sys + self.req.data['excinfo'] = sys.exc_info() + self.req.data['ex'] = e + html = self.view('error', req=self.req) + self.failUnless(re.search(r'^$', + html.source, re.M)) + + + def test_error_submit_nosig(self): + """ + tests that the reportbug controller refuses submission if + there is not content signature + """ + + self.req.form = {'description': u'toto', + } + with self.assertRaises(Forbidden) as cm: + self.ctrl_publish(self.req, 'reportbug') + + def test_error_submit_wrongsig(self): + """ + tests that the reportbug controller refuses submission if the + content signature is invalid + """ + + self.req.form = {'__signature': 'X', + 'description': u'toto', + } + with self.assertRaises(Forbidden) as cm: + self.ctrl_publish(self.req, 'reportbug') + + def test_error_submit_ok(self): + """ + tests that the reportbug controller accept the email submission if the + content signature is valid + """ + + sign = self.vreg.config.sign_text('toto') + self.req.form = {'__signature': sign, + 'description': u'toto', + } + with self.assertRaises(Redirect) as cm: + self.ctrl_publish(self.req, 'reportbug') + +if __name__ == '__main__': + unittest_main() diff -r bd4f5052a532 -r 112a04c0473d web/test/unittest_views_json.py --- a/web/test/unittest_views_json.py Fri Nov 16 11:53:17 2012 +0100 +++ b/web/test/unittest_views_json.py Mon Dec 03 00:03:03 2012 +0100 @@ -1,8 +1,34 @@ +# -*- coding: utf-8 -*- +# copyright 2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of CubicWeb. +# +# CubicWeb is free software: you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 2.1 of the License, or (at your option) +# any later version. +# +# CubicWeb is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with CubicWeb. If not, see . from cubicweb.devtools.testlib import CubicWebTC from cubicweb.utils import json +from cubicweb.web.application import anonymized_request + class JsonViewsTC(CubicWebTC): + anonymize = True + res_jsonp_data = '[["guests", 1]]' + + def setUp(self): + super(JsonViewsTC, self).setUp() + self.config.global_set_option('anonymize-jsonp-queries', self.anonymize) def test_json_rsetexport(self): req = self.request() @@ -19,7 +45,7 @@ data = self.ctrl_publish(req, ctrl='jsonp') self.assertEqual(req.headers_out.getRawHeaders('content-type'), ['application/javascript']) # because jsonp anonymizes data, only 'guests' group should be found - self.assertEqual(data, 'foo([["guests", 1]])') + self.assertEqual(data, 'foo(%s)' % self.res_jsonp_data) def test_json_rsetexport_with_jsonp_and_bad_vid(self): req = self.request() @@ -30,7 +56,7 @@ data = self.ctrl_publish(req, ctrl='jsonp') self.assertEqual(req.headers_out.getRawHeaders('content-type'), ['application/javascript']) # result should be plain json, not the table view - self.assertEqual(data, 'foo([["guests", 1]])') + self.assertEqual(data, 'foo(%s)' % self.res_jsonp_data) def test_json_ersetexport(self): req = self.request() @@ -41,6 +67,10 @@ self.assertEqual(data[1]['name'], 'managers') +class NotAnonymousJsonViewsTC(JsonViewsTC): + anonymize = False + res_jsonp_data = '[["guests", 1], ["managers", 1]]' + if __name__ == '__main__': from logilab.common.testlib import unittest_main unittest_main() diff -r bd4f5052a532 -r 112a04c0473d web/views/basecontrollers.py --- a/web/views/basecontrollers.py Fri Nov 16 11:53:17 2012 +0100 +++ b/web/views/basecontrollers.py Mon Dec 03 00:03:03 2012 +0100 @@ -27,7 +27,8 @@ from logilab.common.deprecation import deprecated from cubicweb import (NoSelectableObject, ObjectNotFound, ValidationError, - AuthenticationError, typed_eid, UndoTransactionException) + AuthenticationError, typed_eid, UndoTransactionException, + Forbidden) from cubicweb.utils import json_dumps from cubicweb.predicates import (authenticated_user, anonymous_user, match_form_params) @@ -277,9 +278,15 @@ def publish(self, rset=None): req = self._cw + desc = req.form['description'] + # The description is generated and signed by cubicweb itself, check + # description's signature so we don't want to send spam here + sign = req.form.get('__signature', '') + if not (sign and req.vreg.config.check_text_sign(desc, sign)): + raise Forbidden('Invalid content') self.sendmail(req.vreg.config['submit-mail'], req._('%s error report') % req.vreg.config.appid, - req.form['description']) + desc) raise Redirect(req.build_url(__message=req._('bug report sent'))) diff -r bd4f5052a532 -r 112a04c0473d web/views/json.py --- a/web/views/json.py Fri Nov 16 11:53:17 2012 +0100 +++ b/web/views/json.py Mon Dec 03 00:03:03 2012 +0100 @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -50,14 +50,20 @@ self._cw.form['vid'] = 'jsonexport' else: # if no vid is specified, use jsonexport self._cw.form['vid'] = 'jsonexport' - with anonymized_request(self._cw): - json_data = super(JsonpController, self).publish(rset) - if 'callback' in self._cw.form: # jsonp - json_padding = self._cw.form['callback'] - # use ``application/javascript`` is ``callback`` parameter is - # provided, let ``application/json`` otherwise - self._cw.set_content_type('application/javascript') - json_data = '%s(%s)' % (json_padding, json_data) + if self._cw.vreg.config['anonymize-jsonp-queries']: + with anonymized_request(self._cw): + return self._get_json_data(rset) + else: + return self._get_json_data(rset) + + def _get_json_data(self, rset): + json_data = super(JsonpController, self).publish(rset) + if 'callback' in self._cw.form: # jsonp + json_padding = self._cw.form['callback'] + # use ``application/javascript`` is ``callback`` parameter is + # provided, let ``application/json`` otherwise + self._cw.set_content_type('application/javascript') + json_data = '%s(%s)' % (json_padding, json_data) return json_data diff -r bd4f5052a532 -r 112a04c0473d web/views/management.py --- a/web/views/management.py Fri Nov 16 11:53:17 2012 +0100 +++ b/web/views/management.py Mon Dec 03 00:03:03 2012 +0100 @@ -20,6 +20,7 @@ __docformat__ = "restructuredtext en" _ = unicode + from logilab.mtconverter import xml_escape from logilab.common.registry import yes @@ -148,6 +149,8 @@ form.add_hidden('description', binfo, # we must use a text area to keep line breaks widget=wdgs.TextArea({'class': 'hidden'})) + # add a signature so one can't send arbitrary text + form.add_hidden('__signature', req.vreg.config.sign_text(binfo)) form.add_hidden('__bugreporting', '1') form.form_buttons = [wdgs.SubmitButton(MAIL_SUBMIT_MSGID)] form.action = req.build_url('reportbug') @@ -171,7 +174,7 @@ """A textual stats output for monitoring tools such as munin """ __regid__ = 'processinfo' - content_type = 'text/txt' + content_type = 'text/plain' templatable = False __select__ = none_rset() & match_user_groups('users', 'managers') diff -r bd4f5052a532 -r 112a04c0473d web/webconfig.py --- a/web/webconfig.py Fri Nov 16 11:53:17 2012 +0100 +++ b/web/webconfig.py Mon Dec 03 00:03:03 2012 +0100 @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -21,10 +21,12 @@ _ = unicode import os +import hmac +from uuid import uuid4 from os.path import join, exists, split, isdir from warnings import warn -from logilab.common.decorators import cached +from logilab.common.decorators import cached, cachedproperty from logilab.common.deprecation import deprecated from cubicweb import ConfigurationError @@ -219,6 +221,12 @@ 'help': 'use modconcat-like URLS to concat and serve JS / CSS files', 'group': 'web', 'level': 2, }), + ('anonymize-jsonp-queries', + {'type': 'yn', + 'default': True, + 'help': 'anonymize the connection before executing any jsonp query.', + 'group': 'web', 'level': 1 + }), )) def fckeditor_installed(self): @@ -251,7 +259,7 @@ def anonymous_user(self): """return a login and password to use for anonymous users. - + None may be returned for both if anonymous connection is not allowed or if an empty login is used in configuration """ @@ -266,6 +274,25 @@ raise ConfigurationError("anonymous information should only contains ascii") return user, passwd + @cachedproperty + def _instance_salt(self): + """This random key/salt is used to sign content to be sent back by + browsers, eg. in the error report form. + """ + return str(uuid4()) + + def sign_text(self, text): + """sign some text for later checking""" + # replace \r\n so we do not depend on whether a browser "reencode" + # original message using \r\n or not + return hmac.new(self._instance_salt, + text.strip().replace('\r\n', '\n')).hexdigest() + + def check_text_sign(self, text, signature): + """check the text signature is equal to the given signature""" + return self.sign_text(text) == signature + + def locate_resource(self, rid): """return the (directory, filename) where the given resource may be found