groups_server.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  1. # Copyright 2021 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. from typing import Dict, List, Tuple, Type
  15. from typing_extensions import Literal
  16. from synapse.api.constants import MAX_GROUP_CATEGORYID_LENGTH, MAX_GROUP_ROLEID_LENGTH
  17. from synapse.api.errors import Codes, SynapseError
  18. from synapse.federation.transport.server._base import (
  19. Authenticator,
  20. BaseFederationServlet,
  21. )
  22. from synapse.http.servlet import parse_string_from_args
  23. from synapse.server import HomeServer
  24. from synapse.types import JsonDict, get_domain_from_id
  25. from synapse.util.ratelimitutils import FederationRateLimiter
  26. class BaseGroupsServerServlet(BaseFederationServlet):
  27. """Abstract base class for federation servlet classes which provides a groups server handler.
  28. See BaseFederationServlet for more information.
  29. """
  30. def __init__(
  31. self,
  32. hs: HomeServer,
  33. authenticator: Authenticator,
  34. ratelimiter: FederationRateLimiter,
  35. server_name: str,
  36. ):
  37. super().__init__(hs, authenticator, ratelimiter, server_name)
  38. self.handler = hs.get_groups_server_handler()
  39. class FederationGroupsProfileServlet(BaseGroupsServerServlet):
  40. """Get/set the basic profile of a group on behalf of a user"""
  41. PATH = "/groups/(?P<group_id>[^/]*)/profile"
  42. async def on_GET(
  43. self,
  44. origin: str,
  45. content: Literal[None],
  46. query: Dict[bytes, List[bytes]],
  47. group_id: str,
  48. ) -> Tuple[int, JsonDict]:
  49. requester_user_id = parse_string_from_args(
  50. query, "requester_user_id", required=True
  51. )
  52. if get_domain_from_id(requester_user_id) != origin:
  53. raise SynapseError(403, "requester_user_id doesn't match origin")
  54. new_content = await self.handler.get_group_profile(group_id, requester_user_id)
  55. return 200, new_content
  56. async def on_POST(
  57. self,
  58. origin: str,
  59. content: JsonDict,
  60. query: Dict[bytes, List[bytes]],
  61. group_id: str,
  62. ) -> Tuple[int, JsonDict]:
  63. requester_user_id = parse_string_from_args(
  64. query, "requester_user_id", required=True
  65. )
  66. if get_domain_from_id(requester_user_id) != origin:
  67. raise SynapseError(403, "requester_user_id doesn't match origin")
  68. new_content = await self.handler.update_group_profile(
  69. group_id, requester_user_id, content
  70. )
  71. return 200, new_content
  72. class FederationGroupsSummaryServlet(BaseGroupsServerServlet):
  73. PATH = "/groups/(?P<group_id>[^/]*)/summary"
  74. async def on_GET(
  75. self,
  76. origin: str,
  77. content: Literal[None],
  78. query: Dict[bytes, List[bytes]],
  79. group_id: str,
  80. ) -> Tuple[int, JsonDict]:
  81. requester_user_id = parse_string_from_args(
  82. query, "requester_user_id", required=True
  83. )
  84. if get_domain_from_id(requester_user_id) != origin:
  85. raise SynapseError(403, "requester_user_id doesn't match origin")
  86. new_content = await self.handler.get_group_summary(group_id, requester_user_id)
  87. return 200, new_content
  88. class FederationGroupsRoomsServlet(BaseGroupsServerServlet):
  89. """Get the rooms in a group on behalf of a user"""
  90. PATH = "/groups/(?P<group_id>[^/]*)/rooms"
  91. async def on_GET(
  92. self,
  93. origin: str,
  94. content: Literal[None],
  95. query: Dict[bytes, List[bytes]],
  96. group_id: str,
  97. ) -> Tuple[int, JsonDict]:
  98. requester_user_id = parse_string_from_args(
  99. query, "requester_user_id", required=True
  100. )
  101. if get_domain_from_id(requester_user_id) != origin:
  102. raise SynapseError(403, "requester_user_id doesn't match origin")
  103. new_content = await self.handler.get_rooms_in_group(group_id, requester_user_id)
  104. return 200, new_content
  105. class FederationGroupsAddRoomsServlet(BaseGroupsServerServlet):
  106. """Add/remove room from group"""
  107. PATH = "/groups/(?P<group_id>[^/]*)/room/(?P<room_id>[^/]*)"
  108. async def on_POST(
  109. self,
  110. origin: str,
  111. content: JsonDict,
  112. query: Dict[bytes, List[bytes]],
  113. group_id: str,
  114. room_id: str,
  115. ) -> Tuple[int, JsonDict]:
  116. requester_user_id = parse_string_from_args(
  117. query, "requester_user_id", required=True
  118. )
  119. if get_domain_from_id(requester_user_id) != origin:
  120. raise SynapseError(403, "requester_user_id doesn't match origin")
  121. new_content = await self.handler.add_room_to_group(
  122. group_id, requester_user_id, room_id, content
  123. )
  124. return 200, new_content
  125. async def on_DELETE(
  126. self,
  127. origin: str,
  128. content: Literal[None],
  129. query: Dict[bytes, List[bytes]],
  130. group_id: str,
  131. room_id: str,
  132. ) -> Tuple[int, JsonDict]:
  133. requester_user_id = parse_string_from_args(
  134. query, "requester_user_id", required=True
  135. )
  136. if get_domain_from_id(requester_user_id) != origin:
  137. raise SynapseError(403, "requester_user_id doesn't match origin")
  138. new_content = await self.handler.remove_room_from_group(
  139. group_id, requester_user_id, room_id
  140. )
  141. return 200, new_content
  142. class FederationGroupsAddRoomsConfigServlet(BaseGroupsServerServlet):
  143. """Update room config in group"""
  144. PATH = (
  145. "/groups/(?P<group_id>[^/]*)/room/(?P<room_id>[^/]*)"
  146. "/config/(?P<config_key>[^/]*)"
  147. )
  148. async def on_POST(
  149. self,
  150. origin: str,
  151. content: JsonDict,
  152. query: Dict[bytes, List[bytes]],
  153. group_id: str,
  154. room_id: str,
  155. config_key: str,
  156. ) -> Tuple[int, JsonDict]:
  157. requester_user_id = parse_string_from_args(
  158. query, "requester_user_id", required=True
  159. )
  160. if get_domain_from_id(requester_user_id) != origin:
  161. raise SynapseError(403, "requester_user_id doesn't match origin")
  162. result = await self.handler.update_room_in_group(
  163. group_id, requester_user_id, room_id, config_key, content
  164. )
  165. return 200, result
  166. class FederationGroupsUsersServlet(BaseGroupsServerServlet):
  167. """Get the users in a group on behalf of a user"""
  168. PATH = "/groups/(?P<group_id>[^/]*)/users"
  169. async def on_GET(
  170. self,
  171. origin: str,
  172. content: Literal[None],
  173. query: Dict[bytes, List[bytes]],
  174. group_id: str,
  175. ) -> Tuple[int, JsonDict]:
  176. requester_user_id = parse_string_from_args(
  177. query, "requester_user_id", required=True
  178. )
  179. if get_domain_from_id(requester_user_id) != origin:
  180. raise SynapseError(403, "requester_user_id doesn't match origin")
  181. new_content = await self.handler.get_users_in_group(group_id, requester_user_id)
  182. return 200, new_content
  183. class FederationGroupsInvitedUsersServlet(BaseGroupsServerServlet):
  184. """Get the users that have been invited to a group"""
  185. PATH = "/groups/(?P<group_id>[^/]*)/invited_users"
  186. async def on_GET(
  187. self,
  188. origin: str,
  189. content: Literal[None],
  190. query: Dict[bytes, List[bytes]],
  191. group_id: str,
  192. ) -> Tuple[int, JsonDict]:
  193. requester_user_id = parse_string_from_args(
  194. query, "requester_user_id", required=True
  195. )
  196. if get_domain_from_id(requester_user_id) != origin:
  197. raise SynapseError(403, "requester_user_id doesn't match origin")
  198. new_content = await self.handler.get_invited_users_in_group(
  199. group_id, requester_user_id
  200. )
  201. return 200, new_content
  202. class FederationGroupsInviteServlet(BaseGroupsServerServlet):
  203. """Ask a group server to invite someone to the group"""
  204. PATH = "/groups/(?P<group_id>[^/]*)/users/(?P<user_id>[^/]*)/invite"
  205. async def on_POST(
  206. self,
  207. origin: str,
  208. content: JsonDict,
  209. query: Dict[bytes, List[bytes]],
  210. group_id: str,
  211. user_id: str,
  212. ) -> Tuple[int, JsonDict]:
  213. requester_user_id = parse_string_from_args(
  214. query, "requester_user_id", required=True
  215. )
  216. if get_domain_from_id(requester_user_id) != origin:
  217. raise SynapseError(403, "requester_user_id doesn't match origin")
  218. new_content = await self.handler.invite_to_group(
  219. group_id, user_id, requester_user_id, content
  220. )
  221. return 200, new_content
  222. class FederationGroupsAcceptInviteServlet(BaseGroupsServerServlet):
  223. """Accept an invitation from the group server"""
  224. PATH = "/groups/(?P<group_id>[^/]*)/users/(?P<user_id>[^/]*)/accept_invite"
  225. async def on_POST(
  226. self,
  227. origin: str,
  228. content: JsonDict,
  229. query: Dict[bytes, List[bytes]],
  230. group_id: str,
  231. user_id: str,
  232. ) -> Tuple[int, JsonDict]:
  233. if get_domain_from_id(user_id) != origin:
  234. raise SynapseError(403, "user_id doesn't match origin")
  235. new_content = await self.handler.accept_invite(group_id, user_id, content)
  236. return 200, new_content
  237. class FederationGroupsJoinServlet(BaseGroupsServerServlet):
  238. """Attempt to join a group"""
  239. PATH = "/groups/(?P<group_id>[^/]*)/users/(?P<user_id>[^/]*)/join"
  240. async def on_POST(
  241. self,
  242. origin: str,
  243. content: JsonDict,
  244. query: Dict[bytes, List[bytes]],
  245. group_id: str,
  246. user_id: str,
  247. ) -> Tuple[int, JsonDict]:
  248. if get_domain_from_id(user_id) != origin:
  249. raise SynapseError(403, "user_id doesn't match origin")
  250. new_content = await self.handler.join_group(group_id, user_id, content)
  251. return 200, new_content
  252. class FederationGroupsRemoveUserServlet(BaseGroupsServerServlet):
  253. """Leave or kick a user from the group"""
  254. PATH = "/groups/(?P<group_id>[^/]*)/users/(?P<user_id>[^/]*)/remove"
  255. async def on_POST(
  256. self,
  257. origin: str,
  258. content: JsonDict,
  259. query: Dict[bytes, List[bytes]],
  260. group_id: str,
  261. user_id: str,
  262. ) -> Tuple[int, JsonDict]:
  263. requester_user_id = parse_string_from_args(
  264. query, "requester_user_id", required=True
  265. )
  266. if get_domain_from_id(requester_user_id) != origin:
  267. raise SynapseError(403, "requester_user_id doesn't match origin")
  268. new_content = await self.handler.remove_user_from_group(
  269. group_id, user_id, requester_user_id, content
  270. )
  271. return 200, new_content
  272. class FederationGroupsSummaryRoomsServlet(BaseGroupsServerServlet):
  273. """Add/remove a room from the group summary, with optional category.
  274. Matches both:
  275. - /groups/:group/summary/rooms/:room_id
  276. - /groups/:group/summary/categories/:category/rooms/:room_id
  277. """
  278. PATH = (
  279. "/groups/(?P<group_id>[^/]*)/summary"
  280. "(/categories/(?P<category_id>[^/]+))?"
  281. "/rooms/(?P<room_id>[^/]*)"
  282. )
  283. async def on_POST(
  284. self,
  285. origin: str,
  286. content: JsonDict,
  287. query: Dict[bytes, List[bytes]],
  288. group_id: str,
  289. category_id: str,
  290. room_id: str,
  291. ) -> Tuple[int, JsonDict]:
  292. requester_user_id = parse_string_from_args(
  293. query, "requester_user_id", required=True
  294. )
  295. if get_domain_from_id(requester_user_id) != origin:
  296. raise SynapseError(403, "requester_user_id doesn't match origin")
  297. if category_id == "":
  298. raise SynapseError(
  299. 400, "category_id cannot be empty string", Codes.INVALID_PARAM
  300. )
  301. if len(category_id) > MAX_GROUP_CATEGORYID_LENGTH:
  302. raise SynapseError(
  303. 400,
  304. "category_id may not be longer than %s characters"
  305. % (MAX_GROUP_CATEGORYID_LENGTH,),
  306. Codes.INVALID_PARAM,
  307. )
  308. resp = await self.handler.update_group_summary_room(
  309. group_id,
  310. requester_user_id,
  311. room_id=room_id,
  312. category_id=category_id,
  313. content=content,
  314. )
  315. return 200, resp
  316. async def on_DELETE(
  317. self,
  318. origin: str,
  319. content: Literal[None],
  320. query: Dict[bytes, List[bytes]],
  321. group_id: str,
  322. category_id: str,
  323. room_id: str,
  324. ) -> Tuple[int, JsonDict]:
  325. requester_user_id = parse_string_from_args(
  326. query, "requester_user_id", required=True
  327. )
  328. if get_domain_from_id(requester_user_id) != origin:
  329. raise SynapseError(403, "requester_user_id doesn't match origin")
  330. if category_id == "":
  331. raise SynapseError(400, "category_id cannot be empty string")
  332. resp = await self.handler.delete_group_summary_room(
  333. group_id, requester_user_id, room_id=room_id, category_id=category_id
  334. )
  335. return 200, resp
  336. class FederationGroupsCategoriesServlet(BaseGroupsServerServlet):
  337. """Get all categories for a group"""
  338. PATH = "/groups/(?P<group_id>[^/]*)/categories/?"
  339. async def on_GET(
  340. self,
  341. origin: str,
  342. content: Literal[None],
  343. query: Dict[bytes, List[bytes]],
  344. group_id: str,
  345. ) -> Tuple[int, JsonDict]:
  346. requester_user_id = parse_string_from_args(
  347. query, "requester_user_id", required=True
  348. )
  349. if get_domain_from_id(requester_user_id) != origin:
  350. raise SynapseError(403, "requester_user_id doesn't match origin")
  351. resp = await self.handler.get_group_categories(group_id, requester_user_id)
  352. return 200, resp
  353. class FederationGroupsCategoryServlet(BaseGroupsServerServlet):
  354. """Add/remove/get a category in a group"""
  355. PATH = "/groups/(?P<group_id>[^/]*)/categories/(?P<category_id>[^/]+)"
  356. async def on_GET(
  357. self,
  358. origin: str,
  359. content: Literal[None],
  360. query: Dict[bytes, List[bytes]],
  361. group_id: str,
  362. category_id: str,
  363. ) -> Tuple[int, JsonDict]:
  364. requester_user_id = parse_string_from_args(
  365. query, "requester_user_id", required=True
  366. )
  367. if get_domain_from_id(requester_user_id) != origin:
  368. raise SynapseError(403, "requester_user_id doesn't match origin")
  369. resp = await self.handler.get_group_category(
  370. group_id, requester_user_id, category_id
  371. )
  372. return 200, resp
  373. async def on_POST(
  374. self,
  375. origin: str,
  376. content: JsonDict,
  377. query: Dict[bytes, List[bytes]],
  378. group_id: str,
  379. category_id: str,
  380. ) -> Tuple[int, JsonDict]:
  381. requester_user_id = parse_string_from_args(
  382. query, "requester_user_id", required=True
  383. )
  384. if get_domain_from_id(requester_user_id) != origin:
  385. raise SynapseError(403, "requester_user_id doesn't match origin")
  386. if category_id == "":
  387. raise SynapseError(400, "category_id cannot be empty string")
  388. if len(category_id) > MAX_GROUP_CATEGORYID_LENGTH:
  389. raise SynapseError(
  390. 400,
  391. "category_id may not be longer than %s characters"
  392. % (MAX_GROUP_CATEGORYID_LENGTH,),
  393. Codes.INVALID_PARAM,
  394. )
  395. resp = await self.handler.upsert_group_category(
  396. group_id, requester_user_id, category_id, content
  397. )
  398. return 200, resp
  399. async def on_DELETE(
  400. self,
  401. origin: str,
  402. content: Literal[None],
  403. query: Dict[bytes, List[bytes]],
  404. group_id: str,
  405. category_id: str,
  406. ) -> Tuple[int, JsonDict]:
  407. requester_user_id = parse_string_from_args(
  408. query, "requester_user_id", required=True
  409. )
  410. if get_domain_from_id(requester_user_id) != origin:
  411. raise SynapseError(403, "requester_user_id doesn't match origin")
  412. if category_id == "":
  413. raise SynapseError(400, "category_id cannot be empty string")
  414. resp = await self.handler.delete_group_category(
  415. group_id, requester_user_id, category_id
  416. )
  417. return 200, resp
  418. class FederationGroupsRolesServlet(BaseGroupsServerServlet):
  419. """Get roles in a group"""
  420. PATH = "/groups/(?P<group_id>[^/]*)/roles/?"
  421. async def on_GET(
  422. self,
  423. origin: str,
  424. content: Literal[None],
  425. query: Dict[bytes, List[bytes]],
  426. group_id: str,
  427. ) -> Tuple[int, JsonDict]:
  428. requester_user_id = parse_string_from_args(
  429. query, "requester_user_id", required=True
  430. )
  431. if get_domain_from_id(requester_user_id) != origin:
  432. raise SynapseError(403, "requester_user_id doesn't match origin")
  433. resp = await self.handler.get_group_roles(group_id, requester_user_id)
  434. return 200, resp
  435. class FederationGroupsRoleServlet(BaseGroupsServerServlet):
  436. """Add/remove/get a role in a group"""
  437. PATH = "/groups/(?P<group_id>[^/]*)/roles/(?P<role_id>[^/]+)"
  438. async def on_GET(
  439. self,
  440. origin: str,
  441. content: Literal[None],
  442. query: Dict[bytes, List[bytes]],
  443. group_id: str,
  444. role_id: str,
  445. ) -> Tuple[int, JsonDict]:
  446. requester_user_id = parse_string_from_args(
  447. query, "requester_user_id", required=True
  448. )
  449. if get_domain_from_id(requester_user_id) != origin:
  450. raise SynapseError(403, "requester_user_id doesn't match origin")
  451. resp = await self.handler.get_group_role(group_id, requester_user_id, role_id)
  452. return 200, resp
  453. async def on_POST(
  454. self,
  455. origin: str,
  456. content: JsonDict,
  457. query: Dict[bytes, List[bytes]],
  458. group_id: str,
  459. role_id: str,
  460. ) -> Tuple[int, JsonDict]:
  461. requester_user_id = parse_string_from_args(
  462. query, "requester_user_id", required=True
  463. )
  464. if get_domain_from_id(requester_user_id) != origin:
  465. raise SynapseError(403, "requester_user_id doesn't match origin")
  466. if role_id == "":
  467. raise SynapseError(
  468. 400, "role_id cannot be empty string", Codes.INVALID_PARAM
  469. )
  470. if len(role_id) > MAX_GROUP_ROLEID_LENGTH:
  471. raise SynapseError(
  472. 400,
  473. "role_id may not be longer than %s characters"
  474. % (MAX_GROUP_ROLEID_LENGTH,),
  475. Codes.INVALID_PARAM,
  476. )
  477. resp = await self.handler.update_group_role(
  478. group_id, requester_user_id, role_id, content
  479. )
  480. return 200, resp
  481. async def on_DELETE(
  482. self,
  483. origin: str,
  484. content: Literal[None],
  485. query: Dict[bytes, List[bytes]],
  486. group_id: str,
  487. role_id: str,
  488. ) -> Tuple[int, JsonDict]:
  489. requester_user_id = parse_string_from_args(
  490. query, "requester_user_id", required=True
  491. )
  492. if get_domain_from_id(requester_user_id) != origin:
  493. raise SynapseError(403, "requester_user_id doesn't match origin")
  494. if role_id == "":
  495. raise SynapseError(400, "role_id cannot be empty string")
  496. resp = await self.handler.delete_group_role(
  497. group_id, requester_user_id, role_id
  498. )
  499. return 200, resp
  500. class FederationGroupsSummaryUsersServlet(BaseGroupsServerServlet):
  501. """Add/remove a user from the group summary, with optional role.
  502. Matches both:
  503. - /groups/:group/summary/users/:user_id
  504. - /groups/:group/summary/roles/:role/users/:user_id
  505. """
  506. PATH = (
  507. "/groups/(?P<group_id>[^/]*)/summary"
  508. "(/roles/(?P<role_id>[^/]+))?"
  509. "/users/(?P<user_id>[^/]*)"
  510. )
  511. async def on_POST(
  512. self,
  513. origin: str,
  514. content: JsonDict,
  515. query: Dict[bytes, List[bytes]],
  516. group_id: str,
  517. role_id: str,
  518. user_id: str,
  519. ) -> Tuple[int, JsonDict]:
  520. requester_user_id = parse_string_from_args(
  521. query, "requester_user_id", required=True
  522. )
  523. if get_domain_from_id(requester_user_id) != origin:
  524. raise SynapseError(403, "requester_user_id doesn't match origin")
  525. if role_id == "":
  526. raise SynapseError(400, "role_id cannot be empty string")
  527. if len(role_id) > MAX_GROUP_ROLEID_LENGTH:
  528. raise SynapseError(
  529. 400,
  530. "role_id may not be longer than %s characters"
  531. % (MAX_GROUP_ROLEID_LENGTH,),
  532. Codes.INVALID_PARAM,
  533. )
  534. resp = await self.handler.update_group_summary_user(
  535. group_id,
  536. requester_user_id,
  537. user_id=user_id,
  538. role_id=role_id,
  539. content=content,
  540. )
  541. return 200, resp
  542. async def on_DELETE(
  543. self,
  544. origin: str,
  545. content: Literal[None],
  546. query: Dict[bytes, List[bytes]],
  547. group_id: str,
  548. role_id: str,
  549. user_id: str,
  550. ) -> Tuple[int, JsonDict]:
  551. requester_user_id = parse_string_from_args(
  552. query, "requester_user_id", required=True
  553. )
  554. if get_domain_from_id(requester_user_id) != origin:
  555. raise SynapseError(403, "requester_user_id doesn't match origin")
  556. if role_id == "":
  557. raise SynapseError(400, "role_id cannot be empty string")
  558. resp = await self.handler.delete_group_summary_user(
  559. group_id, requester_user_id, user_id=user_id, role_id=role_id
  560. )
  561. return 200, resp
  562. class FederationGroupsSettingJoinPolicyServlet(BaseGroupsServerServlet):
  563. """Sets whether a group is joinable without an invite or knock"""
  564. PATH = "/groups/(?P<group_id>[^/]*)/settings/m.join_policy"
  565. async def on_PUT(
  566. self,
  567. origin: str,
  568. content: JsonDict,
  569. query: Dict[bytes, List[bytes]],
  570. group_id: str,
  571. ) -> Tuple[int, JsonDict]:
  572. requester_user_id = parse_string_from_args(
  573. query, "requester_user_id", required=True
  574. )
  575. if get_domain_from_id(requester_user_id) != origin:
  576. raise SynapseError(403, "requester_user_id doesn't match origin")
  577. new_content = await self.handler.set_group_join_policy(
  578. group_id, requester_user_id, content
  579. )
  580. return 200, new_content
  581. GROUP_SERVER_SERVLET_CLASSES: Tuple[Type[BaseFederationServlet], ...] = (
  582. FederationGroupsProfileServlet,
  583. FederationGroupsSummaryServlet,
  584. FederationGroupsRoomsServlet,
  585. FederationGroupsUsersServlet,
  586. FederationGroupsInvitedUsersServlet,
  587. FederationGroupsInviteServlet,
  588. FederationGroupsAcceptInviteServlet,
  589. FederationGroupsJoinServlet,
  590. FederationGroupsRemoveUserServlet,
  591. FederationGroupsSummaryRoomsServlet,
  592. FederationGroupsCategoriesServlet,
  593. FederationGroupsCategoryServlet,
  594. FederationGroupsRolesServlet,
  595. FederationGroupsRoleServlet,
  596. FederationGroupsSummaryUsersServlet,
  597. FederationGroupsAddRoomsServlet,
  598. FederationGroupsAddRoomsConfigServlet,
  599. FederationGroupsSettingJoinPolicyServlet,
  600. )