account_validity_callbacks.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. # Copyright 2023 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.
  14. import logging
  15. from typing import Awaitable, Callable, List, Optional, Tuple
  16. from twisted.web.http import Request
  17. logger = logging.getLogger(__name__)
  18. # Types for callbacks to be registered via the module api
  19. IS_USER_EXPIRED_CALLBACK = Callable[[str], Awaitable[Optional[bool]]]
  20. ON_USER_REGISTRATION_CALLBACK = Callable[[str], Awaitable]
  21. ON_USER_LOGIN_CALLBACK = Callable[[str, Optional[str], Optional[str]], Awaitable]
  22. # Temporary hooks to allow for a transition from `/_matrix/client` endpoints
  23. # to `/_synapse/client/account_validity`. See `register_callbacks` below.
  24. ON_LEGACY_SEND_MAIL_CALLBACK = Callable[[str], Awaitable]
  25. ON_LEGACY_RENEW_CALLBACK = Callable[[str], Awaitable[Tuple[bool, bool, int]]]
  26. ON_LEGACY_ADMIN_REQUEST = Callable[[Request], Awaitable]
  27. class AccountValidityModuleApiCallbacks:
  28. def __init__(self) -> None:
  29. self.is_user_expired_callbacks: List[IS_USER_EXPIRED_CALLBACK] = []
  30. self.on_user_registration_callbacks: List[ON_USER_REGISTRATION_CALLBACK] = []
  31. self.on_user_login_callbacks: List[ON_USER_LOGIN_CALLBACK] = []
  32. self.on_legacy_send_mail_callback: Optional[ON_LEGACY_SEND_MAIL_CALLBACK] = None
  33. self.on_legacy_renew_callback: Optional[ON_LEGACY_RENEW_CALLBACK] = None
  34. # The legacy admin requests callback isn't a protected attribute because we need
  35. # to access it from the admin servlet, which is outside of this handler.
  36. self.on_legacy_admin_request_callback: Optional[ON_LEGACY_ADMIN_REQUEST] = None
  37. def register_callbacks(
  38. self,
  39. is_user_expired: Optional[IS_USER_EXPIRED_CALLBACK] = None,
  40. on_user_registration: Optional[ON_USER_REGISTRATION_CALLBACK] = None,
  41. on_user_login: Optional[ON_USER_LOGIN_CALLBACK] = None,
  42. on_legacy_send_mail: Optional[ON_LEGACY_SEND_MAIL_CALLBACK] = None,
  43. on_legacy_renew: Optional[ON_LEGACY_RENEW_CALLBACK] = None,
  44. on_legacy_admin_request: Optional[ON_LEGACY_ADMIN_REQUEST] = None,
  45. ) -> None:
  46. """Register callbacks from module for each hook."""
  47. if is_user_expired is not None:
  48. self.is_user_expired_callbacks.append(is_user_expired)
  49. if on_user_registration is not None:
  50. self.on_user_registration_callbacks.append(on_user_registration)
  51. if on_user_login is not None:
  52. self.on_user_login_callbacks.append(on_user_login)
  53. # The builtin account validity feature exposes 3 endpoints (send_mail, renew, and
  54. # an admin one). As part of moving the feature into a module, we need to change
  55. # the path from /_matrix/client/unstable/account_validity/... to
  56. # /_synapse/client/account_validity, because:
  57. #
  58. # * the feature isn't part of the Matrix spec thus shouldn't live under /_matrix
  59. # * the way we register servlets means that modules can't register resources
  60. # under /_matrix/client
  61. #
  62. # We need to allow for a transition period between the old and new endpoints
  63. # in order to allow for clients to update (and for emails to be processed).
  64. #
  65. # Once the email-account-validity module is loaded, it will take control of account
  66. # validity by moving the rows from our `account_validity` table into its own table.
  67. #
  68. # Therefore, we need to allow modules (in practice just the one implementing the
  69. # email-based account validity) to temporarily hook into the legacy endpoints so we
  70. # can route the traffic coming into the old endpoints into the module, which is
  71. # why we have the following three temporary hooks.
  72. if on_legacy_send_mail is not None:
  73. if self.on_legacy_send_mail_callback is not None:
  74. raise RuntimeError("Tried to register on_legacy_send_mail twice")
  75. self.on_legacy_send_mail_callback = on_legacy_send_mail
  76. if on_legacy_renew is not None:
  77. if self.on_legacy_renew_callback is not None:
  78. raise RuntimeError("Tried to register on_legacy_renew twice")
  79. self.on_legacy_renew_callback = on_legacy_renew
  80. if on_legacy_admin_request is not None:
  81. if self.on_legacy_admin_request_callback is not None:
  82. raise RuntimeError("Tried to register on_legacy_admin_request twice")
  83. self.on_legacy_admin_request_callback = on_legacy_admin_request