__init__.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  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.api.constants import Direction
  19. from synapse.config.homeserver import HomeServerConfig
  20. from synapse.storage._base import make_in_list_sql_clause
  21. from synapse.storage.database import (
  22. DatabasePool,
  23. LoggingDatabaseConnection,
  24. LoggingTransaction,
  25. )
  26. from synapse.storage.databases.main.stats import UserSortOrder
  27. from synapse.storage.engines import BaseDatabaseEngine
  28. from synapse.storage.types import Cursor
  29. from synapse.types import JsonDict, get_domain_from_id
  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 .experimental_features import ExperimentalFeaturesStore
  45. from .filtering import FilteringWorkerStore
  46. from .keys import KeyStore
  47. from .lock import LockStore
  48. from .media_repository import MediaRepositoryStore
  49. from .metrics import ServerMetricsStore
  50. from .monthly_active_users import MonthlyActiveUsersWorkerStore
  51. from .openid import OpenIdStore
  52. from .presence import PresenceStore
  53. from .profile import ProfileStore
  54. from .purge_events import PurgeEventsStore
  55. from .push_rule import PushRuleStore
  56. from .pusher import PusherStore
  57. from .receipts import ReceiptsStore
  58. from .registration import RegistrationStore
  59. from .rejections import RejectionsStore
  60. from .relations import RelationsStore
  61. from .room import RoomStore
  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. ExperimentalFeaturesStore,
  80. DeviceStore,
  81. RoomMemberStore,
  82. RoomStore,
  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. FilteringWorkerStore,
  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. super().__init__(database, db_conn, hs)
  134. async def get_users(self) -> List[JsonDict]:
  135. """Function to retrieve a list of users in users table.
  136. Returns:
  137. A list of dictionaries representing users.
  138. """
  139. return await self.db_pool.simple_select_list(
  140. table="users",
  141. keyvalues={},
  142. retcols=[
  143. "name",
  144. "password_hash",
  145. "is_guest",
  146. "admin",
  147. "user_type",
  148. "deactivated",
  149. ],
  150. desc="get_users",
  151. )
  152. async def get_users_paginate(
  153. self,
  154. start: int,
  155. limit: int,
  156. user_id: Optional[str] = None,
  157. name: Optional[str] = None,
  158. guests: bool = True,
  159. deactivated: bool = False,
  160. order_by: str = UserSortOrder.NAME.value,
  161. direction: Direction = Direction.FORWARDS,
  162. approved: bool = True,
  163. not_user_types: Optional[List[str]] = None,
  164. ) -> Tuple[List[JsonDict], int]:
  165. """Function to retrieve a paginated list of users from
  166. users list. This will return a json list of users and the
  167. total number of users matching the filter criteria.
  168. Args:
  169. start: start number to begin the query from
  170. limit: number of rows to retrieve
  171. user_id: search for user_id. ignored if name is not None
  172. name: search for local part of user_id or display name
  173. guests: whether to in include guest users
  174. deactivated: whether to include deactivated users
  175. order_by: the sort order of the returned list
  176. direction: sort ascending or descending
  177. approved: whether to include approved users
  178. not_user_types: list of user types to exclude
  179. Returns:
  180. A tuple of a list of mappings from user to information and a count of total users.
  181. """
  182. def get_users_paginate_txn(
  183. txn: LoggingTransaction,
  184. ) -> Tuple[List[JsonDict], int]:
  185. filters = []
  186. args = [self.hs.config.server.server_name]
  187. # Set ordering
  188. order_by_column = UserSortOrder(order_by).value
  189. if direction == Direction.BACKWARDS:
  190. order = "DESC"
  191. else:
  192. order = "ASC"
  193. # `name` is in database already in lower case
  194. if name:
  195. filters.append("(name LIKE ? OR LOWER(displayname) LIKE ?)")
  196. args.extend(["@%" + name.lower() + "%:%", "%" + name.lower() + "%"])
  197. elif user_id:
  198. filters.append("name LIKE ?")
  199. args.extend(["%" + user_id.lower() + "%"])
  200. if not guests:
  201. filters.append("is_guest = 0")
  202. if not deactivated:
  203. filters.append("deactivated = 0")
  204. if not approved:
  205. # We ignore NULL values for the approved flag because these should only
  206. # be already existing users that we consider as already approved.
  207. filters.append("approved IS FALSE")
  208. if not_user_types:
  209. if len(not_user_types) == 1 and not_user_types[0] == "":
  210. # Only exclude NULL type users
  211. filters.append("user_type IS NOT NULL")
  212. else:
  213. not_user_types_has_empty = False
  214. not_user_types_without_empty = []
  215. for not_user_type in not_user_types:
  216. if not_user_type == "":
  217. not_user_types_has_empty = True
  218. else:
  219. not_user_types_without_empty.append(not_user_type)
  220. not_user_type_clause, not_user_type_args = make_in_list_sql_clause(
  221. self.database_engine,
  222. "u.user_type",
  223. not_user_types_without_empty,
  224. )
  225. if not_user_types_has_empty:
  226. # NULL values should be excluded.
  227. # They evaluate to false > nothing to do here.
  228. filters.append("NOT %s" % (not_user_type_clause))
  229. else:
  230. # NULL values should *not* be excluded.
  231. # Add a special predicate to the query.
  232. filters.append(
  233. "(NOT %s OR %s IS NULL)"
  234. % (not_user_type_clause, "u.user_type")
  235. )
  236. args.extend(not_user_type_args)
  237. where_clause = "WHERE " + " AND ".join(filters) if len(filters) > 0 else ""
  238. sql_base = f"""
  239. FROM users as u
  240. LEFT JOIN profiles AS p ON u.name = '@' || p.user_id || ':' || ?
  241. LEFT JOIN erased_users AS eu ON u.name = eu.user_id
  242. {where_clause}
  243. """
  244. sql = "SELECT COUNT(*) as total_users " + sql_base
  245. txn.execute(sql, args)
  246. count = cast(Tuple[int], txn.fetchone())[0]
  247. sql = f"""
  248. SELECT name, user_type, is_guest, admin, deactivated, shadow_banned,
  249. displayname, avatar_url, creation_ts * 1000 as creation_ts, approved,
  250. eu.user_id is not null as erased
  251. {sql_base}
  252. ORDER BY {order_by_column} {order}, u.name ASC
  253. LIMIT ? OFFSET ?
  254. """
  255. args += [limit, start]
  256. txn.execute(sql, args)
  257. users = self.db_pool.cursor_to_dict(txn)
  258. # some of those boolean values are returned as integers when we're on SQLite
  259. columns_to_boolify = ["erased"]
  260. for user in users:
  261. for column in columns_to_boolify:
  262. user[column] = bool(user[column])
  263. return users, count
  264. return await self.db_pool.runInteraction(
  265. "get_users_paginate_txn", get_users_paginate_txn
  266. )
  267. async def search_users(self, term: str) -> Optional[List[JsonDict]]:
  268. """Function to search users list for one or more users with
  269. the matched term.
  270. Args:
  271. term: search term
  272. Returns:
  273. A list of dictionaries or None.
  274. """
  275. return await self.db_pool.simple_search_list(
  276. table="users",
  277. term=term,
  278. col="name",
  279. retcols=["name", "password_hash", "is_guest", "admin", "user_type"],
  280. desc="search_users",
  281. )
  282. def check_database_before_upgrade(
  283. cur: Cursor, database_engine: BaseDatabaseEngine, config: HomeServerConfig
  284. ) -> None:
  285. """Called before upgrading an existing database to check that it is broadly sane
  286. compared with the configuration.
  287. """
  288. logger.info("Checking database for consistency with configuration...")
  289. # if there are any users in the database, check that the username matches our
  290. # configured server name.
  291. cur.execute("SELECT name FROM users LIMIT 1")
  292. rows = cur.fetchall()
  293. if not rows:
  294. return
  295. user_domain = get_domain_from_id(rows[0][0])
  296. if user_domain == config.server.server_name:
  297. return
  298. raise Exception(
  299. "Found users in database not native to %s!\n"
  300. "You cannot change a synapse server_name after it's been configured"
  301. % (config.server.server_name,)
  302. )
  303. __all__ = ["DataStore", "check_database_before_upgrade"]