14
14
15
15
from .webhook_cases import WebhookTestCase , WebhookBasicAuthTestsMixin
16
16
17
- TEST_API_KEY = 'TEST_API_KEY '
17
+ TEST_WEBHOOK_SIGNING_KEY = 'TEST_WEBHOOK_SIGNING_KEY '
18
18
19
19
20
- def mailgun_signature (timestamp , token , api_key ):
20
+ def mailgun_signature (timestamp , token , webhook_signing_key ):
21
21
"""Generates a Mailgun webhook signature"""
22
22
# https://documentation.mailgun.com/en/latest/user_manual.html#securing-webhooks
23
23
return hmac .new (
24
- key = api_key .encode ('ascii' ),
24
+ key = webhook_signing_key .encode ('ascii' ),
25
25
msg = '{timestamp}{token}' .format (timestamp = timestamp , token = token ).encode ('ascii' ),
26
26
digestmod = hashlib .sha256 ).hexdigest ()
27
27
28
28
29
- def mailgun_sign_payload (data , api_key = TEST_API_KEY ):
29
+ def mailgun_sign_payload (data , webhook_signing_key = TEST_WEBHOOK_SIGNING_KEY ):
30
30
"""Add or complete Mailgun webhook signature block in data dict"""
31
31
# Modifies the dict in place
32
32
event_data = data .get ('event-data' , {})
33
33
signature = data .setdefault ('signature' , {})
34
34
token = signature .setdefault ('token' , '1234567890abcdef1234567890abcdef' )
35
35
timestamp = signature .setdefault ('timestamp' ,
36
36
str (int (float (event_data .get ('timestamp' , '1234567890.123' )))))
37
- signature ['signature' ] = mailgun_signature (timestamp , token , api_key = api_key )
37
+ signature ['signature' ] = mailgun_signature (timestamp , token , webhook_signing_key = webhook_signing_key )
38
38
return data
39
39
40
40
41
- def mailgun_sign_legacy_payload (data , api_key = TEST_API_KEY ):
41
+ def mailgun_sign_legacy_payload (data , webhook_signing_key = TEST_WEBHOOK_SIGNING_KEY ):
42
42
"""Add a Mailgun webhook signature to data dict"""
43
43
# Modifies the dict in place
44
44
data .setdefault ('timestamp' , '1234567890' )
45
45
data .setdefault ('token' , '1234567890abcdef1234567890abcdef' )
46
- data ['signature' ] = mailgun_signature (data ['timestamp' ], data ['token' ], api_key = api_key )
46
+ data ['signature' ] = mailgun_signature (data ['timestamp' ], data ['token' ], webhook_signing_key = webhook_signing_key )
47
47
return data
48
48
49
49
@@ -61,14 +61,44 @@ def querydict_to_postdict(qd):
61
61
62
62
@tag ('mailgun' )
63
63
class MailgunWebhookSettingsTestCase (WebhookTestCase ):
64
- def test_requires_api_key (self ):
65
- with self .assertRaises (ImproperlyConfigured ):
64
+ def test_requires_webhook_signing_key (self ):
65
+ with self .assertRaisesMessage (ImproperlyConfigured , "MAILGUN_WEBHOOK_SIGNING_KEY" ):
66
66
self .client .post ('/anymail/mailgun/tracking/' , content_type = "application/json" ,
67
67
data = json .dumps (mailgun_sign_payload ({'event-data' : {'event' : 'delivered' }})))
68
68
69
+ @override_settings (
70
+ ANYMAIL_MAILGUN_API_KEY = 'TEST_API_KEY' ,
71
+ ANYMAIL_MAILGUN_WEBHOOK_SIGNING_KEY = 'TEST_WEBHOOK_SIGNING_KEY' ,
72
+ )
73
+ def test_webhook_signing_is_different_from_api_key (self ):
74
+ """Webhooks should use MAILGUN_WEBHOOK_SIGNING_KEY, not MAILGUN_API_KEY, if both provided"""
75
+ payload = json .dumps (mailgun_sign_payload ({'event-data' : {'event' : 'delivered' }},
76
+ webhook_signing_key = 'TEST_WEBHOOK_SIGNING_KEY' ))
77
+ response = self .client .post ('/anymail/mailgun/tracking/' , content_type = "application/json" , data = payload )
78
+ self .assertEqual (response .status_code , 200 )
79
+
80
+ @override_settings (ANYMAIL_MAILGUN_API_KEY = 'TEST_API_KEY' )
81
+ def test_defaults_webhook_signing_to_api_key (self ):
82
+ """Webhooks should default to MAILGUN_API_KEY if MAILGUN_WEBHOOK_SIGNING_KEY not provided"""
83
+ payload = json .dumps (mailgun_sign_payload ({'event-data' : {'event' : 'delivered' }},
84
+ webhook_signing_key = 'TEST_API_KEY' ))
85
+ response = self .client .post ('/anymail/mailgun/tracking/' , content_type = "application/json" , data = payload )
86
+ self .assertEqual (response .status_code , 200 )
87
+
88
+ def test_webhook_signing_key_view_params (self ):
89
+ """Webhook signing key can be provided as a view param"""
90
+ view = MailgunTrackingWebhookView .as_view (webhook_signing_key = 'VIEW_SIGNING_KEY' )
91
+ view_instance = view .view_class (** view .view_initkwargs )
92
+ self .assertEqual (view_instance .webhook_signing_key , b'VIEW_SIGNING_KEY' )
93
+
94
+ # Can also use `api_key` param for backwards compatiblity with earlier Anymail versions
95
+ view = MailgunTrackingWebhookView .as_view (api_key = 'VIEW_API_KEY' )
96
+ view_instance = view .view_class (** view .view_initkwargs )
97
+ self .assertEqual (view_instance .webhook_signing_key , b'VIEW_API_KEY' )
98
+
69
99
70
100
@tag ('mailgun' )
71
- @override_settings (ANYMAIL_MAILGUN_API_KEY = TEST_API_KEY )
101
+ @override_settings (ANYMAIL_MAILGUN_WEBHOOK_SIGNING_KEY = TEST_WEBHOOK_SIGNING_KEY )
72
102
class MailgunWebhookSecurityTestCase (WebhookTestCase , WebhookBasicAuthTestsMixin ):
73
103
should_warn_if_no_auth = False # because we check webhook signature
74
104
@@ -90,14 +120,14 @@ def test_verifies_missing_signature(self):
90
120
91
121
def test_verifies_bad_signature (self ):
92
122
data = mailgun_sign_payload ({'event-data' : {'event' : 'delivered' }},
93
- api_key = "wrong API key" )
123
+ webhook_signing_key = "wrong signing key" )
94
124
response = self .client .post ('/anymail/mailgun/tracking/' , content_type = "application/json" ,
95
125
data = json .dumps (data ))
96
126
self .assertEqual (response .status_code , 400 )
97
127
98
128
99
129
@tag ('mailgun' )
100
- @override_settings (ANYMAIL_MAILGUN_API_KEY = TEST_API_KEY )
130
+ @override_settings (ANYMAIL_MAILGUN_WEBHOOK_SIGNING_KEY = TEST_WEBHOOK_SIGNING_KEY )
101
131
class MailgunTestCase (WebhookTestCase ):
102
132
# Tests for Mailgun's new webhooks (announced 2018-06-29)
103
133
@@ -449,7 +479,7 @@ def test_clicked_event(self):
449
479
450
480
451
481
@tag ('mailgun' )
452
- @override_settings (ANYMAIL_MAILGUN_API_KEY = TEST_API_KEY )
482
+ @override_settings (ANYMAIL_MAILGUN_WEBHOOK_SIGNING_KEY = TEST_WEBHOOK_SIGNING_KEY )
453
483
class MailgunLegacyTestCase (WebhookTestCase ):
454
484
# Tests for Mailgun's "legacy" webhooks
455
485
# (which were the only webhooks available prior to Anymail 4.0)
0 commit comments