groups.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962
  1. # Copyright 2017 Vector Creations Ltd
  2. # Copyright 2018 New Vector Ltd
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import logging
  16. from functools import wraps
  17. from typing import TYPE_CHECKING, Any, Awaitable, Callable, Optional, Tuple
  18. from twisted.web.server import Request
  19. from synapse.api.constants import (
  20. MAX_GROUP_CATEGORYID_LENGTH,
  21. MAX_GROUP_ROLEID_LENGTH,
  22. MAX_GROUPID_LENGTH,
  23. )
  24. from synapse.api.errors import Codes, SynapseError
  25. from synapse.handlers.groups_local import GroupsLocalHandler
  26. from synapse.http.server import HttpServer
  27. from synapse.http.servlet import (
  28. RestServlet,
  29. assert_params_in_dict,
  30. parse_json_object_from_request,
  31. )
  32. from synapse.http.site import SynapseRequest
  33. from synapse.types import GroupID, JsonDict
  34. from ._base import client_patterns
  35. if TYPE_CHECKING:
  36. from synapse.server import HomeServer
  37. logger = logging.getLogger(__name__)
  38. def _validate_group_id(
  39. f: Callable[..., Awaitable[Tuple[int, JsonDict]]]
  40. ) -> Callable[..., Awaitable[Tuple[int, JsonDict]]]:
  41. """Wrapper to validate the form of the group ID.
  42. Can be applied to any on_FOO methods that accepts a group ID as a URL parameter.
  43. """
  44. @wraps(f)
  45. def wrapper(
  46. self: RestServlet, request: Request, group_id: str, *args: Any, **kwargs: Any
  47. ) -> Awaitable[Tuple[int, JsonDict]]:
  48. if not GroupID.is_valid(group_id):
  49. raise SynapseError(400, "%s is not a legal group ID" % (group_id,))
  50. return f(self, request, group_id, *args, **kwargs)
  51. return wrapper
  52. class GroupServlet(RestServlet):
  53. """Get the group profile"""
  54. PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/profile$")
  55. def __init__(self, hs: "HomeServer"):
  56. super().__init__()
  57. self.auth = hs.get_auth()
  58. self.clock = hs.get_clock()
  59. self.groups_handler = hs.get_groups_local_handler()
  60. @_validate_group_id
  61. async def on_GET(
  62. self, request: SynapseRequest, group_id: str
  63. ) -> Tuple[int, JsonDict]:
  64. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  65. requester_user_id = requester.user.to_string()
  66. group_description = await self.groups_handler.get_group_profile(
  67. group_id, requester_user_id
  68. )
  69. return 200, group_description
  70. @_validate_group_id
  71. async def on_POST(
  72. self, request: SynapseRequest, group_id: str
  73. ) -> Tuple[int, JsonDict]:
  74. requester = await self.auth.get_user_by_req(request)
  75. requester_user_id = requester.user.to_string()
  76. content = parse_json_object_from_request(request)
  77. assert_params_in_dict(
  78. content, ("name", "avatar_url", "short_description", "long_description")
  79. )
  80. assert isinstance(
  81. self.groups_handler, GroupsLocalHandler
  82. ), "Workers cannot create group profiles."
  83. await self.groups_handler.update_group_profile(
  84. group_id, requester_user_id, content
  85. )
  86. return 200, {}
  87. class GroupSummaryServlet(RestServlet):
  88. """Get the full group summary"""
  89. PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/summary$")
  90. def __init__(self, hs: "HomeServer"):
  91. super().__init__()
  92. self.auth = hs.get_auth()
  93. self.clock = hs.get_clock()
  94. self.groups_handler = hs.get_groups_local_handler()
  95. @_validate_group_id
  96. async def on_GET(
  97. self, request: SynapseRequest, group_id: str
  98. ) -> Tuple[int, JsonDict]:
  99. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  100. requester_user_id = requester.user.to_string()
  101. get_group_summary = await self.groups_handler.get_group_summary(
  102. group_id, requester_user_id
  103. )
  104. return 200, get_group_summary
  105. class GroupSummaryRoomsCatServlet(RestServlet):
  106. """Update/delete a rooms entry in the summary.
  107. Matches both:
  108. - /groups/:group/summary/rooms/:room_id
  109. - /groups/:group/summary/categories/:category/rooms/:room_id
  110. """
  111. PATTERNS = client_patterns(
  112. "/groups/(?P<group_id>[^/]*)/summary"
  113. "(/categories/(?P<category_id>[^/]+))?"
  114. "/rooms/(?P<room_id>[^/]*)$"
  115. )
  116. def __init__(self, hs: "HomeServer"):
  117. super().__init__()
  118. self.auth = hs.get_auth()
  119. self.clock = hs.get_clock()
  120. self.groups_handler = hs.get_groups_local_handler()
  121. @_validate_group_id
  122. async def on_PUT(
  123. self,
  124. request: SynapseRequest,
  125. group_id: str,
  126. category_id: Optional[str],
  127. room_id: str,
  128. ) -> Tuple[int, JsonDict]:
  129. requester = await self.auth.get_user_by_req(request)
  130. requester_user_id = requester.user.to_string()
  131. if category_id == "":
  132. raise SynapseError(400, "category_id cannot be empty", Codes.INVALID_PARAM)
  133. if category_id and len(category_id) > MAX_GROUP_CATEGORYID_LENGTH:
  134. raise SynapseError(
  135. 400,
  136. "category_id may not be longer than %s characters"
  137. % (MAX_GROUP_CATEGORYID_LENGTH,),
  138. Codes.INVALID_PARAM,
  139. )
  140. content = parse_json_object_from_request(request)
  141. assert isinstance(
  142. self.groups_handler, GroupsLocalHandler
  143. ), "Workers cannot modify group summaries."
  144. resp = await self.groups_handler.update_group_summary_room(
  145. group_id,
  146. requester_user_id,
  147. room_id=room_id,
  148. category_id=category_id,
  149. content=content,
  150. )
  151. return 200, resp
  152. @_validate_group_id
  153. async def on_DELETE(
  154. self, request: SynapseRequest, group_id: str, category_id: str, room_id: str
  155. ) -> Tuple[int, JsonDict]:
  156. requester = await self.auth.get_user_by_req(request)
  157. requester_user_id = requester.user.to_string()
  158. assert isinstance(
  159. self.groups_handler, GroupsLocalHandler
  160. ), "Workers cannot modify group profiles."
  161. resp = await self.groups_handler.delete_group_summary_room(
  162. group_id, requester_user_id, room_id=room_id, category_id=category_id
  163. )
  164. return 200, resp
  165. class GroupCategoryServlet(RestServlet):
  166. """Get/add/update/delete a group category"""
  167. PATTERNS = client_patterns(
  168. "/groups/(?P<group_id>[^/]*)/categories/(?P<category_id>[^/]+)$"
  169. )
  170. def __init__(self, hs: "HomeServer"):
  171. super().__init__()
  172. self.auth = hs.get_auth()
  173. self.clock = hs.get_clock()
  174. self.groups_handler = hs.get_groups_local_handler()
  175. @_validate_group_id
  176. async def on_GET(
  177. self, request: SynapseRequest, group_id: str, category_id: str
  178. ) -> Tuple[int, JsonDict]:
  179. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  180. requester_user_id = requester.user.to_string()
  181. category = await self.groups_handler.get_group_category(
  182. group_id, requester_user_id, category_id=category_id
  183. )
  184. return 200, category
  185. @_validate_group_id
  186. async def on_PUT(
  187. self, request: SynapseRequest, group_id: str, category_id: str
  188. ) -> Tuple[int, JsonDict]:
  189. requester = await self.auth.get_user_by_req(request)
  190. requester_user_id = requester.user.to_string()
  191. if not category_id:
  192. raise SynapseError(400, "category_id cannot be empty", Codes.INVALID_PARAM)
  193. if len(category_id) > MAX_GROUP_CATEGORYID_LENGTH:
  194. raise SynapseError(
  195. 400,
  196. "category_id may not be longer than %s characters"
  197. % (MAX_GROUP_CATEGORYID_LENGTH,),
  198. Codes.INVALID_PARAM,
  199. )
  200. content = parse_json_object_from_request(request)
  201. assert isinstance(
  202. self.groups_handler, GroupsLocalHandler
  203. ), "Workers cannot modify group categories."
  204. resp = await self.groups_handler.update_group_category(
  205. group_id, requester_user_id, category_id=category_id, content=content
  206. )
  207. return 200, resp
  208. @_validate_group_id
  209. async def on_DELETE(
  210. self, request: SynapseRequest, group_id: str, category_id: str
  211. ) -> Tuple[int, JsonDict]:
  212. requester = await self.auth.get_user_by_req(request)
  213. requester_user_id = requester.user.to_string()
  214. assert isinstance(
  215. self.groups_handler, GroupsLocalHandler
  216. ), "Workers cannot modify group categories."
  217. resp = await self.groups_handler.delete_group_category(
  218. group_id, requester_user_id, category_id=category_id
  219. )
  220. return 200, resp
  221. class GroupCategoriesServlet(RestServlet):
  222. """Get all group categories"""
  223. PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/categories/$")
  224. def __init__(self, hs: "HomeServer"):
  225. super().__init__()
  226. self.auth = hs.get_auth()
  227. self.clock = hs.get_clock()
  228. self.groups_handler = hs.get_groups_local_handler()
  229. @_validate_group_id
  230. async def on_GET(
  231. self, request: SynapseRequest, group_id: str
  232. ) -> Tuple[int, JsonDict]:
  233. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  234. requester_user_id = requester.user.to_string()
  235. category = await self.groups_handler.get_group_categories(
  236. group_id, requester_user_id
  237. )
  238. return 200, category
  239. class GroupRoleServlet(RestServlet):
  240. """Get/add/update/delete a group role"""
  241. PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/roles/(?P<role_id>[^/]+)$")
  242. def __init__(self, hs: "HomeServer"):
  243. super().__init__()
  244. self.auth = hs.get_auth()
  245. self.clock = hs.get_clock()
  246. self.groups_handler = hs.get_groups_local_handler()
  247. @_validate_group_id
  248. async def on_GET(
  249. self, request: SynapseRequest, group_id: str, role_id: str
  250. ) -> Tuple[int, JsonDict]:
  251. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  252. requester_user_id = requester.user.to_string()
  253. category = await self.groups_handler.get_group_role(
  254. group_id, requester_user_id, role_id=role_id
  255. )
  256. return 200, category
  257. @_validate_group_id
  258. async def on_PUT(
  259. self, request: SynapseRequest, group_id: str, role_id: str
  260. ) -> Tuple[int, JsonDict]:
  261. requester = await self.auth.get_user_by_req(request)
  262. requester_user_id = requester.user.to_string()
  263. if not role_id:
  264. raise SynapseError(400, "role_id cannot be empty", Codes.INVALID_PARAM)
  265. if len(role_id) > MAX_GROUP_ROLEID_LENGTH:
  266. raise SynapseError(
  267. 400,
  268. "role_id may not be longer than %s characters"
  269. % (MAX_GROUP_ROLEID_LENGTH,),
  270. Codes.INVALID_PARAM,
  271. )
  272. content = parse_json_object_from_request(request)
  273. assert isinstance(
  274. self.groups_handler, GroupsLocalHandler
  275. ), "Workers cannot modify group roles."
  276. resp = await self.groups_handler.update_group_role(
  277. group_id, requester_user_id, role_id=role_id, content=content
  278. )
  279. return 200, resp
  280. @_validate_group_id
  281. async def on_DELETE(
  282. self, request: SynapseRequest, group_id: str, role_id: str
  283. ) -> Tuple[int, JsonDict]:
  284. requester = await self.auth.get_user_by_req(request)
  285. requester_user_id = requester.user.to_string()
  286. assert isinstance(
  287. self.groups_handler, GroupsLocalHandler
  288. ), "Workers cannot modify group roles."
  289. resp = await self.groups_handler.delete_group_role(
  290. group_id, requester_user_id, role_id=role_id
  291. )
  292. return 200, resp
  293. class GroupRolesServlet(RestServlet):
  294. """Get all group roles"""
  295. PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/roles/$")
  296. def __init__(self, hs: "HomeServer"):
  297. super().__init__()
  298. self.auth = hs.get_auth()
  299. self.clock = hs.get_clock()
  300. self.groups_handler = hs.get_groups_local_handler()
  301. @_validate_group_id
  302. async def on_GET(
  303. self, request: SynapseRequest, group_id: str
  304. ) -> Tuple[int, JsonDict]:
  305. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  306. requester_user_id = requester.user.to_string()
  307. category = await self.groups_handler.get_group_roles(
  308. group_id, requester_user_id
  309. )
  310. return 200, category
  311. class GroupSummaryUsersRoleServlet(RestServlet):
  312. """Update/delete a user's entry in the summary.
  313. Matches both:
  314. - /groups/:group/summary/users/:room_id
  315. - /groups/:group/summary/roles/:role/users/:user_id
  316. """
  317. PATTERNS = client_patterns(
  318. "/groups/(?P<group_id>[^/]*)/summary"
  319. "(/roles/(?P<role_id>[^/]+))?"
  320. "/users/(?P<user_id>[^/]*)$"
  321. )
  322. def __init__(self, hs: "HomeServer"):
  323. super().__init__()
  324. self.auth = hs.get_auth()
  325. self.clock = hs.get_clock()
  326. self.groups_handler = hs.get_groups_local_handler()
  327. @_validate_group_id
  328. async def on_PUT(
  329. self,
  330. request: SynapseRequest,
  331. group_id: str,
  332. role_id: Optional[str],
  333. user_id: str,
  334. ) -> Tuple[int, JsonDict]:
  335. requester = await self.auth.get_user_by_req(request)
  336. requester_user_id = requester.user.to_string()
  337. if role_id == "":
  338. raise SynapseError(400, "role_id cannot be empty", Codes.INVALID_PARAM)
  339. if role_id and len(role_id) > MAX_GROUP_ROLEID_LENGTH:
  340. raise SynapseError(
  341. 400,
  342. "role_id may not be longer than %s characters"
  343. % (MAX_GROUP_ROLEID_LENGTH,),
  344. Codes.INVALID_PARAM,
  345. )
  346. content = parse_json_object_from_request(request)
  347. assert isinstance(
  348. self.groups_handler, GroupsLocalHandler
  349. ), "Workers cannot modify group summaries."
  350. resp = await self.groups_handler.update_group_summary_user(
  351. group_id,
  352. requester_user_id,
  353. user_id=user_id,
  354. role_id=role_id,
  355. content=content,
  356. )
  357. return 200, resp
  358. @_validate_group_id
  359. async def on_DELETE(
  360. self, request: SynapseRequest, group_id: str, role_id: str, user_id: str
  361. ) -> Tuple[int, JsonDict]:
  362. requester = await self.auth.get_user_by_req(request)
  363. requester_user_id = requester.user.to_string()
  364. assert isinstance(
  365. self.groups_handler, GroupsLocalHandler
  366. ), "Workers cannot modify group summaries."
  367. resp = await self.groups_handler.delete_group_summary_user(
  368. group_id, requester_user_id, user_id=user_id, role_id=role_id
  369. )
  370. return 200, resp
  371. class GroupRoomServlet(RestServlet):
  372. """Get all rooms in a group"""
  373. PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/rooms$")
  374. def __init__(self, hs: "HomeServer"):
  375. super().__init__()
  376. self.auth = hs.get_auth()
  377. self.clock = hs.get_clock()
  378. self.groups_handler = hs.get_groups_local_handler()
  379. @_validate_group_id
  380. async def on_GET(
  381. self, request: SynapseRequest, group_id: str
  382. ) -> Tuple[int, JsonDict]:
  383. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  384. requester_user_id = requester.user.to_string()
  385. result = await self.groups_handler.get_rooms_in_group(
  386. group_id, requester_user_id
  387. )
  388. return 200, result
  389. class GroupUsersServlet(RestServlet):
  390. """Get all users in a group"""
  391. PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/users$")
  392. def __init__(self, hs: "HomeServer"):
  393. super().__init__()
  394. self.auth = hs.get_auth()
  395. self.clock = hs.get_clock()
  396. self.groups_handler = hs.get_groups_local_handler()
  397. @_validate_group_id
  398. async def on_GET(
  399. self, request: SynapseRequest, group_id: str
  400. ) -> Tuple[int, JsonDict]:
  401. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  402. requester_user_id = requester.user.to_string()
  403. result = await self.groups_handler.get_users_in_group(
  404. group_id, requester_user_id
  405. )
  406. return 200, result
  407. class GroupInvitedUsersServlet(RestServlet):
  408. """Get users invited to a group"""
  409. PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/invited_users$")
  410. def __init__(self, hs: "HomeServer"):
  411. super().__init__()
  412. self.auth = hs.get_auth()
  413. self.clock = hs.get_clock()
  414. self.groups_handler = hs.get_groups_local_handler()
  415. @_validate_group_id
  416. async def on_GET(
  417. self, request: SynapseRequest, group_id: str
  418. ) -> Tuple[int, JsonDict]:
  419. requester = await self.auth.get_user_by_req(request)
  420. requester_user_id = requester.user.to_string()
  421. result = await self.groups_handler.get_invited_users_in_group(
  422. group_id, requester_user_id
  423. )
  424. return 200, result
  425. class GroupSettingJoinPolicyServlet(RestServlet):
  426. """Set group join policy"""
  427. PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/settings/m.join_policy$")
  428. def __init__(self, hs: "HomeServer"):
  429. super().__init__()
  430. self.auth = hs.get_auth()
  431. self.groups_handler = hs.get_groups_local_handler()
  432. @_validate_group_id
  433. async def on_PUT(
  434. self, request: SynapseRequest, group_id: str
  435. ) -> Tuple[int, JsonDict]:
  436. requester = await self.auth.get_user_by_req(request)
  437. requester_user_id = requester.user.to_string()
  438. content = parse_json_object_from_request(request)
  439. assert isinstance(
  440. self.groups_handler, GroupsLocalHandler
  441. ), "Workers cannot modify group join policy."
  442. result = await self.groups_handler.set_group_join_policy(
  443. group_id, requester_user_id, content
  444. )
  445. return 200, result
  446. class GroupCreateServlet(RestServlet):
  447. """Create a group"""
  448. PATTERNS = client_patterns("/create_group$")
  449. def __init__(self, hs: "HomeServer"):
  450. super().__init__()
  451. self.auth = hs.get_auth()
  452. self.clock = hs.get_clock()
  453. self.groups_handler = hs.get_groups_local_handler()
  454. self.server_name = hs.hostname
  455. async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
  456. requester = await self.auth.get_user_by_req(request)
  457. requester_user_id = requester.user.to_string()
  458. # TODO: Create group on remote server
  459. content = parse_json_object_from_request(request)
  460. localpart = content.pop("localpart")
  461. group_id = GroupID(localpart, self.server_name).to_string()
  462. if not localpart:
  463. raise SynapseError(400, "Group ID cannot be empty", Codes.INVALID_PARAM)
  464. if len(group_id) > MAX_GROUPID_LENGTH:
  465. raise SynapseError(
  466. 400,
  467. "Group ID may not be longer than %s characters" % (MAX_GROUPID_LENGTH,),
  468. Codes.INVALID_PARAM,
  469. )
  470. assert isinstance(
  471. self.groups_handler, GroupsLocalHandler
  472. ), "Workers cannot create groups."
  473. result = await self.groups_handler.create_group(
  474. group_id, requester_user_id, content
  475. )
  476. return 200, result
  477. class GroupAdminRoomsServlet(RestServlet):
  478. """Add a room to the group"""
  479. PATTERNS = client_patterns(
  480. "/groups/(?P<group_id>[^/]*)/admin/rooms/(?P<room_id>[^/]*)$"
  481. )
  482. def __init__(self, hs: "HomeServer"):
  483. super().__init__()
  484. self.auth = hs.get_auth()
  485. self.clock = hs.get_clock()
  486. self.groups_handler = hs.get_groups_local_handler()
  487. @_validate_group_id
  488. async def on_PUT(
  489. self, request: SynapseRequest, group_id: str, room_id: str
  490. ) -> Tuple[int, JsonDict]:
  491. requester = await self.auth.get_user_by_req(request)
  492. requester_user_id = requester.user.to_string()
  493. content = parse_json_object_from_request(request)
  494. assert isinstance(
  495. self.groups_handler, GroupsLocalHandler
  496. ), "Workers cannot modify rooms in a group."
  497. result = await self.groups_handler.add_room_to_group(
  498. group_id, requester_user_id, room_id, content
  499. )
  500. return 200, result
  501. @_validate_group_id
  502. async def on_DELETE(
  503. self, request: SynapseRequest, group_id: str, room_id: str
  504. ) -> Tuple[int, JsonDict]:
  505. requester = await self.auth.get_user_by_req(request)
  506. requester_user_id = requester.user.to_string()
  507. assert isinstance(
  508. self.groups_handler, GroupsLocalHandler
  509. ), "Workers cannot modify group categories."
  510. result = await self.groups_handler.remove_room_from_group(
  511. group_id, requester_user_id, room_id
  512. )
  513. return 200, result
  514. class GroupAdminRoomsConfigServlet(RestServlet):
  515. """Update the config of a room in a group"""
  516. PATTERNS = client_patterns(
  517. "/groups/(?P<group_id>[^/]*)/admin/rooms/(?P<room_id>[^/]*)"
  518. "/config/(?P<config_key>[^/]*)$"
  519. )
  520. def __init__(self, hs: "HomeServer"):
  521. super().__init__()
  522. self.auth = hs.get_auth()
  523. self.clock = hs.get_clock()
  524. self.groups_handler = hs.get_groups_local_handler()
  525. @_validate_group_id
  526. async def on_PUT(
  527. self, request: SynapseRequest, group_id: str, room_id: str, config_key: str
  528. ) -> Tuple[int, JsonDict]:
  529. requester = await self.auth.get_user_by_req(request)
  530. requester_user_id = requester.user.to_string()
  531. content = parse_json_object_from_request(request)
  532. assert isinstance(
  533. self.groups_handler, GroupsLocalHandler
  534. ), "Workers cannot modify group categories."
  535. result = await self.groups_handler.update_room_in_group(
  536. group_id, requester_user_id, room_id, config_key, content
  537. )
  538. return 200, result
  539. class GroupAdminUsersInviteServlet(RestServlet):
  540. """Invite a user to the group"""
  541. PATTERNS = client_patterns(
  542. "/groups/(?P<group_id>[^/]*)/admin/users/invite/(?P<user_id>[^/]*)$"
  543. )
  544. def __init__(self, hs: "HomeServer"):
  545. super().__init__()
  546. self.auth = hs.get_auth()
  547. self.clock = hs.get_clock()
  548. self.groups_handler = hs.get_groups_local_handler()
  549. self.store = hs.get_datastores().main
  550. self.is_mine_id = hs.is_mine_id
  551. @_validate_group_id
  552. async def on_PUT(
  553. self, request: SynapseRequest, group_id: str, user_id: str
  554. ) -> Tuple[int, JsonDict]:
  555. requester = await self.auth.get_user_by_req(request)
  556. requester_user_id = requester.user.to_string()
  557. content = parse_json_object_from_request(request)
  558. config = content.get("config", {})
  559. assert isinstance(
  560. self.groups_handler, GroupsLocalHandler
  561. ), "Workers cannot invite users to a group."
  562. result = await self.groups_handler.invite(
  563. group_id, user_id, requester_user_id, config
  564. )
  565. return 200, result
  566. class GroupAdminUsersKickServlet(RestServlet):
  567. """Kick a user from the group"""
  568. PATTERNS = client_patterns(
  569. "/groups/(?P<group_id>[^/]*)/admin/users/remove/(?P<user_id>[^/]*)$"
  570. )
  571. def __init__(self, hs: "HomeServer"):
  572. super().__init__()
  573. self.auth = hs.get_auth()
  574. self.clock = hs.get_clock()
  575. self.groups_handler = hs.get_groups_local_handler()
  576. @_validate_group_id
  577. async def on_PUT(
  578. self, request: SynapseRequest, group_id: str, user_id: str
  579. ) -> Tuple[int, JsonDict]:
  580. requester = await self.auth.get_user_by_req(request)
  581. requester_user_id = requester.user.to_string()
  582. content = parse_json_object_from_request(request)
  583. assert isinstance(
  584. self.groups_handler, GroupsLocalHandler
  585. ), "Workers cannot kick users from a group."
  586. result = await self.groups_handler.remove_user_from_group(
  587. group_id, user_id, requester_user_id, content
  588. )
  589. return 200, result
  590. class GroupSelfLeaveServlet(RestServlet):
  591. """Leave a joined group"""
  592. PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/self/leave$")
  593. def __init__(self, hs: "HomeServer"):
  594. super().__init__()
  595. self.auth = hs.get_auth()
  596. self.clock = hs.get_clock()
  597. self.groups_handler = hs.get_groups_local_handler()
  598. @_validate_group_id
  599. async def on_PUT(
  600. self, request: SynapseRequest, group_id: str
  601. ) -> Tuple[int, JsonDict]:
  602. requester = await self.auth.get_user_by_req(request)
  603. requester_user_id = requester.user.to_string()
  604. content = parse_json_object_from_request(request)
  605. assert isinstance(
  606. self.groups_handler, GroupsLocalHandler
  607. ), "Workers cannot leave a group for a users."
  608. result = await self.groups_handler.remove_user_from_group(
  609. group_id, requester_user_id, requester_user_id, content
  610. )
  611. return 200, result
  612. class GroupSelfJoinServlet(RestServlet):
  613. """Attempt to join a group, or knock"""
  614. PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/self/join$")
  615. def __init__(self, hs: "HomeServer"):
  616. super().__init__()
  617. self.auth = hs.get_auth()
  618. self.clock = hs.get_clock()
  619. self.groups_handler = hs.get_groups_local_handler()
  620. @_validate_group_id
  621. async def on_PUT(
  622. self, request: SynapseRequest, group_id: str
  623. ) -> Tuple[int, JsonDict]:
  624. requester = await self.auth.get_user_by_req(request)
  625. requester_user_id = requester.user.to_string()
  626. content = parse_json_object_from_request(request)
  627. assert isinstance(
  628. self.groups_handler, GroupsLocalHandler
  629. ), "Workers cannot join a user to a group."
  630. result = await self.groups_handler.join_group(
  631. group_id, requester_user_id, content
  632. )
  633. return 200, result
  634. class GroupSelfAcceptInviteServlet(RestServlet):
  635. """Accept a group invite"""
  636. PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/self/accept_invite$")
  637. def __init__(self, hs: "HomeServer"):
  638. super().__init__()
  639. self.auth = hs.get_auth()
  640. self.clock = hs.get_clock()
  641. self.groups_handler = hs.get_groups_local_handler()
  642. @_validate_group_id
  643. async def on_PUT(
  644. self, request: SynapseRequest, group_id: str
  645. ) -> Tuple[int, JsonDict]:
  646. requester = await self.auth.get_user_by_req(request)
  647. requester_user_id = requester.user.to_string()
  648. content = parse_json_object_from_request(request)
  649. assert isinstance(
  650. self.groups_handler, GroupsLocalHandler
  651. ), "Workers cannot accept an invite to a group."
  652. result = await self.groups_handler.accept_invite(
  653. group_id, requester_user_id, content
  654. )
  655. return 200, result
  656. class GroupSelfUpdatePublicityServlet(RestServlet):
  657. """Update whether we publicise a users membership of a group"""
  658. PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/self/update_publicity$")
  659. def __init__(self, hs: "HomeServer"):
  660. super().__init__()
  661. self.auth = hs.get_auth()
  662. self.clock = hs.get_clock()
  663. self.store = hs.get_datastores().main
  664. @_validate_group_id
  665. async def on_PUT(
  666. self, request: SynapseRequest, group_id: str
  667. ) -> Tuple[int, JsonDict]:
  668. requester = await self.auth.get_user_by_req(request)
  669. requester_user_id = requester.user.to_string()
  670. content = parse_json_object_from_request(request)
  671. publicise = content["publicise"]
  672. await self.store.update_group_publicity(group_id, requester_user_id, publicise)
  673. return 200, {}
  674. class PublicisedGroupsForUserServlet(RestServlet):
  675. """Get the list of groups a user is advertising"""
  676. PATTERNS = client_patterns("/publicised_groups/(?P<user_id>[^/]*)$")
  677. def __init__(self, hs: "HomeServer"):
  678. super().__init__()
  679. self.auth = hs.get_auth()
  680. self.clock = hs.get_clock()
  681. self.store = hs.get_datastores().main
  682. self.groups_handler = hs.get_groups_local_handler()
  683. async def on_GET(
  684. self, request: SynapseRequest, user_id: str
  685. ) -> Tuple[int, JsonDict]:
  686. await self.auth.get_user_by_req(request, allow_guest=True)
  687. result = await self.groups_handler.get_publicised_groups_for_user(user_id)
  688. return 200, result
  689. class PublicisedGroupsForUsersServlet(RestServlet):
  690. """Get the list of groups a user is advertising"""
  691. PATTERNS = client_patterns("/publicised_groups$")
  692. def __init__(self, hs: "HomeServer"):
  693. super().__init__()
  694. self.auth = hs.get_auth()
  695. self.clock = hs.get_clock()
  696. self.store = hs.get_datastores().main
  697. self.groups_handler = hs.get_groups_local_handler()
  698. async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
  699. await self.auth.get_user_by_req(request, allow_guest=True)
  700. content = parse_json_object_from_request(request)
  701. user_ids = content["user_ids"]
  702. result = await self.groups_handler.bulk_get_publicised_groups(user_ids)
  703. return 200, result
  704. class GroupsForUserServlet(RestServlet):
  705. """Get all groups the logged in user is joined to"""
  706. PATTERNS = client_patterns("/joined_groups$")
  707. def __init__(self, hs: "HomeServer"):
  708. super().__init__()
  709. self.auth = hs.get_auth()
  710. self.clock = hs.get_clock()
  711. self.groups_handler = hs.get_groups_local_handler()
  712. async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
  713. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  714. requester_user_id = requester.user.to_string()
  715. result = await self.groups_handler.get_joined_groups(requester_user_id)
  716. return 200, result
  717. def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
  718. GroupServlet(hs).register(http_server)
  719. GroupSummaryServlet(hs).register(http_server)
  720. GroupInvitedUsersServlet(hs).register(http_server)
  721. GroupUsersServlet(hs).register(http_server)
  722. GroupRoomServlet(hs).register(http_server)
  723. GroupSettingJoinPolicyServlet(hs).register(http_server)
  724. GroupCreateServlet(hs).register(http_server)
  725. GroupAdminRoomsServlet(hs).register(http_server)
  726. GroupAdminRoomsConfigServlet(hs).register(http_server)
  727. GroupAdminUsersInviteServlet(hs).register(http_server)
  728. GroupAdminUsersKickServlet(hs).register(http_server)
  729. GroupSelfLeaveServlet(hs).register(http_server)
  730. GroupSelfJoinServlet(hs).register(http_server)
  731. GroupSelfAcceptInviteServlet(hs).register(http_server)
  732. GroupsForUserServlet(hs).register(http_server)
  733. GroupCategoryServlet(hs).register(http_server)
  734. GroupCategoriesServlet(hs).register(http_server)
  735. GroupSummaryRoomsCatServlet(hs).register(http_server)
  736. GroupRoleServlet(hs).register(http_server)
  737. GroupRolesServlet(hs).register(http_server)
  738. GroupSelfUpdatePublicityServlet(hs).register(http_server)
  739. GroupSummaryUsersRoleServlet(hs).register(http_server)
  740. PublicisedGroupsForUserServlet(hs).register(http_server)
  741. PublicisedGroupsForUsersServlet(hs).register(http_server)