--- 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
--- 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"
--- 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):
--- 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 <david.douard@logilab.fr> Fri, 30 Nov 2012 19:25:20 +0100
+
cubicweb (3.15.5-1) unstable; urgency=low
* New upstream release
--- 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
--- 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 <http://packages.ubuntu.com/lucid/libgecode19>`_.
+ 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::
--- 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"
--- 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 ""
--- 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:"
--- 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é"
--- 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 <http://www.gnu.org/licenses/>.
-"""Common utilies to format / semd emails."""
+"""Common utilies to format / send emails."""
__docformat__ = "restructuredtext en"
--- 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
--- 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()]
--- 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 ##########################
--- 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):
--- /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 <http://www.gnu.org/licenses/>.
+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()
--- /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 <http://www.gnu.org/licenses/>.
+
+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'^<input name="__signature" type="hidden" value="[0-9a-f]{32}" />$',
+ 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()
--- 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 <http://www.gnu.org/licenses/>.
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()
--- 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')))
--- 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
--- 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')
--- 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