diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 11d2d04..f16c903 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -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. @@ -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. diff --git a/src/django_otp/plugins/otp_email/models.py b/src/django_otp/plugins/otp_email/models.py index 1762891..e0705c8 100644 --- a/src/django_otp/plugins/otp_email/models.py +++ b/src/django_otp/plugins/otp_email/models.py @@ -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: diff --git a/src/django_otp/plugins/otp_email/tests.py b/src/django_otp/plugins/otp_email/tests.py index 520d8e2..f7f3435 100644 --- a/src/django_otp/plugins/otp_email/tests.py +++ b/src/django_otp/plugins/otp_email/tests.py @@ -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()