1# Copyright 2020 The Matrix.org Foundation C.I.C. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14import logging 15from typing import TYPE_CHECKING, Tuple 16 17from twisted.web.server import Request 18 19from synapse.api.errors import ThreepidValidationError 20from synapse.config.emailconfig import ThreepidBehaviour 21from synapse.http.server import DirectServeHtmlResource 22from synapse.http.servlet import parse_string 23from synapse.util.stringutils import assert_valid_client_secret 24 25if TYPE_CHECKING: 26 from synapse.server import HomeServer 27 28logger = logging.getLogger(__name__) 29 30 31class PasswordResetSubmitTokenResource(DirectServeHtmlResource): 32 """Handles 3PID validation token submission 33 34 This resource gets mounted under /_synapse/client/password_reset/email/submit_token 35 """ 36 37 isLeaf = 1 38 39 def __init__(self, hs: "HomeServer"): 40 """ 41 Args: 42 hs: server 43 """ 44 super().__init__() 45 46 self.clock = hs.get_clock() 47 self.store = hs.get_datastore() 48 49 self._local_threepid_handling_disabled_due_to_email_config = ( 50 hs.config.email.local_threepid_handling_disabled_due_to_email_config 51 ) 52 self._confirmation_email_template = ( 53 hs.config.email.email_password_reset_template_confirmation_html 54 ) 55 self._email_password_reset_template_success_html = ( 56 hs.config.email.email_password_reset_template_success_html_content 57 ) 58 self._failure_email_template = ( 59 hs.config.email.email_password_reset_template_failure_html 60 ) 61 62 # This resource should not be mounted if threepid behaviour is not LOCAL 63 assert hs.config.email.threepid_behaviour_email == ThreepidBehaviour.LOCAL 64 65 async def _async_render_GET(self, request: Request) -> Tuple[int, bytes]: 66 sid = parse_string(request, "sid", required=True) 67 token = parse_string(request, "token", required=True) 68 client_secret = parse_string(request, "client_secret", required=True) 69 assert_valid_client_secret(client_secret) 70 71 # Show a confirmation page, just in case someone accidentally clicked this link when 72 # they didn't mean to 73 template_vars = { 74 "sid": sid, 75 "token": token, 76 "client_secret": client_secret, 77 } 78 return ( 79 200, 80 self._confirmation_email_template.render(**template_vars).encode("utf-8"), 81 ) 82 83 async def _async_render_POST(self, request: Request) -> Tuple[int, bytes]: 84 sid = parse_string(request, "sid", required=True) 85 token = parse_string(request, "token", required=True) 86 client_secret = parse_string(request, "client_secret", required=True) 87 88 # Attempt to validate a 3PID session 89 try: 90 # Mark the session as valid 91 next_link = await self.store.validate_threepid_session( 92 sid, client_secret, token, self.clock.time_msec() 93 ) 94 95 # Perform a 302 redirect if next_link is set 96 if next_link: 97 if next_link.startswith("file:///"): 98 logger.warning( 99 "Not redirecting to next_link as it is a local file: address" 100 ) 101 else: 102 next_link_bytes = next_link.encode("utf-8") 103 request.setHeader("Location", next_link_bytes) 104 return ( 105 302, 106 ( 107 b'You are being redirected to <a src="%s">%s</a>.' 108 % (next_link_bytes, next_link_bytes) 109 ), 110 ) 111 112 # Otherwise show the success template 113 html_bytes = self._email_password_reset_template_success_html.encode( 114 "utf-8" 115 ) 116 status_code = 200 117 except ThreepidValidationError as e: 118 status_code = e.code 119 120 # Show a failure page with a reason 121 template_vars = {"failure_reason": e.msg} 122 html_bytes = self._failure_email_template.render(**template_vars).encode( 123 "utf-8" 124 ) 125 126 return status_code, html_bytes 127