Compatibility with earlier Python 2.7 versions

Compatibility with Python 2.7 versions older than 2.7.7

* Use Django's constant_time_compare method
* Include sparkpost in test requirements
* Don't use non-public `EnvironmentVarGuard` in tests

Fixes #41
This commit is contained in:
Seb Bacon
2016-11-01 18:24:51 +00:00
committed by Mike Edmunds
parent d54d7ecff5
commit f0589e3338
4 changed files with 9 additions and 12 deletions

View File

@@ -3,6 +3,7 @@ from datetime import datetime
import hashlib import hashlib
import hmac import hmac
from django.utils.crypto import constant_time_compare
from django.utils.timezone import utc from django.utils.timezone import utc
from .base import AnymailBaseWebhookView from .base import AnymailBaseWebhookView
@@ -34,7 +35,7 @@ class MailgunBaseWebhookView(AnymailBaseWebhookView):
raise AnymailWebhookValidationFailure("Mailgun webhook called without required security fields") raise AnymailWebhookValidationFailure("Mailgun webhook called without required security fields")
expected_signature = hmac.new(key=self.api_key, msg='{}{}'.format(timestamp, token).encode('ascii'), expected_signature = hmac.new(key=self.api_key, msg='{}{}'.format(timestamp, token).encode('ascii'),
digestmod=hashlib.sha256).hexdigest() digestmod=hashlib.sha256).hexdigest()
if not hmac.compare_digest(signature, expected_signature): if not constant_time_compare(signature, expected_signature):
raise AnymailWebhookValidationFailure("Mailgun webhook called with incorrect signature") raise AnymailWebhookValidationFailure("Mailgun webhook called with incorrect signature")
def parse_events(self, request): def parse_events(self, request):

View File

@@ -4,6 +4,7 @@ from datetime import datetime
import hashlib import hashlib
import hmac import hmac
from base64 import b64encode from base64 import b64encode
from django.utils.crypto import constant_time_compare
from django.utils.timezone import utc from django.utils.timezone import utc
from .base import AnymailBaseWebhookView from .base import AnymailBaseWebhookView
@@ -44,7 +45,7 @@ class MandrillSignatureMixin(object):
expected_signature = b64encode(hmac.new(key=self.webhook_key, msg=signed_data.encode('utf-8'), expected_signature = b64encode(hmac.new(key=self.webhook_key, msg=signed_data.encode('utf-8'),
digestmod=hashlib.sha1).digest()) digestmod=hashlib.sha1).digest())
if not hmac.compare_digest(signature, expected_signature): if not constant_time_compare(signature, expected_signature):
raise AnymailWebhookValidationFailure("Mandrill webhook called with incorrect signature") raise AnymailWebhookValidationFailure("Mandrill webhook called with incorrect signature")

View File

@@ -47,7 +47,7 @@ setup(
}, },
include_package_data=True, include_package_data=True,
test_suite="runtests.runtests", test_suite="runtests.runtests",
tests_require=["mock"], tests_require=["mock", "sparkpost"],
classifiers=[ classifiers=[
"Development Status :: 2 - Pre-Alpha", "Development Status :: 2 - Pre-Alpha",
"Programming Language :: Python", "Programming Language :: Python",

View File

@@ -3,6 +3,7 @@
from datetime import datetime, date from datetime import datetime, date
from email.mime.base import MIMEBase from email.mime.base import MIMEBase
from email.mime.image import MIMEImage from email.mime.image import MIMEImage
import os
import requests import requests
import six import six
@@ -17,13 +18,6 @@ from anymail.exceptions import (AnymailAPIError, AnymailUnsupportedFeature, Anym
AnymailConfigurationError) AnymailConfigurationError)
from anymail.message import attach_inline_image_file from anymail.message import attach_inline_image_file
try:
# noinspection PyUnresolvedReferences
from test.support import EnvironmentVarGuard # python3
except ImportError:
# noinspection PyUnresolvedReferences
from test.test_support import EnvironmentVarGuard # python2
from .utils import AnymailTestMixin, decode_att, SAMPLE_IMAGE_FILENAME, sample_image_path, sample_image_content from .utils import AnymailTestMixin, decode_att, SAMPLE_IMAGE_FILENAME, sample_image_path, sample_image_content
@@ -579,8 +573,9 @@ class SparkPostBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin
def test_api_key_in_env(self): def test_api_key_in_env(self):
"""SparkPost package allows API key in env var; make sure Anymail works with that""" """SparkPost package allows API key in env var; make sure Anymail works with that"""
with EnvironmentVarGuard() as env: with patch.dict(
env['SPARKPOST_API_KEY'] = 'key_from_environment' os.environ,
{'SPARKPOST_API_KEY': 'key_from_environment'}):
conn = mail.get_connection() conn = mail.get_connection()
# Poke into implementation details to verify: # Poke into implementation details to verify:
self.assertIsNone(conn.api_key) # Anymail prop self.assertIsNone(conn.api_key) # Anymail prop