You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

186 lines
7.0 KiB

  1. # Copyright 2015, 2016 OpenMarket Ltd
  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.
  14. import logging
  15. from typing import TYPE_CHECKING
  16. from twisted.web.server import Request
  17. from synapse.api.constants import LoginType
  18. from synapse.api.errors import LoginError, SynapseError
  19. from synapse.api.urls import CLIENT_API_PREFIX
  20. from synapse.http.server import HttpServer, respond_with_html
  21. from synapse.http.servlet import RestServlet, parse_string
  22. from synapse.http.site import SynapseRequest
  23. from ._base import client_patterns
  24. if TYPE_CHECKING:
  25. from synapse.server import HomeServer
  26. logger = logging.getLogger(__name__)
  27. class AuthRestServlet(RestServlet):
  28. """
  29. Handles Client / Server API authentication in any situations where it
  30. cannot be handled in the normal flow (with requests to the same endpoint).
  31. Current use is for web fallback auth.
  32. """
  33. PATTERNS = client_patterns(r"/auth/(?P<stagetype>[\w\.]*)/fallback/web")
  34. def __init__(self, hs: "HomeServer"):
  35. super().__init__()
  36. self.hs = hs
  37. self.auth = hs.get_auth()
  38. self.auth_handler = hs.get_auth_handler()
  39. self.registration_handler = hs.get_registration_handler()
  40. self.recaptcha_template = hs.config.captcha.recaptcha_template
  41. self.terms_template = hs.config.consent.terms_template
  42. self.registration_token_template = (
  43. hs.config.registration.registration_token_template
  44. )
  45. self.success_template = hs.config.registration.fallback_success_template
  46. async def on_GET(self, request: SynapseRequest, stagetype: str) -> None:
  47. session = parse_string(request, "session")
  48. if not session:
  49. raise SynapseError(400, "No session supplied")
  50. if stagetype == LoginType.RECAPTCHA:
  51. html = self.recaptcha_template.render(
  52. session=session,
  53. myurl="%s/v3/auth/%s/fallback/web"
  54. % (CLIENT_API_PREFIX, LoginType.RECAPTCHA),
  55. sitekey=self.hs.config.captcha.recaptcha_public_key,
  56. )
  57. elif stagetype == LoginType.TERMS:
  58. html = self.terms_template.render(
  59. session=session,
  60. terms_url="%s_matrix/consent?v=%s"
  61. % (
  62. self.hs.config.server.public_baseurl,
  63. self.hs.config.consent.user_consent_version,
  64. ),
  65. myurl="%s/v3/auth/%s/fallback/web"
  66. % (CLIENT_API_PREFIX, LoginType.TERMS),
  67. )
  68. elif stagetype == LoginType.SSO:
  69. # Display a confirmation page which prompts the user to
  70. # re-authenticate with their SSO provider.
  71. html = await self.auth_handler.start_sso_ui_auth(request, session)
  72. elif stagetype == LoginType.REGISTRATION_TOKEN:
  73. html = self.registration_token_template.render(
  74. session=session,
  75. myurl=f"{CLIENT_API_PREFIX}/r0/auth/{LoginType.REGISTRATION_TOKEN}/fallback/web",
  76. )
  77. else:
  78. raise SynapseError(404, "Unknown auth stage type")
  79. # Render the HTML and return.
  80. respond_with_html(request, 200, html)
  81. return None
  82. async def on_POST(self, request: Request, stagetype: str) -> None:
  83. session = parse_string(request, "session")
  84. if not session:
  85. raise SynapseError(400, "No session supplied")
  86. if stagetype == LoginType.RECAPTCHA:
  87. response = parse_string(request, "g-recaptcha-response")
  88. if not response:
  89. raise SynapseError(400, "No captcha response supplied")
  90. authdict = {"response": response, "session": session}
  91. try:
  92. await self.auth_handler.add_oob_auth(
  93. LoginType.RECAPTCHA, authdict, request.getClientAddress().host
  94. )
  95. except LoginError as e:
  96. # Authentication failed, let user try again
  97. html = self.recaptcha_template.render(
  98. session=session,
  99. myurl="%s/v3/auth/%s/fallback/web"
  100. % (CLIENT_API_PREFIX, LoginType.RECAPTCHA),
  101. sitekey=self.hs.config.captcha.recaptcha_public_key,
  102. error=e.msg,
  103. )
  104. else:
  105. # No LoginError was raised, so authentication was successful
  106. html = self.success_template.render()
  107. elif stagetype == LoginType.TERMS:
  108. authdict = {"session": session}
  109. try:
  110. await self.auth_handler.add_oob_auth(
  111. LoginType.TERMS, authdict, request.getClientAddress().host
  112. )
  113. except LoginError as e:
  114. # Authentication failed, let user try again
  115. html = self.terms_template.render(
  116. session=session,
  117. terms_url="%s_matrix/consent?v=%s"
  118. % (
  119. self.hs.config.server.public_baseurl,
  120. self.hs.config.consent.user_consent_version,
  121. ),
  122. myurl="%s/v3/auth/%s/fallback/web"
  123. % (CLIENT_API_PREFIX, LoginType.TERMS),
  124. error=e.msg,
  125. )
  126. else:
  127. # No LoginError was raised, so authentication was successful
  128. html = self.success_template.render()
  129. elif stagetype == LoginType.SSO:
  130. # The SSO fallback workflow should not post here,
  131. raise SynapseError(404, "Fallback SSO auth does not support POST requests.")
  132. elif stagetype == LoginType.REGISTRATION_TOKEN:
  133. token = parse_string(request, "token", required=True)
  134. authdict = {"session": session, "token": token}
  135. try:
  136. await self.auth_handler.add_oob_auth(
  137. LoginType.REGISTRATION_TOKEN,
  138. authdict,
  139. request.getClientAddress().host,
  140. )
  141. except LoginError as e:
  142. html = self.registration_token_template.render(
  143. session=session,
  144. myurl=f"{CLIENT_API_PREFIX}/r0/auth/{LoginType.REGISTRATION_TOKEN}/fallback/web",
  145. error=e.msg,
  146. )
  147. else:
  148. html = self.success_template.render()
  149. else:
  150. raise SynapseError(404, "Unknown auth stage type")
  151. # Render the HTML and return.
  152. respond_with_html(request, 200, html)
  153. return None
  154. def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
  155. AuthRestServlet(hs).register(http_server)