mirror of
				https://github.com/matrix-org/synapse.git
				synced 2025-10-25 22:32:03 +02:00 
			
		
		
		
	Merge pull request #2374 from matrix-org/erikj/group_server_local
Add local group server support
This commit is contained in:
		
						commit
						b3bf6a1218
					
				| @ -472,6 +472,99 @@ class TransportLayerClient(object): | |||||||
| 
 | 
 | ||||||
|         defer.returnValue(content) |         defer.returnValue(content) | ||||||
| 
 | 
 | ||||||
|  |     @log_function | ||||||
|  |     def get_group_profile(self, destination, group_id, requester_user_id): | ||||||
|  |         """Get a group profile | ||||||
|  |         """ | ||||||
|  |         path = PREFIX + "/groups/%s/profile" % (group_id,) | ||||||
|  | 
 | ||||||
|  |         return self.client.get_json( | ||||||
|  |             destination=destination, | ||||||
|  |             path=path, | ||||||
|  |             args={"requester_user_id": requester_user_id}, | ||||||
|  |             ignore_backoff=True, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     @log_function | ||||||
|  |     def get_group_summary(self, destination, group_id, requester_user_id): | ||||||
|  |         """Get a group summary | ||||||
|  |         """ | ||||||
|  |         path = PREFIX + "/groups/%s/summary" % (group_id,) | ||||||
|  | 
 | ||||||
|  |         return self.client.get_json( | ||||||
|  |             destination=destination, | ||||||
|  |             path=path, | ||||||
|  |             args={"requester_user_id": requester_user_id}, | ||||||
|  |             ignore_backoff=True, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     @log_function | ||||||
|  |     def get_rooms_in_group(self, destination, group_id, requester_user_id): | ||||||
|  |         """Get all rooms in a group | ||||||
|  |         """ | ||||||
|  |         path = PREFIX + "/groups/%s/rooms" % (group_id,) | ||||||
|  | 
 | ||||||
|  |         return self.client.get_json( | ||||||
|  |             destination=destination, | ||||||
|  |             path=path, | ||||||
|  |             args={"requester_user_id": requester_user_id}, | ||||||
|  |             ignore_backoff=True, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     def add_room_to_group(self, destination, group_id, requester_user_id, room_id, | ||||||
|  |                           content): | ||||||
|  |         """Add a room to a group | ||||||
|  |         """ | ||||||
|  |         path = PREFIX + "/groups/%s/room/%s" % (group_id, room_id,) | ||||||
|  | 
 | ||||||
|  |         return self.client.post_json( | ||||||
|  |             destination=destination, | ||||||
|  |             path=path, | ||||||
|  |             args={"requester_user_id": requester_user_id}, | ||||||
|  |             data=content, | ||||||
|  |             ignore_backoff=True, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     @log_function | ||||||
|  |     def get_users_in_group(self, destination, group_id, requester_user_id): | ||||||
|  |         """Get users in a group | ||||||
|  |         """ | ||||||
|  |         path = PREFIX + "/groups/%s/users" % (group_id,) | ||||||
|  | 
 | ||||||
|  |         return self.client.get_json( | ||||||
|  |             destination=destination, | ||||||
|  |             path=path, | ||||||
|  |             args={"requester_user_id": requester_user_id}, | ||||||
|  |             ignore_backoff=True, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     @log_function | ||||||
|  |     def accept_group_invite(self, destination, group_id, user_id, content): | ||||||
|  |         """Accept a group invite | ||||||
|  |         """ | ||||||
|  |         path = PREFIX + "/groups/%s/users/%s/accept_invite" % (group_id, user_id) | ||||||
|  | 
 | ||||||
|  |         return self.client.post_json( | ||||||
|  |             destination=destination, | ||||||
|  |             path=path, | ||||||
|  |             data=content, | ||||||
|  |             ignore_backoff=True, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     @log_function | ||||||
|  |     def invite_to_group(self, destination, group_id, user_id, requester_user_id, content): | ||||||
|  |         """Invite a user to a group | ||||||
|  |         """ | ||||||
|  |         path = PREFIX + "/groups/%s/users/%s/invite" % (group_id, user_id) | ||||||
|  | 
 | ||||||
|  |         return self.client.post_json( | ||||||
|  |             destination=destination, | ||||||
|  |             path=path, | ||||||
|  |             args=requester_user_id, | ||||||
|  |             data=content, | ||||||
|  |             ignore_backoff=True, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|     @log_function |     @log_function | ||||||
|     def invite_to_group_notification(self, destination, group_id, user_id, content): |     def invite_to_group_notification(self, destination, group_id, user_id, content): | ||||||
|         """Sent by group server to inform a user's server that they have been |         """Sent by group server to inform a user's server that they have been | ||||||
| @ -487,6 +580,21 @@ class TransportLayerClient(object): | |||||||
|             ignore_backoff=True, |             ignore_backoff=True, | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|  |     @log_function | ||||||
|  |     def remove_user_from_group(self, destination, group_id, requester_user_id, | ||||||
|  |                                user_id, content): | ||||||
|  |         """Remove a user fron a group | ||||||
|  |         """ | ||||||
|  |         path = PREFIX + "/groups/%s/users/%s/remove" % (group_id, user_id) | ||||||
|  | 
 | ||||||
|  |         return self.client.post_json( | ||||||
|  |             destination=destination, | ||||||
|  |             path=path, | ||||||
|  |             args={"requester_user_id": requester_user_id}, | ||||||
|  |             data=content, | ||||||
|  |             ignore_backoff=True, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|     @log_function |     @log_function | ||||||
|     def remove_user_from_group_notification(self, destination, group_id, user_id, |     def remove_user_from_group_notification(self, destination, group_id, user_id, | ||||||
|                                             content): |                                             content): | ||||||
| @ -517,3 +625,190 @@ class TransportLayerClient(object): | |||||||
|             data=content, |             data=content, | ||||||
|             ignore_backoff=True, |             ignore_backoff=True, | ||||||
|         ) |         ) | ||||||
|  | 
 | ||||||
|  |     @log_function | ||||||
|  |     def update_group_summary_room(self, destination, group_id, user_id, room_id, | ||||||
|  |                                   category_id, content): | ||||||
|  |         """Update a room entry in a group summary | ||||||
|  |         """ | ||||||
|  |         if category_id: | ||||||
|  |             path = PREFIX + "/groups/%s/summary/categories/%s/rooms/%s" % ( | ||||||
|  |                 group_id, category_id, room_id, | ||||||
|  |             ) | ||||||
|  |         else: | ||||||
|  |             path = PREFIX + "/groups/%s/summary/rooms/%s" % (group_id, room_id,) | ||||||
|  | 
 | ||||||
|  |         return self.client.post_json( | ||||||
|  |             destination=destination, | ||||||
|  |             path=path, | ||||||
|  |             args={"requester_user_id": user_id}, | ||||||
|  |             data=content, | ||||||
|  |             ignore_backoff=True, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     @log_function | ||||||
|  |     def delete_group_summary_room(self, destination, group_id, user_id, room_id, | ||||||
|  |                                   category_id): | ||||||
|  |         """Delete a room entry in a group summary | ||||||
|  |         """ | ||||||
|  |         if category_id: | ||||||
|  |             path = PREFIX + "/groups/%s/summary/categories/%s/rooms/%s" % ( | ||||||
|  |                 group_id, category_id, room_id, | ||||||
|  |             ) | ||||||
|  |         else: | ||||||
|  |             path = PREFIX + "/groups/%s/summary/rooms/%s" % (group_id, room_id,) | ||||||
|  | 
 | ||||||
|  |         return self.client.delete_json( | ||||||
|  |             destination=destination, | ||||||
|  |             path=path, | ||||||
|  |             args={"requester_user_id": user_id}, | ||||||
|  |             ignore_backoff=True, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     @log_function | ||||||
|  |     def get_group_categories(self, destination, group_id, requester_user_id): | ||||||
|  |         """Get all categories in a group | ||||||
|  |         """ | ||||||
|  |         path = PREFIX + "/groups/%s/categories" % (group_id,) | ||||||
|  | 
 | ||||||
|  |         return self.client.get_json( | ||||||
|  |             destination=destination, | ||||||
|  |             path=path, | ||||||
|  |             args={"requester_user_id": requester_user_id}, | ||||||
|  |             ignore_backoff=True, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     @log_function | ||||||
|  |     def get_group_category(self, destination, group_id, requester_user_id, category_id): | ||||||
|  |         """Get category info in a group | ||||||
|  |         """ | ||||||
|  |         path = PREFIX + "/groups/%s/categories/%s" % (group_id, category_id,) | ||||||
|  | 
 | ||||||
|  |         return self.client.get_json( | ||||||
|  |             destination=destination, | ||||||
|  |             path=path, | ||||||
|  |             args={"requester_user_id": requester_user_id}, | ||||||
|  |             ignore_backoff=True, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     @log_function | ||||||
|  |     def update_group_category(self, destination, group_id, requester_user_id, category_id, | ||||||
|  |                               content): | ||||||
|  |         """Update a category in a group | ||||||
|  |         """ | ||||||
|  |         path = PREFIX + "/groups/%s/categories/%s" % (group_id, category_id,) | ||||||
|  | 
 | ||||||
|  |         return self.client.post_json( | ||||||
|  |             destination=destination, | ||||||
|  |             path=path, | ||||||
|  |             args={"requester_user_id": requester_user_id}, | ||||||
|  |             data=content, | ||||||
|  |             ignore_backoff=True, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     @log_function | ||||||
|  |     def delete_group_category(self, destination, group_id, requester_user_id, | ||||||
|  |                               category_id): | ||||||
|  |         """Delete a category in a group | ||||||
|  |         """ | ||||||
|  |         path = PREFIX + "/groups/%s/categories/%s" % (group_id, category_id,) | ||||||
|  | 
 | ||||||
|  |         return self.client.delete_json( | ||||||
|  |             destination=destination, | ||||||
|  |             path=path, | ||||||
|  |             args={"requester_user_id": requester_user_id}, | ||||||
|  |             ignore_backoff=True, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     @log_function | ||||||
|  |     def get_group_roles(self, destination, group_id, requester_user_id): | ||||||
|  |         """Get all roles in a group | ||||||
|  |         """ | ||||||
|  |         path = PREFIX + "/groups/%s/roles" % (group_id,) | ||||||
|  | 
 | ||||||
|  |         return self.client.get_json( | ||||||
|  |             destination=destination, | ||||||
|  |             path=path, | ||||||
|  |             args={"requester_user_id": requester_user_id}, | ||||||
|  |             ignore_backoff=True, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     @log_function | ||||||
|  |     def get_group_role(self, destination, group_id, requester_user_id, role_id): | ||||||
|  |         """Get a roles info | ||||||
|  |         """ | ||||||
|  |         path = PREFIX + "/groups/%s/roles/%s" % (group_id, role_id,) | ||||||
|  | 
 | ||||||
|  |         return self.client.get_json( | ||||||
|  |             destination=destination, | ||||||
|  |             path=path, | ||||||
|  |             args={"requester_user_id": requester_user_id}, | ||||||
|  |             ignore_backoff=True, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     @log_function | ||||||
|  |     def update_group_role(self, destination, group_id, requester_user_id, role_id, | ||||||
|  |                           content): | ||||||
|  |         """Update a role in a group | ||||||
|  |         """ | ||||||
|  |         path = PREFIX + "/groups/%s/roles/%s" % (group_id, role_id,) | ||||||
|  | 
 | ||||||
|  |         return self.client.post_json( | ||||||
|  |             destination=destination, | ||||||
|  |             path=path, | ||||||
|  |             args={"requester_user_id": requester_user_id}, | ||||||
|  |             data=content, | ||||||
|  |             ignore_backoff=True, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     @log_function | ||||||
|  |     def delete_group_role(self, destination, group_id, requester_user_id, role_id): | ||||||
|  |         """Delete a role in a group | ||||||
|  |         """ | ||||||
|  |         path = PREFIX + "/groups/%s/roles/%s" % (group_id, role_id,) | ||||||
|  | 
 | ||||||
|  |         return self.client.delete_json( | ||||||
|  |             destination=destination, | ||||||
|  |             path=path, | ||||||
|  |             args={"requester_user_id": requester_user_id}, | ||||||
|  |             ignore_backoff=True, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     @log_function | ||||||
|  |     def update_group_summary_user(self, destination, group_id, requester_user_id, | ||||||
|  |                                   user_id, role_id, content): | ||||||
|  |         """Update a users entry in a group | ||||||
|  |         """ | ||||||
|  |         if role_id: | ||||||
|  |             path = PREFIX + "/groups/%s/summary/roles/%s/users/%s" % ( | ||||||
|  |                 group_id, role_id, user_id, | ||||||
|  |             ) | ||||||
|  |         else: | ||||||
|  |             path = PREFIX + "/groups/%s/summary/users/%s" % (group_id, user_id,) | ||||||
|  | 
 | ||||||
|  |         return self.client.post_json( | ||||||
|  |             destination=destination, | ||||||
|  |             path=path, | ||||||
|  |             args={"requester_user_id": requester_user_id}, | ||||||
|  |             data=content, | ||||||
|  |             ignore_backoff=True, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     @log_function | ||||||
|  |     def delete_group_summary_user(self, destination, group_id, requester_user_id, | ||||||
|  |                                   user_id, role_id): | ||||||
|  |         """Delete a users entry in a group | ||||||
|  |         """ | ||||||
|  |         if role_id: | ||||||
|  |             path = PREFIX + "/groups/%s/summary/roles/%s/users/%s" % ( | ||||||
|  |                 group_id, role_id, user_id, | ||||||
|  |             ) | ||||||
|  |         else: | ||||||
|  |             path = PREFIX + "/groups/%s/summary/users/%s" % (group_id, user_id,) | ||||||
|  | 
 | ||||||
|  |         return self.client.delete_json( | ||||||
|  |             destination=destination, | ||||||
|  |             path=path, | ||||||
|  |             args={"requester_user_id": requester_user_id}, | ||||||
|  |             ignore_backoff=True, | ||||||
|  |         ) | ||||||
|  | |||||||
| @ -616,7 +616,7 @@ class FederationGroupsProfileServlet(BaseFederationServlet): | |||||||
| 
 | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def on_GET(self, origin, content, query, group_id): |     def on_GET(self, origin, content, query, group_id): | ||||||
|         requester_user_id = query["requester_user_id"] |         requester_user_id = parse_string_from_args(query, "requester_user_id") | ||||||
|         if get_domain_from_id(requester_user_id) != origin: |         if get_domain_from_id(requester_user_id) != origin: | ||||||
|             raise SynapseError(403, "requester_user_id doesn't match origin") |             raise SynapseError(403, "requester_user_id doesn't match origin") | ||||||
| 
 | 
 | ||||||
| @ -632,7 +632,7 @@ class FederationGroupsSummaryServlet(BaseFederationServlet): | |||||||
| 
 | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def on_GET(self, origin, content, query, group_id): |     def on_GET(self, origin, content, query, group_id): | ||||||
|         requester_user_id = query["requester_user_id"] |         requester_user_id = parse_string_from_args(query, "requester_user_id") | ||||||
|         if get_domain_from_id(requester_user_id) != origin: |         if get_domain_from_id(requester_user_id) != origin: | ||||||
|             raise SynapseError(403, "requester_user_id doesn't match origin") |             raise SynapseError(403, "requester_user_id doesn't match origin") | ||||||
| 
 | 
 | ||||||
| @ -650,7 +650,7 @@ class FederationGroupsRoomsServlet(BaseFederationServlet): | |||||||
| 
 | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def on_GET(self, origin, content, query, group_id): |     def on_GET(self, origin, content, query, group_id): | ||||||
|         requester_user_id = query["requester_user_id"] |         requester_user_id = parse_string_from_args(query, "requester_user_id") | ||||||
|         if get_domain_from_id(requester_user_id) != origin: |         if get_domain_from_id(requester_user_id) != origin: | ||||||
|             raise SynapseError(403, "requester_user_id doesn't match origin") |             raise SynapseError(403, "requester_user_id doesn't match origin") | ||||||
| 
 | 
 | ||||||
| @ -668,11 +668,11 @@ class FederationGroupsAddRoomsServlet(BaseFederationServlet): | |||||||
| 
 | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def on_POST(self, origin, content, query, group_id, room_id): |     def on_POST(self, origin, content, query, group_id, room_id): | ||||||
|         requester_user_id = query["requester_user_id"] |         requester_user_id = parse_string_from_args(query, "requester_user_id") | ||||||
|         if get_domain_from_id(requester_user_id) != origin: |         if get_domain_from_id(requester_user_id) != origin: | ||||||
|             raise SynapseError(403, "requester_user_id doesn't match origin") |             raise SynapseError(403, "requester_user_id doesn't match origin") | ||||||
| 
 | 
 | ||||||
|         new_content = yield self.handler.add_room( |         new_content = yield self.handler.add_room_to_group( | ||||||
|             group_id, requester_user_id, room_id, content |             group_id, requester_user_id, room_id, content | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
| @ -686,7 +686,7 @@ class FederationGroupsUsersServlet(BaseFederationServlet): | |||||||
| 
 | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def on_GET(self, origin, content, query, group_id): |     def on_GET(self, origin, content, query, group_id): | ||||||
|         requester_user_id = query["requester_user_id"] |         requester_user_id = parse_string_from_args(query, "requester_user_id") | ||||||
|         if get_domain_from_id(requester_user_id) != origin: |         if get_domain_from_id(requester_user_id) != origin: | ||||||
|             raise SynapseError(403, "requester_user_id doesn't match origin") |             raise SynapseError(403, "requester_user_id doesn't match origin") | ||||||
| 
 | 
 | ||||||
| @ -704,7 +704,7 @@ class FederationGroupsInviteServlet(BaseFederationServlet): | |||||||
| 
 | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def on_POST(self, origin, content, query, group_id, user_id): |     def on_POST(self, origin, content, query, group_id, user_id): | ||||||
|         requester_user_id = query["requester_user_id"] |         requester_user_id = parse_string_from_args(query, "requester_user_id") | ||||||
|         if get_domain_from_id(requester_user_id) != origin: |         if get_domain_from_id(requester_user_id) != origin: | ||||||
|             raise SynapseError(403, "requester_user_id doesn't match origin") |             raise SynapseError(403, "requester_user_id doesn't match origin") | ||||||
| 
 | 
 | ||||||
| @ -739,7 +739,7 @@ class FederationGroupsRemoveUserServlet(BaseFederationServlet): | |||||||
| 
 | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def on_POST(self, origin, content, query, group_id, user_id): |     def on_POST(self, origin, content, query, group_id, user_id): | ||||||
|         requester_user_id = query["requester_user_id"] |         requester_user_id = parse_string_from_args(query, "requester_user_id") | ||||||
|         if get_domain_from_id(requester_user_id) != origin: |         if get_domain_from_id(requester_user_id) != origin: | ||||||
|             raise SynapseError(403, "requester_user_id doesn't match origin") |             raise SynapseError(403, "requester_user_id doesn't match origin") | ||||||
| 
 | 
 | ||||||
| @ -750,6 +750,40 @@ class FederationGroupsRemoveUserServlet(BaseFederationServlet): | |||||||
|         defer.returnValue((200, new_content)) |         defer.returnValue((200, new_content)) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class FederationGroupsLocalInviteServlet(BaseFederationServlet): | ||||||
|  |     """A group server has invited a local user | ||||||
|  |     """ | ||||||
|  |     PATH = "/groups/local/(?P<group_id>[^/]*)/users/(?P<user_id>[^/]*)/invite$" | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_POST(self, origin, content, query, group_id, user_id): | ||||||
|  |         if get_domain_from_id(group_id) != origin: | ||||||
|  |             raise SynapseError(403, "group_id doesn't match origin") | ||||||
|  | 
 | ||||||
|  |         new_content = yield self.handler.on_invite( | ||||||
|  |             group_id, user_id, content, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, new_content)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class FederationGroupsRemoveLocalUserServlet(BaseFederationServlet): | ||||||
|  |     """A group server has removed a local user | ||||||
|  |     """ | ||||||
|  |     PATH = "/groups/local/(?P<group_id>[^/]*)/users/(?P<user_id>[^/]*)/remove$" | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_POST(self, origin, content, query, group_id, user_id): | ||||||
|  |         if get_domain_from_id(group_id) != origin: | ||||||
|  |             raise SynapseError(403, "user_id doesn't match origin") | ||||||
|  | 
 | ||||||
|  |         new_content = yield self.handler.user_removed_from_group( | ||||||
|  |             group_id, user_id, content, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, new_content)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class FederationGroupsRenewAttestaionServlet(BaseFederationServlet): | class FederationGroupsRenewAttestaionServlet(BaseFederationServlet): | ||||||
|     """A group or user's server renews their attestation |     """A group or user's server renews their attestation | ||||||
|     """ |     """ | ||||||
| @ -781,7 +815,7 @@ class FederationGroupsSummaryRoomsServlet(BaseFederationServlet): | |||||||
| 
 | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def on_POST(self, origin, content, query, group_id, category_id, room_id): |     def on_POST(self, origin, content, query, group_id, category_id, room_id): | ||||||
|         requester_user_id = query["requester_user_id"] |         requester_user_id = parse_string_from_args(query, "requester_user_id") | ||||||
|         if get_domain_from_id(requester_user_id) != origin: |         if get_domain_from_id(requester_user_id) != origin: | ||||||
|             raise SynapseError(403, "requester_user_id doesn't match origin") |             raise SynapseError(403, "requester_user_id doesn't match origin") | ||||||
| 
 | 
 | ||||||
| @ -799,7 +833,7 @@ class FederationGroupsSummaryRoomsServlet(BaseFederationServlet): | |||||||
| 
 | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def on_DELETE(self, origin, content, query, group_id, category_id, room_id): |     def on_DELETE(self, origin, content, query, group_id, category_id, room_id): | ||||||
|         requester_user_id = query["requester_user_id"] |         requester_user_id = parse_string_from_args(query, "requester_user_id") | ||||||
|         if get_domain_from_id(requester_user_id) != origin: |         if get_domain_from_id(requester_user_id) != origin: | ||||||
|             raise SynapseError(403, "requester_user_id doesn't match origin") |             raise SynapseError(403, "requester_user_id doesn't match origin") | ||||||
| 
 | 
 | ||||||
| @ -824,7 +858,7 @@ class FederationGroupsCategoriesServlet(BaseFederationServlet): | |||||||
| 
 | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def on_GET(self, origin, content, query, group_id): |     def on_GET(self, origin, content, query, group_id): | ||||||
|         requester_user_id = query["requester_user_id"] |         requester_user_id = parse_string_from_args(query, "requester_user_id") | ||||||
|         if get_domain_from_id(requester_user_id) != origin: |         if get_domain_from_id(requester_user_id) != origin: | ||||||
|             raise SynapseError(403, "requester_user_id doesn't match origin") |             raise SynapseError(403, "requester_user_id doesn't match origin") | ||||||
| 
 | 
 | ||||||
| @ -844,7 +878,7 @@ class FederationGroupsCategoryServlet(BaseFederationServlet): | |||||||
| 
 | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def on_GET(self, origin, content, query, group_id, category_id): |     def on_GET(self, origin, content, query, group_id, category_id): | ||||||
|         requester_user_id = query["requester_user_id"] |         requester_user_id = parse_string_from_args(query, "requester_user_id") | ||||||
|         if get_domain_from_id(requester_user_id) != origin: |         if get_domain_from_id(requester_user_id) != origin: | ||||||
|             raise SynapseError(403, "requester_user_id doesn't match origin") |             raise SynapseError(403, "requester_user_id doesn't match origin") | ||||||
| 
 | 
 | ||||||
| @ -856,7 +890,7 @@ class FederationGroupsCategoryServlet(BaseFederationServlet): | |||||||
| 
 | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def on_POST(self, origin, content, query, group_id, category_id): |     def on_POST(self, origin, content, query, group_id, category_id): | ||||||
|         requester_user_id = query["requester_user_id"] |         requester_user_id = parse_string_from_args(query, "requester_user_id") | ||||||
|         if get_domain_from_id(requester_user_id) != origin: |         if get_domain_from_id(requester_user_id) != origin: | ||||||
|             raise SynapseError(403, "requester_user_id doesn't match origin") |             raise SynapseError(403, "requester_user_id doesn't match origin") | ||||||
| 
 | 
 | ||||||
| @ -871,7 +905,7 @@ class FederationGroupsCategoryServlet(BaseFederationServlet): | |||||||
| 
 | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def on_DELETE(self, origin, content, query, group_id, category_id): |     def on_DELETE(self, origin, content, query, group_id, category_id): | ||||||
|         requester_user_id = query["requester_user_id"] |         requester_user_id = parse_string_from_args(query, "requester_user_id") | ||||||
|         if get_domain_from_id(requester_user_id) != origin: |         if get_domain_from_id(requester_user_id) != origin: | ||||||
|             raise SynapseError(403, "requester_user_id doesn't match origin") |             raise SynapseError(403, "requester_user_id doesn't match origin") | ||||||
| 
 | 
 | ||||||
| @ -894,7 +928,7 @@ class FederationGroupsRolesServlet(BaseFederationServlet): | |||||||
| 
 | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def on_GET(self, origin, content, query, group_id): |     def on_GET(self, origin, content, query, group_id): | ||||||
|         requester_user_id = query["requester_user_id"] |         requester_user_id = parse_string_from_args(query, "requester_user_id") | ||||||
|         if get_domain_from_id(requester_user_id) != origin: |         if get_domain_from_id(requester_user_id) != origin: | ||||||
|             raise SynapseError(403, "requester_user_id doesn't match origin") |             raise SynapseError(403, "requester_user_id doesn't match origin") | ||||||
| 
 | 
 | ||||||
| @ -914,7 +948,7 @@ class FederationGroupsRoleServlet(BaseFederationServlet): | |||||||
| 
 | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def on_GET(self, origin, content, query, group_id, role_id): |     def on_GET(self, origin, content, query, group_id, role_id): | ||||||
|         requester_user_id = query["requester_user_id"] |         requester_user_id = parse_string_from_args(query, "requester_user_id") | ||||||
|         if get_domain_from_id(requester_user_id) != origin: |         if get_domain_from_id(requester_user_id) != origin: | ||||||
|             raise SynapseError(403, "requester_user_id doesn't match origin") |             raise SynapseError(403, "requester_user_id doesn't match origin") | ||||||
| 
 | 
 | ||||||
| @ -926,7 +960,7 @@ class FederationGroupsRoleServlet(BaseFederationServlet): | |||||||
| 
 | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def on_POST(self, origin, content, query, group_id, role_id): |     def on_POST(self, origin, content, query, group_id, role_id): | ||||||
|         requester_user_id = query["requester_user_id"] |         requester_user_id = parse_string_from_args(query, "requester_user_id") | ||||||
|         if get_domain_from_id(requester_user_id) != origin: |         if get_domain_from_id(requester_user_id) != origin: | ||||||
|             raise SynapseError(403, "requester_user_id doesn't match origin") |             raise SynapseError(403, "requester_user_id doesn't match origin") | ||||||
| 
 | 
 | ||||||
| @ -941,7 +975,7 @@ class FederationGroupsRoleServlet(BaseFederationServlet): | |||||||
| 
 | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def on_DELETE(self, origin, content, query, group_id, role_id): |     def on_DELETE(self, origin, content, query, group_id, role_id): | ||||||
|         requester_user_id = query["requester_user_id"] |         requester_user_id = parse_string_from_args(query, "requester_user_id") | ||||||
|         if get_domain_from_id(requester_user_id) != origin: |         if get_domain_from_id(requester_user_id) != origin: | ||||||
|             raise SynapseError(403, "requester_user_id doesn't match origin") |             raise SynapseError(403, "requester_user_id doesn't match origin") | ||||||
| 
 | 
 | ||||||
| @ -970,7 +1004,7 @@ class FederationGroupsSummaryUsersServlet(BaseFederationServlet): | |||||||
| 
 | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def on_POST(self, origin, content, query, group_id, role_id, user_id): |     def on_POST(self, origin, content, query, group_id, role_id, user_id): | ||||||
|         requester_user_id = query["requester_user_id"] |         requester_user_id = parse_string_from_args(query, "requester_user_id") | ||||||
|         if get_domain_from_id(requester_user_id) != origin: |         if get_domain_from_id(requester_user_id) != origin: | ||||||
|             raise SynapseError(403, "requester_user_id doesn't match origin") |             raise SynapseError(403, "requester_user_id doesn't match origin") | ||||||
| 
 | 
 | ||||||
| @ -988,7 +1022,7 @@ class FederationGroupsSummaryUsersServlet(BaseFederationServlet): | |||||||
| 
 | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def on_DELETE(self, origin, content, query, group_id, role_id, user_id): |     def on_DELETE(self, origin, content, query, group_id, role_id, user_id): | ||||||
|         requester_user_id = query["requester_user_id"] |         requester_user_id = parse_string_from_args(query, "requester_user_id") | ||||||
|         if get_domain_from_id(requester_user_id) != origin: |         if get_domain_from_id(requester_user_id) != origin: | ||||||
|             raise SynapseError(403, "requester_user_id doesn't match origin") |             raise SynapseError(403, "requester_user_id doesn't match origin") | ||||||
| 
 | 
 | ||||||
| @ -1053,6 +1087,12 @@ GROUP_SERVER_SERVLET_CLASSES = ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | GROUP_LOCAL_SERVLET_CLASSES = ( | ||||||
|  |     FederationGroupsLocalInviteServlet, | ||||||
|  |     FederationGroupsRemoveLocalUserServlet, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| GROUP_ATTESTATION_SERVLET_CLASSES = ( | GROUP_ATTESTATION_SERVLET_CLASSES = ( | ||||||
|     FederationGroupsRenewAttestaionServlet, |     FederationGroupsRenewAttestaionServlet, | ||||||
| ) | ) | ||||||
| @ -1083,6 +1123,14 @@ def register_servlets(hs, resource, authenticator, ratelimiter): | |||||||
|             server_name=hs.hostname, |             server_name=hs.hostname, | ||||||
|         ).register(resource) |         ).register(resource) | ||||||
| 
 | 
 | ||||||
|  |     for servletclass in GROUP_LOCAL_SERVLET_CLASSES: | ||||||
|  |         servletclass( | ||||||
|  |             handler=hs.get_groups_local_handler(), | ||||||
|  |             authenticator=authenticator, | ||||||
|  |             ratelimiter=ratelimiter, | ||||||
|  |             server_name=hs.hostname, | ||||||
|  |         ).register(resource) | ||||||
|  | 
 | ||||||
|     for servletclass in GROUP_ATTESTATION_SERVLET_CLASSES: |     for servletclass in GROUP_ATTESTATION_SERVLET_CLASSES: | ||||||
|         servletclass( |         servletclass( | ||||||
|             handler=hs.get_groups_attestation_renewer(), |             handler=hs.get_groups_attestation_renewer(), | ||||||
|  | |||||||
| @ -293,7 +293,9 @@ class GroupsServerHandler(object): | |||||||
|                                   content): |                                   content): | ||||||
|         """Add/update a users entry in the group summary |         """Add/update a users entry in the group summary | ||||||
|         """ |         """ | ||||||
|         yield self.check_group_is_ours(group_id, and_exists=True, and_is_admin=user_id) |         yield self.check_group_is_ours( | ||||||
|  |             group_id, and_exists=True, and_is_admin=requester_user_id, | ||||||
|  |         ) | ||||||
| 
 | 
 | ||||||
|         order = content.get("order", None) |         order = content.get("order", None) | ||||||
| 
 | 
 | ||||||
| @ -313,7 +315,9 @@ class GroupsServerHandler(object): | |||||||
|     def delete_group_summary_user(self, group_id, requester_user_id, user_id, role_id): |     def delete_group_summary_user(self, group_id, requester_user_id, user_id, role_id): | ||||||
|         """Remove a user from the group summary |         """Remove a user from the group summary | ||||||
|         """ |         """ | ||||||
|         yield self.check_group_is_ours(group_id, and_exists=True, and_is_admin=user_id) |         yield self.check_group_is_ours( | ||||||
|  |             group_id, and_exists=True, and_is_admin=requester_user_id, | ||||||
|  |         ) | ||||||
| 
 | 
 | ||||||
|         yield self.store.remove_user_from_summary( |         yield self.store.remove_user_from_summary( | ||||||
|             group_id=group_id, |             group_id=group_id, | ||||||
| @ -426,7 +430,7 @@ class GroupsServerHandler(object): | |||||||
|         }) |         }) | ||||||
| 
 | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def add_room(self, group_id, requester_user_id, room_id, content): |     def add_room_to_group(self, group_id, requester_user_id, room_id, content): | ||||||
|         """Add room to group |         """Add room to group | ||||||
|         """ |         """ | ||||||
|         yield self.check_group_is_ours( |         yield self.check_group_is_ours( | ||||||
| @ -462,7 +466,9 @@ class GroupsServerHandler(object): | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if self.hs.is_mine_id(user_id): |         if self.hs.is_mine_id(user_id): | ||||||
|             raise NotImplementedError() |             groups_local = self.hs.get_groups_local_handler() | ||||||
|  |             res = yield groups_local.on_invite(group_id, user_id, content) | ||||||
|  |             local_attestation = None | ||||||
|         else: |         else: | ||||||
|             local_attestation = self.attestations.create_attestation(group_id, user_id) |             local_attestation = self.attestations.create_attestation(group_id, user_id) | ||||||
|             content.update({ |             content.update({ | ||||||
| @ -590,7 +596,8 @@ class GroupsServerHandler(object): | |||||||
| 
 | 
 | ||||||
|         if is_kick: |         if is_kick: | ||||||
|             if self.hs.is_mine_id(user_id): |             if self.hs.is_mine_id(user_id): | ||||||
|                 raise NotImplementedError() |                 groups_local = self.hs.get_groups_local_handler() | ||||||
|  |                 yield groups_local.user_removed_from_group(group_id, user_id, {}) | ||||||
|             else: |             else: | ||||||
|                 yield self.transport_client.remove_user_from_group_notification( |                 yield self.transport_client.remove_user_from_group_notification( | ||||||
|                     get_domain_from_id(user_id), group_id, user_id, {} |                     get_domain_from_id(user_id), group_id, user_id, {} | ||||||
|  | |||||||
							
								
								
									
										307
									
								
								synapse/handlers/groups_local.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										307
									
								
								synapse/handlers/groups_local.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,307 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Copyright 2017 Vector Creations Ltd | ||||||
|  | # | ||||||
|  | # Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  | # you may not use this file except in compliance with the License. | ||||||
|  | # You may obtain a copy of the License at | ||||||
|  | # | ||||||
|  | #     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  | # | ||||||
|  | # Unless required by applicable law or agreed to in writing, software | ||||||
|  | # distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  | # See the License for the specific language governing permissions and | ||||||
|  | # limitations under the License. | ||||||
|  | 
 | ||||||
|  | from twisted.internet import defer | ||||||
|  | 
 | ||||||
|  | from synapse.api.errors import SynapseError | ||||||
|  | from synapse.types import get_domain_from_id | ||||||
|  | 
 | ||||||
|  | import logging | ||||||
|  | 
 | ||||||
|  | logger = logging.getLogger(__name__) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # TODO: Validate attestations | ||||||
|  | # TODO: Allow users to "knock" or simpkly join depending on rules | ||||||
|  | # TODO: is_priveged flag to users and is_public to users and rooms | ||||||
|  | # TODO: Roles | ||||||
|  | # TODO: Audit log for admins (profile updates, membership changes, users who tried | ||||||
|  | #       to join but were rejected, etc) | ||||||
|  | # TODO: Flairs | ||||||
|  | # TODO: Add group memebership  /sync | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def _create_rerouter(func_name): | ||||||
|  |     """Returns a function that looks at the group id and calls the function | ||||||
|  |     on federation or the local group server if the group is local | ||||||
|  |     """ | ||||||
|  |     def f(self, group_id, *args, **kwargs): | ||||||
|  |         if self.is_mine_id(group_id): | ||||||
|  |             return getattr(self.groups_server_handler, func_name)( | ||||||
|  |                 group_id, *args, **kwargs | ||||||
|  |             ) | ||||||
|  |         else: | ||||||
|  |             destination = get_domain_from_id(group_id) | ||||||
|  |             return getattr(self.transport_client, func_name)( | ||||||
|  |                 destination, group_id, *args, **kwargs | ||||||
|  |             ) | ||||||
|  |     return f | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GroupsLocalHandler(object): | ||||||
|  |     def __init__(self, hs): | ||||||
|  |         self.hs = hs | ||||||
|  |         self.store = hs.get_datastore() | ||||||
|  |         self.room_list_handler = hs.get_room_list_handler() | ||||||
|  |         self.groups_server_handler = hs.get_groups_server_handler() | ||||||
|  |         self.transport_client = hs.get_federation_transport_client() | ||||||
|  |         self.auth = hs.get_auth() | ||||||
|  |         self.clock = hs.get_clock() | ||||||
|  |         self.keyring = hs.get_keyring() | ||||||
|  |         self.is_mine_id = hs.is_mine_id | ||||||
|  |         self.signing_key = hs.config.signing_key[0] | ||||||
|  |         self.server_name = hs.hostname | ||||||
|  |         self.attestations = hs.get_groups_attestation_signing() | ||||||
|  | 
 | ||||||
|  |         # Ensure attestations get renewed | ||||||
|  |         hs.get_groups_attestation_renewer() | ||||||
|  | 
 | ||||||
|  |     # The following functions merely route the query to the local groups server | ||||||
|  |     # or federation depending on if the group is local or remote | ||||||
|  | 
 | ||||||
|  |     get_group_profile = _create_rerouter("get_group_profile") | ||||||
|  |     get_rooms_in_group = _create_rerouter("get_rooms_in_group") | ||||||
|  | 
 | ||||||
|  |     add_room_to_group = _create_rerouter("add_room_to_group") | ||||||
|  | 
 | ||||||
|  |     update_group_summary_room = _create_rerouter("update_group_summary_room") | ||||||
|  |     delete_group_summary_room = _create_rerouter("delete_group_summary_room") | ||||||
|  | 
 | ||||||
|  |     update_group_category = _create_rerouter("update_group_category") | ||||||
|  |     delete_group_category = _create_rerouter("delete_group_category") | ||||||
|  |     get_group_category = _create_rerouter("get_group_category") | ||||||
|  |     get_group_categories = _create_rerouter("get_group_categories") | ||||||
|  | 
 | ||||||
|  |     update_group_summary_user = _create_rerouter("update_group_summary_user") | ||||||
|  |     delete_group_summary_user = _create_rerouter("delete_group_summary_user") | ||||||
|  | 
 | ||||||
|  |     update_group_role = _create_rerouter("update_group_role") | ||||||
|  |     delete_group_role = _create_rerouter("delete_group_role") | ||||||
|  |     get_group_role = _create_rerouter("get_group_role") | ||||||
|  |     get_group_roles = _create_rerouter("get_group_roles") | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def get_group_summary(self, group_id, requester_user_id): | ||||||
|  |         """Get the group summary for a group. | ||||||
|  | 
 | ||||||
|  |         If the group is remote we check that the users have valid attestations. | ||||||
|  |         """ | ||||||
|  |         if self.is_mine_id(group_id): | ||||||
|  |             res = yield self.groups_server_handler.get_group_summary( | ||||||
|  |                 group_id, requester_user_id | ||||||
|  |             ) | ||||||
|  |             defer.returnValue(res) | ||||||
|  | 
 | ||||||
|  |         res = yield self.transport_client.get_group_summary( | ||||||
|  |             get_domain_from_id(group_id), group_id, requester_user_id, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         # Loop through the users and validate the attestations. | ||||||
|  |         chunk = res["users_section"]["users"] | ||||||
|  |         valid_users = [] | ||||||
|  |         for entry in chunk: | ||||||
|  |             g_user_id = entry["user_id"] | ||||||
|  |             attestation = entry.pop("attestation") | ||||||
|  |             try: | ||||||
|  |                 yield self.attestations.verify_attestation( | ||||||
|  |                     attestation, | ||||||
|  |                     group_id=group_id, | ||||||
|  |                     user_id=g_user_id, | ||||||
|  |                 ) | ||||||
|  |                 valid_users.append(entry) | ||||||
|  |             except Exception as e: | ||||||
|  |                 logger.info("Failed to verify user is in group: %s", e) | ||||||
|  | 
 | ||||||
|  |         res["users_section"]["users"] = valid_users | ||||||
|  | 
 | ||||||
|  |         res["users_section"]["users"].sort(key=lambda e: e.get("order", 0)) | ||||||
|  |         res["rooms_section"]["rooms"].sort(key=lambda e: e.get("order", 0)) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue(res) | ||||||
|  | 
 | ||||||
|  |     def create_group(self, group_id, user_id, content): | ||||||
|  |         """Create a group | ||||||
|  |         """ | ||||||
|  | 
 | ||||||
|  |         logger.info("Asking to create group with ID: %r", group_id) | ||||||
|  | 
 | ||||||
|  |         if self.is_mine_id(group_id): | ||||||
|  |             return self.groups_server_handler.create_group( | ||||||
|  |                 group_id, user_id, content | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |         return self.transport_client.create_group( | ||||||
|  |             get_domain_from_id(group_id), group_id, user_id, content, | ||||||
|  |         )  # TODO | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def get_users_in_group(self, group_id, requester_user_id): | ||||||
|  |         """Get users in a group | ||||||
|  |         """ | ||||||
|  |         if self.is_mine_id(group_id): | ||||||
|  |             res = yield self.groups_server_handler.get_users_in_group( | ||||||
|  |                 group_id, requester_user_id | ||||||
|  |             ) | ||||||
|  |             defer.returnValue(res) | ||||||
|  | 
 | ||||||
|  |         res = yield self.transport_client.get_users_in_group( | ||||||
|  |             get_domain_from_id(group_id), group_id, requester_user_id, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         chunk = res["chunk"] | ||||||
|  |         valid_entries = [] | ||||||
|  |         for entry in chunk: | ||||||
|  |             g_user_id = entry["user_id"] | ||||||
|  |             attestation = entry.pop("attestation") | ||||||
|  |             try: | ||||||
|  |                 yield self.attestations.verify_attestation( | ||||||
|  |                     attestation, | ||||||
|  |                     group_id=group_id, | ||||||
|  |                     user_id=g_user_id, | ||||||
|  |                 ) | ||||||
|  |                 valid_entries.append(entry) | ||||||
|  |             except Exception as e: | ||||||
|  |                 logger.info("Failed to verify user is in group: %s", e) | ||||||
|  | 
 | ||||||
|  |         res["chunk"] = valid_entries | ||||||
|  | 
 | ||||||
|  |         defer.returnValue(res) | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def join_group(self, group_id, user_id, content): | ||||||
|  |         """Request to join a group | ||||||
|  |         """ | ||||||
|  |         raise NotImplementedError()  # TODO | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def accept_invite(self, group_id, user_id, content): | ||||||
|  |         """Accept an invite to a group | ||||||
|  |         """ | ||||||
|  |         if self.is_mine_id(group_id): | ||||||
|  |             yield self.groups_server_handler.accept_invite( | ||||||
|  |                 group_id, user_id, content | ||||||
|  |             ) | ||||||
|  |             local_attestation = None | ||||||
|  |             remote_attestation = None | ||||||
|  |         else: | ||||||
|  |             local_attestation = self.attestations.create_attestation(group_id, user_id) | ||||||
|  |             content["attestation"] = local_attestation | ||||||
|  | 
 | ||||||
|  |             res = yield self.transport_client.accept_group_invite( | ||||||
|  |                 get_domain_from_id(group_id), group_id, user_id, content, | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |             remote_attestation = res["attestation"] | ||||||
|  | 
 | ||||||
|  |             yield self.attestations.verify_attestation( | ||||||
|  |                 remote_attestation, | ||||||
|  |                 group_id=group_id, | ||||||
|  |                 user_id=user_id, | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |         yield self.store.register_user_group_membership( | ||||||
|  |             group_id, user_id, | ||||||
|  |             membership="join", | ||||||
|  |             is_admin=False, | ||||||
|  |             local_attestation=local_attestation, | ||||||
|  |             remote_attestation=remote_attestation, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue({}) | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def invite(self, group_id, user_id, requester_user_id, config): | ||||||
|  |         """Invite a user to a group | ||||||
|  |         """ | ||||||
|  |         content = { | ||||||
|  |             "requester_user_id": requester_user_id, | ||||||
|  |             "config": config, | ||||||
|  |         } | ||||||
|  |         if self.is_mine_id(group_id): | ||||||
|  |             res = yield self.groups_server_handler.invite_to_group( | ||||||
|  |                 group_id, user_id, requester_user_id, content, | ||||||
|  |             ) | ||||||
|  |         else: | ||||||
|  |             res = yield self.transport_client.invite_to_group( | ||||||
|  |                 get_domain_from_id(group_id), group_id, user_id, requester_user_id, | ||||||
|  |                 content, | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue(res) | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_invite(self, group_id, user_id, content): | ||||||
|  |         """One of our users were invited to a group | ||||||
|  |         """ | ||||||
|  |         # TODO: Support auto join and rejection | ||||||
|  | 
 | ||||||
|  |         if not self.is_mine_id(user_id): | ||||||
|  |             raise SynapseError(400, "User not on this server") | ||||||
|  | 
 | ||||||
|  |         local_profile = {} | ||||||
|  |         if "profile" in content: | ||||||
|  |             if "name" in content["profile"]: | ||||||
|  |                 local_profile["name"] = content["profile"]["name"] | ||||||
|  |             if "avatar_url" in content["profile"]: | ||||||
|  |                 local_profile["avatar_url"] = content["profile"]["avatar_url"] | ||||||
|  | 
 | ||||||
|  |         yield self.store.register_user_group_membership( | ||||||
|  |             group_id, user_id, | ||||||
|  |             membership="invite", | ||||||
|  |             content={"profile": local_profile, "inviter": content["inviter"]}, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue({"state": "invite"}) | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def remove_user_from_group(self, group_id, user_id, requester_user_id, content): | ||||||
|  |         """Remove a user from a group | ||||||
|  |         """ | ||||||
|  |         if user_id == requester_user_id: | ||||||
|  |             yield self.store.register_user_group_membership( | ||||||
|  |                 group_id, user_id, | ||||||
|  |                 membership="leave", | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |             # TODO: Should probably remember that we tried to leave so that we can | ||||||
|  |             # retry if the group server is currently down. | ||||||
|  | 
 | ||||||
|  |         if self.is_mine_id(group_id): | ||||||
|  |             res = yield self.groups_server_handler.remove_user_from_group( | ||||||
|  |                 group_id, user_id, requester_user_id, content, | ||||||
|  |             ) | ||||||
|  |         else: | ||||||
|  |             content["requester_user_id"] = requester_user_id | ||||||
|  |             res = yield self.transport_client.remove_user_from_group( | ||||||
|  |                 get_domain_from_id(group_id), group_id, requester_user_id, | ||||||
|  |                 user_id, content, | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue(res) | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def user_removed_from_group(self, group_id, user_id, content): | ||||||
|  |         """One of our users was removed/kicked from a group | ||||||
|  |         """ | ||||||
|  |         # TODO: Check if user in group | ||||||
|  |         yield self.store.register_user_group_membership( | ||||||
|  |             group_id, user_id, | ||||||
|  |             membership="leave", | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def get_joined_groups(self, user_id): | ||||||
|  |         group_ids = yield self.store.get_joined_groups(user_id) | ||||||
|  |         defer.returnValue({"groups": group_ids}) | ||||||
| @ -347,7 +347,7 @@ class MatrixFederationHttpClient(object): | |||||||
| 
 | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def post_json(self, destination, path, data={}, long_retries=False, |     def post_json(self, destination, path, data={}, long_retries=False, | ||||||
|                   timeout=None, ignore_backoff=False): |                   timeout=None, ignore_backoff=False, args={}): | ||||||
|         """ Sends the specifed json data using POST |         """ Sends the specifed json data using POST | ||||||
| 
 | 
 | ||||||
|         Args: |         Args: | ||||||
| @ -383,6 +383,7 @@ class MatrixFederationHttpClient(object): | |||||||
|             destination, |             destination, | ||||||
|             "POST", |             "POST", | ||||||
|             path, |             path, | ||||||
|  |             query_bytes=encode_query_args(args), | ||||||
|             body_callback=body_callback, |             body_callback=body_callback, | ||||||
|             headers_dict={"Content-Type": ["application/json"]}, |             headers_dict={"Content-Type": ["application/json"]}, | ||||||
|             long_retries=long_retries, |             long_retries=long_retries, | ||||||
| @ -427,13 +428,6 @@ class MatrixFederationHttpClient(object): | |||||||
|         """ |         """ | ||||||
|         logger.debug("get_json args: %s", args) |         logger.debug("get_json args: %s", args) | ||||||
| 
 | 
 | ||||||
|         encoded_args = {} |  | ||||||
|         for k, vs in args.items(): |  | ||||||
|             if isinstance(vs, basestring): |  | ||||||
|                 vs = [vs] |  | ||||||
|             encoded_args[k] = [v.encode("UTF-8") for v in vs] |  | ||||||
| 
 |  | ||||||
|         query_bytes = urllib.urlencode(encoded_args, True) |  | ||||||
|         logger.debug("Query bytes: %s Retry DNS: %s", args, retry_on_dns_fail) |         logger.debug("Query bytes: %s Retry DNS: %s", args, retry_on_dns_fail) | ||||||
| 
 | 
 | ||||||
|         def body_callback(method, url_bytes, headers_dict): |         def body_callback(method, url_bytes, headers_dict): | ||||||
| @ -444,7 +438,7 @@ class MatrixFederationHttpClient(object): | |||||||
|             destination, |             destination, | ||||||
|             "GET", |             "GET", | ||||||
|             path, |             path, | ||||||
|             query_bytes=query_bytes, |             query_bytes=encode_query_args(args), | ||||||
|             body_callback=body_callback, |             body_callback=body_callback, | ||||||
|             retry_on_dns_fail=retry_on_dns_fail, |             retry_on_dns_fail=retry_on_dns_fail, | ||||||
|             timeout=timeout, |             timeout=timeout, | ||||||
| @ -460,6 +454,52 @@ class MatrixFederationHttpClient(object): | |||||||
| 
 | 
 | ||||||
|         defer.returnValue(json.loads(body)) |         defer.returnValue(json.loads(body)) | ||||||
| 
 | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def delete_json(self, destination, path, long_retries=False, | ||||||
|  |                     timeout=None, ignore_backoff=False, args={}): | ||||||
|  |         """Send a DELETE request to the remote expecting some json response | ||||||
|  | 
 | ||||||
|  |         Args: | ||||||
|  |             destination (str): The remote server to send the HTTP request | ||||||
|  |                 to. | ||||||
|  |             path (str): The HTTP path. | ||||||
|  |             long_retries (bool): A boolean that indicates whether we should | ||||||
|  |                 retry for a short or long time. | ||||||
|  |             timeout(int): How long to try (in ms) the destination for before | ||||||
|  |                 giving up. None indicates no timeout. | ||||||
|  |             ignore_backoff (bool): true to ignore the historical backoff data and | ||||||
|  |                 try the request anyway. | ||||||
|  |         Returns: | ||||||
|  |             Deferred: Succeeds when we get a 2xx HTTP response. The result | ||||||
|  |             will be the decoded JSON body. | ||||||
|  | 
 | ||||||
|  |             Fails with ``HTTPRequestException`` if we get an HTTP response | ||||||
|  |             code >= 300. | ||||||
|  | 
 | ||||||
|  |             Fails with ``NotRetryingDestination`` if we are not yet ready | ||||||
|  |             to retry this server. | ||||||
|  |         """ | ||||||
|  | 
 | ||||||
|  |         response = yield self._request( | ||||||
|  |             destination, | ||||||
|  |             "DELETE", | ||||||
|  |             path, | ||||||
|  |             query_bytes=encode_query_args(args), | ||||||
|  |             headers_dict={"Content-Type": ["application/json"]}, | ||||||
|  |             long_retries=long_retries, | ||||||
|  |             timeout=timeout, | ||||||
|  |             ignore_backoff=ignore_backoff, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         if 200 <= response.code < 300: | ||||||
|  |             # We need to update the transactions table to say it was sent? | ||||||
|  |             check_content_type_is_json(response.headers) | ||||||
|  | 
 | ||||||
|  |         with logcontext.PreserveLoggingContext(): | ||||||
|  |             body = yield readBody(response) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue(json.loads(body)) | ||||||
|  | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def get_file(self, destination, path, output_stream, args={}, |     def get_file(self, destination, path, output_stream, args={}, | ||||||
|                  retry_on_dns_fail=True, max_size=None, |                  retry_on_dns_fail=True, max_size=None, | ||||||
| @ -610,3 +650,15 @@ def check_content_type_is_json(headers): | |||||||
|         raise RuntimeError( |         raise RuntimeError( | ||||||
|             "Content-Type not application/json: was '%s'" % c_type |             "Content-Type not application/json: was '%s'" % c_type | ||||||
|         ) |         ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def encode_query_args(args): | ||||||
|  |     encoded_args = {} | ||||||
|  |     for k, vs in args.items(): | ||||||
|  |         if isinstance(vs, basestring): | ||||||
|  |             vs = [vs] | ||||||
|  |         encoded_args[k] = [v.encode("UTF-8") for v in vs] | ||||||
|  | 
 | ||||||
|  |     query_bytes = urllib.urlencode(encoded_args, True) | ||||||
|  | 
 | ||||||
|  |     return query_bytes | ||||||
|  | |||||||
| @ -52,6 +52,7 @@ from synapse.rest.client.v2_alpha import ( | |||||||
|     thirdparty, |     thirdparty, | ||||||
|     sendtodevice, |     sendtodevice, | ||||||
|     user_directory, |     user_directory, | ||||||
|  |     groups, | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| from synapse.http.server import JsonResource | from synapse.http.server import JsonResource | ||||||
| @ -102,3 +103,4 @@ class ClientRestResource(JsonResource): | |||||||
|         thirdparty.register_servlets(hs, client_resource) |         thirdparty.register_servlets(hs, client_resource) | ||||||
|         sendtodevice.register_servlets(hs, client_resource) |         sendtodevice.register_servlets(hs, client_resource) | ||||||
|         user_directory.register_servlets(hs, client_resource) |         user_directory.register_servlets(hs, client_resource) | ||||||
|  |         groups.register_servlets(hs, client_resource) | ||||||
|  | |||||||
							
								
								
									
										589
									
								
								synapse/rest/client/v2_alpha/groups.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										589
									
								
								synapse/rest/client/v2_alpha/groups.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,589 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Copyright 2017 Vector Creations Ltd | ||||||
|  | # | ||||||
|  | # Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  | # you may not use this file except in compliance with the License. | ||||||
|  | # You may obtain a copy of the License at | ||||||
|  | # | ||||||
|  | #     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  | # | ||||||
|  | # Unless required by applicable law or agreed to in writing, software | ||||||
|  | # distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  | # See the License for the specific language governing permissions and | ||||||
|  | # limitations under the License. | ||||||
|  | 
 | ||||||
|  | from twisted.internet import defer | ||||||
|  | 
 | ||||||
|  | from synapse.http.servlet import RestServlet, parse_json_object_from_request | ||||||
|  | from synapse.types import GroupID | ||||||
|  | 
 | ||||||
|  | from ._base import client_v2_patterns | ||||||
|  | 
 | ||||||
|  | import logging | ||||||
|  | 
 | ||||||
|  | logger = logging.getLogger(__name__) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GroupServlet(RestServlet): | ||||||
|  |     """Get the group profile | ||||||
|  |     """ | ||||||
|  |     PATTERNS = client_v2_patterns("/groups/(?P<group_id>[^/]*)/profile$") | ||||||
|  | 
 | ||||||
|  |     def __init__(self, hs): | ||||||
|  |         super(GroupServlet, self).__init__() | ||||||
|  |         self.auth = hs.get_auth() | ||||||
|  |         self.clock = hs.get_clock() | ||||||
|  |         self.groups_handler = hs.get_groups_local_handler() | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_GET(self, request, group_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         group_description = yield self.groups_handler.get_group_profile(group_id, user_id) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, group_description)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GroupSummaryServlet(RestServlet): | ||||||
|  |     """Get the full group summary | ||||||
|  |     """ | ||||||
|  |     PATTERNS = client_v2_patterns("/groups/(?P<group_id>[^/]*)/summary$") | ||||||
|  | 
 | ||||||
|  |     def __init__(self, hs): | ||||||
|  |         super(GroupSummaryServlet, self).__init__() | ||||||
|  |         self.auth = hs.get_auth() | ||||||
|  |         self.clock = hs.get_clock() | ||||||
|  |         self.groups_handler = hs.get_groups_local_handler() | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_GET(self, request, group_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         get_group_summary = yield self.groups_handler.get_group_summary(group_id, user_id) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, get_group_summary)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GroupSummaryRoomsCatServlet(RestServlet): | ||||||
|  |     """Update/delete a rooms entry in the summary. | ||||||
|  | 
 | ||||||
|  |     Matches both: | ||||||
|  |         - /groups/:group/summary/rooms/:room_id | ||||||
|  |         - /groups/:group/summary/categories/:category/rooms/:room_id | ||||||
|  |     """ | ||||||
|  |     PATTERNS = client_v2_patterns( | ||||||
|  |         "/groups/(?P<group_id>[^/]*)/summary" | ||||||
|  |         "(/categories/(?P<category_id>[^/]+))?" | ||||||
|  |         "/rooms/(?P<room_id>[^/]*)$" | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     def __init__(self, hs): | ||||||
|  |         super(GroupSummaryRoomsCatServlet, self).__init__() | ||||||
|  |         self.auth = hs.get_auth() | ||||||
|  |         self.clock = hs.get_clock() | ||||||
|  |         self.groups_handler = hs.get_groups_local_handler() | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_PUT(self, request, group_id, category_id, room_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         content = parse_json_object_from_request(request) | ||||||
|  |         resp = yield self.groups_handler.update_group_summary_room( | ||||||
|  |             group_id, user_id, | ||||||
|  |             room_id=room_id, | ||||||
|  |             category_id=category_id, | ||||||
|  |             content=content, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, resp)) | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_DELETE(self, request, group_id, category_id, room_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         resp = yield self.groups_handler.delete_group_summary_room( | ||||||
|  |             group_id, user_id, | ||||||
|  |             room_id=room_id, | ||||||
|  |             category_id=category_id, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, resp)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GroupCategoryServlet(RestServlet): | ||||||
|  |     """Get/add/update/delete a group category | ||||||
|  |     """ | ||||||
|  |     PATTERNS = client_v2_patterns( | ||||||
|  |         "/groups/(?P<group_id>[^/]*)/categories/(?P<category_id>[^/]+)$" | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     def __init__(self, hs): | ||||||
|  |         super(GroupCategoryServlet, self).__init__() | ||||||
|  |         self.auth = hs.get_auth() | ||||||
|  |         self.clock = hs.get_clock() | ||||||
|  |         self.groups_handler = hs.get_groups_local_handler() | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_GET(self, request, group_id, category_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         category = yield self.groups_handler.get_group_category( | ||||||
|  |             group_id, user_id, | ||||||
|  |             category_id=category_id, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, category)) | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_PUT(self, request, group_id, category_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         content = parse_json_object_from_request(request) | ||||||
|  |         resp = yield self.groups_handler.update_group_category( | ||||||
|  |             group_id, user_id, | ||||||
|  |             category_id=category_id, | ||||||
|  |             content=content, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, resp)) | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_DELETE(self, request, group_id, category_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         resp = yield self.groups_handler.delete_group_category( | ||||||
|  |             group_id, user_id, | ||||||
|  |             category_id=category_id, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, resp)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GroupCategoriesServlet(RestServlet): | ||||||
|  |     """Get all group categories | ||||||
|  |     """ | ||||||
|  |     PATTERNS = client_v2_patterns( | ||||||
|  |         "/groups/(?P<group_id>[^/]*)/categories/$" | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     def __init__(self, hs): | ||||||
|  |         super(GroupCategoriesServlet, self).__init__() | ||||||
|  |         self.auth = hs.get_auth() | ||||||
|  |         self.clock = hs.get_clock() | ||||||
|  |         self.groups_handler = hs.get_groups_local_handler() | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_GET(self, request, group_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         category = yield self.groups_handler.get_group_categories( | ||||||
|  |             group_id, user_id, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, category)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GroupRoleServlet(RestServlet): | ||||||
|  |     """Get/add/update/delete a group role | ||||||
|  |     """ | ||||||
|  |     PATTERNS = client_v2_patterns( | ||||||
|  |         "/groups/(?P<group_id>[^/]*)/roles/(?P<role_id>[^/]+)$" | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     def __init__(self, hs): | ||||||
|  |         super(GroupRoleServlet, self).__init__() | ||||||
|  |         self.auth = hs.get_auth() | ||||||
|  |         self.clock = hs.get_clock() | ||||||
|  |         self.groups_handler = hs.get_groups_local_handler() | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_GET(self, request, group_id, role_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         category = yield self.groups_handler.get_group_role( | ||||||
|  |             group_id, user_id, | ||||||
|  |             role_id=role_id, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, category)) | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_PUT(self, request, group_id, role_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         content = parse_json_object_from_request(request) | ||||||
|  |         resp = yield self.groups_handler.update_group_role( | ||||||
|  |             group_id, user_id, | ||||||
|  |             role_id=role_id, | ||||||
|  |             content=content, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, resp)) | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_DELETE(self, request, group_id, role_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         resp = yield self.groups_handler.delete_group_role( | ||||||
|  |             group_id, user_id, | ||||||
|  |             role_id=role_id, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, resp)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GroupRolesServlet(RestServlet): | ||||||
|  |     """Get all group roles | ||||||
|  |     """ | ||||||
|  |     PATTERNS = client_v2_patterns( | ||||||
|  |         "/groups/(?P<group_id>[^/]*)/roles/$" | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     def __init__(self, hs): | ||||||
|  |         super(GroupRolesServlet, self).__init__() | ||||||
|  |         self.auth = hs.get_auth() | ||||||
|  |         self.clock = hs.get_clock() | ||||||
|  |         self.groups_handler = hs.get_groups_local_handler() | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_GET(self, request, group_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         category = yield self.groups_handler.get_group_roles( | ||||||
|  |             group_id, user_id, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, category)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GroupSummaryUsersRoleServlet(RestServlet): | ||||||
|  |     """Update/delete a user's entry in the summary. | ||||||
|  | 
 | ||||||
|  |     Matches both: | ||||||
|  |         - /groups/:group/summary/users/:room_id | ||||||
|  |         - /groups/:group/summary/roles/:role/users/:user_id | ||||||
|  |     """ | ||||||
|  |     PATTERNS = client_v2_patterns( | ||||||
|  |         "/groups/(?P<group_id>[^/]*)/summary" | ||||||
|  |         "(/roles/(?P<role_id>[^/]+))?" | ||||||
|  |         "/users/(?P<user_id>[^/]*)$" | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     def __init__(self, hs): | ||||||
|  |         super(GroupSummaryUsersRoleServlet, self).__init__() | ||||||
|  |         self.auth = hs.get_auth() | ||||||
|  |         self.clock = hs.get_clock() | ||||||
|  |         self.groups_handler = hs.get_groups_local_handler() | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_PUT(self, request, group_id, role_id, user_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         requester_user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         content = parse_json_object_from_request(request) | ||||||
|  |         resp = yield self.groups_handler.update_group_summary_user( | ||||||
|  |             group_id, requester_user_id, | ||||||
|  |             user_id=user_id, | ||||||
|  |             role_id=role_id, | ||||||
|  |             content=content, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, resp)) | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_DELETE(self, request, group_id, role_id, user_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         requester_user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         resp = yield self.groups_handler.delete_group_summary_user( | ||||||
|  |             group_id, requester_user_id, | ||||||
|  |             user_id=user_id, | ||||||
|  |             role_id=role_id, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, resp)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GroupRoomServlet(RestServlet): | ||||||
|  |     """Get all rooms in a group | ||||||
|  |     """ | ||||||
|  |     PATTERNS = client_v2_patterns("/groups/(?P<group_id>[^/]*)/rooms$") | ||||||
|  | 
 | ||||||
|  |     def __init__(self, hs): | ||||||
|  |         super(GroupRoomServlet, self).__init__() | ||||||
|  |         self.auth = hs.get_auth() | ||||||
|  |         self.clock = hs.get_clock() | ||||||
|  |         self.groups_handler = hs.get_groups_local_handler() | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_GET(self, request, group_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         result = yield self.groups_handler.get_rooms_in_group(group_id, user_id) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, result)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GroupUsersServlet(RestServlet): | ||||||
|  |     """Get all users in a group | ||||||
|  |     """ | ||||||
|  |     PATTERNS = client_v2_patterns("/groups/(?P<group_id>[^/]*)/users$") | ||||||
|  | 
 | ||||||
|  |     def __init__(self, hs): | ||||||
|  |         super(GroupUsersServlet, self).__init__() | ||||||
|  |         self.auth = hs.get_auth() | ||||||
|  |         self.clock = hs.get_clock() | ||||||
|  |         self.groups_handler = hs.get_groups_local_handler() | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_GET(self, request, group_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         result = yield self.groups_handler.get_users_in_group(group_id, user_id) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, result)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GroupCreateServlet(RestServlet): | ||||||
|  |     """Create a group | ||||||
|  |     """ | ||||||
|  |     PATTERNS = client_v2_patterns("/create_group$") | ||||||
|  | 
 | ||||||
|  |     def __init__(self, hs): | ||||||
|  |         super(GroupCreateServlet, self).__init__() | ||||||
|  |         self.auth = hs.get_auth() | ||||||
|  |         self.clock = hs.get_clock() | ||||||
|  |         self.groups_handler = hs.get_groups_local_handler() | ||||||
|  |         self.server_name = hs.hostname | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_POST(self, request): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         # TODO: Create group on remote server | ||||||
|  |         content = parse_json_object_from_request(request) | ||||||
|  |         localpart = content.pop("localpart") | ||||||
|  |         group_id = GroupID.create(localpart, self.server_name).to_string() | ||||||
|  | 
 | ||||||
|  |         result = yield self.groups_handler.create_group(group_id, user_id, content) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, result)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GroupAdminRoomsServlet(RestServlet): | ||||||
|  |     """Add a room to the group | ||||||
|  |     """ | ||||||
|  |     PATTERNS = client_v2_patterns( | ||||||
|  |         "/groups/(?P<group_id>[^/]*)/admin/rooms/(?P<room_id>[^/]*)$" | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     def __init__(self, hs): | ||||||
|  |         super(GroupAdminRoomsServlet, self).__init__() | ||||||
|  |         self.auth = hs.get_auth() | ||||||
|  |         self.clock = hs.get_clock() | ||||||
|  |         self.groups_handler = hs.get_groups_local_handler() | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_PUT(self, request, group_id, room_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         content = parse_json_object_from_request(request) | ||||||
|  |         result = yield self.groups_handler.add_room_to_group( | ||||||
|  |             group_id, user_id, room_id, content, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, result)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GroupAdminUsersInviteServlet(RestServlet): | ||||||
|  |     """Invite a user to the group | ||||||
|  |     """ | ||||||
|  |     PATTERNS = client_v2_patterns( | ||||||
|  |         "/groups/(?P<group_id>[^/]*)/admin/users/invite/(?P<user_id>[^/]*)$" | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     def __init__(self, hs): | ||||||
|  |         super(GroupAdminUsersInviteServlet, self).__init__() | ||||||
|  |         self.auth = hs.get_auth() | ||||||
|  |         self.clock = hs.get_clock() | ||||||
|  |         self.groups_handler = hs.get_groups_local_handler() | ||||||
|  |         self.store = hs.get_datastore() | ||||||
|  |         self.is_mine_id = hs.is_mine_id | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_PUT(self, request, group_id, user_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         requester_user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         content = parse_json_object_from_request(request) | ||||||
|  |         config = content.get("config", {}) | ||||||
|  |         result = yield self.groups_handler.invite( | ||||||
|  |             group_id, user_id, requester_user_id, config, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, result)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GroupAdminUsersKickServlet(RestServlet): | ||||||
|  |     """Kick a user from the group | ||||||
|  |     """ | ||||||
|  |     PATTERNS = client_v2_patterns( | ||||||
|  |         "/groups/(?P<group_id>[^/]*)/admin/users/remove/(?P<user_id>[^/]*)$" | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     def __init__(self, hs): | ||||||
|  |         super(GroupAdminUsersKickServlet, self).__init__() | ||||||
|  |         self.auth = hs.get_auth() | ||||||
|  |         self.clock = hs.get_clock() | ||||||
|  |         self.groups_handler = hs.get_groups_local_handler() | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_PUT(self, request, group_id, user_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         requester_user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         content = parse_json_object_from_request(request) | ||||||
|  |         result = yield self.groups_handler.remove_user_from_group( | ||||||
|  |             group_id, user_id, requester_user_id, content, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, result)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GroupSelfLeaveServlet(RestServlet): | ||||||
|  |     """Leave a joined group | ||||||
|  |     """ | ||||||
|  |     PATTERNS = client_v2_patterns( | ||||||
|  |         "/groups/(?P<group_id>[^/]*)/self/leave$" | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     def __init__(self, hs): | ||||||
|  |         super(GroupSelfLeaveServlet, self).__init__() | ||||||
|  |         self.auth = hs.get_auth() | ||||||
|  |         self.clock = hs.get_clock() | ||||||
|  |         self.groups_handler = hs.get_groups_local_handler() | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_PUT(self, request, group_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         requester_user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         content = parse_json_object_from_request(request) | ||||||
|  |         result = yield self.groups_handler.remove_user_from_group( | ||||||
|  |             group_id, requester_user_id, requester_user_id, content, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, result)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GroupSelfJoinServlet(RestServlet): | ||||||
|  |     """Attempt to join a group, or knock | ||||||
|  |     """ | ||||||
|  |     PATTERNS = client_v2_patterns( | ||||||
|  |         "/groups/(?P<group_id>[^/]*)/self/join$" | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     def __init__(self, hs): | ||||||
|  |         super(GroupSelfJoinServlet, self).__init__() | ||||||
|  |         self.auth = hs.get_auth() | ||||||
|  |         self.clock = hs.get_clock() | ||||||
|  |         self.groups_handler = hs.get_groups_local_handler() | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_PUT(self, request, group_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         requester_user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         content = parse_json_object_from_request(request) | ||||||
|  |         result = yield self.groups_handler.join_group( | ||||||
|  |             group_id, requester_user_id, content, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, result)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GroupSelfAcceptInviteServlet(RestServlet): | ||||||
|  |     """Accept a group invite | ||||||
|  |     """ | ||||||
|  |     PATTERNS = client_v2_patterns( | ||||||
|  |         "/groups/(?P<group_id>[^/]*)/self/accept_invite$" | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     def __init__(self, hs): | ||||||
|  |         super(GroupSelfAcceptInviteServlet, self).__init__() | ||||||
|  |         self.auth = hs.get_auth() | ||||||
|  |         self.clock = hs.get_clock() | ||||||
|  |         self.groups_handler = hs.get_groups_local_handler() | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_PUT(self, request, group_id): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         requester_user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         content = parse_json_object_from_request(request) | ||||||
|  |         result = yield self.groups_handler.accept_invite( | ||||||
|  |             group_id, requester_user_id, content, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, result)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GroupsForUserServlet(RestServlet): | ||||||
|  |     """Get all groups the logged in user is joined to | ||||||
|  |     """ | ||||||
|  |     PATTERNS = client_v2_patterns( | ||||||
|  |         "/joined_groups$" | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     def __init__(self, hs): | ||||||
|  |         super(GroupsForUserServlet, self).__init__() | ||||||
|  |         self.auth = hs.get_auth() | ||||||
|  |         self.clock = hs.get_clock() | ||||||
|  |         self.groups_handler = hs.get_groups_local_handler() | ||||||
|  | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def on_GET(self, request): | ||||||
|  |         requester = yield self.auth.get_user_by_req(request) | ||||||
|  |         user_id = requester.user.to_string() | ||||||
|  | 
 | ||||||
|  |         result = yield self.groups_handler.get_joined_groups(user_id) | ||||||
|  | 
 | ||||||
|  |         defer.returnValue((200, result)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def register_servlets(hs, http_server): | ||||||
|  |     GroupServlet(hs).register(http_server) | ||||||
|  |     GroupSummaryServlet(hs).register(http_server) | ||||||
|  |     GroupUsersServlet(hs).register(http_server) | ||||||
|  |     GroupRoomServlet(hs).register(http_server) | ||||||
|  |     GroupCreateServlet(hs).register(http_server) | ||||||
|  |     GroupAdminRoomsServlet(hs).register(http_server) | ||||||
|  |     GroupAdminUsersInviteServlet(hs).register(http_server) | ||||||
|  |     GroupAdminUsersKickServlet(hs).register(http_server) | ||||||
|  |     GroupSelfLeaveServlet(hs).register(http_server) | ||||||
|  |     GroupSelfJoinServlet(hs).register(http_server) | ||||||
|  |     GroupSelfAcceptInviteServlet(hs).register(http_server) | ||||||
|  |     GroupsForUserServlet(hs).register(http_server) | ||||||
|  |     GroupCategoryServlet(hs).register(http_server) | ||||||
|  |     GroupCategoriesServlet(hs).register(http_server) | ||||||
|  |     GroupSummaryRoomsCatServlet(hs).register(http_server) | ||||||
|  |     GroupRoleServlet(hs).register(http_server) | ||||||
|  |     GroupRolesServlet(hs).register(http_server) | ||||||
|  |     GroupSummaryUsersRoleServlet(hs).register(http_server) | ||||||
| @ -50,6 +50,7 @@ from synapse.handlers.initial_sync import InitialSyncHandler | |||||||
| from synapse.handlers.receipts import ReceiptsHandler | from synapse.handlers.receipts import ReceiptsHandler | ||||||
| from synapse.handlers.read_marker import ReadMarkerHandler | from synapse.handlers.read_marker import ReadMarkerHandler | ||||||
| from synapse.handlers.user_directory import UserDirectoyHandler | from synapse.handlers.user_directory import UserDirectoyHandler | ||||||
|  | from synapse.handlers.groups_local import GroupsLocalHandler | ||||||
| from synapse.groups.groups_server import GroupsServerHandler | from synapse.groups.groups_server import GroupsServerHandler | ||||||
| from synapse.groups.attestations import GroupAttestionRenewer, GroupAttestationSigning | from synapse.groups.attestations import GroupAttestionRenewer, GroupAttestationSigning | ||||||
| from synapse.http.client import SimpleHttpClient, InsecureInterceptableContextFactory | from synapse.http.client import SimpleHttpClient, InsecureInterceptableContextFactory | ||||||
| @ -141,6 +142,7 @@ class HomeServer(object): | |||||||
|         'read_marker_handler', |         'read_marker_handler', | ||||||
|         'action_generator', |         'action_generator', | ||||||
|         'user_directory_handler', |         'user_directory_handler', | ||||||
|  |         'groups_local_handler', | ||||||
|         'groups_server_handler', |         'groups_server_handler', | ||||||
|         'groups_attestation_signing', |         'groups_attestation_signing', | ||||||
|         'groups_attestation_renewer', |         'groups_attestation_renewer', | ||||||
| @ -314,6 +316,9 @@ class HomeServer(object): | |||||||
|     def build_user_directory_handler(self): |     def build_user_directory_handler(self): | ||||||
|         return UserDirectoyHandler(self) |         return UserDirectoyHandler(self) | ||||||
| 
 | 
 | ||||||
|  |     def build_groups_local_handler(self): | ||||||
|  |         return GroupsLocalHandler(self) | ||||||
|  | 
 | ||||||
|     def build_groups_server_handler(self): |     def build_groups_server_handler(self): | ||||||
|         return GroupsServerHandler(self) |         return GroupsServerHandler(self) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -756,6 +756,95 @@ class GroupServerStore(SQLBaseStore): | |||||||
|             desc="add_room_to_group", |             desc="add_room_to_group", | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|  |     @defer.inlineCallbacks | ||||||
|  |     def register_user_group_membership(self, group_id, user_id, membership, | ||||||
|  |                                        is_admin=False, content={}, | ||||||
|  |                                        local_attestation=None, | ||||||
|  |                                        remote_attestation=None, | ||||||
|  |                                        ): | ||||||
|  |         """Registers that a local user is a member of a (local or remote) group. | ||||||
|  | 
 | ||||||
|  |         Args: | ||||||
|  |             group_id (str) | ||||||
|  |             user_id (str) | ||||||
|  |             membership (str) | ||||||
|  |             is_admin (bool) | ||||||
|  |             content (dict): Content of the membership, e.g. includes the inviter | ||||||
|  |                 if the user has been invited. | ||||||
|  |             local_attestation (dict): If remote group then store the fact that we | ||||||
|  |                 have given out an attestation, else None. | ||||||
|  |             remote_attestation (dict): If remote group then store the remote | ||||||
|  |                 attestation from the group, else None. | ||||||
|  |         """ | ||||||
|  |         def _register_user_group_membership_txn(txn): | ||||||
|  |             # TODO: Upsert? | ||||||
|  |             self._simple_delete_txn( | ||||||
|  |                 txn, | ||||||
|  |                 table="local_group_membership", | ||||||
|  |                 keyvalues={ | ||||||
|  |                     "group_id": group_id, | ||||||
|  |                     "user_id": user_id, | ||||||
|  |                 }, | ||||||
|  |             ) | ||||||
|  |             self._simple_insert_txn( | ||||||
|  |                 txn, | ||||||
|  |                 table="local_group_membership", | ||||||
|  |                 values={ | ||||||
|  |                     "group_id": group_id, | ||||||
|  |                     "user_id": user_id, | ||||||
|  |                     "is_admin": is_admin, | ||||||
|  |                     "membership": membership, | ||||||
|  |                     "content": json.dumps(content), | ||||||
|  |                 }, | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |             # TODO: Insert profile to ensure it comes down stream if its a join. | ||||||
|  | 
 | ||||||
|  |             if membership == "join": | ||||||
|  |                 if local_attestation: | ||||||
|  |                     self._simple_insert_txn( | ||||||
|  |                         txn, | ||||||
|  |                         table="group_attestations_renewals", | ||||||
|  |                         values={ | ||||||
|  |                             "group_id": group_id, | ||||||
|  |                             "user_id": user_id, | ||||||
|  |                             "valid_until_ms": local_attestation["valid_until_ms"], | ||||||
|  |                         } | ||||||
|  |                     ) | ||||||
|  |                 if remote_attestation: | ||||||
|  |                     self._simple_insert_txn( | ||||||
|  |                         txn, | ||||||
|  |                         table="group_attestations_remote", | ||||||
|  |                         values={ | ||||||
|  |                             "group_id": group_id, | ||||||
|  |                             "user_id": user_id, | ||||||
|  |                             "valid_until_ms": remote_attestation["valid_until_ms"], | ||||||
|  |                             "attestation_json": json.dumps(remote_attestation), | ||||||
|  |                         } | ||||||
|  |                     ) | ||||||
|  |             else: | ||||||
|  |                 self._simple_delete_txn( | ||||||
|  |                     txn, | ||||||
|  |                     table="group_attestations_renewals", | ||||||
|  |                     keyvalues={ | ||||||
|  |                         "group_id": group_id, | ||||||
|  |                         "user_id": user_id, | ||||||
|  |                     }, | ||||||
|  |                 ) | ||||||
|  |                 self._simple_delete_txn( | ||||||
|  |                     txn, | ||||||
|  |                     table="group_attestations_remote", | ||||||
|  |                     keyvalues={ | ||||||
|  |                         "group_id": group_id, | ||||||
|  |                         "user_id": user_id, | ||||||
|  |                     }, | ||||||
|  |                 ) | ||||||
|  | 
 | ||||||
|  |         yield self.runInteraction( | ||||||
|  |             "register_user_group_membership", | ||||||
|  |             _register_user_group_membership_txn, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|     @defer.inlineCallbacks |     @defer.inlineCallbacks | ||||||
|     def create_group(self, group_id, user_id, name, avatar_url, short_description, |     def create_group(self, group_id, user_id, name, avatar_url, short_description, | ||||||
|                      long_description,): |                      long_description,): | ||||||
| @ -837,3 +926,14 @@ class GroupServerStore(SQLBaseStore): | |||||||
|             defer.returnValue(json.loads(row["attestation_json"])) |             defer.returnValue(json.loads(row["attestation_json"])) | ||||||
| 
 | 
 | ||||||
|         defer.returnValue(None) |         defer.returnValue(None) | ||||||
|  | 
 | ||||||
|  |     def get_joined_groups(self, user_id): | ||||||
|  |         return self._simple_select_onecol( | ||||||
|  |             table="local_group_membership", | ||||||
|  |             keyvalues={ | ||||||
|  |                 "user_id": user_id, | ||||||
|  |                 "membership": "join", | ||||||
|  |             }, | ||||||
|  |             retcol="group_id", | ||||||
|  |             desc="get_joined_groups", | ||||||
|  |         ) | ||||||
|  | |||||||
| @ -142,3 +142,16 @@ CREATE TABLE group_attestations_remote ( | |||||||
| CREATE INDEX group_attestations_remote_g_idx ON group_attestations_remote(group_id, user_id); | CREATE INDEX group_attestations_remote_g_idx ON group_attestations_remote(group_id, user_id); | ||||||
| CREATE INDEX group_attestations_remote_u_idx ON group_attestations_remote(user_id); | CREATE INDEX group_attestations_remote_u_idx ON group_attestations_remote(user_id); | ||||||
| CREATE INDEX group_attestations_remote_v_idx ON group_attestations_remote(valid_until_ms); | CREATE INDEX group_attestations_remote_v_idx ON group_attestations_remote(valid_until_ms); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | -- The group membership for the HS's users | ||||||
|  | CREATE TABLE local_group_membership ( | ||||||
|  |     group_id TEXT NOT NULL, | ||||||
|  |     user_id TEXT NOT NULL, | ||||||
|  |     is_admin BOOLEAN NOT NULL, | ||||||
|  |     membership TEXT NOT NULL, | ||||||
|  |     content TEXT NOT NULL | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | CREATE INDEX local_group_membership_u_idx ON local_group_membership(user_id, group_id); | ||||||
|  | CREATE INDEX local_group_membership_g_idx ON local_group_membership(group_id); | ||||||
|  | |||||||
| @ -156,6 +156,11 @@ class EventID(DomainSpecificString): | |||||||
|     SIGIL = "$" |     SIGIL = "$" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class GroupID(DomainSpecificString): | ||||||
|  |     """Structure representing a group ID.""" | ||||||
|  |     SIGIL = "+" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class StreamToken( | class StreamToken( | ||||||
|     namedtuple("Token", ( |     namedtuple("Token", ( | ||||||
|         "room_key", |         "room_key", | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user