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

[2/2] Allow homeservers to send registration emails | Accepting the verification #5940

Merged
merged 14 commits into from
Sep 5, 2019
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/5940.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add the ability to send registration emails from the homeserver rather than delegating to an identity server.
29 changes: 6 additions & 23 deletions synapse/handlers/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def validate_user_via_ui_auth(self, requester, request_body, clientip):
return params

@defer.inlineCallbacks
def check_auth(self, flows, clientdict, clientip, password_servlet=False):
def check_auth(self, flows, clientdict, clientip):
"""
Takes a dictionary sent by the client in the login / registration
protocol and handles the User-Interactive Auth flow.
Expand All @@ -183,16 +183,6 @@ def check_auth(self, flows, clientdict, clientip, password_servlet=False):

clientip (str): The IP address of the client.

password_servlet (bool): Whether the request originated from
PasswordRestServlet.
XXX: This is a temporary hack to distinguish between checking
for threepid validations locally (in the case of password
resets) and using the identity server (in the case of binding
a 3PID during registration). Once we start using the
homeserver for both tasks, this distinction will no longer be
necessary.


Returns:
defer.Deferred[dict, dict, str]: a deferred tuple of
(creds, params, session_id).
Expand Down Expand Up @@ -248,9 +238,7 @@ def check_auth(self, flows, clientdict, clientip, password_servlet=False):
if "type" in authdict:
login_type = authdict["type"]
try:
result = yield self._check_auth_dict(
authdict, clientip, password_servlet=password_servlet
)
result = yield self._check_auth_dict(authdict, clientip)
if result:
creds[login_type] = result
self._save_session(session)
Expand Down Expand Up @@ -357,7 +345,7 @@ def get_session_data(self, session_id, key, default=None):
return sess.setdefault("serverdict", {}).get(key, default)

@defer.inlineCallbacks
def _check_auth_dict(self, authdict, clientip, password_servlet=False):
def _check_auth_dict(self, authdict, clientip):
"""Attempt to validate the auth dict provided by a client

Args:
Expand All @@ -377,9 +365,7 @@ def _check_auth_dict(self, authdict, clientip, password_servlet=False):
if checker is not None:
# XXX: Temporary workaround for having Synapse handle password resets
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
# See AuthHandler.check_auth for further details
res = yield checker(
authdict, clientip=clientip, password_servlet=password_servlet
)
res = yield checker(authdict, clientip=clientip)
return res

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

@defer.inlineCallbacks
def _check_threepid(self, medium, authdict, password_servlet=False, **kwargs):
def _check_threepid(self, medium, authdict, **kwargs):
if "threepid_creds" not in authdict:
raise LoginError(400, "Missing threepid_creds", Codes.MISSING_PARAM)

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

logger.info("Getting validated threepid. threepidcreds: %r", (threepid_creds,))
if (
not password_servlet
or self.hs.config.threepid_behaviour_email == ThreepidBehaviour.REMOTE
):
if self.hs.config.threepid_behaviour_email == ThreepidBehaviour.REMOTE:
threepid = yield identity_handler.threepid_from_creds(threepid_creds)
elif self.hs.config.threepid_behaviour_email == ThreepidBehaviour.LOCAL:
row = yield self.store.get_threepid_validation_session(
Expand Down
21 changes: 21 additions & 0 deletions synapse/push/mailer.py
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,27 @@ def format_ts_filter(value, format):
return time.strftime(format, time.localtime(value / 1000))


# XXX: This method and the next could likely be combined in a smarter way
@staticmethod
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
def load_jinja2_template(template_dir, template_filename, template_vars):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's confusing to have a load_jinja2_templates which just returns the templates, and load_jinja2_template which also calls render. I'd make a case for hoisting the render call to the caller which will save the template_vars param, but I guess you could rename the function instead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've refactored load_jinja2_templates into a single function which can do the work of both.

The entire refactor is contained within 2ee17d7.

"""Loads a jinja2 template with variables to insert
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved

Args:
template_dir (str): The directory where templates are stored
template_filename (str): The name of the template in the template_dir
template_vars (Dict): Dictionary of keys in the template
alongside their values to insert

Returns:
str containing the contents of the rendered template
"""
loader = jinja2.FileSystemLoader(template_dir)
env = jinja2.Environment(loader=loader)

template = env.get_template(template_filename)
return template.render(**template_vars)


def load_jinja2_templates(config, template_html_name, template_text_name):
"""Load the jinja2 email templates from disk

Expand Down
25 changes: 3 additions & 22 deletions synapse/rest/client/v2_alpha/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@

from six.moves import http_client

import jinja2

from twisted.internet import defer

from synapse.api.constants import LoginType
Expand All @@ -32,6 +30,7 @@
parse_json_object_from_request,
parse_string,
)
from synapse.push.mailer import load_jinja2_template
from synapse.util.msisdn import phone_number_to_msisdn
from synapse.util.threepids import check_3pid_allowed

Expand Down Expand Up @@ -215,6 +214,7 @@ def __init__(self, hs):

@defer.inlineCallbacks
def on_GET(self, request, medium):
# We currently only handle threepid token submissions for email
if medium != "email":
raise SynapseError(
400, "This medium is currently not supported for password resets"
Expand Down Expand Up @@ -256,7 +256,7 @@ def on_GET(self, request, medium):
request.setResponseCode(200)
except ThreepidValidationError as e:
# Show a failure page with a reason
html = self.load_jinja2_template(
html = load_jinja2_template(
self.config.email_template_dir,
self.config.email_password_reset_template_failure_html,
template_vars={"failure_reason": e.msg},
Expand All @@ -266,24 +266,6 @@ def on_GET(self, request, medium):
request.write(html.encode("utf-8"))
finish_request(request)

def load_jinja2_template(self, template_dir, template_filename, template_vars):
"""Loads a jinja2 template with variables to insert

Args:
template_dir (str): The directory where templates are stored
template_filename (str): The name of the template in the template_dir
template_vars (Dict): Dictionary of keys in the template
alongside their values to insert

Returns:
str containing the contents of the rendered template
"""
loader = jinja2.FileSystemLoader(template_dir)
env = jinja2.Environment(loader=loader)

template = env.get_template(template_filename)
return template.render(**template_vars)

@defer.inlineCallbacks
def on_POST(self, request, medium):
if medium != "email":
Expand Down Expand Up @@ -340,7 +322,6 @@ def on_POST(self, request):
[[LoginType.EMAIL_IDENTITY], [LoginType.MSISDN]],
body,
self.hs.get_ip_from_request(request),
password_servlet=True,
)

if LoginType.EMAIL_IDENTITY in result:
Expand Down
5 changes: 2 additions & 3 deletions synapse/rest/client/v2_alpha/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
parse_json_object_from_request,
parse_string,
)
from synapse.push.mailer import load_jinja2_template
from synapse.util.msisdn import phone_number_to_msisdn
from synapse.util.ratelimitutils import FederationRateLimiter
from synapse.util.threepids import check_3pid_allowed
Expand Down Expand Up @@ -284,7 +285,7 @@ def on_GET(self, request, medium):
request.setResponseCode(200)
except ThreepidValidationError as e:
# Show a failure page with a reason
html = self.load_jinja2_template(
html = load_jinja2_template(
self.config.email_template_dir,
self.config.email_registration_template_failure_html,
template_vars={"failure_reason": e.msg},
Expand Down Expand Up @@ -386,7 +387,6 @@ def on_POST(self, request):
if kind == b"guest":
ret = yield self._do_guest_registration(body, address=client_addr)
return ret
return
elif kind != b"user":
raise UnrecognizedRequestError(
"Do not understand membership kind: %s" % (kind,)
Expand Down Expand Up @@ -436,7 +436,6 @@ def on_POST(self, request):
desired_username, access_token, body
)
return (200, result) # we throw for non 200 responses
return

# for regular registration, downcase the provided username before
# attempting to register it. This should mean
Expand Down