stages/email: allow overriding of destination email in plan context
closes #2445 Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
0c0b9ca84a
commit
9ad4c736f1
|
@ -23,6 +23,7 @@ from authentik.stages.email.utils import TemplateEmailMessage
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
PLAN_CONTEXT_EMAIL_SENT = "email_sent"
|
PLAN_CONTEXT_EMAIL_SENT = "email_sent"
|
||||||
|
PLAN_CONTEXT_EMAIL_OVERRIDE = "email"
|
||||||
|
|
||||||
|
|
||||||
class EmailChallenge(Challenge):
|
class EmailChallenge(Challenge):
|
||||||
|
@ -83,13 +84,16 @@ class EmailStageView(ChallengeStageView):
|
||||||
"""Helper function that sends the actual email. Implies that you've
|
"""Helper function that sends the actual email. Implies that you've
|
||||||
already checked that there is a pending user."""
|
already checked that there is a pending user."""
|
||||||
pending_user = self.executor.plan.context[PLAN_CONTEXT_PENDING_USER]
|
pending_user = self.executor.plan.context[PLAN_CONTEXT_PENDING_USER]
|
||||||
|
email = self.executor.plan.context.get(PLAN_CONTEXT_EMAIL_OVERRIDE, None)
|
||||||
|
if not email:
|
||||||
|
email = pending_user.email
|
||||||
current_stage: EmailStage = self.executor.current_stage
|
current_stage: EmailStage = self.executor.current_stage
|
||||||
token = self.get_token()
|
token = self.get_token()
|
||||||
# Send mail to user
|
# Send mail to user
|
||||||
message = TemplateEmailMessage(
|
message = TemplateEmailMessage(
|
||||||
subject=_(current_stage.subject),
|
subject=_(current_stage.subject),
|
||||||
template_name=current_stage.template,
|
template_name=current_stage.template,
|
||||||
to=[pending_user.email],
|
to=[email],
|
||||||
template_context={
|
template_context={
|
||||||
"url": self.get_full_url(**{QS_KEY_TOKEN: token.key}),
|
"url": self.get_full_url(**{QS_KEY_TOKEN: token.key}),
|
||||||
"user": pending_user,
|
"user": pending_user,
|
||||||
|
|
|
@ -14,7 +14,7 @@ from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
||||||
from authentik.flows.tests import FlowTestCase
|
from authentik.flows.tests import FlowTestCase
|
||||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||||
from authentik.stages.email.models import EmailStage
|
from authentik.stages.email.models import EmailStage
|
||||||
from authentik.stages.email.stage import QS_KEY_TOKEN
|
from authentik.stages.email.stage import PLAN_CONTEXT_EMAIL_OVERRIDE, QS_KEY_TOKEN
|
||||||
|
|
||||||
|
|
||||||
class TestEmailStage(FlowTestCase):
|
class TestEmailStage(FlowTestCase):
|
||||||
|
@ -75,6 +75,27 @@ class TestEmailStage(FlowTestCase):
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEqual(len(mail.outbox), 1)
|
self.assertEqual(len(mail.outbox), 1)
|
||||||
self.assertEqual(mail.outbox[0].subject, "authentik")
|
self.assertEqual(mail.outbox[0].subject, "authentik")
|
||||||
|
self.assertEqual(mail.outbox[0].to, ["test@beryju.org"])
|
||||||
|
|
||||||
|
def test_pending_user_override(self):
|
||||||
|
"""Test with pending user (override to)"""
|
||||||
|
plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()])
|
||||||
|
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||||
|
plan.context[PLAN_CONTEXT_EMAIL_OVERRIDE] = "foo@bar.baz"
|
||||||
|
session = self.client.session
|
||||||
|
session[SESSION_KEY_PLAN] = plan
|
||||||
|
session.save()
|
||||||
|
|
||||||
|
url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||||
|
with patch(
|
||||||
|
"authentik.stages.email.models.EmailStage.backend_class",
|
||||||
|
PropertyMock(return_value=EmailBackend),
|
||||||
|
):
|
||||||
|
response = self.client.post(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(len(mail.outbox), 1)
|
||||||
|
self.assertEqual(mail.outbox[0].subject, "authentik")
|
||||||
|
self.assertEqual(mail.outbox[0].to, ["foo@bar.baz"])
|
||||||
|
|
||||||
def test_use_global_settings(self):
|
def test_use_global_settings(self):
|
||||||
"""Test use_global_settings"""
|
"""Test use_global_settings"""
|
||||||
|
|
|
@ -6,6 +6,21 @@ This stage can be used for email verification. authentik's background worker wil
|
||||||
|
|
||||||
![](email_recovery.png)
|
![](email_recovery.png)
|
||||||
|
|
||||||
|
## Behaviour
|
||||||
|
|
||||||
|
By default, the email is sent to the currently pending user. To override this, you can set `email` in the plan's context to another email address, which will override the user's email address (the user won't be changed).
|
||||||
|
|
||||||
|
For example, create this expression policy and bind it to the email stage:
|
||||||
|
|
||||||
|
```python
|
||||||
|
request.context["email"] = "foo@bar.baz"
|
||||||
|
# Or get it from a prompt
|
||||||
|
# request.context["email"] = request.context["prompt_data"]["email"]
|
||||||
|
# Or another user attribute
|
||||||
|
# request.context["email"] = request.context["pending_user"].attributes.get("otherEmail")
|
||||||
|
return True
|
||||||
|
```
|
||||||
|
|
||||||
## Custom Templates
|
## Custom Templates
|
||||||
|
|
||||||
You can also use custom email templates, to use your own design or layout.
|
You can also use custom email templates, to use your own design or layout.
|
||||||
|
|
Reference in New Issue