Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 182fc19

Browse files
[2/2] Allow homeservers to send registration emails | Accepting the verification (#5940)
Fixes: #5751 Fixes: #5928 #5835 allowed Synapse to send registration emails to the user. Now we need to accept them and have it succeed the `m.login.email.identity` registration flow step. `account_threepid_handler` will also be switched from a `str` in the config file to a dictionary which contains entries for `msisdn` and `email`, each with their own `str`. This will let people use an external server to handle `msisdn` registration and password reset requests, while using Synapse for email-based things. And the `password_servlet` hack that was introduced in https://github.com/matrix-org/synapse/pull/5377/files#diff-b8464485d36f6f87caee3f4d82524213R189 to distinguish a registration call from a password reset call will be removed.
1 parent 891afb5 commit 182fc19

File tree

7 files changed

+105
-93
lines changed

7 files changed

+105
-93
lines changed

changelog.d/5940.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add the ability to send registration emails from the homeserver rather than delegating to an identity server.

synapse/handlers/account_validity.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
class AccountValidityHandler(object):
3939
def __init__(self, hs):
4040
self.hs = hs
41+
self.config = hs.config
4142
self.store = self.hs.get_datastore()
4243
self.sendmail = self.hs.get_sendmail()
4344
self.clock = self.hs.get_clock()
@@ -62,9 +63,14 @@ def __init__(self, hs):
6263
self._raw_from = email.utils.parseaddr(self._from_string)[1]
6364

6465
self._template_html, self._template_text = load_jinja2_templates(
65-
config=self.hs.config,
66-
template_html_name=self.hs.config.email_expiry_template_html,
67-
template_text_name=self.hs.config.email_expiry_template_text,
66+
self.config.email_template_dir,
67+
[
68+
self.config.email_expiry_template_html,
69+
self.config.email_expiry_template_text,
70+
],
71+
apply_format_ts_filter=True,
72+
apply_mxc_to_http_filter=True,
73+
public_baseurl=self.config.public_baseurl,
6874
)
6975

7076
# Check the renewal emails to send and send them every 30min.

synapse/handlers/auth.py

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ def validate_user_via_ui_auth(self, requester, request_body, clientip):
159159
return params
160160

161161
@defer.inlineCallbacks
162-
def check_auth(self, flows, clientdict, clientip, password_servlet=False):
162+
def check_auth(self, flows, clientdict, clientip):
163163
"""
164164
Takes a dictionary sent by the client in the login / registration
165165
protocol and handles the User-Interactive Auth flow.
@@ -183,16 +183,6 @@ def check_auth(self, flows, clientdict, clientip, password_servlet=False):
183183
184184
clientip (str): The IP address of the client.
185185
186-
password_servlet (bool): Whether the request originated from
187-
PasswordRestServlet.
188-
XXX: This is a temporary hack to distinguish between checking
189-
for threepid validations locally (in the case of password
190-
resets) and using the identity server (in the case of binding
191-
a 3PID during registration). Once we start using the
192-
homeserver for both tasks, this distinction will no longer be
193-
necessary.
194-
195-
196186
Returns:
197187
defer.Deferred[dict, dict, str]: a deferred tuple of
198188
(creds, params, session_id).
@@ -248,9 +238,7 @@ def check_auth(self, flows, clientdict, clientip, password_servlet=False):
248238
if "type" in authdict:
249239
login_type = authdict["type"]
250240
try:
251-
result = yield self._check_auth_dict(
252-
authdict, clientip, password_servlet=password_servlet
253-
)
241+
result = yield self._check_auth_dict(authdict, clientip)
254242
if result:
255243
creds[login_type] = result
256244
self._save_session(session)
@@ -357,7 +345,7 @@ def get_session_data(self, session_id, key, default=None):
357345
return sess.setdefault("serverdict", {}).get(key, default)
358346

359347
@defer.inlineCallbacks
360-
def _check_auth_dict(self, authdict, clientip, password_servlet=False):
348+
def _check_auth_dict(self, authdict, clientip):
361349
"""Attempt to validate the auth dict provided by a client
362350
363351
Args:
@@ -375,11 +363,7 @@ def _check_auth_dict(self, authdict, clientip, password_servlet=False):
375363
login_type = authdict["type"]
376364
checker = self.checkers.get(login_type)
377365
if checker is not None:
378-
# XXX: Temporary workaround for having Synapse handle password resets
379-
# See AuthHandler.check_auth for further details
380-
res = yield checker(
381-
authdict, clientip=clientip, password_servlet=password_servlet
382-
)
366+
res = yield checker(authdict, clientip=clientip)
383367
return res
384368

385369
# build a v1-login-style dict out of the authdict and fall back to the
@@ -450,7 +434,7 @@ def _check_terms_auth(self, authdict, **kwargs):
450434
return defer.succeed(True)
451435

452436
@defer.inlineCallbacks
453-
def _check_threepid(self, medium, authdict, password_servlet=False, **kwargs):
437+
def _check_threepid(self, medium, authdict, **kwargs):
454438
if "threepid_creds" not in authdict:
455439
raise LoginError(400, "Missing threepid_creds", Codes.MISSING_PARAM)
456440

@@ -459,10 +443,7 @@ def _check_threepid(self, medium, authdict, password_servlet=False, **kwargs):
459443
identity_handler = self.hs.get_handlers().identity_handler
460444

461445
logger.info("Getting validated threepid. threepidcreds: %r", (threepid_creds,))
462-
if (
463-
not password_servlet
464-
or self.hs.config.threepid_behaviour_email == ThreepidBehaviour.REMOTE
465-
):
446+
if self.hs.config.threepid_behaviour_email == ThreepidBehaviour.REMOTE:
466447
threepid = yield identity_handler.threepid_from_creds(threepid_creds)
467448
elif self.hs.config.threepid_behaviour_email == ThreepidBehaviour.LOCAL:
468449
row = yield self.store.get_threepid_validation_session(

synapse/push/mailer.py

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -629,25 +629,50 @@ def format_ts_filter(value, format):
629629
return time.strftime(format, time.localtime(value / 1000))
630630

631631

632-
def load_jinja2_templates(config, template_html_name, template_text_name):
633-
"""Load the jinja2 email templates from disk
632+
def load_jinja2_templates(
633+
template_dir,
634+
template_filenames,
635+
apply_format_ts_filter=False,
636+
apply_mxc_to_http_filter=False,
637+
public_baseurl=None,
638+
):
639+
"""Loads and returns one or more jinja2 templates and applies optional filters
640+
641+
Args:
642+
template_dir (str): The directory where templates are stored
643+
template_filenames (list[str]): A list of template filenames
644+
apply_format_ts_filter (bool): Whether to apply a template filter that formats
645+
timestamps
646+
apply_mxc_to_http_filter (bool): Whether to apply a template filter that converts
647+
mxc urls to http urls
648+
public_baseurl (str|None): The public baseurl of the server. Required for
649+
apply_mxc_to_http_filter to be enabled
634650
635651
Returns:
636-
(template_html, template_text)
652+
A list of jinja2 templates corresponding to the given list of filenames,
653+
with order preserved
637654
"""
638-
logger.info("loading email templates from '%s'", config.email_template_dir)
639-
loader = jinja2.FileSystemLoader(config.email_template_dir)
655+
logger.info(
656+
"loading email templates %s from '%s'", template_filenames, template_dir
657+
)
658+
loader = jinja2.FileSystemLoader(template_dir)
640659
env = jinja2.Environment(loader=loader)
641-
env.filters["format_ts"] = format_ts_filter
642-
env.filters["mxc_to_http"] = _create_mxc_to_http_filter(config)
643660

644-
template_html = env.get_template(template_html_name)
645-
template_text = env.get_template(template_text_name)
661+
if apply_format_ts_filter:
662+
env.filters["format_ts"] = format_ts_filter
663+
664+
if apply_mxc_to_http_filter and public_baseurl:
665+
env.filters["mxc_to_http"] = _create_mxc_to_http_filter(public_baseurl)
666+
667+
templates = []
668+
for template_filename in template_filenames:
669+
template = env.get_template(template_filename)
670+
templates.append(template)
646671

647-
return template_html, template_text
672+
return templates
648673

649674

650-
def _create_mxc_to_http_filter(config):
675+
def _create_mxc_to_http_filter(public_baseurl):
651676
def mxc_to_http_filter(value, width, height, resize_method="crop"):
652677
if value[0:6] != "mxc://":
653678
return ""
@@ -660,7 +685,7 @@ def mxc_to_http_filter(value, width, height, resize_method="crop"):
660685

661686
params = {"width": width, "height": height, "method": resize_method}
662687
return "%s_matrix/media/v1/thumbnail/%s?%s%s" % (
663-
config.public_baseurl,
688+
public_baseurl,
664689
serverAndMediaId,
665690
urllib.parse.urlencode(params),
666691
fragment or "",

synapse/push/pusher.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,24 @@
3535
class PusherFactory(object):
3636
def __init__(self, hs):
3737
self.hs = hs
38+
self.config = hs.config
3839

3940
self.pusher_types = {"http": HttpPusher}
4041

4142
logger.info("email enable notifs: %r", hs.config.email_enable_notifs)
4243
if hs.config.email_enable_notifs:
4344
self.mailers = {} # app_name -> Mailer
4445

45-
templates = load_jinja2_templates(
46-
config=hs.config,
47-
template_html_name=hs.config.email_notif_template_html,
48-
template_text_name=hs.config.email_notif_template_text,
46+
self.notif_template_html, self.notif_template_text = load_jinja2_templates(
47+
self.config.email_template_dir,
48+
[
49+
self.config.email_notif_template_html,
50+
self.config.email_notif_template_text,
51+
],
52+
apply_format_ts_filter=True,
53+
apply_mxc_to_http_filter=True,
54+
public_baseurl=self.config.public_baseurl,
4955
)
50-
self.notif_template_html, self.notif_template_text = templates
5156

5257
self.pusher_types["email"] = self._create_email_pusher
5358

@@ -78,6 +83,6 @@ def _app_name_from_pusherdict(self, pusherdict):
7883
if "data" in pusherdict and "brand" in pusherdict["data"]:
7984
app_name = pusherdict["data"]["brand"]
8085
else:
81-
app_name = self.hs.config.email_app_name
86+
app_name = self.config.email_app_name
8287

8388
return app_name

synapse/rest/client/v2_alpha/account.py

Lines changed: 20 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818

1919
from six.moves import http_client
2020

21-
import jinja2
22-
2321
from twisted.internet import defer
2422

2523
from synapse.api.constants import LoginType
@@ -32,6 +30,7 @@
3230
parse_json_object_from_request,
3331
parse_string,
3432
)
33+
from synapse.push.mailer import Mailer, load_jinja2_templates
3534
from synapse.util.msisdn import phone_number_to_msisdn
3635
from synapse.util.threepids import check_3pid_allowed
3736

@@ -51,18 +50,21 @@ def __init__(self, hs):
5150
self.identity_handler = hs.get_handlers().identity_handler
5251

5352
if self.config.threepid_behaviour_email == ThreepidBehaviour.LOCAL:
54-
from synapse.push.mailer import Mailer, load_jinja2_templates
55-
56-
templates = load_jinja2_templates(
57-
config=hs.config,
58-
template_html_name=hs.config.email_password_reset_template_html,
59-
template_text_name=hs.config.email_password_reset_template_text,
53+
template_html, template_text = load_jinja2_templates(
54+
self.config.email_template_dir,
55+
[
56+
self.config.email_password_reset_template_html,
57+
self.config.email_password_reset_template_text,
58+
],
59+
apply_format_ts_filter=True,
60+
apply_mxc_to_http_filter=True,
61+
public_baseurl=self.config.public_baseurl,
6062
)
6163
self.mailer = Mailer(
6264
hs=self.hs,
6365
app_name=self.config.email_app_name,
64-
template_html=templates[0],
65-
template_text=templates[1],
66+
template_html=template_html,
67+
template_text=template_text,
6668
)
6769

6870
@defer.inlineCallbacks
@@ -215,6 +217,7 @@ def __init__(self, hs):
215217

216218
@defer.inlineCallbacks
217219
def on_GET(self, request, medium):
220+
# We currently only handle threepid token submissions for email
218221
if medium != "email":
219222
raise SynapseError(
220223
400, "This medium is currently not supported for password resets"
@@ -255,35 +258,20 @@ def on_GET(self, request, medium):
255258
html = self.config.email_password_reset_template_success_html
256259
request.setResponseCode(200)
257260
except ThreepidValidationError as e:
261+
request.setResponseCode(e.code)
262+
258263
# Show a failure page with a reason
259-
html = self.load_jinja2_template(
264+
html_template = load_jinja2_templates(
260265
self.config.email_template_dir,
261-
self.config.email_password_reset_template_failure_html,
262-
template_vars={"failure_reason": e.msg},
266+
[self.config.email_password_reset_template_failure_html],
263267
)
264-
request.setResponseCode(e.code)
268+
269+
template_vars = {"failure_reason": e.msg}
270+
html = html_template.render(**template_vars)
265271

266272
request.write(html.encode("utf-8"))
267273
finish_request(request)
268274

269-
def load_jinja2_template(self, template_dir, template_filename, template_vars):
270-
"""Loads a jinja2 template with variables to insert
271-
272-
Args:
273-
template_dir (str): The directory where templates are stored
274-
template_filename (str): The name of the template in the template_dir
275-
template_vars (Dict): Dictionary of keys in the template
276-
alongside their values to insert
277-
278-
Returns:
279-
str containing the contents of the rendered template
280-
"""
281-
loader = jinja2.FileSystemLoader(template_dir)
282-
env = jinja2.Environment(loader=loader)
283-
284-
template = env.get_template(template_filename)
285-
return template.render(**template_vars)
286-
287275
@defer.inlineCallbacks
288276
def on_POST(self, request, medium):
289277
if medium != "email":
@@ -340,7 +328,6 @@ def on_POST(self, request):
340328
[[LoginType.EMAIL_IDENTITY], [LoginType.MSISDN]],
341329
body,
342330
self.hs.get_ip_from_request(request),
343-
password_servlet=True,
344331
)
345332

346333
if LoginType.EMAIL_IDENTITY in result:

0 commit comments

Comments
 (0)