Permalink
Browse files

Bugfix: CSRF Error in comments. Add unittests for this.

  • Loading branch information...
1 parent 0d8845a commit 61e1809e6b57f229461b3087090b4661b8d6ec81 @jedie committed Jan 6, 2012
View
2 pylucid_project/__init__.py
@@ -17,7 +17,7 @@
import subprocess
-__version__ = (0, 10, 1)
+__version__ = (0, 10, 2)
VERSION_STRING = '.'.join(str(part) for part in __version__)
View
3 pylucid_project/media/PyLucid/pylucid_js_tools.js
@@ -356,7 +356,8 @@ function get_pylucid_comments_form() {
log("post_data:"+post_data);
$.ajax({
- type: "POST",
+ async: false,
+ type: "GET",
url: "?pylucid_comments=get_form",
data: post_data,
dataType: "html",
View
74 pylucid_project/pylucid_plugins/pylucid_comments/tests.py
@@ -9,7 +9,7 @@
- PyLucid initial data contains english and german pages.
- There exist only "PyLucid CMS" blog entry in english and german
- :copyleft: 2010-2011 by the PyLucid team, see AUTHORS for more details.
+ :copyleft: 2010-2012 by the PyLucid team, see AUTHORS for more details.
:license: GNU GPL v3 or above, see LICENSE for more details.
"""
@@ -46,35 +46,37 @@ def getValidData(self, obj, **kwargs):
return d
-class PyLucidCommentsPageMetaTest(PyLucidCommentsTestCase):
-
+class PyLucidCommentsPageMetaTestCase(PyLucidCommentsTestCase):
+ """
+ Base for all PageMeta tests.
+ """
def _pre_setup(self, *args, **kwargs):
- super(PyLucidCommentsPageMetaTest, self)._pre_setup(*args, **kwargs)
+ super(PyLucidCommentsTestCase, self)._pre_setup(*args, **kwargs)
self.pagemeta = PageMeta.on_site.all()[0]
self.absolute_url = self.pagemeta.get_absolute_url()
+ self.get_form_url = self.absolute_url + "?pylucid_comments=get_form"
+ self.submit_url = self.absolute_url + "?pylucid_comments=submit"
def setUp(self):
Comment.objects.all().delete()
self._old_ADMINS = settings.ADMINS
settings.ADMINS = (('John', 'john@example.com'), ('Mary', 'mary@example.com'))
- super(PyLucidCommentsPageMetaTest, self).setUp()
+ super(PyLucidCommentsTestCase, self).setUp()
def tearDown(self):
- super(PyLucidCommentsPageMetaTest, self).tearDown()
+ super(PyLucidCommentsTestCase, self).tearDown()
settings.ADMINS = self._old_ADMINS
def _get_form(self):
- url = self.absolute_url + "?pylucid_comments=get_form"
data = self.getValidData(self.pagemeta)
- response = self.client.post(url,
- {
- "content_type": data["content_type"],
- "object_pk": data["object_pk"]
- },
- HTTP_X_REQUESTED_WITH='XMLHttpRequest'
- )
+ url = self.get_form_url
+ url += "&content_type=%s" % data["content_type"]
+ url += "&object_pk=%s" % data["object_pk"]
+ response = self.client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
return response
+
+class PyLucidCommentsPageMetaTest(PyLucidCommentsPageMetaTestCase):
def test_get_form(self):
""" get the comment form via AJAX """
response = self._get_form()
@@ -99,11 +101,10 @@ def test_submit_comment(self):
settings.DEBUG = True # Display a comment error page
self.failUnless(Comment.objects.count() == 0)
self.failUnless(len(mail.outbox) == 0, len(mail.outbox))
- url = self.absolute_url + "?pylucid_comments=submit"
# submit a valid comments form
data = self.getValidData(self.pagemeta, comment="from test_submit_comment()")
- response = self.client.post(url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+ response = self.client.post(self.submit_url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
# Check if comment created
self.failUnless(Comment.objects.count() == 1)
@@ -188,11 +189,10 @@ def test_submit_no_comment(self):
def test_submit_spam(self):
settings.DEBUG = True # Display a comment error page
self.failUnless(Comment.objects.count() == 0)
- url = self.absolute_url + "?pylucid_comments=submit"
data = self.getValidData(self.pagemeta,
comment="Penis enlargement pills: http://en.wikipedia.org/wiki/Penis_enlargement ;)"
)
- response = self.client.post(url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+ response = self.client.post(self.submit_url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
# Check if page should reload (JavaScript do this)
self.failUnlessEqual(response.content, 'reload')
@@ -274,12 +274,50 @@ def test_DOS_attack(self):
self.failUnless(tested_banned == True)
+class PyLucidCommentsCsrfPageMetaTest(PyLucidCommentsPageMetaTestCase):
+ """
+ Test the Cross Site Request Forgery protection in comments.
+ """
+ def setUp(self):
+ super(PyLucidCommentsPageMetaTestCase, self).setUp()
+ settings.DEBUG = True
+ self.client = Client(enforce_csrf_checks=True)
+
+ def tearDown(self):
+ super(PyLucidCommentsPageMetaTestCase, self).tearDown()
+ settings.DEBUG = False
+
+ def test_submit_form_without_token(self):
+ # submit a valid comments form, but without csrf token
+ data = self.getValidData(self.pagemeta, comment="from test_submit_comment()")
+ response = self.client.post(self.submit_url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+ self.assertResponse(response, must_contain=("Forbidden", "No CSRF or session cookie."))
+
+ def test_submit_form_with_token(self):
+ # get the current csrf token
+ response = self._get_form()
+ self.assertIn(settings.CSRF_COOKIE_NAME, response.cookies)
+ csrf_token = response.cookies[settings.CSRF_COOKIE_NAME].value
+
+ self.failUnless(Comment.objects.count() == 0)
+
+ data = self.getValidData(self.pagemeta, comment="from test_submit_comment()")
+ data["csrfmiddlewaretoken"] = csrf_token
+ response = self.client.post(self.submit_url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+
+ # Check if comment created
+ self.failUnless(Comment.objects.count() == 1)
+
+ # Check if page should reload (JavaScript do this)
+ self.failUnlessEqual(response.content, 'reload')
+
if __name__ == "__main__":
# Run all unittest directly
from django.core import management
tests = __file__
+# tests = "pylucid_plugins.pylucid_comments.tests.PyLucidCommentsCsrfPageMetaTest"
management.call_command('test', tests,
verbosity=1,
View
17 pylucid_project/pylucid_plugins/pylucid_comments/views.py
@@ -7,7 +7,7 @@
TODO:
* Update existing unittest (e.g. blog, lexicon)
- :copyleft: 2010-2011 by the PyLucid team, see AUTHORS for more details.
+ :copyleft: 2010-2012 by the PyLucid team, see AUTHORS for more details.
:license: GNU GPL v3 or above, see LICENSE for more details.p
"""
@@ -28,6 +28,7 @@
from django.template.context import RequestContext
from django.template.loader import render_to_string
from django.utils.translation import ugettext as _
+from django.views.decorators.csrf import csrf_protect, csrf_exempt
from django_tools.decorators import render_to
from django_tools.utils.client_storage import ClientCookieStorage, InvalidCookieData
@@ -133,18 +134,18 @@ def comment_was_posted_handler(sender, **kwargs):
comment_was_posted.connect(comment_was_posted_handler)
-@check_request(APP_LABEL, "_get_form() error", must_post=True, must_ajax=True)
+@check_request(APP_LABEL, "_get_form() error", must_post=False, must_ajax=True)
@render_to("pylucid_comments/comment_form.html")
def _get_form(request):
""" Send the comment form to via AJAX request """
try:
- ctype = request.POST["content_type"].split(".", 1)
+ ctype = request.GET["content_type"].split(".", 1)
model = models.get_model(*ctype)
except Exception, err:
return bad_request(APP_LABEL, "error", "Wrong content type: %s" % err)
try:
- object_pk = request.POST["object_pk"]
+ object_pk = request.GET["object_pk"]
target = model._default_manager.using(None).get(pk=object_pk)
except Exception, err:
return bad_request(APP_LABEL, "error", "Wrong object_pk: %s" % err)
@@ -173,9 +174,17 @@ def _get_form(request):
form = comments.get_form()(target, initial=data)
+ # IMPORTANT: We must do the following, so that the
+ # CsrfViewMiddleware.process_response() would set the CSRF_COOKIE
+ # see also # https://github.com/jedie/PyLucid/issues/61
+ # XXX in Django => 1.4 we can use @ensure_csrf_cookie
+ # https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#django.views.decorators.csrf.ensure_csrf_cookie
+ request.META["CSRF_COOKIE_USED"] = True
+
return {"form":form}
+@csrf_protect
@check_request(APP_LABEL, "_form_submission() error", must_ajax=True)
def _form_submission(request):
""" Handle a AJAX comment form submission """

0 comments on commit 61e1809

Please sign in to comment.