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

Show a confirmation page during user password reset #8004

Merged
merged 36 commits into from
Sep 10, 2020
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9003eb4
Add confirmation page template
anoadragon453 Aug 21, 2020
5f7a834
Load the new template
anoadragon453 Jul 30, 2020
d9a19fc
Create new password reset confirmation endpoint
anoadragon453 Aug 21, 2020
6140b32
Add confirmation as a step to the password reset unit tests
anoadragon453 Aug 21, 2020
622946e
Remind people about the new template in the upgrade notes
anoadragon453 Aug 21, 2020
3568e18
Add changelog
anoadragon453 Jul 31, 2020
8cc8ee4
Remove unused fields
anoadragon453 Aug 21, 2020
b41b051
typing
anoadragon453 Aug 21, 2020
0a2b11f
Ensure we don't fail a request due to the config
anoadragon453 Aug 21, 2020
1d79f7b
Remove another unused class var
anoadragon453 Aug 21, 2020
3f4e350
Merge branch 'develop' of github.com:matrix-org/synapse into anoa/pas…
anoadragon453 Aug 26, 2020
d6addba
Update synapse/rest/client/v2_alpha/account.py
anoadragon453 Aug 28, 2020
990178f
Pull things from, instead of copying the entirety of, the config
anoadragon453 Aug 28, 2020
1b4458e
Return 400 when accessing submit_token/_confirm with REMOTE behaviour
anoadragon453 Aug 28, 2020
0608fe1
Convert confirmation from request args to HTML form data
anoadragon453 Aug 28, 2020
d9f5b4c
Combine the submit_token and submit_token_confirm endpoints
anoadragon453 Aug 28, 2020
f25209c
Switch from an unstable /_matrix/client prefix to /_synapse/client
anoadragon453 Aug 28, 2020
5060a8e
Update UPGRADE.rst
anoadragon453 Sep 1, 2020
8711981
Merge branch 'develop' of github.com:matrix-org/synapse into anoa/pas…
anoadragon453 Sep 2, 2020
1c1dc03
Merge branch 'anoa/password_reset_confirmation' of github.com:matrix-…
anoadragon453 Sep 2, 2020
9d90642
Add new resource to password reset unit tests
anoadragon453 Sep 2, 2020
7073605
Move PasswordResetSubmitTokenServlet to new synapse/client resource
anoadragon453 Sep 2, 2020
8efa7ac
Move synapse_client_patterns to synapse/client resource folder
anoadragon453 Sep 2, 2020
ad231e3
Add new PasswordResetRestResource JsonResource
anoadragon453 Sep 2, 2020
c1b9ad2
Add PasswordResetRestResource to HomeServer
anoadragon453 Sep 2, 2020
cb7c903
Convert synapse.rest.synapse to a python package
anoadragon453 Sep 3, 2020
76e9000
Rename synapse module to internal due to conflicts
anoadragon453 Sep 3, 2020
717961d
Remove action call from HTML template, remove medium placeholder
anoadragon453 Sep 3, 2020
775e310
Update docstring
anoadragon453 Sep 3, 2020
5ed7cf7
Switch from serlvets to base resources
anoadragon453 Sep 3, 2020
e2f0574
Merge branch 'develop' of github.com:matrix-org/synapse into anoa/pas…
anoadragon453 Sep 9, 2020
c3f6ff5
Inline password reset confirmation template filename
anoadragon453 Sep 9, 2020
2b6bde7
Typing and returning things directly
anoadragon453 Sep 9, 2020
0ad0753
Remove checks for threepid behaviour and add an assert
anoadragon453 Sep 9, 2020
c460e3b
v1.20.0 -> v1.21.0
anoadragon453 Sep 9, 2020
c5af421
Rename 'internal' dir to 'synapse'
anoadragon453 Sep 9, 2020
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
24 changes: 24 additions & 0 deletions UPGRADE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,30 @@ for example:
wget https://packages.matrix.org/debian/pool/main/m/matrix-synapse-py3/matrix-synapse-py3_1.3.0+stretch1_amd64.deb
dpkg -i matrix-synapse-py3_1.3.0+stretch1_amd64.deb

Upgrading to v1.20.0
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
====================

New HTML templates
------------------

A new HTML template,
`password_reset_confirmation.html <https://github.com/matrix-org/synapse/blob/develop/synapse/res/templates/password_reset_confirmation.html>`_,
has been added to the ``synapse/res/templates`` directory. If you are using a
custom template directory, you may want to copy the template over and modify it.

Note that as of v1.20.0, templates do not need to be included in custom template
directories for Synapse to start. The default templates will be used if a custom
template cannot be found.

This page will appear to the user after clicking a password reset link that has
been emailed to them.

To complete password reset, the page must include a way to make a `POST`
request to
``/_synapse/client/password_reset/{medium}/submit_token``
with the query parameters from the original link, presented as a URL-encoded form. See the file
itself for more details.

Upgrading to v1.18.0
====================

Expand Down
1 change: 1 addition & 0 deletions changelog.d/8004.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Require the user to confirm that their password should be reset after clicking the email confirmation link.
10 changes: 7 additions & 3 deletions docs/sample_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2021,9 +2021,13 @@ email:
# * The contents of password reset emails sent by the homeserver:
# 'password_reset.html' and 'password_reset.txt'
#
# * HTML pages for success and failure that a user will see when they follow
# the link in the password reset email: 'password_reset_success.html' and
# 'password_reset_failure.html'
# * An HTML page that a user will see when they follow the link in the password
# reset email. The user will be asked to confirm the action before their
# password is reset: 'password_reset_confirmation.html'
#
# * HTML pages for success and failure that a user will see when they confirm
# the password reset flow using the page above: 'password_reset_success.html'
# and 'password_reset_failure.html'
#
# * The contents of address verification emails sent during registration:
# 'registration.html' and 'registration.txt'
Expand Down
1 change: 1 addition & 0 deletions synapse/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

from synapse.config import ConfigError

SYNAPSE_CLIENT_API_PREFIX = "/_synapse/client"
CLIENT_API_PREFIX = "/_matrix/client"
FEDERATION_PREFIX = "/_matrix/federation"
FEDERATION_V1_PREFIX = FEDERATION_PREFIX + "/v1"
Expand Down
7 changes: 7 additions & 0 deletions synapse/app/homeserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
from synapse.app import _base
from synapse.app._base import listen_ssl, listen_tcp, quit_with_error
from synapse.config._base import ConfigError
from synapse.config.emailconfig import ThreepidBehaviour
from synapse.config.homeserver import HomeServerConfig
from synapse.config.server import ListenerConfig
from synapse.federation.transport.server import TransportLayerServer
Expand Down Expand Up @@ -209,6 +210,12 @@ def _configure_named_resource(self, name, compress=False):

resources["/_matrix/saml2"] = SAML2Resource(self)

if self.get_config().threepid_behaviour_email == ThreepidBehaviour.LOCAL:
from synapse.rest.internal.client import PasswordResetRestResource

password_reset = PasswordResetRestResource(self)
resources["/_synapse/client/password_reset"] = password_reset

if name == "consent":
from synapse.rest.consent.consent_resource import ConsentResource

Expand Down
15 changes: 12 additions & 3 deletions synapse/config/emailconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ def read_config(self, config, **kwargs):
"add_threepid_template_text", "add_threepid.txt"
)

password_reset_template_confirmation_html = (
"password_reset_confirmation.html"
)
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
password_reset_template_failure_html = email_config.get(
"password_reset_template_failure_html", "password_reset_failure.html"
)
Expand Down Expand Up @@ -228,6 +231,7 @@ def read_config(self, config, **kwargs):
self.email_registration_template_text,
self.email_add_threepid_template_html,
self.email_add_threepid_template_text,
self.email_password_reset_template_confirmation_html,
self.email_password_reset_template_failure_html,
self.email_registration_template_failure_html,
self.email_add_threepid_template_failure_html,
Expand All @@ -242,6 +246,7 @@ def read_config(self, config, **kwargs):
registration_template_text,
add_threepid_template_html,
add_threepid_template_text,
password_reset_template_confirmation_html,
password_reset_template_failure_html,
registration_template_failure_html,
add_threepid_template_failure_html,
Expand Down Expand Up @@ -404,9 +409,13 @@ def generate_config_section(self, config_dir_path, server_name, **kwargs):
# * The contents of password reset emails sent by the homeserver:
# 'password_reset.html' and 'password_reset.txt'
#
# * HTML pages for success and failure that a user will see when they follow
# the link in the password reset email: 'password_reset_success.html' and
# 'password_reset_failure.html'
# * An HTML page that a user will see when they follow the link in the password
# reset email. The user will be asked to confirm the action before their
# password is reset: 'password_reset_confirmation.html'
#
# * HTML pages for success and failure that a user will see when they confirm
# the password reset flow using the page above: 'password_reset_success.html'
# and 'password_reset_failure.html'
#
# * The contents of address verification emails sent during registration:
# 'registration.html' and 'registration.txt'
Expand Down
2 changes: 1 addition & 1 deletion synapse/push/mailer.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ async def send_password_reset_mail(self, email_address, token, client_secret, si
params = {"token": token, "client_secret": client_secret, "sid": sid}
link = (
self.hs.config.public_baseurl
+ "_matrix/client/unstable/password_reset/email/submit_token?%s"
+ "_synapse/client/password_reset/email/submit_token?%s"
% urllib.parse.urlencode(params)
)

Expand Down
16 changes: 16 additions & 0 deletions synapse/res/templates/password_reset_confirmation.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<html>
<head></head>
<body>
<!--Use a hidden form to resubmit the information necessary to reset the password-->
<form action="/_synapse/client/password_reset/{{ medium }}/submit_token" method="post">
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
<input type="hidden" name="sid" value="{{ sid }}">
<input type="hidden" name="token" value="{{ token }}">
<input type="hidden" name="client_secret" value="{{ client_secret }}">

<p>You have requested to <strong>reset your Matrix account password</strong>. Click the link below to confirm this action. <br /><br />
If you did not mean to do this, please close this page and your password will not be changed.</p>
<p><button type="submit">Confirm changing my password</button></p>
</form>
</body>
</html>

76 changes: 0 additions & 76 deletions synapse/rest/client/v2_alpha/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,81 +144,6 @@ async def on_POST(self, request):
return 200, ret


class PasswordResetSubmitTokenServlet(RestServlet):
"""Handles 3PID validation token submission"""

PATTERNS = client_patterns(
"/password_reset/(?P<medium>[^/]*)/submit_token$", releases=(), unstable=True
)

def __init__(self, hs):
"""
Args:
hs (synapse.server.HomeServer): server
"""
super(PasswordResetSubmitTokenServlet, self).__init__()
self.hs = hs
self.auth = hs.get_auth()
self.config = hs.config
self.clock = hs.get_clock()
self.store = hs.get_datastore()
if self.config.threepid_behaviour_email == ThreepidBehaviour.LOCAL:
self._failure_email_template = (
self.config.email_password_reset_template_failure_html
)

async 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"
)
if self.config.threepid_behaviour_email == ThreepidBehaviour.OFF:
if self.config.local_threepid_handling_disabled_due_to_email_config:
logger.warning(
"Password reset emails have been disabled due to lack of an email config"
)
raise SynapseError(
400, "Email-based password resets are disabled on this server"
)

sid = parse_string(request, "sid", required=True)
token = parse_string(request, "token", required=True)
client_secret = parse_string(request, "client_secret", required=True)
assert_valid_client_secret(client_secret)

# Attempt to validate a 3PID session
try:
# Mark the session as valid
next_link = await self.store.validate_threepid_session(
sid, client_secret, token, self.clock.time_msec()
)

# Perform a 302 redirect if next_link is set
if next_link:
if next_link.startswith("file:///"):
logger.warning(
"Not redirecting to next_link as it is a local file: address"
)
else:
request.setResponseCode(302)
request.setHeader("Location", next_link)
finish_request(request)
return None

# Otherwise show the success template
html = self.config.email_password_reset_template_success_html_content
status_code = 200
except ThreepidValidationError as e:
status_code = e.code

# Show a failure page with a reason
template_vars = {"failure_reason": e.msg}
html = self._failure_email_template.render(**template_vars)

respond_with_html(request, status_code, html)


class PasswordRestServlet(RestServlet):
PATTERNS = client_patterns("/account/password$")

Expand Down Expand Up @@ -890,7 +815,6 @@ async def on_GET(self, request):

def register_servlets(hs, http_server):
EmailPasswordRequestTokenRestServlet(hs).register(http_server)
PasswordResetSubmitTokenServlet(hs).register(http_server)
PasswordRestServlet(hs).register(http_server)
DeactivateAccountRestServlet(hs).register(http_server)
EmailThreepidRequestTokenRestServlet(hs).register(http_server)
Expand Down
Empty file.
37 changes: 37 additions & 0 deletions synapse/rest/internal/client/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
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 not entirely convinced that internal is the right name here, though I must admit I'm struggling to come up with better alternatives.

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 renamed it to synapse (which requires a modification to an import in synapse/rest/__init.py. Does this a) sound good b) seem sane?

# Copyright 2020 The Matrix.org Foundation C.I.C.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import TYPE_CHECKING

from synapse.http.server import JsonResource
from synapse.rest.internal.client import password_reset

if TYPE_CHECKING:
from synapse.server import HomeServer


class PasswordResetRestResource(JsonResource):
"""Synapse Internal Resource for password reset functionality

This resource gets mounted under /_synapse/client
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
"""

def __init__(self, hs: "HomeServer"):
JsonResource.__init__(self, hs, canonical_json=False)
self.register_servlets(self, hs)

@staticmethod
def register_servlets(synapse_client_resource: JsonResource, hs: "HomeServer"):
password_reset.register_servlets(hs, synapse_client_resource)
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
37 changes: 37 additions & 0 deletions synapse/rest/internal/client/_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
# Copyright 2020 The Matrix.org Foundation C.I.C.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""This module contains base REST classes for constructing client v1 servlets.
"""
import logging
import re
from typing import Iterable, Pattern

from synapse.api.urls import SYNAPSE_CLIENT_API_PREFIX

logger = logging.getLogger(__name__)


def synapse_client_patterns(path_regex: str) -> Iterable[Pattern]:
"""Creates a regex compiled client path with the correct synapse client
path prefix.

Args:
path_regex: The regex string to match. This should NOT have a ^
as this will be prefixed.
Returns:
An iterable of patterns.
"""
return [re.compile("^" + SYNAPSE_CLIENT_API_PREFIX + path_regex)]
Loading