Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[1.2.X] Fixed #14156 -- Modified the way CSRF protection is applied t…

…o flatpages so that the flatpage middleware doesn't cause all POSTs resulting in 404s to turn into 403s. Thanks to patrys for the report.

Backport of r13641 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.2.X@13643 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 2370a1038071bacd713721f5ad7a0811225bb040 1 parent 35411fd
Russell Keith-Magee authored August 27, 2010
32  django/contrib/flatpages/fixtures/sample_flatpages.json
... ...
@@ -0,0 +1,32 @@
  1
+[
  2
+    {
  3
+        "pk": 1,
  4
+        "model": "flatpages.flatpage",
  5
+        "fields": {
  6
+            "registration_required": false,
  7
+            "title": "A Flatpage",
  8
+            "url": "/flatpage/",
  9
+            "template_name": "",
  10
+            "sites": [
  11
+                1
  12
+            ],
  13
+            "content": "Isn't it flat!",
  14
+            "enable_comments": false
  15
+        }
  16
+    },
  17
+    {
  18
+        "pk": 2,
  19
+        "model": "flatpages.flatpage",
  20
+        "fields": {
  21
+            "registration_required": true,
  22
+            "title": "Sekrit Flatpage",
  23
+            "url": "/sekrit/",
  24
+            "template_name": "",
  25
+            "sites": [
  26
+                1
  27
+            ],
  28
+            "content": "Isn't it sekrit!",
  29
+            "enable_comments": false
  30
+        }
  31
+    }
  32
+]
3  django/contrib/flatpages/tests/__init__.py
... ...
@@ -0,0 +1,3 @@
  1
+from django.contrib.flatpages.tests.csrf import *
  2
+from django.contrib.flatpages.tests.middleware import *
  3
+from django.contrib.flatpages.tests.views import *
70  django/contrib/flatpages/tests/csrf.py
... ...
@@ -0,0 +1,70 @@
  1
+import os
  2
+from django.conf import settings
  3
+from django.test import TestCase, Client
  4
+
  5
+class FlatpageCSRFTests(TestCase):
  6
+    fixtures = ['sample_flatpages']
  7
+    urls = 'django.contrib.flatpages.tests.urls'
  8
+
  9
+    def setUp(self):
  10
+        self.client = Client(enforce_csrf_checks=True)
  11
+        self.old_MIDDLEWARE_CLASSES = settings.MIDDLEWARE_CLASSES
  12
+        flatpage_middleware_class = 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware'
  13
+        csrf_middleware_class = 'django.middleware.csrf.CsrfViewMiddleware'
  14
+        if csrf_middleware_class not in settings.MIDDLEWARE_CLASSES:
  15
+            settings.MIDDLEWARE_CLASSES += (csrf_middleware_class,)
  16
+        if flatpage_middleware_class not in settings.MIDDLEWARE_CLASSES:
  17
+            settings.MIDDLEWARE_CLASSES += (flatpage_middleware_class,)
  18
+        self.old_TEMPLATE_DIRS = settings.TEMPLATE_DIRS
  19
+        settings.TEMPLATE_DIRS = (
  20
+            os.path.join(
  21
+                os.path.dirname(__file__),
  22
+                'templates'
  23
+            ),
  24
+        )
  25
+
  26
+    def tearDown(self):
  27
+        settings.MIDDLEWARE_CLASSES = self.old_MIDDLEWARE_CLASSES
  28
+        settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS
  29
+
  30
+    def test_view_flatpage(self):
  31
+        "A flatpage can be served through a view, even when the middleware is in use"
  32
+        response = self.client.get('/flatpage_root/flatpage/')
  33
+        self.assertEquals(response.status_code, 200)
  34
+        self.assertContains(response, "<p>Isn't it flat!</p>")
  35
+
  36
+    def test_view_non_existent_flatpage(self):
  37
+        "A non-existent flatpage raises 404 when served through a view, even when the middleware is in use"
  38
+        response = self.client.get('/flatpage_root/no_such_flatpage/')
  39
+        self.assertEquals(response.status_code, 404)
  40
+
  41
+    def test_view_authenticated_flatpage(self):
  42
+        "A flatpage served through a view can require authentication"
  43
+        response = self.client.get('/flatpage_root/sekrit/')
  44
+        self.assertRedirects(response, '/accounts/login/?next=/flatpage_root/sekrit/')
  45
+
  46
+    def test_fallback_flatpage(self):
  47
+        "A flatpage can be served by the fallback middlware"
  48
+        response = self.client.get('/flatpage/')
  49
+        self.assertEquals(response.status_code, 200)
  50
+        self.assertContains(response, "<p>Isn't it flat!</p>")
  51
+
  52
+    def test_fallback_non_existent_flatpage(self):
  53
+        "A non-existent flatpage raises a 404 when served by the fallback middlware"
  54
+        response = self.client.get('/no_such_flatpage/')
  55
+        self.assertEquals(response.status_code, 404)
  56
+
  57
+    def test_post_view_flatpage(self):
  58
+        "POSTing to a flatpage served through a view will raise a CSRF error if no token is provided (Refs #14156)"
  59
+        response = self.client.post('/flatpage_root/flatpage/')
  60
+        self.assertEquals(response.status_code, 403)
  61
+
  62
+    def test_post_fallback_flatpage(self):
  63
+        "POSTing to a flatpage served by the middleware will raise a CSRF error if no token is provided (Refs #14156)"
  64
+        response = self.client.post('/flatpage/')
  65
+        self.assertEquals(response.status_code, 403)
  66
+
  67
+    def test_post_unknown_page(self):
  68
+        "POSTing to an unknown page isn't caught as a 403 CSRF error"
  69
+        response = self.client.post('/no_such_page/')
  70
+        self.assertEquals(response.status_code, 404)
56  django/contrib/flatpages/tests/middleware.py
... ...
@@ -0,0 +1,56 @@
  1
+import os
  2
+from django.conf import settings
  3
+from django.test import TestCase
  4
+
  5
+class FlatpageMiddlewareTests(TestCase):
  6
+    fixtures = ['sample_flatpages']
  7
+    urls = 'django.contrib.flatpages.tests.urls'
  8
+
  9
+    def setUp(self):
  10
+        self.old_MIDDLEWARE_CLASSES = settings.MIDDLEWARE_CLASSES
  11
+        flatpage_middleware_class = 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware'
  12
+        if flatpage_middleware_class not in settings.MIDDLEWARE_CLASSES:
  13
+            settings.MIDDLEWARE_CLASSES += (flatpage_middleware_class,)
  14
+        self.old_TEMPLATE_DIRS = settings.TEMPLATE_DIRS
  15
+        settings.TEMPLATE_DIRS = (
  16
+            os.path.join(
  17
+                os.path.dirname(__file__),
  18
+                'templates'
  19
+            ),
  20
+        )
  21
+
  22
+    def tearDown(self):
  23
+        settings.MIDDLEWARE_CLASSES = self.old_MIDDLEWARE_CLASSES
  24
+        settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS
  25
+
  26
+    def test_view_flatpage(self):
  27
+        "A flatpage can be served through a view, even when the middleware is in use"
  28
+        response = self.client.get('/flatpage_root/flatpage/')
  29
+        self.assertEquals(response.status_code, 200)
  30
+        self.assertContains(response, "<p>Isn't it flat!</p>")
  31
+
  32
+    def test_view_non_existent_flatpage(self):
  33
+        "A non-existent flatpage raises 404 when served through a view, even when the middleware is in use"
  34
+        response = self.client.get('/flatpage_root/no_such_flatpage/')
  35
+        self.assertEquals(response.status_code, 404)
  36
+
  37
+    def test_view_authenticated_flatpage(self):
  38
+        "A flatpage served through a view can require authentication"
  39
+        response = self.client.get('/flatpage_root/sekrit/')
  40
+        self.assertRedirects(response, '/accounts/login/?next=/flatpage_root/sekrit/')
  41
+
  42
+    def test_fallback_flatpage(self):
  43
+        "A flatpage can be served by the fallback middlware"
  44
+        response = self.client.get('/flatpage/')
  45
+        self.assertEquals(response.status_code, 200)
  46
+        self.assertContains(response, "<p>Isn't it flat!</p>")
  47
+
  48
+    def test_fallback_non_existent_flatpage(self):
  49
+        "A non-existent flatpage raises a 404 when served by the fallback middlware"
  50
+        response = self.client.get('/no_such_flatpage/')
  51
+        self.assertEquals(response.status_code, 404)
  52
+
  53
+    def test_fallback_authenticated_flatpage(self):
  54
+        "A flatpage served by the middleware can require authentication"
  55
+        response = self.client.get('/sekrit/')
  56
+        self.assertRedirects(response, '/accounts/login/?next=/sekrit/')
1  django/contrib/flatpages/tests/templates/404.html
... ...
@@ -0,0 +1 @@
  1
+<h1>Oh Noes!</h1>
2  django/contrib/flatpages/tests/templates/flatpages/default.html
... ...
@@ -0,0 +1,2 @@
  1
+<h1>{{ flatpage.title }}</h1>
  2
+<p>{{ flatpage.content }}</p>
8  django/contrib/flatpages/tests/urls.py
... ...
@@ -0,0 +1,8 @@
  1
+from django.conf.urls.defaults import *
  2
+
  3
+# special urls for flatpage test cases
  4
+urlpatterns = patterns('',
  5
+    (r'^flatpage_root', include('django.contrib.flatpages.urls')),
  6
+    (r'^accounts/', include('django.contrib.auth.urls')),
  7
+)
  8
+
50  django/contrib/flatpages/tests/views.py
... ...
@@ -0,0 +1,50 @@
  1
+import os
  2
+from django.conf import settings
  3
+from django.test import TestCase
  4
+
  5
+class FlatpageViewTests(TestCase):
  6
+    fixtures = ['sample_flatpages']
  7
+    urls = 'django.contrib.flatpages.tests.urls'
  8
+
  9
+    def setUp(self):
  10
+        self.old_MIDDLEWARE_CLASSES = settings.MIDDLEWARE_CLASSES
  11
+        flatpage_middleware_class = 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware'
  12
+        if flatpage_middleware_class in settings.MIDDLEWARE_CLASSES:
  13
+            settings.MIDDLEWARE_CLASSES = tuple(m for m in settings.MIDDLEWARE_CLASSES if m != flatpage_middleware_class)
  14
+        self.old_TEMPLATE_DIRS = settings.TEMPLATE_DIRS
  15
+        settings.TEMPLATE_DIRS = (
  16
+            os.path.join(
  17
+                os.path.dirname(__file__),
  18
+                'templates'
  19
+            ),
  20
+        )
  21
+        settings.DEBUG = True
  22
+    def tearDown(self):
  23
+        settings.MIDDLEWARE_CLASSES = self.old_MIDDLEWARE_CLASSES
  24
+        settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS
  25
+
  26
+    def test_view_flatpage(self):
  27
+        "A flatpage can be served through a view"
  28
+        response = self.client.get('/flatpage_root/flatpage/')
  29
+        self.assertEquals(response.status_code, 200)
  30
+        self.assertContains(response, "<p>Isn't it flat!</p>")
  31
+
  32
+    def test_view_non_existent_flatpage(self):
  33
+        "A non-existent flatpage raises 404 when served through a view"
  34
+        response = self.client.get('/flatpage_root/no_such_flatpage/')
  35
+        self.assertEquals(response.status_code, 404)
  36
+
  37
+    def test_view_authenticated_flatpage(self):
  38
+        "A flatpage served through a view can require authentication"
  39
+        response = self.client.get('/flatpage_root/sekrit/')
  40
+        self.assertRedirects(response, '/accounts/login/?next=/flatpage_root/sekrit/')
  41
+
  42
+    def test_fallback_flatpage(self):
  43
+        "A fallback flatpage won't be served if the middleware is disabled"
  44
+        response = self.client.get('/flatpage/')
  45
+        self.assertEquals(response.status_code, 404)
  46
+
  47
+    def test_fallback_non_existent_flatpage(self):
  48
+        "A non-existent flatpage won't be served if the fallback middlware is disabled"
  49
+        response = self.client.get('/no_such_flatpage/')
  50
+        self.assertEquals(response.status_code, 404)
14  django/contrib/flatpages/views.py
@@ -13,10 +13,13 @@
13 13
 # when a 404 is raised, which often means CsrfViewMiddleware.process_view
14 14
 # has not been called even if CsrfViewMiddleware is installed. So we need
15 15
 # to use @csrf_protect, in case the template needs {% csrf_token %}.
16  
-@csrf_protect
  16
+# However, we can't just wrap this view; if no matching flatpage exists,
  17
+# or a redirect is required for authentication, the 404 needs to be returned
  18
+# without any CSRF checks. Therefore, we only
  19
+# CSRF protect the internal implementation.
17 20
 def flatpage(request, url):
18 21
     """
19  
-    Flat page view.
  22
+    Public interface to the flat page view.
20 23
 
21 24
     Models: `flatpages.flatpages`
22 25
     Templates: Uses the template defined by the ``template_name`` field,
@@ -30,6 +33,13 @@ def flatpage(request, url):
30 33
     if not url.startswith('/'):
31 34
         url = "/" + url
32 35
     f = get_object_or_404(FlatPage, url__exact=url, sites__id__exact=settings.SITE_ID)
  36
+    return render_flatpage(request, f)
  37
+
  38
+@csrf_protect
  39
+def render_flatpage(request, f):
  40
+    """
  41
+    Internal interface to the flat page view.
  42
+    """
33 43
     # If registration is required for accessing this page, and the user isn't
34 44
     # logged in, redirect to the login page.
35 45
     if f.registration_required and not request.user.is_authenticated():
0  ...sts/templates/registration/login.html b/django/contrib/flatpages/tests/templates/registration/login.html
No changes.

0 notes on commit 2370a10

Please sign in to comment.
Something went wrong with that request. Please try again.