Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to pass extra context when rendering email templates #82

Merged
merged 2 commits into from May 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/source/overview.rst
Expand Up @@ -415,6 +415,9 @@ Default: ``None``
A raw template string to use for the email body. The render context will
include the generated token in the ``token`` key.

Dictionary sent to :meth:`~django_otp.plugins.otp_email.models.EmailDevice.generate_challenge`
under ``extra_context`` will be also used as context for building the email.

If this and :setting:`OTP_EMAIL_BODY_TEMPLATE_PATH` are not set, we'll render
the template 'otp/email/token.txt', which you'll most likely want to override.

Expand All @@ -428,6 +431,9 @@ Default: ``otp/email/token.txt``
A path string to a template file to use for the email body. The render context
will include the generated token in the ``token`` key.

Dictionary sent to :meth:`~django_otp.plugins.otp_email.models.EmailDevice.generate_challenge`
under ``extra_context`` will be also used as context for building the email.

If this and :setting:`OTP_EMAIL_BODY_TEMPLATE` are not set, we'll render the
template 'otp/email/token.txt', which you'll most likely want to override.

Expand Down
8 changes: 6 additions & 2 deletions src/django_otp/plugins/otp_email/models.py
Expand Up @@ -47,13 +47,17 @@ class EmailDevice(ThrottlingMixin, SideChannelDevice):
def get_throttle_factor(self):
return settings.OTP_EMAIL_THROTTLE_FACTOR

def generate_challenge(self):
def generate_challenge(self, extra_context=None):
"""
Generates a random token and emails it to the user.

:param extra_context: If provided, the dictionary will be used as context,
along with the token, for generating the email. Default is ``None``.
:type extra_context: dict
"""
self.generate_token(valid_secs=settings.OTP_EMAIL_TOKEN_VALIDITY)

context = {'token': self.token}
context = {'token': self.token, **(extra_context or {})}
if settings.OTP_EMAIL_BODY_TEMPLATE:
body = Template(settings.OTP_EMAIL_BODY_TEMPLATE).render(Context(context))
else:
Expand Down
23 changes: 23 additions & 0 deletions src/django_otp/plugins/otp_email/tests.py
Expand Up @@ -132,6 +132,29 @@ def test_settings_template_path(self):
with self.subTest(field='body'):
self.assertEqual(msg.body, "Test template 3: {}\n".format(self.device.token))

@override_settings(
OTP_EMAIL_SENDER="webmaster@example.com",
OTP_EMAIL_SUBJECT="Test subject",
OTP_EMAIL_BODY_TEMPLATE="Test template 4: {{token}} {{foo}} {{bar}}",
)
def test_settings_extra_template_options(self):
extra_context = {"foo": "extra 1", "bar": "extra 2"}
self.device.generate_challenge(extra_context)

self.assertEqual(len(mail.outbox), 1)

msg = mail.outbox[0]

with self.subTest(field='from_email'):
self.assertEqual(msg.from_email, "webmaster@example.com")
with self.subTest(field='subject'):
self.assertEqual(msg.subject, "Test subject")
with self.subTest(field='body'):
self.assertEqual(
msg.body,
"Test template 4: {} {} {}".format(self.device.token, extra_context["foo"], extra_context["bar"])
)

def test_alternative_email(self):
self.device.email = 'alice2@example.com'
self.device.save()
Expand Down