summaryrefslogtreecommitdiff
path: root/synapse/rest/synapse/client/password_reset.py
blob: 9e4fbc0cbd082887ad47f2bf4547734da397ebc0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# -*- 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.
import logging
from typing import TYPE_CHECKING, Tuple

from twisted.web.http import Request

from synapse.api.errors import ThreepidValidationError
from synapse.config.emailconfig import ThreepidBehaviour
from synapse.http.server import DirectServeHtmlResource
from synapse.http.servlet import parse_string
from synapse.util.stringutils import assert_valid_client_secret

if TYPE_CHECKING:
    from synapse.server import HomeServer

logger = logging.getLogger(__name__)


class PasswordResetSubmitTokenResource(DirectServeHtmlResource):
    """Handles 3PID validation token submission

    This resource gets mounted under /_synapse/client/password_reset/email/submit_token
    """

    isLeaf = 1

    def __init__(self, hs: "HomeServer"):
        """
        Args:
            hs: server
        """
        super().__init__()

        self.clock = hs.get_clock()
        self.store = hs.get_datastore()

        self._local_threepid_handling_disabled_due_to_email_config = (
            hs.config.local_threepid_handling_disabled_due_to_email_config
        )
        self._confirmation_email_template = (
            hs.config.email_password_reset_template_confirmation_html
        )
        self._email_password_reset_template_success_html = (
            hs.config.email_password_reset_template_success_html_content
        )
        self._failure_email_template = (
            hs.config.email_password_reset_template_failure_html
        )

        # This resource should not be mounted if threepid behaviour is not LOCAL
        assert hs.config.threepid_behaviour_email == ThreepidBehaviour.LOCAL

    async def _async_render_GET(self, request: Request) -> Tuple[int, bytes]:
        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)

        # Show a confirmation page, just in case someone accidentally clicked this link when
        # they didn't mean to
        template_vars = {
            "sid": sid,
            "token": token,
            "client_secret": client_secret,
        }
        return (
            200,
            self._confirmation_email_template.render(**template_vars).encode("utf-8"),
        )

    async def _async_render_POST(self, request: Request) -> Tuple[int, bytes]:
        sid = parse_string(request, "sid", required=True)
        token = parse_string(request, "token", required=True)
        client_secret = parse_string(request, "client_secret", required=True)

        # 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:
                    next_link_bytes = next_link.encode("utf-8")
                    request.setHeader("Location", next_link_bytes)
                    return (
                        302,
                        (
                            b'You are being redirected to <a src="%s">%s</a>.'
                            % (next_link_bytes, next_link_bytes)
                        ),
                    )

            # Otherwise show the success template
            html_bytes = self._email_password_reset_template_success_html.encode(
                "utf-8"
            )
            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_bytes = self._failure_email_template.render(**template_vars).encode(
                "utf-8"
            )

        return status_code, html_bytes