Skip to content

Commit

Permalink
Webhooks: fix 403 Forbidden errors (csrf check)
Browse files Browse the repository at this point in the history
* csrf_exempt must be applied to View.dispatch,
  not View.post.

* In base WebhookTestCase, enable Django test Client
  enforce_csrf_checks. (Test Client by default disables
  CSRF protection.)

Closes #19
  • Loading branch information
medmunds committed May 31, 2016
1 parent 34af81a commit af0e36a
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 2 deletions.
5 changes: 4 additions & 1 deletion anymail/webhooks/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,14 @@ def parse_events(self, request):

http_method_names = ["post", "head", "options"]

@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super(AnymailBaseWebhookView, self).dispatch(request, *args, **kwargs)

def head(self, request, *args, **kwargs):
# Some ESPs verify the webhook with a HEAD request at configuration time
return HttpResponse()

@method_decorator(csrf_exempt)
def post(self, request, *args, **kwargs):
# Normal Django exception handling will do the right thing:
# - AnymailWebhookValidationFailure will turn into an HTTP 400 response
Expand Down
12 changes: 12 additions & 0 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from base64 import b64decode
from contextlib import contextmanager

from django.test import Client


def decode_att(att):
"""Returns the original data from base64-encoded attachment content"""
Expand Down Expand Up @@ -142,3 +144,13 @@ def __exit__(self, exc_type, exc_value, tb):
self.expected_regex.pattern, str(first_matching)))
self._raiseFailure("{} not triggered".format(exc_name))


class ClientWithCsrfChecks(Client):
"""Django test Client that enforces CSRF checks
https://docs.djangoproject.com/en/1.9/ref/csrf/#testing
"""

def __init__(self, **defaults):
super(ClientWithCsrfChecks, self).__init__(
enforce_csrf_checks=True, **defaults)
4 changes: 3 additions & 1 deletion tests/webhook_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from anymail.exceptions import AnymailInsecureWebhookWarning
from anymail.signals import tracking, inbound

from .utils import AnymailTestMixin
from .utils import AnymailTestMixin, ClientWithCsrfChecks


def event_handler(sender, event, esp_name, **kwargs):
Expand All @@ -22,6 +22,8 @@ class WebhookTestCase(AnymailTestMixin, SimpleTestCase):
- sets up basic auth by default (since most ESP webhooks warn if it's not enabled)
"""

client_class = ClientWithCsrfChecks

def setUp(self):
super(WebhookTestCase, self).setUp()
# Use correct basic auth by default (individual tests can override):
Expand Down

0 comments on commit af0e36a

Please sign in to comment.