mirror of
https://github.com/pacnpal/django-anymail.git
synced 2025-12-20 11:51:05 -05:00
Webhooks: fix 403 Forbidden errors (csrf check)
* 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
This commit is contained in:
@@ -104,11 +104,14 @@ class AnymailBaseWebhookView(AnymailBasicAuthMixin, View):
|
|||||||
|
|
||||||
http_method_names = ["post", "head", "options"]
|
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):
|
def head(self, request, *args, **kwargs):
|
||||||
# Some ESPs verify the webhook with a HEAD request at configuration time
|
# Some ESPs verify the webhook with a HEAD request at configuration time
|
||||||
return HttpResponse()
|
return HttpResponse()
|
||||||
|
|
||||||
@method_decorator(csrf_exempt)
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
# Normal Django exception handling will do the right thing:
|
# Normal Django exception handling will do the right thing:
|
||||||
# - AnymailWebhookValidationFailure will turn into an HTTP 400 response
|
# - AnymailWebhookValidationFailure will turn into an HTTP 400 response
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import warnings
|
|||||||
from base64 import b64decode
|
from base64 import b64decode
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
from django.test import Client
|
||||||
|
|
||||||
|
|
||||||
def decode_att(att):
|
def decode_att(att):
|
||||||
"""Returns the original data from base64-encoded attachment content"""
|
"""Returns the original data from base64-encoded attachment content"""
|
||||||
@@ -142,3 +144,13 @@ class _AssertWarnsContext(object):
|
|||||||
self.expected_regex.pattern, str(first_matching)))
|
self.expected_regex.pattern, str(first_matching)))
|
||||||
self._raiseFailure("{} not triggered".format(exc_name))
|
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)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from mock import create_autospec, ANY
|
|||||||
from anymail.exceptions import AnymailInsecureWebhookWarning
|
from anymail.exceptions import AnymailInsecureWebhookWarning
|
||||||
from anymail.signals import tracking, inbound
|
from anymail.signals import tracking, inbound
|
||||||
|
|
||||||
from .utils import AnymailTestMixin
|
from .utils import AnymailTestMixin, ClientWithCsrfChecks
|
||||||
|
|
||||||
|
|
||||||
def event_handler(sender, event, esp_name, **kwargs):
|
def event_handler(sender, event, esp_name, **kwargs):
|
||||||
@@ -22,6 +22,8 @@ class WebhookTestCase(AnymailTestMixin, SimpleTestCase):
|
|||||||
- sets up basic auth by default (since most ESP webhooks warn if it's not enabled)
|
- sets up basic auth by default (since most ESP webhooks warn if it's not enabled)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
client_class = ClientWithCsrfChecks
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(WebhookTestCase, self).setUp()
|
super(WebhookTestCase, self).setUp()
|
||||||
# Use correct basic auth by default (individual tests can override):
|
# Use correct basic auth by default (individual tests can override):
|
||||||
|
|||||||
Reference in New Issue
Block a user