Docs: document DEBUG_API_REQUESTS setting

(And add a system check to warn about its use in production deployment.)
This commit is contained in:
Mike Edmunds
2019-12-15 14:23:03 -08:00
committed by GitHub
parent 95080bfeb9
commit 0c66e1eed9
5 changed files with 63 additions and 2 deletions

View File

@@ -1,7 +1,7 @@
from django.apps import AppConfig
from django.core import checks
from .checks import check_deprecated_settings
from .checks import check_deprecated_settings, check_insecure_settings
class AnymailBaseConfig(AppConfig):
@@ -10,3 +10,4 @@ class AnymailBaseConfig(AppConfig):
def ready(self):
checks.register(check_deprecated_settings)
checks.register(check_insecure_settings)

View File

@@ -1,6 +1,8 @@
from django.conf import settings
from django.core import checks
from anymail.utils import get_anymail_setting
def check_deprecated_settings(app_configs, **kwargs):
errors = []
@@ -24,3 +26,18 @@ def check_deprecated_settings(app_configs, **kwargs):
))
return errors
def check_insecure_settings(app_configs, **kwargs):
errors = []
# anymail.W002: DEBUG_API_REQUESTS can leak private information
if get_anymail_setting("debug_api_requests", default=False) and not settings.DEBUG:
errors.append(checks.Warning(
"You have enabled the ANYMAIL setting DEBUG_API_REQUESTS, which can "
"leak API keys and other sensitive data into logs or the console.",
hint="You should not use DEBUG_API_REQUESTS in production deployment.",
id="anymail.W002",
))
return errors

View File

@@ -44,6 +44,14 @@ often help you pinpoint the problem...
other than Anymail. And you can look through the :setting:`EMAIL_FILE_PATH`
file contents afterward to see if you're generating the email you want.
**Examine the raw API communication**
Sometimes you just want to see exactly what Anymail is telling your ESP to do
and how your ESP is responding. In a dev environment, enable the Anymail setting
:setting:`DEBUG_API_REQUESTS <ANYMAIL_DEBUG_API_REQUESTS>`
to show the raw HTTP requests and responses from (most) ESP APIs. (This is not
recommended in production, as it can leak sensitive data into your logs.)
.. _contact:
.. _support:

View File

@@ -299,3 +299,25 @@ For Requests-based Anymail backends, the timeout value used for all API calls to
The default is 30 seconds. You can set to a single float, a 2-tuple of floats for
separate connection and read timeouts, or `None` to disable timeouts (not recommended).
See :ref:`requests:timeouts` in the Requests docs for more information.
.. setting:: ANYMAIL_DEBUG_API_REQUESTS
.. rubric:: DEBUG_API_REQUESTS
.. versionadded:: 4.3
When set to `True`, outputs the raw API communication with the ESP, to assist in
debugging. Each HTTP request and ESP response is dumped to :data:`sys.stdout` once
the response is received.
.. caution::
Do not enable :setting:`!DEBUG_API_REQUESTS` in production deployments. The debug
output will include your API keys, email addresses, and other sensitive data
that you generally don't want to capture in server logs or reveal on the console.
:setting:`!DEBUG_API_REQUESTS` only applies to sending email through Requests-based
Anymail backends. For other backends, there may be similar debugging facilities
available in the ESP's API wrapper package (e.g., ``boto3.set_stream_logger`` for
Amazon SES).

View File

@@ -2,7 +2,7 @@ from django.core import checks
from django.test import SimpleTestCase
from django.test.utils import override_settings
from anymail.checks import check_deprecated_settings
from anymail.checks import check_deprecated_settings, check_insecure_settings
from .utils import AnymailTestMixin
@@ -25,3 +25,16 @@ class DeprecatedSettingsTests(SimpleTestCase, AnymailTestMixin):
hint="You must update your settings.py.",
id="anymail.E001",
)])
class InsecureSettingsTests(SimpleTestCase, AnymailTestMixin):
@override_settings(ANYMAIL={"DEBUG_API_REQUESTS": True})
def test_debug_api_requests_deployed(self):
errors = check_insecure_settings(None)
self.assertEqual(len(errors), 1)
self.assertEqual(errors[0].id, "anymail.W002")
@override_settings(ANYMAIL={"DEBUG_API_REQUESTS": True}, DEBUG=True)
def test_debug_api_requests_debug(self):
errors = check_insecure_settings(None)
self.assertEqual(len(errors), 0) # no warning in DEBUG (non-production) config