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.
 
 
 
 
 
 

326 lines
11 KiB

  1. # Copyright 2014-2016 OpenMarket Ltd
  2. # Copyright 2018 New Vector Ltd
  3. # Copyright 2019-2021 The Matrix.org Foundation C.I.C.
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. import logging
  17. from typing import TYPE_CHECKING, List, Optional, Tuple, cast
  18. from synapse.config.homeserver import HomeServerConfig
  19. from synapse.storage.database import (
  20. DatabasePool,
  21. LoggingDatabaseConnection,
  22. LoggingTransaction,
  23. )
  24. from synapse.storage.databases.main.stats import UserSortOrder
  25. from synapse.storage.engines import BaseDatabaseEngine
  26. from synapse.storage.types import Cursor
  27. from synapse.storage.util.id_generators import StreamIdGenerator
  28. from synapse.types import JsonDict, get_domain_from_id
  29. from synapse.util.caches.stream_change_cache import StreamChangeCache
  30. from .account_data import AccountDataStore
  31. from .appservice import ApplicationServiceStore, ApplicationServiceTransactionStore
  32. from .cache import CacheInvalidationWorkerStore
  33. from .censor_events import CensorEventsStore
  34. from .client_ips import ClientIpWorkerStore
  35. from .deviceinbox import DeviceInboxStore
  36. from .devices import DeviceStore
  37. from .directory import DirectoryStore
  38. from .e2e_room_keys import EndToEndRoomKeyStore
  39. from .end_to_end_keys import EndToEndKeyStore
  40. from .event_federation import EventFederationStore
  41. from .event_push_actions import EventPushActionsStore
  42. from .events_bg_updates import EventsBackgroundUpdatesStore
  43. from .events_forward_extremities import EventForwardExtremitiesStore
  44. from .filtering import FilteringStore
  45. from .keys import KeyStore
  46. from .lock import LockStore
  47. from .media_repository import MediaRepositoryStore
  48. from .metrics import ServerMetricsStore
  49. from .monthly_active_users import MonthlyActiveUsersWorkerStore
  50. from .openid import OpenIdStore
  51. from .presence import PresenceStore
  52. from .profile import ProfileStore
  53. from .purge_events import PurgeEventsStore
  54. from .push_rule import PushRuleStore
  55. from .pusher import PusherStore
  56. from .receipts import ReceiptsStore
  57. from .registration import RegistrationStore
  58. from .rejections import RejectionsStore
  59. from .relations import RelationsStore
  60. from .room import RoomStore
  61. from .room_batch import RoomBatchStore
  62. from .roommember import RoomMemberStore
  63. from .search import SearchStore
  64. from .session import SessionStore
  65. from .signatures import SignatureStore
  66. from .state import StateStore
  67. from .stats import StatsStore
  68. from .stream import StreamWorkerStore
  69. from .tags import TagsStore
  70. from .transactions import TransactionWorkerStore
  71. from .ui_auth import UIAuthStore
  72. from .user_directory import UserDirectoryStore
  73. from .user_erasure_store import UserErasureStore
  74. if TYPE_CHECKING:
  75. from synapse.server import HomeServer
  76. logger = logging.getLogger(__name__)
  77. class DataStore(
  78. EventsBackgroundUpdatesStore,
  79. DeviceStore,
  80. RoomMemberStore,
  81. RoomStore,
  82. RoomBatchStore,
  83. RegistrationStore,
  84. ProfileStore,
  85. PresenceStore,
  86. TransactionWorkerStore,
  87. DirectoryStore,
  88. KeyStore,
  89. StateStore,
  90. SignatureStore,
  91. ApplicationServiceStore,
  92. PurgeEventsStore,
  93. EventFederationStore,
  94. MediaRepositoryStore,
  95. RejectionsStore,
  96. FilteringStore,
  97. PusherStore,
  98. PushRuleStore,
  99. ApplicationServiceTransactionStore,
  100. EventPushActionsStore,
  101. ServerMetricsStore,
  102. ReceiptsStore,
  103. EndToEndKeyStore,
  104. EndToEndRoomKeyStore,
  105. SearchStore,
  106. TagsStore,
  107. AccountDataStore,
  108. StreamWorkerStore,
  109. OpenIdStore,
  110. ClientIpWorkerStore,
  111. DeviceInboxStore,
  112. UserDirectoryStore,
  113. UserErasureStore,
  114. MonthlyActiveUsersWorkerStore,
  115. StatsStore,
  116. RelationsStore,
  117. CensorEventsStore,
  118. UIAuthStore,
  119. EventForwardExtremitiesStore,
  120. CacheInvalidationWorkerStore,
  121. LockStore,
  122. SessionStore,
  123. ):
  124. def __init__(
  125. self,
  126. database: DatabasePool,
  127. db_conn: LoggingDatabaseConnection,
  128. hs: "HomeServer",
  129. ):
  130. self.hs = hs
  131. self._clock = hs.get_clock()
  132. self.database_engine = database.engine
  133. self._device_list_id_gen = StreamIdGenerator(
  134. db_conn,
  135. "device_lists_stream",
  136. "stream_id",
  137. extra_tables=[
  138. ("user_signature_stream", "stream_id"),
  139. ("device_lists_outbound_pokes", "stream_id"),
  140. ("device_lists_changes_in_room", "stream_id"),
  141. ],
  142. )
  143. super().__init__(database, db_conn, hs)
  144. events_max = self._stream_id_gen.get_current_token()
  145. curr_state_delta_prefill, min_curr_state_delta_id = self.db_pool.get_cache_dict(
  146. db_conn,
  147. "current_state_delta_stream",
  148. entity_column="room_id",
  149. stream_column="stream_id",
  150. max_value=events_max, # As we share the stream id with events token
  151. limit=1000,
  152. )
  153. self._curr_state_delta_stream_cache = StreamChangeCache(
  154. "_curr_state_delta_stream_cache",
  155. min_curr_state_delta_id,
  156. prefilled_cache=curr_state_delta_prefill,
  157. )
  158. self._stream_order_on_start = self.get_room_max_stream_ordering()
  159. self._min_stream_order_on_start = self.get_room_min_stream_ordering()
  160. def get_device_stream_token(self) -> int:
  161. # TODO: shouldn't this be moved to `DeviceWorkerStore`?
  162. return self._device_list_id_gen.get_current_token()
  163. async def get_users(self) -> List[JsonDict]:
  164. """Function to retrieve a list of users in users table.
  165. Returns:
  166. A list of dictionaries representing users.
  167. """
  168. return await self.db_pool.simple_select_list(
  169. table="users",
  170. keyvalues={},
  171. retcols=[
  172. "name",
  173. "password_hash",
  174. "is_guest",
  175. "admin",
  176. "user_type",
  177. "deactivated",
  178. ],
  179. desc="get_users",
  180. )
  181. async def get_users_paginate(
  182. self,
  183. start: int,
  184. limit: int,
  185. user_id: Optional[str] = None,
  186. name: Optional[str] = None,
  187. guests: bool = True,
  188. deactivated: bool = False,
  189. order_by: str = UserSortOrder.USER_ID.value,
  190. direction: str = "f",
  191. ) -> Tuple[List[JsonDict], int]:
  192. """Function to retrieve a paginated list of users from
  193. users list. This will return a json list of users and the
  194. total number of users matching the filter criteria.
  195. Args:
  196. start: start number to begin the query from
  197. limit: number of rows to retrieve
  198. user_id: search for user_id. ignored if name is not None
  199. name: search for local part of user_id or display name
  200. guests: whether to in include guest users
  201. deactivated: whether to include deactivated users
  202. order_by: the sort order of the returned list
  203. direction: sort ascending or descending
  204. Returns:
  205. A tuple of a list of mappings from user to information and a count of total users.
  206. """
  207. def get_users_paginate_txn(
  208. txn: LoggingTransaction,
  209. ) -> Tuple[List[JsonDict], int]:
  210. filters = []
  211. args = [self.hs.config.server.server_name]
  212. # Set ordering
  213. order_by_column = UserSortOrder(order_by).value
  214. if direction == "b":
  215. order = "DESC"
  216. else:
  217. order = "ASC"
  218. # `name` is in database already in lower case
  219. if name:
  220. filters.append("(name LIKE ? OR LOWER(displayname) LIKE ?)")
  221. args.extend(["@%" + name.lower() + "%:%", "%" + name.lower() + "%"])
  222. elif user_id:
  223. filters.append("name LIKE ?")
  224. args.extend(["%" + user_id.lower() + "%"])
  225. if not guests:
  226. filters.append("is_guest = 0")
  227. if not deactivated:
  228. filters.append("deactivated = 0")
  229. where_clause = "WHERE " + " AND ".join(filters) if len(filters) > 0 else ""
  230. sql_base = f"""
  231. FROM users as u
  232. LEFT JOIN profiles AS p ON u.name = '@' || p.user_id || ':' || ?
  233. {where_clause}
  234. """
  235. sql = "SELECT COUNT(*) as total_users " + sql_base
  236. txn.execute(sql, args)
  237. count = cast(Tuple[int], txn.fetchone())[0]
  238. sql = f"""
  239. SELECT name, user_type, is_guest, admin, deactivated, shadow_banned,
  240. displayname, avatar_url, creation_ts * 1000 as creation_ts
  241. {sql_base}
  242. ORDER BY {order_by_column} {order}, u.name ASC
  243. LIMIT ? OFFSET ?
  244. """
  245. args += [limit, start]
  246. txn.execute(sql, args)
  247. users = self.db_pool.cursor_to_dict(txn)
  248. return users, count
  249. return await self.db_pool.runInteraction(
  250. "get_users_paginate_txn", get_users_paginate_txn
  251. )
  252. async def search_users(self, term: str) -> Optional[List[JsonDict]]:
  253. """Function to search users list for one or more users with
  254. the matched term.
  255. Args:
  256. term: search term
  257. Returns:
  258. A list of dictionaries or None.
  259. """
  260. return await self.db_pool.simple_search_list(
  261. table="users",
  262. term=term,
  263. col="name",
  264. retcols=["name", "password_hash", "is_guest", "admin", "user_type"],
  265. desc="search_users",
  266. )
  267. def check_database_before_upgrade(
  268. cur: Cursor, database_engine: BaseDatabaseEngine, config: HomeServerConfig
  269. ) -> None:
  270. """Called before upgrading an existing database to check that it is broadly sane
  271. compared with the configuration.
  272. """
  273. logger.info("Checking database for consistency with configuration...")
  274. # if there are any users in the database, check that the username matches our
  275. # configured server name.
  276. cur.execute("SELECT name FROM users LIMIT 1")
  277. rows = cur.fetchall()
  278. if not rows:
  279. return
  280. user_domain = get_domain_from_id(rows[0][0])
  281. if user_domain == config.server.server_name:
  282. return
  283. raise Exception(
  284. "Found users in database not native to %s!\n"
  285. "You cannot change a synapse server_name after it's been configured"
  286. % (config.server.server_name,)
  287. )
  288. __all__ = ["DataStore", "check_database_before_upgrade"]