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

[1/2] Allow homeservers to send registration emails | Sending the email #5835

Merged
merged 37 commits into from
Aug 30, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
eacd505
Allow homeservers to send registration emails
anoadragon453 Aug 8, 2019
959c051
Add changelog
anoadragon453 Aug 8, 2019
0197954
lint
anoadragon453 Aug 8, 2019
c7a2317
update
anoadragon453 Aug 8, 2019
053638d
account_threepid_delegate defaults to empty string. fix bug
anoadragon453 Aug 9, 2019
176dd5d
fix config
anoadragon453 Aug 14, 2019
73df394
lint
anoadragon453 Aug 14, 2019
c092a35
Fix lack of self.config
anoadragon453 Aug 14, 2019
616ee20
Standardize on self.store/self.config
anoadragon453 Aug 14, 2019
994c51f
Merge branch 'develop' into anoa/reg_email_sending_email
anoadragon453 Aug 14, 2019
cc5983d
Warn users with deprecated config option to update their config
anoadragon453 Aug 15, 2019
4f035bd
Fix registration email subject
anoadragon453 Aug 15, 2019
c8ba612
Actually send registration emails when registering
anoadragon453 Aug 15, 2019
7f402b1
Descope adding an email to your account
anoadragon453 Aug 15, 2019
858414f
Don't allow multiple path_regexes in client_patterns
anoadragon453 Aug 16, 2019
7e983f9
break up password reset and registration submit_token servlets
anoadragon453 Aug 16, 2019
7cd1133
lint
anoadragon453 Aug 16, 2019
6b053d3
Send emails through the configured identity server
anoadragon453 Aug 16, 2019
a6e22d7
Merge branch 'anoa/reg_email' into anoa/reg_email_sending_email
anoadragon453 Aug 19, 2019
a03cc2a
Split functionality off into other PRs
anoadragon453 Aug 19, 2019
075541a
Merge 'anoa/reg_email' into 'anoa/reg_email_sending_email'
anoadragon453 Aug 28, 2019
9e1e774
Merge branch 'anoa/reg_email' into anoa/reg_email_sending_email
anoadragon453 Aug 28, 2019
798e72b
lint
anoadragon453 Aug 28, 2019
1bc713d
Apply suggestions from code review
anoadragon453 Aug 28, 2019
70127b8
fixes from suggestions
anoadragon453 Aug 28, 2019
03d3789
Merge branch 'anoa/reg_email_sending_email' of github.com:matrix-org/…
anoadragon453 Aug 28, 2019
75b279e
Add v1.4.0 upgrade notes to UPGRADE.rst
anoadragon453 Aug 28, 2019
53c5432
Add email template information
anoadragon453 Aug 28, 2019
f14b097
lint
anoadragon453 Aug 28, 2019
6706844
Make things work again
anoadragon453 Aug 28, 2019
b29c62b
Update UPGRADE.rst to talk more about email reg
anoadragon453 Aug 29, 2019
9b1a340
Update UPGRADE.rst with reg success and fail templates
anoadragon453 Aug 29, 2019
ace8fa5
Address review comments
anoadragon453 Aug 29, 2019
5113d9e
Add password reset template information to UPGRADE.rst for Synapse v1
anoadragon453 Aug 29, 2019
06815e8
Tokens -> messages
anoadragon453 Aug 29, 2019
4dd5b97
Merge branch 'anoa/reg_email' into anoa/reg_email_sending_email
anoadragon453 Aug 29, 2019
80abdf2
Merge branch 'anoa/reg_email' into anoa/reg_email_sending_email
anoadragon453 Aug 30, 2019
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/5835.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Ability to send registration emails from the homeserver.
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
38 changes: 25 additions & 13 deletions docs/sample_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,25 @@ uploads_path: "DATADIR/uploads"
# - matrix.org
# - vector.im

# Handle threepid (email/phone etc) registration and password resets
# through an identity server. Only change if you know what you are
# doing
#
# IMPORTANT! This will give a malicious or overtaken identity server
# the ability to reset passwords for your users and/or learn your
# user's email/phone numbers! Make absolutely sure that you want to do
# this! It is strongly recommended that these messages be sent by the
# homeserver instead
#
# If this option is an empty string and SMTP options have not been
# configured, registration by email and resetting user passwords via
# email will be disabled
#
# Otherwise, to enable set this option to the reachable domain name for
# an identity server (e.g "matrix.org")
#
#account_threepid_delegate: ""

# Users who register on this homeserver will automatically be joined
# to these rooms
#
Expand Down Expand Up @@ -1148,19 +1167,6 @@ password_config:
# #
# riot_base_url: "http://localhost/riot"
#
# # Enable sending password reset emails via the configured, trusted
# # identity servers
# #
# # IMPORTANT! This will give a malicious or overtaken identity server
# # the ability to reset passwords for your users! Make absolutely sure
# # that you want to do this! It is strongly recommended that password
# # reset emails be sent by the homeserver instead
# #
# # If this option is set to false and SMTP options have not been
# # configured, resetting user passwords via email will be disabled
# #
# #trust_identity_server_for_password_resets: false
#
# # Configure the time that a validation email or text message code
# # will expire after sending
# #
Expand Down Expand Up @@ -1197,6 +1203,12 @@ password_config:
# #
# #password_reset_template_success_html: password_reset_success.html
# #password_reset_template_failure_html: password_reset_failure.html
#
# # Templates for registration success and failure pages that a user
# # will see after attempting to register using an email or phone
# #
# #registration_template_success_html: registration_success.html
# #registration_template_failure_html: registration_failure.html


#password_providers:
Expand Down
83 changes: 48 additions & 35 deletions synapse/config/emailconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,16 @@ def read_config(self, config, **kwargs):
"renew_at"
)

email_trust_identity_server_for_password_resets = email_config.get(
"trust_identity_server_for_password_resets", False
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
self.email_threepid_behaviour = (
"local" if self.account_threepid_delegate else "remote"
)
self.email_password_reset_behaviour = (
"remote" if email_trust_identity_server_for_password_resets else "local"
)
self.password_resets_were_disabled_due_to_email_config = False
if self.email_password_reset_behaviour == "local" and email_config == {}:
self.local_threepid_emails_disabled_due_to_config = False
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
if self.email_threepid_behaviour == "local" and email_config == {}:
# We cannot warn the user this has happened here
# Instead do so when a user attempts to reset their password
self.password_resets_were_disabled_due_to_email_config = True
self.local_threepid_emails_disabled_due_to_config = True

self.email_password_reset_behaviour = "off"
self.email_threepid_behaviour = "off"

# Get lifetime of a validation token in milliseconds
self.email_validation_token_lifetime = self.parse_duration(
Expand All @@ -96,7 +93,7 @@ def read_config(self, config, **kwargs):
if (
self.email_enable_notifs
or account_validity_renewal_enabled
or self.email_password_reset_behaviour == "local"
or self.email_threepid_behaviour == "local"
):
# make sure we can import the required deps
import jinja2
Expand All @@ -106,7 +103,7 @@ def read_config(self, config, **kwargs):
jinja2
bleach

if self.email_password_reset_behaviour == "local":
if self.email_threepid_behaviour == "local":
required = ["smtp_host", "smtp_port", "notif_from"]

missing = []
Expand All @@ -125,40 +122,63 @@ def read_config(self, config, **kwargs):
% (", ".join(missing),)
)

# Templates for password reset emails
# These email templates have placeholders in them, and thus must be
# parsed using a templating engine during a request
self.email_password_reset_template_html = email_config.get(
"password_reset_template_html", "password_reset.html"
)
self.email_password_reset_template_text = email_config.get(
"password_reset_template_text", "password_reset.txt"
)
self.email_password_reset_failure_template = email_config.get(
"password_reset_failure_template", "password_reset_failure.html"
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
self.email_registration_template_html = email_config.get(
"registration_template_html", "registration.html"
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
)
self.email_registration_template_text = email_config.get(
"registration_template_text", "registration.txt"
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
)
self.email_password_reset_template_failure_html = email_config.get(
"password_reset_template_failure_html", "password_reset_failure.html"
)
self.email_registration_template_failure_html = email_config.get(
"registration_template_failure_html", "registration_failure.html"
)
# This template does not support any replaceable variables, so we will
# read it from the disk once during setup
email_password_reset_success_template = email_config.get(
"password_reset_success_template", "password_reset_success.html"

# These templates do not support any placeholder variables, so we
Copy link
Member

Choose a reason for hiding this comment

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

I'm kinda wondering if a better solution is to remove the special-casing and stick them through the template engine anyway.

Copy link
Member Author

Choose a reason for hiding this comment

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

It would be more clear, but we'd have to pull the template from the disk every time we get a registration request. So a question of code quality versus performance.

Copy link
Member

Choose a reason for hiding this comment

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

given we have to do that anyway for the other templates, it feels like a hit worth taking. But also, maybe something to think about another time.

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'll punt it for later.

# will read them from disk once during setup
email_password_reset_template_success_html = email_config.get(
"password_reset_template_success_html", "password_reset_success.html"
)
email_registration_template_success_html = email_config.get(
"registration_template_success_html", "registration_success.html"
)

# Check templates exist
for f in [
self.email_password_reset_template_html,
self.email_password_reset_template_text,
self.email_password_reset_failure_template,
email_password_reset_success_template,
self.email_registration_template_html,
self.email_registration_template_text,
self.email_password_reset_template_failure_html,
email_password_reset_template_success_html,
email_registration_template_success_html,
]:
p = os.path.join(self.email_template_dir, f)
if not os.path.isfile(p):
raise ConfigError("Unable to find template file %s" % (p,))

# Retrieve content of web templates
filepath = os.path.join(
self.email_template_dir, email_password_reset_success_template
self.email_template_dir, email_password_reset_template_success_html
)
self.email_password_reset_success_html_content = self.read_file(
self.email_password_reset_template_success_html = self.read_file(
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
filepath, "email.password_reset_template_success_html"
)
filepath = os.path.join(
self.email_template_dir, email_registration_template_success_html
)
self.email_registration_template_success_html = self.read_file(
filepath, "email.registration_template_success_html"
)

if self.email_enable_notifs:
required = [
Expand Down Expand Up @@ -239,19 +259,6 @@ def generate_config_section(self, config_dir_path, server_name, **kwargs):
# #
# riot_base_url: "http://localhost/riot"
#
# # Enable sending password reset emails via the configured, trusted
# # identity servers
# #
# # IMPORTANT! This will give a malicious or overtaken identity server
# # the ability to reset passwords for your users! Make absolutely sure
# # that you want to do this! It is strongly recommended that password
# # reset emails be sent by the homeserver instead
# #
# # If this option is set to false and SMTP options have not been
# # configured, resetting user passwords via email will be disabled
# #
# #trust_identity_server_for_password_resets: false
#
# # Configure the time that a validation email or text message code
# # will expire after sending
# #
Expand Down Expand Up @@ -288,4 +295,10 @@ def generate_config_section(self, config_dir_path, server_name, **kwargs):
# #
# #password_reset_template_success_html: password_reset_success.html
# #password_reset_template_failure_html: password_reset_failure.html
#
# # Templates for registration success and failure pages that a user
# # will see after attempting to register using an email or phone
# #
# #registration_template_success_html: registration_success.html
# #registration_template_failure_html: registration_failure.html
"""
20 changes: 20 additions & 0 deletions synapse/config/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ def read_config(self, config, **kwargs):
self.trusted_third_party_id_servers = config.get(
"trusted_third_party_id_servers", ["matrix.org", "vector.im"]
)
self.account_threepid_delegate = config.get("account_threepid_delegate", "")
self.default_identity_server = config.get("default_identity_server")
self.allow_guest_access = config.get("allow_guest_access", False)

Expand Down Expand Up @@ -261,6 +262,25 @@ def generate_config_section(self, generate_secrets=False, **kwargs):
# - matrix.org
# - vector.im

# Handle threepid (email/phone etc) registration and password resets
# through an identity server. Only change if you know what you are
# doing
#
# IMPORTANT! This will give a malicious or overtaken identity server
# the ability to reset passwords for your users and/or learn your
# user's email/phone numbers! Make absolutely sure that you want to do
# this! It is strongly recommended that these messages be sent by the
# homeserver instead
#
# If this option is an empty string and SMTP options have not been
# configured, registration by email and resetting user passwords via
# email will be disabled
#
# Otherwise, to enable set this option to the reachable domain name for
# an identity server (e.g "matrix.org")
#
#account_threepid_delegate: ""

# Users who register on this homeserver will automatically be joined
# to these rooms
#
Expand Down
7 changes: 2 additions & 5 deletions synapse/handlers/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -458,12 +458,9 @@ 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.email_password_reset_behaviour == "remote"
):
if not password_servlet or self.hs.config.email_threepid_behaviour == "remote":
threepid = yield identity_handler.threepid_from_creds(threepid_creds)
elif self.hs.config.email_password_reset_behaviour == "local":
elif self.hs.config.email_threepid_behaviour == "local":
row = yield self.store.get_threepid_validation_session(
medium,
threepid_creds["client_secret"],
Expand Down
12 changes: 0 additions & 12 deletions synapse/handlers/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,18 +414,6 @@ def register_email(self, threepidCreds):
if not check_3pid_allowed(self.hs, threepid["medium"], threepid["address"]):
raise RegistrationError(403, "Third party identifier is not allowed")

@defer.inlineCallbacks
def bind_emails(self, user_id, threepidCreds):
"""Links emails with a user ID and informs an identity server.

Used only by c/s api v1
"""

# Now we have a matrix ID, bind it to the threepids we were given
for c in threepidCreds:
# XXX: This should be a deferred list, shouldn't it?
yield self.identity_handler.bind_threepid(c, user_id)

def check_user_id_not_appservice_exclusive(self, user_id, allowed_appservice=None):
# don't allow people to register the server notices mxid
if self._server_notices_mxid is not None:
Expand Down
Loading