Repository: encode/django-rest-framework

Issue:
CSRFCheck fails from csrf cookie with django 1.11
## Checklist

- [x] I have verified that that issue exists against the `master` branch of Django REST framework.
- [x] I have searched for similar issues in both open and closed tickets and cannot find a duplicate.
- [x] This is not a usage question. (Those should be directed to the [discussion group](https://groups.google.com/forum/#!forum/django-rest-framework) instead.)
- [x] This cannot be dealt with as a third party library. (We prefer new functionality to be [in the form of third party libraries](http://www.django-rest-framework.org/topics/third-party-resources/#about-third-party-packages) where possible.)
- [x] I have reduced the issue to the simplest possible case.
- [x] I have included a failing test as a pull request. (If you are unable to do so we can still accept the issue.)

## Steps to reproduce

* use `Django~=1.11.6` (the issue does not happen with 1.10 or earlier 1.11s. I haven't tested with 2.x)
* use session auth and have a valid `csrf_token` cookie
* do a POST/PUT request using a DRF API

## Expected behavior

Get a successful response.

## Actual behavior

We get this returned:
```
{"detail":"CSRF Failed: CSRF cookie not set."}
```


## What's going on

It looks like `SessionAuthentication.enforce_csrf` uses `CSRFCheck` which is a thin subclass of `CSRFViewMiddleware`.

In django 1.11.6 this change was made in `CSRFViewMiddleware`: https://github.com/django/django/commit/42847327d1277451ee7a61716f7b9f62f50ecbdc

([full code](https://github.com/django/django/blob/stable/1.11.x/django/middleware/csrf.py#L289-L294) for django 1.11.x in that file)

It is clear that the `process_view` method expects `request.META['CSRF_COOKIE']` to be populated. But it is now not done in the body of `process_view` but earlier in the middleware processing, in the `process_request` call.

This works fine when called as a middleware, but when `process_view` is called independently/directly as DRF does, this breaks and rejects the request since it couldn't find the cookie.

This could be considered a Django bug, though in normal django usage it works okay because `process_request` is called before `process_view`. Maybe DRF should call both?


Discussion:
Any word on this, or pointers to extra information I could supply that would be useful? Thanks

Resolving patch:
diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py
--- a/rest_framework/authentication.py
+++ b/rest_framework/authentication.py
@@ -135,7 +135,10 @@ def enforce_csrf(self, request):
         """
         Enforce CSRF validation for session based authentication.
         """
-        reason = CSRFCheck().process_view(request, None, (), {})
+        check = CSRFCheck()
+        # populates request.META['CSRF_COOKIE'], which is used in process_view()
+        check.process_request(request)
+        reason = check.process_view(request, None, (), {})
         if reason:
             # CSRF failed, bail with explicit error message
             raise exceptions.PermissionDenied('CSRF Failed: %s' % reason)


Test patch:
diff --git a/tests/test_authentication.py b/tests/test_authentication.py
--- a/tests/test_authentication.py
+++ b/tests/test_authentication.py
@@ -5,6 +5,7 @@
 import base64
 
 import pytest
+from django.conf import settings
 from django.conf.urls import include, url
 from django.contrib.auth.models import User
 from django.db import models
@@ -202,6 +203,26 @@ def test_post_form_session_auth_failing_csrf(self):
         response = self.csrf_client.post('/session/', {'example': 'example'})
         assert response.status_code == status.HTTP_403_FORBIDDEN
 
+    def test_post_form_session_auth_passing_csrf(self):
+        """
+        Ensure POSTing form over session authentication with CSRF token succeeds.
+        Regression test for #6088
+        """
+        from django.middleware.csrf import _get_new_csrf_token
+
+        self.csrf_client.login(username=self.username, password=self.password)
+
+        # Set the csrf_token cookie so that CsrfViewMiddleware._get_token() works
+        token = _get_new_csrf_token()
+        self.csrf_client.cookies[settings.CSRF_COOKIE_NAME] = token
+
+        # Post the token matching the cookie value
+        response = self.csrf_client.post('/session/', {
+            'example': 'example',
+            'csrfmiddlewaretoken': token,
+        })
+        assert response.status_code == status.HTTP_200_OK
+
     def test_post_form_session_auth_passing(self):
         """
         Ensure POSTing form over session authentication with logged in