Merge branch 'develop' of github.com:matrix-org/synapse into matrix-org-hotfixes

matrix-org-hotfixes-identity
Erik Johnston 2019-09-06 14:10:53 +01:00
commit 9240622c1a
5 changed files with 125 additions and 124 deletions

1
changelog.d/5993.feature Normal file
View File

@ -0,0 +1 @@
Add the ability to send registration emails from the homeserver rather than delegating to an identity server.

1
changelog.d/5994.feature Normal file
View File

@ -0,0 +1 @@
Add the ability to send registration emails from the homeserver rather than delegating to an identity server.

View File

@ -261,7 +261,7 @@ class PasswordResetSubmitTokenServlet(RestServlet):
request.setResponseCode(e.code) request.setResponseCode(e.code)
# Show a failure page with a reason # Show a failure page with a reason
html_template = load_jinja2_templates( html_template, = load_jinja2_templates(
self.config.email_template_dir, self.config.email_template_dir,
[self.config.email_password_reset_template_failure_html], [self.config.email_password_reset_template_failure_html],
) )

View File

@ -293,7 +293,7 @@ class RegistrationSubmitTokenServlet(RestServlet):
request.setResponseCode(e.code) request.setResponseCode(e.code)
# Show a failure page with a reason # Show a failure page with a reason
html_template = load_jinja2_templates( html_template, = load_jinja2_templates(
self.config.email_template_dir, self.config.email_template_dir,
[self.config.email_registration_template_failure_html], [self.config.email_registration_template_failure_html],
) )

View File

@ -614,81 +614,84 @@ class RegistrationWorkerStore(SQLBaseStore):
# Convert the integer into a boolean. # Convert the integer into a boolean.
return res == 1 return res == 1
def validate_threepid_session(self, session_id, client_secret, token, current_ts): def get_threepid_validation_session(
"""Attempt to validate a threepid session using a token self, medium, client_secret, address=None, sid=None, validated=True
):
"""Gets a session_id and last_send_attempt (if available) for a
client_secret/medium/(address|session_id) combo
Args: Args:
session_id (str): The id of a validation session medium (str|None): The medium of the 3PID
client_secret (str): A unique string provided by the client to address (str|None): The address of the 3PID
sid (str|None): The ID of the validation session
client_secret (str|None): A unique string provided by the client to
help identify this validation attempt help identify this validation attempt
token (str): A validation token validated (bool|None): Whether sessions should be filtered by
current_ts (int): The current unix time in milliseconds. Used for whether they have been validated already or not. None to
checking token expiry status perform no filtering
Returns: Returns:
deferred str|None: A str representing a link to redirect the user deferred {str, int}|None: A dict containing the
to if there is one. latest session_id and send_attempt count for this 3PID.
Otherwise None if there hasn't been a previous attempt
""" """
keyvalues = {"medium": medium, "client_secret": client_secret}
if address:
keyvalues["address"] = address
if sid:
keyvalues["session_id"] = sid
# Insert everything into a transaction in order to run atomically assert address or sid
def validate_threepid_session_txn(txn):
row = self._simple_select_one_txn( def get_threepid_validation_session_txn(txn):
txn, sql = """
table="threepid_validation_session", SELECT address, session_id, medium, client_secret,
keyvalues={"session_id": session_id}, last_send_attempt, validated_at
retcols=["client_secret", "validated_at"], FROM threepid_validation_session WHERE %s
allow_none=True, """ % (
" AND ".join("%s = ?" % k for k in iterkeys(keyvalues)),
) )
if not row: if validated is not None:
raise ThreepidValidationError(400, "Unknown session_id") sql += " AND validated_at IS " + ("NOT NULL" if validated else "NULL")
retrieved_client_secret = row["client_secret"]
validated_at = row["validated_at"]
if retrieved_client_secret != client_secret: sql += " LIMIT 1"
raise ThreepidValidationError(
400, "This client_secret does not match the provided session_id"
)
row = self._simple_select_one_txn( txn.execute(sql, list(keyvalues.values()))
txn, rows = self.cursor_to_dict(txn)
table="threepid_validation_token", if not rows:
keyvalues={"session_id": session_id, "token": token}, return None
retcols=["expires", "next_link"],
allow_none=True,
)
if not row: return rows[0]
raise ThreepidValidationError(
400, "Validation token not found or has expired"
)
expires = row["expires"]
next_link = row["next_link"]
# If the session is already validated, no need to revalidate
if validated_at:
return next_link
if expires <= current_ts:
raise ThreepidValidationError(
400, "This token has expired. Please request a new one"
)
# Looks good. Validate the session
self._simple_update_txn(
txn,
table="threepid_validation_session",
keyvalues={"session_id": session_id},
updatevalues={"validated_at": self.clock.time_msec()},
)
return next_link
# Return next_link if it exists
return self.runInteraction( return self.runInteraction(
"validate_threepid_session_txn", validate_threepid_session_txn "get_threepid_validation_session", get_threepid_validation_session_txn
) )
def delete_threepid_session(self, session_id):
"""Removes a threepid validation session from the database. This can
be done after validation has been performed and whatever action was
waiting on it has been carried out
Args:
session_id (str): The ID of the session to delete
"""
def delete_threepid_session_txn(txn):
self._simple_delete_txn(
txn,
table="threepid_validation_token",
keyvalues={"session_id": session_id},
)
self._simple_delete_txn(
txn,
table="threepid_validation_session",
keyvalues={"session_id": session_id},
)
return self.runInteraction(
"delete_threepid_session", delete_threepid_session_txn
)
class RegistrationStore( class RegistrationStore(
@ -1158,58 +1161,79 @@ class RegistrationStore(
return 1 return 1
def get_threepid_validation_session( def validate_threepid_session(self, session_id, client_secret, token, current_ts):
self, medium, client_secret, address=None, sid=None, validated=True """Attempt to validate a threepid session using a token
):
"""Gets a session_id and last_send_attempt (if available) for a
client_secret/medium/(address|session_id) combo
Args: Args:
medium (str|None): The medium of the 3PID session_id (str): The id of a validation session
address (str|None): The address of the 3PID client_secret (str): A unique string provided by the client to
sid (str|None): The ID of the validation session
client_secret (str|None): A unique string provided by the client to
help identify this validation attempt help identify this validation attempt
validated (bool|None): Whether sessions should be filtered by token (str): A validation token
whether they have been validated already or not. None to current_ts (int): The current unix time in milliseconds. Used for
perform no filtering checking token expiry status
Returns: Returns:
deferred {str, int}|None: A dict containing the deferred str|None: A str representing a link to redirect the user
latest session_id and send_attempt count for this 3PID. to if there is one.
Otherwise None if there hasn't been a previous attempt
""" """
keyvalues = {"medium": medium, "client_secret": client_secret}
if address:
keyvalues["address"] = address
if sid:
keyvalues["session_id"] = sid
assert address or sid # Insert everything into a transaction in order to run atomically
def validate_threepid_session_txn(txn):
def get_threepid_validation_session_txn(txn): row = self._simple_select_one_txn(
sql = """ txn,
SELECT address, session_id, medium, client_secret, table="threepid_validation_session",
last_send_attempt, validated_at keyvalues={"session_id": session_id},
FROM threepid_validation_session WHERE %s retcols=["client_secret", "validated_at"],
""" % ( allow_none=True,
" AND ".join("%s = ?" % k for k in iterkeys(keyvalues)),
) )
if validated is not None: if not row:
sql += " AND validated_at IS " + ("NOT NULL" if validated else "NULL") raise ThreepidValidationError(400, "Unknown session_id")
retrieved_client_secret = row["client_secret"]
validated_at = row["validated_at"]
sql += " LIMIT 1" if retrieved_client_secret != client_secret:
raise ThreepidValidationError(
400, "This client_secret does not match the provided session_id"
)
txn.execute(sql, list(keyvalues.values())) row = self._simple_select_one_txn(
rows = self.cursor_to_dict(txn) txn,
if not rows: table="threepid_validation_token",
return None keyvalues={"session_id": session_id, "token": token},
retcols=["expires", "next_link"],
allow_none=True,
)
return rows[0] if not row:
raise ThreepidValidationError(
400, "Validation token not found or has expired"
)
expires = row["expires"]
next_link = row["next_link"]
# If the session is already validated, no need to revalidate
if validated_at:
return next_link
if expires <= current_ts:
raise ThreepidValidationError(
400, "This token has expired. Please request a new one"
)
# Looks good. Validate the session
self._simple_update_txn(
txn,
table="threepid_validation_session",
keyvalues={"session_id": session_id},
updatevalues={"validated_at": self.clock.time_msec()},
)
return next_link
# Return next_link if it exists
return self.runInteraction( return self.runInteraction(
"get_threepid_validation_session", get_threepid_validation_session_txn "validate_threepid_session_txn", validate_threepid_session_txn
) )
def upsert_threepid_validation_session( def upsert_threepid_validation_session(
@ -1324,31 +1348,6 @@ class RegistrationStore(
self.clock.time_msec(), self.clock.time_msec(),
) )
def delete_threepid_session(self, session_id):
"""Removes a threepid validation session from the database. This can
be done after validation has been performed and whatever action was
waiting on it has been carried out
Args:
session_id (str): The ID of the session to delete
"""
def delete_threepid_session_txn(txn):
self._simple_delete_txn(
txn,
table="threepid_validation_token",
keyvalues={"session_id": session_id},
)
self._simple_delete_txn(
txn,
table="threepid_validation_session",
keyvalues={"session_id": session_id},
)
return self.runInteraction(
"delete_threepid_session", delete_threepid_session_txn
)
def set_user_deactivated_status_txn(self, txn, user_id, deactivated): def set_user_deactivated_status_txn(self, txn, user_id, deactivated):
self._simple_update_one_txn( self._simple_update_one_txn(
txn=txn, txn=txn,