Kaynağa Gözat

Remove user-visible groups/communities code (#12553)

Makes it so that groups/communities no longer exist from a user-POV. E.g. we remove:

* All API endpoints (including Client-Server, Server-Server, and admin).
* Documented configuration options (and the experimental flag, which is now unused).
* Special handling during room upgrades.
* The `groups` section of the `/sync` response.
tags/v1.61.0rc1
Patrick Cloke 2 yıl önce
committed by GitHub
ebeveyn
işleme
a8db8c6eba
Veri tabanında bu imza için bilinen anahtar bulunamadı GPG Anahtar Kimliği: 4AEE18F83AFDEB23
19 değiştirilmiş dosya ile 3 ekleme ve 2215 silme
  1. +1
    -0
      changelog.d/12553.removal
  2. +0
    -10
      docs/sample_config.yaml
  3. +0
    -17
      docs/usage/configuration/config_documentation.md
  4. +0
    -5
      synapse/api/constants.py
  5. +0
    -4
      synapse/app/generic_worker.py
  6. +0
    -3
      synapse/config/experimental.py
  7. +0
    -12
      synapse/config/groups.py
  8. +1
    -47
      synapse/federation/transport/server/__init__.py
  9. +0
    -115
      synapse/federation/transport/server/groups_local.py
  10. +0
    -755
      synapse/federation/transport/server/groups_server.py
  11. +0
    -11
      synapse/handlers/room_member.py
  12. +0
    -65
      synapse/handlers/sync.py
  13. +0
    -3
      synapse/rest/__init__.py
  14. +0
    -3
      synapse/rest/admin/__init__.py
  15. +0
    -50
      synapse/rest/admin/groups.py
  16. +0
    -962
      synapse/rest/client/groups.py
  17. +0
    -8
      synapse/rest/client/sync.py
  18. +1
    -89
      tests/rest/admin/test_admin.py
  19. +0
    -56
      tests/rest/client/test_groups.py

+ 1
- 0
changelog.d/12553.removal Dosyayı Görüntüle

@@ -0,0 +1 @@
Remove support for the non-standard groups/communities feature from Synapse.

+ 0
- 10
docs/sample_config.yaml Dosyayı Görüntüle

@@ -2521,16 +2521,6 @@ push:
# "events_default": 1


# Uncomment to allow non-server-admin users to create groups on this server
#
#enable_group_creation: true

# If enabled, non server admins can only create groups with local parts
# starting with this prefix
#
#group_creation_prefix: "unofficial_"



# User Directory configuration
#


+ 0
- 17
docs/usage/configuration/config_documentation.md Dosyayı Görüntüle

@@ -3145,25 +3145,6 @@ Example configuration:
encryption_enabled_by_default_for_room_type: invite
```
---
Config option: `enable_group_creation`

Set to true to allow non-server-admin users to create groups on this server

Example configuration:
```yaml
enable_group_creation: true
```
Config option: `group_creation_prefix`

If enabled/present, non-server admins can only create groups with local parts
starting with this prefix.

Example configuration:
```yaml
group_creation_prefix: "unofficial_"
```
Config option: `user_directory`

This setting defines options related to the user directory.


+ 0
- 5
synapse/api/constants.py Dosyayı Görüntüle

@@ -31,11 +31,6 @@ MAX_ALIAS_LENGTH = 255
# the maximum length for a user id is 255 characters
MAX_USERID_LENGTH = 255

# The maximum length for a group id is 255 characters
MAX_GROUPID_LENGTH = 255
MAX_GROUP_CATEGORYID_LENGTH = 255
MAX_GROUP_ROLEID_LENGTH = 255


class Membership:



+ 0
- 4
synapse/app/generic_worker.py Dosyayı Görüntüle

@@ -69,7 +69,6 @@ from synapse.rest.admin import register_servlets_for_media_repo
from synapse.rest.client import (
account_data,
events,
groups,
initial_sync,
login,
presence,
@@ -323,9 +322,6 @@ class GenericWorkerServer(HomeServer):

presence.register_servlets(self, resource)

if self.config.experimental.groups_enabled:
groups.register_servlets(self, resource)

resources.update({CLIENT_API_PREFIX: resource})

resources.update(build_synapse_client_resource_tree(self))


+ 0
- 3
synapse/config/experimental.py Dosyayı Görüntüle

@@ -73,9 +73,6 @@ class ExperimentalConfig(Config):
# MSC3720 (Account status endpoint)
self.msc3720_enabled: bool = experimental.get("msc3720_enabled", False)

# The deprecated groups feature.
self.groups_enabled: bool = experimental.get("groups_enabled", False)

# MSC2654: Unread counts
self.msc2654_enabled: bool = experimental.get("msc2654_enabled", False)



+ 0
- 12
synapse/config/groups.py Dosyayı Görüntüle

@@ -25,15 +25,3 @@ class GroupsConfig(Config):
def read_config(self, config: JsonDict, **kwargs: Any) -> None:
self.enable_group_creation = config.get("enable_group_creation", False)
self.group_creation_prefix = config.get("group_creation_prefix", "")

def generate_config_section(self, **kwargs: Any) -> str:
return """\
# Uncomment to allow non-server-admin users to create groups on this server
#
#enable_group_creation: true

# If enabled, non server admins can only create groups with local parts
# starting with this prefix
#
#group_creation_prefix: "unofficial_"
"""

+ 1
- 47
synapse/federation/transport/server/__init__.py Dosyayı Görüntüle

@@ -27,10 +27,6 @@ from synapse.federation.transport.server.federation import (
FederationAccountStatusServlet,
FederationTimestampLookupServlet,
)
from synapse.federation.transport.server.groups_local import GROUP_LOCAL_SERVLET_CLASSES
from synapse.federation.transport.server.groups_server import (
GROUP_SERVER_SERVLET_CLASSES,
)
from synapse.http.server import HttpServer, JsonResource
from synapse.http.servlet import (
parse_boolean_from_args,
@@ -199,38 +195,6 @@ class PublicRoomList(BaseFederationServlet):
return 200, data


class FederationGroupsRenewAttestaionServlet(BaseFederationServlet):
"""A group or user's server renews their attestation"""

PATH = "/groups/(?P<group_id>[^/]*)/renew_attestation/(?P<user_id>[^/]*)"

def __init__(
self,
hs: "HomeServer",
authenticator: Authenticator,
ratelimiter: FederationRateLimiter,
server_name: str,
):
super().__init__(hs, authenticator, ratelimiter, server_name)
self.handler = hs.get_groups_attestation_renewer()

async def on_POST(
self,
origin: str,
content: JsonDict,
query: Dict[bytes, List[bytes]],
group_id: str,
user_id: str,
) -> Tuple[int, JsonDict]:
# We don't need to check auth here as we check the attestation signatures

new_content = await self.handler.on_renew_attestation(
group_id, user_id, content
)

return 200, new_content


class OpenIdUserInfo(BaseFederationServlet):
"""
Exchange a bearer token for information about a user.
@@ -292,16 +256,9 @@ class OpenIdUserInfo(BaseFederationServlet):
SERVLET_GROUPS: Dict[str, Iterable[Type[BaseFederationServlet]]] = {
"federation": FEDERATION_SERVLET_CLASSES,
"room_list": (PublicRoomList,),
"group_server": GROUP_SERVER_SERVLET_CLASSES,
"group_local": GROUP_LOCAL_SERVLET_CLASSES,
"group_attestation": (FederationGroupsRenewAttestaionServlet,),
"openid": (OpenIdUserInfo,),
}

DEFAULT_SERVLET_GROUPS = ("federation", "room_list", "openid")

GROUP_SERVLET_GROUPS = ("group_server", "group_local", "group_attestation")


def register_servlets(
hs: "HomeServer",
@@ -324,10 +281,7 @@ def register_servlets(
Defaults to ``DEFAULT_SERVLET_GROUPS``.
"""
if not servlet_groups:
servlet_groups = DEFAULT_SERVLET_GROUPS
# Only allow the groups servlets if the deprecated groups feature is enabled.
if hs.config.experimental.groups_enabled:
servlet_groups = servlet_groups + GROUP_SERVLET_GROUPS
servlet_groups = SERVLET_GROUPS.keys()

for servlet_group in servlet_groups:
# Skip unknown servlet groups.


+ 0
- 115
synapse/federation/transport/server/groups_local.py Dosyayı Görüntüle

@@ -1,115 +0,0 @@
# Copyright 2021 The Matrix.org Foundation C.I.C.
#
# 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 typing import TYPE_CHECKING, Dict, List, Tuple, Type

from synapse.api.errors import SynapseError
from synapse.federation.transport.server._base import (
Authenticator,
BaseFederationServlet,
)
from synapse.handlers.groups_local import GroupsLocalHandler
from synapse.types import JsonDict, get_domain_from_id
from synapse.util.ratelimitutils import FederationRateLimiter

if TYPE_CHECKING:
from synapse.server import HomeServer


class BaseGroupsLocalServlet(BaseFederationServlet):
"""Abstract base class for federation servlet classes which provides a groups local handler.

See BaseFederationServlet for more information.
"""

def __init__(
self,
hs: "HomeServer",
authenticator: Authenticator,
ratelimiter: FederationRateLimiter,
server_name: str,
):
super().__init__(hs, authenticator, ratelimiter, server_name)
self.handler = hs.get_groups_local_handler()


class FederationGroupsLocalInviteServlet(BaseGroupsLocalServlet):
"""A group server has invited a local user"""

PATH = "/groups/local/(?P<group_id>[^/]*)/users/(?P<user_id>[^/]*)/invite"

async def on_POST(
self,
origin: str,
content: JsonDict,
query: Dict[bytes, List[bytes]],
group_id: str,
user_id: str,
) -> Tuple[int, JsonDict]:
if get_domain_from_id(group_id) != origin:
raise SynapseError(403, "group_id doesn't match origin")

assert isinstance(
self.handler, GroupsLocalHandler
), "Workers cannot handle group invites."

new_content = await self.handler.on_invite(group_id, user_id, content)

return 200, new_content


class FederationGroupsRemoveLocalUserServlet(BaseGroupsLocalServlet):
"""A group server has removed a local user"""

PATH = "/groups/local/(?P<group_id>[^/]*)/users/(?P<user_id>[^/]*)/remove"

async def on_POST(
self,
origin: str,
content: JsonDict,
query: Dict[bytes, List[bytes]],
group_id: str,
user_id: str,
) -> Tuple[int, None]:
if get_domain_from_id(group_id) != origin:
raise SynapseError(403, "user_id doesn't match origin")

assert isinstance(
self.handler, GroupsLocalHandler
), "Workers cannot handle group removals."

await self.handler.user_removed_from_group(group_id, user_id, content)

return 200, None


class FederationGroupsBulkPublicisedServlet(BaseGroupsLocalServlet):
"""Get roles in a group"""

PATH = "/get_groups_publicised"

async def on_POST(
self, origin: str, content: JsonDict, query: Dict[bytes, List[bytes]]
) -> Tuple[int, JsonDict]:
resp = await self.handler.bulk_get_publicised_groups(
content["user_ids"], proxy=False
)

return 200, resp


GROUP_LOCAL_SERVLET_CLASSES: Tuple[Type[BaseFederationServlet], ...] = (
FederationGroupsLocalInviteServlet,
FederationGroupsRemoveLocalUserServlet,
FederationGroupsBulkPublicisedServlet,
)

+ 0
- 755
synapse/federation/transport/server/groups_server.py Dosyayı Görüntüle

@@ -1,755 +0,0 @@
# Copyright 2021 The Matrix.org Foundation C.I.C.
#
# 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 typing import TYPE_CHECKING, Dict, List, Tuple, Type

from typing_extensions import Literal

from synapse.api.constants import MAX_GROUP_CATEGORYID_LENGTH, MAX_GROUP_ROLEID_LENGTH
from synapse.api.errors import Codes, SynapseError
from synapse.federation.transport.server._base import (
Authenticator,
BaseFederationServlet,
)
from synapse.http.servlet import parse_string_from_args
from synapse.types import JsonDict, get_domain_from_id
from synapse.util.ratelimitutils import FederationRateLimiter

if TYPE_CHECKING:
from synapse.server import HomeServer


class BaseGroupsServerServlet(BaseFederationServlet):
"""Abstract base class for federation servlet classes which provides a groups server handler.

See BaseFederationServlet for more information.
"""

def __init__(
self,
hs: "HomeServer",
authenticator: Authenticator,
ratelimiter: FederationRateLimiter,
server_name: str,
):
super().__init__(hs, authenticator, ratelimiter, server_name)
self.handler = hs.get_groups_server_handler()


class FederationGroupsProfileServlet(BaseGroupsServerServlet):
"""Get/set the basic profile of a group on behalf of a user"""

PATH = "/groups/(?P<group_id>[^/]*)/profile"

async def on_GET(
self,
origin: str,
content: Literal[None],
query: Dict[bytes, List[bytes]],
group_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

new_content = await self.handler.get_group_profile(group_id, requester_user_id)

return 200, new_content

async def on_POST(
self,
origin: str,
content: JsonDict,
query: Dict[bytes, List[bytes]],
group_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

new_content = await self.handler.update_group_profile(
group_id, requester_user_id, content
)

return 200, new_content


class FederationGroupsSummaryServlet(BaseGroupsServerServlet):
PATH = "/groups/(?P<group_id>[^/]*)/summary"

async def on_GET(
self,
origin: str,
content: Literal[None],
query: Dict[bytes, List[bytes]],
group_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

new_content = await self.handler.get_group_summary(group_id, requester_user_id)

return 200, new_content


class FederationGroupsRoomsServlet(BaseGroupsServerServlet):
"""Get the rooms in a group on behalf of a user"""

PATH = "/groups/(?P<group_id>[^/]*)/rooms"

async def on_GET(
self,
origin: str,
content: Literal[None],
query: Dict[bytes, List[bytes]],
group_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

new_content = await self.handler.get_rooms_in_group(group_id, requester_user_id)

return 200, new_content


class FederationGroupsAddRoomsServlet(BaseGroupsServerServlet):
"""Add/remove room from group"""

PATH = "/groups/(?P<group_id>[^/]*)/room/(?P<room_id>[^/]*)"

async def on_POST(
self,
origin: str,
content: JsonDict,
query: Dict[bytes, List[bytes]],
group_id: str,
room_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

new_content = await self.handler.add_room_to_group(
group_id, requester_user_id, room_id, content
)

return 200, new_content

async def on_DELETE(
self,
origin: str,
content: Literal[None],
query: Dict[bytes, List[bytes]],
group_id: str,
room_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

new_content = await self.handler.remove_room_from_group(
group_id, requester_user_id, room_id
)

return 200, new_content


class FederationGroupsAddRoomsConfigServlet(BaseGroupsServerServlet):
"""Update room config in group"""

PATH = (
"/groups/(?P<group_id>[^/]*)/room/(?P<room_id>[^/]*)"
"/config/(?P<config_key>[^/]*)"
)

async def on_POST(
self,
origin: str,
content: JsonDict,
query: Dict[bytes, List[bytes]],
group_id: str,
room_id: str,
config_key: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

result = await self.handler.update_room_in_group(
group_id, requester_user_id, room_id, config_key, content
)

return 200, result


class FederationGroupsUsersServlet(BaseGroupsServerServlet):
"""Get the users in a group on behalf of a user"""

PATH = "/groups/(?P<group_id>[^/]*)/users"

async def on_GET(
self,
origin: str,
content: Literal[None],
query: Dict[bytes, List[bytes]],
group_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

new_content = await self.handler.get_users_in_group(group_id, requester_user_id)

return 200, new_content


class FederationGroupsInvitedUsersServlet(BaseGroupsServerServlet):
"""Get the users that have been invited to a group"""

PATH = "/groups/(?P<group_id>[^/]*)/invited_users"

async def on_GET(
self,
origin: str,
content: Literal[None],
query: Dict[bytes, List[bytes]],
group_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

new_content = await self.handler.get_invited_users_in_group(
group_id, requester_user_id
)

return 200, new_content


class FederationGroupsInviteServlet(BaseGroupsServerServlet):
"""Ask a group server to invite someone to the group"""

PATH = "/groups/(?P<group_id>[^/]*)/users/(?P<user_id>[^/]*)/invite"

async def on_POST(
self,
origin: str,
content: JsonDict,
query: Dict[bytes, List[bytes]],
group_id: str,
user_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

new_content = await self.handler.invite_to_group(
group_id, user_id, requester_user_id, content
)

return 200, new_content


class FederationGroupsAcceptInviteServlet(BaseGroupsServerServlet):
"""Accept an invitation from the group server"""

PATH = "/groups/(?P<group_id>[^/]*)/users/(?P<user_id>[^/]*)/accept_invite"

async def on_POST(
self,
origin: str,
content: JsonDict,
query: Dict[bytes, List[bytes]],
group_id: str,
user_id: str,
) -> Tuple[int, JsonDict]:
if get_domain_from_id(user_id) != origin:
raise SynapseError(403, "user_id doesn't match origin")

new_content = await self.handler.accept_invite(group_id, user_id, content)

return 200, new_content


class FederationGroupsJoinServlet(BaseGroupsServerServlet):
"""Attempt to join a group"""

PATH = "/groups/(?P<group_id>[^/]*)/users/(?P<user_id>[^/]*)/join"

async def on_POST(
self,
origin: str,
content: JsonDict,
query: Dict[bytes, List[bytes]],
group_id: str,
user_id: str,
) -> Tuple[int, JsonDict]:
if get_domain_from_id(user_id) != origin:
raise SynapseError(403, "user_id doesn't match origin")

new_content = await self.handler.join_group(group_id, user_id, content)

return 200, new_content


class FederationGroupsRemoveUserServlet(BaseGroupsServerServlet):
"""Leave or kick a user from the group"""

PATH = "/groups/(?P<group_id>[^/]*)/users/(?P<user_id>[^/]*)/remove"

async def on_POST(
self,
origin: str,
content: JsonDict,
query: Dict[bytes, List[bytes]],
group_id: str,
user_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

new_content = await self.handler.remove_user_from_group(
group_id, user_id, requester_user_id, content
)

return 200, new_content


class FederationGroupsSummaryRoomsServlet(BaseGroupsServerServlet):
"""Add/remove a room from the group summary, with optional category.

Matches both:
- /groups/:group/summary/rooms/:room_id
- /groups/:group/summary/categories/:category/rooms/:room_id
"""

PATH = (
"/groups/(?P<group_id>[^/]*)/summary"
"(/categories/(?P<category_id>[^/]+))?"
"/rooms/(?P<room_id>[^/]*)"
)

async def on_POST(
self,
origin: str,
content: JsonDict,
query: Dict[bytes, List[bytes]],
group_id: str,
category_id: str,
room_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

if category_id == "":
raise SynapseError(
400, "category_id cannot be empty string", Codes.INVALID_PARAM
)

if len(category_id) > MAX_GROUP_CATEGORYID_LENGTH:
raise SynapseError(
400,
"category_id may not be longer than %s characters"
% (MAX_GROUP_CATEGORYID_LENGTH,),
Codes.INVALID_PARAM,
)

resp = await self.handler.update_group_summary_room(
group_id,
requester_user_id,
room_id=room_id,
category_id=category_id,
content=content,
)

return 200, resp

async def on_DELETE(
self,
origin: str,
content: Literal[None],
query: Dict[bytes, List[bytes]],
group_id: str,
category_id: str,
room_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

if category_id == "":
raise SynapseError(400, "category_id cannot be empty string")

resp = await self.handler.delete_group_summary_room(
group_id, requester_user_id, room_id=room_id, category_id=category_id
)

return 200, resp


class FederationGroupsCategoriesServlet(BaseGroupsServerServlet):
"""Get all categories for a group"""

PATH = "/groups/(?P<group_id>[^/]*)/categories/?"

async def on_GET(
self,
origin: str,
content: Literal[None],
query: Dict[bytes, List[bytes]],
group_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

resp = await self.handler.get_group_categories(group_id, requester_user_id)

return 200, resp


class FederationGroupsCategoryServlet(BaseGroupsServerServlet):
"""Add/remove/get a category in a group"""

PATH = "/groups/(?P<group_id>[^/]*)/categories/(?P<category_id>[^/]+)"

async def on_GET(
self,
origin: str,
content: Literal[None],
query: Dict[bytes, List[bytes]],
group_id: str,
category_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

resp = await self.handler.get_group_category(
group_id, requester_user_id, category_id
)

return 200, resp

async def on_POST(
self,
origin: str,
content: JsonDict,
query: Dict[bytes, List[bytes]],
group_id: str,
category_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

if category_id == "":
raise SynapseError(400, "category_id cannot be empty string")

if len(category_id) > MAX_GROUP_CATEGORYID_LENGTH:
raise SynapseError(
400,
"category_id may not be longer than %s characters"
% (MAX_GROUP_CATEGORYID_LENGTH,),
Codes.INVALID_PARAM,
)

resp = await self.handler.upsert_group_category(
group_id, requester_user_id, category_id, content
)

return 200, resp

async def on_DELETE(
self,
origin: str,
content: Literal[None],
query: Dict[bytes, List[bytes]],
group_id: str,
category_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

if category_id == "":
raise SynapseError(400, "category_id cannot be empty string")

resp = await self.handler.delete_group_category(
group_id, requester_user_id, category_id
)

return 200, resp


class FederationGroupsRolesServlet(BaseGroupsServerServlet):
"""Get roles in a group"""

PATH = "/groups/(?P<group_id>[^/]*)/roles/?"

async def on_GET(
self,
origin: str,
content: Literal[None],
query: Dict[bytes, List[bytes]],
group_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

resp = await self.handler.get_group_roles(group_id, requester_user_id)

return 200, resp


class FederationGroupsRoleServlet(BaseGroupsServerServlet):
"""Add/remove/get a role in a group"""

PATH = "/groups/(?P<group_id>[^/]*)/roles/(?P<role_id>[^/]+)"

async def on_GET(
self,
origin: str,
content: Literal[None],
query: Dict[bytes, List[bytes]],
group_id: str,
role_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

resp = await self.handler.get_group_role(group_id, requester_user_id, role_id)

return 200, resp

async def on_POST(
self,
origin: str,
content: JsonDict,
query: Dict[bytes, List[bytes]],
group_id: str,
role_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

if role_id == "":
raise SynapseError(
400, "role_id cannot be empty string", Codes.INVALID_PARAM
)

if len(role_id) > MAX_GROUP_ROLEID_LENGTH:
raise SynapseError(
400,
"role_id may not be longer than %s characters"
% (MAX_GROUP_ROLEID_LENGTH,),
Codes.INVALID_PARAM,
)

resp = await self.handler.update_group_role(
group_id, requester_user_id, role_id, content
)

return 200, resp

async def on_DELETE(
self,
origin: str,
content: Literal[None],
query: Dict[bytes, List[bytes]],
group_id: str,
role_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

if role_id == "":
raise SynapseError(400, "role_id cannot be empty string")

resp = await self.handler.delete_group_role(
group_id, requester_user_id, role_id
)

return 200, resp


class FederationGroupsSummaryUsersServlet(BaseGroupsServerServlet):
"""Add/remove a user from the group summary, with optional role.

Matches both:
- /groups/:group/summary/users/:user_id
- /groups/:group/summary/roles/:role/users/:user_id
"""

PATH = (
"/groups/(?P<group_id>[^/]*)/summary"
"(/roles/(?P<role_id>[^/]+))?"
"/users/(?P<user_id>[^/]*)"
)

async def on_POST(
self,
origin: str,
content: JsonDict,
query: Dict[bytes, List[bytes]],
group_id: str,
role_id: str,
user_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

if role_id == "":
raise SynapseError(400, "role_id cannot be empty string")

if len(role_id) > MAX_GROUP_ROLEID_LENGTH:
raise SynapseError(
400,
"role_id may not be longer than %s characters"
% (MAX_GROUP_ROLEID_LENGTH,),
Codes.INVALID_PARAM,
)

resp = await self.handler.update_group_summary_user(
group_id,
requester_user_id,
user_id=user_id,
role_id=role_id,
content=content,
)

return 200, resp

async def on_DELETE(
self,
origin: str,
content: Literal[None],
query: Dict[bytes, List[bytes]],
group_id: str,
role_id: str,
user_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

if role_id == "":
raise SynapseError(400, "role_id cannot be empty string")

resp = await self.handler.delete_group_summary_user(
group_id, requester_user_id, user_id=user_id, role_id=role_id
)

return 200, resp


class FederationGroupsSettingJoinPolicyServlet(BaseGroupsServerServlet):
"""Sets whether a group is joinable without an invite or knock"""

PATH = "/groups/(?P<group_id>[^/]*)/settings/m.join_policy"

async def on_PUT(
self,
origin: str,
content: JsonDict,
query: Dict[bytes, List[bytes]],
group_id: str,
) -> Tuple[int, JsonDict]:
requester_user_id = parse_string_from_args(
query, "requester_user_id", required=True
)
if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin")

new_content = await self.handler.set_group_join_policy(
group_id, requester_user_id, content
)

return 200, new_content


GROUP_SERVER_SERVLET_CLASSES: Tuple[Type[BaseFederationServlet], ...] = (
FederationGroupsProfileServlet,
FederationGroupsSummaryServlet,
FederationGroupsRoomsServlet,
FederationGroupsUsersServlet,
FederationGroupsInvitedUsersServlet,
FederationGroupsInviteServlet,
FederationGroupsAcceptInviteServlet,
FederationGroupsJoinServlet,
FederationGroupsRemoveUserServlet,
FederationGroupsSummaryRoomsServlet,
FederationGroupsCategoriesServlet,
FederationGroupsCategoryServlet,
FederationGroupsRolesServlet,
FederationGroupsRoleServlet,
FederationGroupsSummaryUsersServlet,
FederationGroupsAddRoomsServlet,
FederationGroupsAddRoomsConfigServlet,
FederationGroupsSettingJoinPolicyServlet,
)

+ 0
- 11
synapse/handlers/room_member.py Dosyayı Görüntüle

@@ -1081,17 +1081,6 @@ class RoomMemberHandler(metaclass=abc.ABCMeta):
# Transfer alias mappings in the room directory
await self.store.update_aliases_for_room(old_room_id, room_id)

# Check if any groups we own contain the predecessor room
local_group_ids = await self.store.get_local_groups_for_room(old_room_id)
for group_id in local_group_ids:
# Add new the new room to those groups
await self.store.add_room_to_group(
group_id, room_id, old_room is not None and old_room["is_public"]
)

# Remove the old room from those groups
await self.store.remove_room_from_group(group_id, old_room_id)

async def copy_user_state_on_room_upgrade(
self, old_room_id: str, new_room_id: str, user_ids: Iterable[str]
) -> None:


+ 0
- 65
synapse/handlers/sync.py Dosyayı Görüntüle

@@ -166,16 +166,6 @@ class KnockedSyncResult:
return True


@attr.s(slots=True, frozen=True, auto_attribs=True)
class GroupsSyncResult:
join: JsonDict
invite: JsonDict
leave: JsonDict

def __bool__(self) -> bool:
return bool(self.join or self.invite or self.leave)


@attr.s(slots=True, auto_attribs=True)
class _RoomChanges:
"""The set of room entries to include in the sync, plus the set of joined
@@ -206,7 +196,6 @@ class SyncResult:
for this device
device_unused_fallback_key_types: List of key types that have an unused fallback
key
groups: Group updates, if any
"""

next_batch: StreamToken
@@ -220,7 +209,6 @@ class SyncResult:
device_lists: DeviceListUpdates
device_one_time_keys_count: JsonDict
device_unused_fallback_key_types: List[str]
groups: Optional[GroupsSyncResult]

def __bool__(self) -> bool:
"""Make the result appear empty if there are no updates. This is used
@@ -236,7 +224,6 @@ class SyncResult:
or self.account_data
or self.to_device
or self.device_lists
or self.groups
)


@@ -1157,10 +1144,6 @@ class SyncHandler:
await self.store.get_e2e_unused_fallback_key_types(user_id, device_id)
)

if self.hs_config.experimental.groups_enabled:
logger.debug("Fetching group data")
await self._generate_sync_entry_for_groups(sync_result_builder)

num_events = 0

# debug for https://github.com/matrix-org/synapse/issues/9424
@@ -1184,57 +1167,11 @@ class SyncHandler:
archived=sync_result_builder.archived,
to_device=sync_result_builder.to_device,
device_lists=device_lists,
groups=sync_result_builder.groups,
device_one_time_keys_count=one_time_key_counts,
device_unused_fallback_key_types=unused_fallback_key_types,
next_batch=sync_result_builder.now_token,
)

@measure_func("_generate_sync_entry_for_groups")
async def _generate_sync_entry_for_groups(
self, sync_result_builder: "SyncResultBuilder"
) -> None:
user_id = sync_result_builder.sync_config.user.to_string()
since_token = sync_result_builder.since_token
now_token = sync_result_builder.now_token

if since_token and since_token.groups_key:
results = await self.store.get_groups_changes_for_user(
user_id, since_token.groups_key, now_token.groups_key
)
else:
results = await self.store.get_all_groups_for_user(
user_id, now_token.groups_key
)

invited = {}
joined = {}
left = {}
for result in results:
membership = result["membership"]
group_id = result["group_id"]
gtype = result["type"]
content = result["content"]

if membership == "join":
if gtype == "membership":
# TODO: Add profile
content.pop("membership", None)
joined[group_id] = content["content"]
else:
joined.setdefault(group_id, {})[gtype] = content
elif membership == "invite":
if gtype == "membership":
content.pop("membership", None)
invited[group_id] = content["content"]
else:
if gtype == "membership":
left[group_id] = content["content"]

sync_result_builder.groups = GroupsSyncResult(
join=joined, invite=invited, leave=left
)

@measure_func("_generate_sync_entry_for_device_list")
async def _generate_sync_entry_for_device_list(
self,
@@ -2333,7 +2270,6 @@ class SyncResultBuilder:
invited
knocked
archived
groups
to_device
"""

@@ -2349,7 +2285,6 @@ class SyncResultBuilder:
invited: List[InvitedSyncResult] = attr.Factory(list)
knocked: List[KnockedSyncResult] = attr.Factory(list)
archived: List[ArchivedSyncResult] = attr.Factory(list)
groups: Optional[GroupsSyncResult] = None
to_device: List[JsonDict] = attr.Factory(list)

def calculate_user_changes(self) -> Tuple[Set[str], Set[str]]:


+ 0
- 3
synapse/rest/__init__.py Dosyayı Görüntüle

@@ -26,7 +26,6 @@ from synapse.rest.client import (
directory,
events,
filter,
groups,
initial_sync,
keys,
knock,
@@ -118,8 +117,6 @@ class ClientRestResource(JsonResource):
thirdparty.register_servlets(hs, client_resource)
sendtodevice.register_servlets(hs, client_resource)
user_directory.register_servlets(hs, client_resource)
if hs.config.experimental.groups_enabled:
groups.register_servlets(hs, client_resource)
room_upgrade_rest_servlet.register_servlets(hs, client_resource)
room_batch.register_servlets(hs, client_resource)
capabilities.register_servlets(hs, client_resource)


+ 0
- 3
synapse/rest/admin/__init__.py Dosyayı Görüntüle

@@ -47,7 +47,6 @@ from synapse.rest.admin.federation import (
DestinationRestServlet,
ListDestinationsRestServlet,
)
from synapse.rest.admin.groups import DeleteGroupAdminRestServlet
from synapse.rest.admin.media import ListMediaInRoom, register_servlets_for_media_repo
from synapse.rest.admin.registration_tokens import (
ListRegistrationTokensRestServlet,
@@ -293,8 +292,6 @@ def register_servlets_for_client_rest_resource(
ResetPasswordRestServlet(hs).register(http_server)
SearchUsersRestServlet(hs).register(http_server)
UserRegisterServlet(hs).register(http_server)
if hs.config.experimental.groups_enabled:
DeleteGroupAdminRestServlet(hs).register(http_server)
AccountValidityRenewServlet(hs).register(http_server)

# Load the media repo ones if we're using them. Otherwise load the servlets which


+ 0
- 50
synapse/rest/admin/groups.py Dosyayı Görüntüle

@@ -1,50 +0,0 @@
# Copyright 2019 The Matrix.org Foundation C.I.C.
#
# 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.
import logging
from http import HTTPStatus
from typing import TYPE_CHECKING, Tuple

from synapse.api.errors import SynapseError
from synapse.http.servlet import RestServlet
from synapse.http.site import SynapseRequest
from synapse.rest.admin._base import admin_patterns, assert_user_is_admin
from synapse.types import JsonDict

if TYPE_CHECKING:
from synapse.server import HomeServer

logger = logging.getLogger(__name__)


class DeleteGroupAdminRestServlet(RestServlet):
"""Allows deleting of local groups"""

PATTERNS = admin_patterns("/delete_group/(?P<group_id>[^/]*)$")

def __init__(self, hs: "HomeServer"):
self.group_server = hs.get_groups_server_handler()
self.is_mine_id = hs.is_mine_id
self.auth = hs.get_auth()

async def on_POST(
self, request: SynapseRequest, group_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
await assert_user_is_admin(self.auth, requester.user)

if not self.is_mine_id(group_id):
raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only delete local groups")

await self.group_server.delete_group(group_id, requester.user.to_string())
return HTTPStatus.OK, {}

+ 0
- 962
synapse/rest/client/groups.py Dosyayı Görüntüle

@@ -1,962 +0,0 @@
# Copyright 2017 Vector Creations Ltd
# Copyright 2018 New Vector 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.

import logging
from functools import wraps
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Optional, Tuple

from twisted.web.server import Request

from synapse.api.constants import (
MAX_GROUP_CATEGORYID_LENGTH,
MAX_GROUP_ROLEID_LENGTH,
MAX_GROUPID_LENGTH,
)
from synapse.api.errors import Codes, SynapseError
from synapse.handlers.groups_local import GroupsLocalHandler
from synapse.http.server import HttpServer
from synapse.http.servlet import (
RestServlet,
assert_params_in_dict,
parse_json_object_from_request,
)
from synapse.http.site import SynapseRequest
from synapse.types import GroupID, JsonDict

from ._base import client_patterns

if TYPE_CHECKING:
from synapse.server import HomeServer

logger = logging.getLogger(__name__)


def _validate_group_id(
f: Callable[..., Awaitable[Tuple[int, JsonDict]]]
) -> Callable[..., Awaitable[Tuple[int, JsonDict]]]:
"""Wrapper to validate the form of the group ID.

Can be applied to any on_FOO methods that accepts a group ID as a URL parameter.
"""

@wraps(f)
def wrapper(
self: RestServlet, request: Request, group_id: str, *args: Any, **kwargs: Any
) -> Awaitable[Tuple[int, JsonDict]]:
if not GroupID.is_valid(group_id):
raise SynapseError(400, "%s is not a legal group ID" % (group_id,))

return f(self, request, group_id, *args, **kwargs)

return wrapper


class GroupServlet(RestServlet):
"""Get the group profile"""

PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/profile$")

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.groups_handler = hs.get_groups_local_handler()

@_validate_group_id
async def on_GET(
self, request: SynapseRequest, group_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request, allow_guest=True)
requester_user_id = requester.user.to_string()

group_description = await self.groups_handler.get_group_profile(
group_id, requester_user_id
)

return 200, group_description

@_validate_group_id
async def on_POST(
self, request: SynapseRequest, group_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
requester_user_id = requester.user.to_string()

content = parse_json_object_from_request(request)
assert_params_in_dict(
content, ("name", "avatar_url", "short_description", "long_description")
)
assert isinstance(
self.groups_handler, GroupsLocalHandler
), "Workers cannot create group profiles."
await self.groups_handler.update_group_profile(
group_id, requester_user_id, content
)

return 200, {}


class GroupSummaryServlet(RestServlet):
"""Get the full group summary"""

PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/summary$")

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.groups_handler = hs.get_groups_local_handler()

@_validate_group_id
async def on_GET(
self, request: SynapseRequest, group_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request, allow_guest=True)
requester_user_id = requester.user.to_string()

get_group_summary = await self.groups_handler.get_group_summary(
group_id, requester_user_id
)

return 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_patterns(
"/groups/(?P<group_id>[^/]*)/summary"
"(/categories/(?P<category_id>[^/]+))?"
"/rooms/(?P<room_id>[^/]*)$"
)

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.groups_handler = hs.get_groups_local_handler()

@_validate_group_id
async def on_PUT(
self,
request: SynapseRequest,
group_id: str,
category_id: Optional[str],
room_id: str,
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
requester_user_id = requester.user.to_string()

if category_id == "":
raise SynapseError(400, "category_id cannot be empty", Codes.INVALID_PARAM)

if category_id and len(category_id) > MAX_GROUP_CATEGORYID_LENGTH:
raise SynapseError(
400,
"category_id may not be longer than %s characters"
% (MAX_GROUP_CATEGORYID_LENGTH,),
Codes.INVALID_PARAM,
)

content = parse_json_object_from_request(request)
assert isinstance(
self.groups_handler, GroupsLocalHandler
), "Workers cannot modify group summaries."
resp = await self.groups_handler.update_group_summary_room(
group_id,
requester_user_id,
room_id=room_id,
category_id=category_id,
content=content,
)

return 200, resp

@_validate_group_id
async def on_DELETE(
self, request: SynapseRequest, group_id: str, category_id: str, room_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
requester_user_id = requester.user.to_string()

assert isinstance(
self.groups_handler, GroupsLocalHandler
), "Workers cannot modify group profiles."
resp = await self.groups_handler.delete_group_summary_room(
group_id, requester_user_id, room_id=room_id, category_id=category_id
)

return 200, resp


class GroupCategoryServlet(RestServlet):
"""Get/add/update/delete a group category"""

PATTERNS = client_patterns(
"/groups/(?P<group_id>[^/]*)/categories/(?P<category_id>[^/]+)$"
)

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.groups_handler = hs.get_groups_local_handler()

@_validate_group_id
async def on_GET(
self, request: SynapseRequest, group_id: str, category_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request, allow_guest=True)
requester_user_id = requester.user.to_string()

category = await self.groups_handler.get_group_category(
group_id, requester_user_id, category_id=category_id
)

return 200, category

@_validate_group_id
async def on_PUT(
self, request: SynapseRequest, group_id: str, category_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
requester_user_id = requester.user.to_string()

if not category_id:
raise SynapseError(400, "category_id cannot be empty", Codes.INVALID_PARAM)

if len(category_id) > MAX_GROUP_CATEGORYID_LENGTH:
raise SynapseError(
400,
"category_id may not be longer than %s characters"
% (MAX_GROUP_CATEGORYID_LENGTH,),
Codes.INVALID_PARAM,
)

content = parse_json_object_from_request(request)
assert isinstance(
self.groups_handler, GroupsLocalHandler
), "Workers cannot modify group categories."
resp = await self.groups_handler.update_group_category(
group_id, requester_user_id, category_id=category_id, content=content
)

return 200, resp

@_validate_group_id
async def on_DELETE(
self, request: SynapseRequest, group_id: str, category_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
requester_user_id = requester.user.to_string()

assert isinstance(
self.groups_handler, GroupsLocalHandler
), "Workers cannot modify group categories."
resp = await self.groups_handler.delete_group_category(
group_id, requester_user_id, category_id=category_id
)

return 200, resp


class GroupCategoriesServlet(RestServlet):
"""Get all group categories"""

PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/categories/$")

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.groups_handler = hs.get_groups_local_handler()

@_validate_group_id
async def on_GET(
self, request: SynapseRequest, group_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request, allow_guest=True)
requester_user_id = requester.user.to_string()

category = await self.groups_handler.get_group_categories(
group_id, requester_user_id
)

return 200, category


class GroupRoleServlet(RestServlet):
"""Get/add/update/delete a group role"""

PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/roles/(?P<role_id>[^/]+)$")

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.groups_handler = hs.get_groups_local_handler()

@_validate_group_id
async def on_GET(
self, request: SynapseRequest, group_id: str, role_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request, allow_guest=True)
requester_user_id = requester.user.to_string()

category = await self.groups_handler.get_group_role(
group_id, requester_user_id, role_id=role_id
)

return 200, category

@_validate_group_id
async def on_PUT(
self, request: SynapseRequest, group_id: str, role_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
requester_user_id = requester.user.to_string()

if not role_id:
raise SynapseError(400, "role_id cannot be empty", Codes.INVALID_PARAM)

if len(role_id) > MAX_GROUP_ROLEID_LENGTH:
raise SynapseError(
400,
"role_id may not be longer than %s characters"
% (MAX_GROUP_ROLEID_LENGTH,),
Codes.INVALID_PARAM,
)

content = parse_json_object_from_request(request)
assert isinstance(
self.groups_handler, GroupsLocalHandler
), "Workers cannot modify group roles."
resp = await self.groups_handler.update_group_role(
group_id, requester_user_id, role_id=role_id, content=content
)

return 200, resp

@_validate_group_id
async def on_DELETE(
self, request: SynapseRequest, group_id: str, role_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
requester_user_id = requester.user.to_string()

assert isinstance(
self.groups_handler, GroupsLocalHandler
), "Workers cannot modify group roles."
resp = await self.groups_handler.delete_group_role(
group_id, requester_user_id, role_id=role_id
)

return 200, resp


class GroupRolesServlet(RestServlet):
"""Get all group roles"""

PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/roles/$")

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.groups_handler = hs.get_groups_local_handler()

@_validate_group_id
async def on_GET(
self, request: SynapseRequest, group_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request, allow_guest=True)
requester_user_id = requester.user.to_string()

category = await self.groups_handler.get_group_roles(
group_id, requester_user_id
)

return 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_patterns(
"/groups/(?P<group_id>[^/]*)/summary"
"(/roles/(?P<role_id>[^/]+))?"
"/users/(?P<user_id>[^/]*)$"
)

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.groups_handler = hs.get_groups_local_handler()

@_validate_group_id
async def on_PUT(
self,
request: SynapseRequest,
group_id: str,
role_id: Optional[str],
user_id: str,
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
requester_user_id = requester.user.to_string()

if role_id == "":
raise SynapseError(400, "role_id cannot be empty", Codes.INVALID_PARAM)

if role_id and len(role_id) > MAX_GROUP_ROLEID_LENGTH:
raise SynapseError(
400,
"role_id may not be longer than %s characters"
% (MAX_GROUP_ROLEID_LENGTH,),
Codes.INVALID_PARAM,
)

content = parse_json_object_from_request(request)
assert isinstance(
self.groups_handler, GroupsLocalHandler
), "Workers cannot modify group summaries."
resp = await self.groups_handler.update_group_summary_user(
group_id,
requester_user_id,
user_id=user_id,
role_id=role_id,
content=content,
)

return 200, resp

@_validate_group_id
async def on_DELETE(
self, request: SynapseRequest, group_id: str, role_id: str, user_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
requester_user_id = requester.user.to_string()

assert isinstance(
self.groups_handler, GroupsLocalHandler
), "Workers cannot modify group summaries."
resp = await self.groups_handler.delete_group_summary_user(
group_id, requester_user_id, user_id=user_id, role_id=role_id
)

return 200, resp


class GroupRoomServlet(RestServlet):
"""Get all rooms in a group"""

PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/rooms$")

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.groups_handler = hs.get_groups_local_handler()

@_validate_group_id
async def on_GET(
self, request: SynapseRequest, group_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request, allow_guest=True)
requester_user_id = requester.user.to_string()

result = await self.groups_handler.get_rooms_in_group(
group_id, requester_user_id
)

return 200, result


class GroupUsersServlet(RestServlet):
"""Get all users in a group"""

PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/users$")

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.groups_handler = hs.get_groups_local_handler()

@_validate_group_id
async def on_GET(
self, request: SynapseRequest, group_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request, allow_guest=True)
requester_user_id = requester.user.to_string()

result = await self.groups_handler.get_users_in_group(
group_id, requester_user_id
)

return 200, result


class GroupInvitedUsersServlet(RestServlet):
"""Get users invited to a group"""

PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/invited_users$")

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.groups_handler = hs.get_groups_local_handler()

@_validate_group_id
async def on_GET(
self, request: SynapseRequest, group_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
requester_user_id = requester.user.to_string()

result = await self.groups_handler.get_invited_users_in_group(
group_id, requester_user_id
)

return 200, result


class GroupSettingJoinPolicyServlet(RestServlet):
"""Set group join policy"""

PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/settings/m.join_policy$")

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.groups_handler = hs.get_groups_local_handler()

@_validate_group_id
async def on_PUT(
self, request: SynapseRequest, group_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
requester_user_id = requester.user.to_string()

content = parse_json_object_from_request(request)

assert isinstance(
self.groups_handler, GroupsLocalHandler
), "Workers cannot modify group join policy."
result = await self.groups_handler.set_group_join_policy(
group_id, requester_user_id, content
)

return 200, result


class GroupCreateServlet(RestServlet):
"""Create a group"""

PATTERNS = client_patterns("/create_group$")

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.groups_handler = hs.get_groups_local_handler()
self.server_name = hs.hostname

async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
requester_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(localpart, self.server_name).to_string()

if not localpart:
raise SynapseError(400, "Group ID cannot be empty", Codes.INVALID_PARAM)

if len(group_id) > MAX_GROUPID_LENGTH:
raise SynapseError(
400,
"Group ID may not be longer than %s characters" % (MAX_GROUPID_LENGTH,),
Codes.INVALID_PARAM,
)

assert isinstance(
self.groups_handler, GroupsLocalHandler
), "Workers cannot create groups."
result = await self.groups_handler.create_group(
group_id, requester_user_id, content
)

return 200, result


class GroupAdminRoomsServlet(RestServlet):
"""Add a room to the group"""

PATTERNS = client_patterns(
"/groups/(?P<group_id>[^/]*)/admin/rooms/(?P<room_id>[^/]*)$"
)

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.groups_handler = hs.get_groups_local_handler()

@_validate_group_id
async def on_PUT(
self, request: SynapseRequest, group_id: str, room_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
requester_user_id = requester.user.to_string()

content = parse_json_object_from_request(request)
assert isinstance(
self.groups_handler, GroupsLocalHandler
), "Workers cannot modify rooms in a group."
result = await self.groups_handler.add_room_to_group(
group_id, requester_user_id, room_id, content
)

return 200, result

@_validate_group_id
async def on_DELETE(
self, request: SynapseRequest, group_id: str, room_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
requester_user_id = requester.user.to_string()

assert isinstance(
self.groups_handler, GroupsLocalHandler
), "Workers cannot modify group categories."
result = await self.groups_handler.remove_room_from_group(
group_id, requester_user_id, room_id
)

return 200, result


class GroupAdminRoomsConfigServlet(RestServlet):
"""Update the config of a room in a group"""

PATTERNS = client_patterns(
"/groups/(?P<group_id>[^/]*)/admin/rooms/(?P<room_id>[^/]*)"
"/config/(?P<config_key>[^/]*)$"
)

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.groups_handler = hs.get_groups_local_handler()

@_validate_group_id
async def on_PUT(
self, request: SynapseRequest, group_id: str, room_id: str, config_key: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
requester_user_id = requester.user.to_string()

content = parse_json_object_from_request(request)
assert isinstance(
self.groups_handler, GroupsLocalHandler
), "Workers cannot modify group categories."
result = await self.groups_handler.update_room_in_group(
group_id, requester_user_id, room_id, config_key, content
)

return 200, result


class GroupAdminUsersInviteServlet(RestServlet):
"""Invite a user to the group"""

PATTERNS = client_patterns(
"/groups/(?P<group_id>[^/]*)/admin/users/invite/(?P<user_id>[^/]*)$"
)

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.groups_handler = hs.get_groups_local_handler()
self.store = hs.get_datastores().main
self.is_mine_id = hs.is_mine_id

@_validate_group_id
async def on_PUT(
self, request: SynapseRequest, group_id: str, user_id: str
) -> Tuple[int, JsonDict]:
requester = await 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", {})
assert isinstance(
self.groups_handler, GroupsLocalHandler
), "Workers cannot invite users to a group."
result = await self.groups_handler.invite(
group_id, user_id, requester_user_id, config
)

return 200, result


class GroupAdminUsersKickServlet(RestServlet):
"""Kick a user from the group"""

PATTERNS = client_patterns(
"/groups/(?P<group_id>[^/]*)/admin/users/remove/(?P<user_id>[^/]*)$"
)

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.groups_handler = hs.get_groups_local_handler()

@_validate_group_id
async def on_PUT(
self, request: SynapseRequest, group_id: str, user_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
requester_user_id = requester.user.to_string()

content = parse_json_object_from_request(request)
assert isinstance(
self.groups_handler, GroupsLocalHandler
), "Workers cannot kick users from a group."
result = await self.groups_handler.remove_user_from_group(
group_id, user_id, requester_user_id, content
)

return 200, result


class GroupSelfLeaveServlet(RestServlet):
"""Leave a joined group"""

PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/self/leave$")

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.groups_handler = hs.get_groups_local_handler()

@_validate_group_id
async def on_PUT(
self, request: SynapseRequest, group_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
requester_user_id = requester.user.to_string()

content = parse_json_object_from_request(request)
assert isinstance(
self.groups_handler, GroupsLocalHandler
), "Workers cannot leave a group for a users."
result = await self.groups_handler.remove_user_from_group(
group_id, requester_user_id, requester_user_id, content
)

return 200, result


class GroupSelfJoinServlet(RestServlet):
"""Attempt to join a group, or knock"""

PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/self/join$")

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.groups_handler = hs.get_groups_local_handler()

@_validate_group_id
async def on_PUT(
self, request: SynapseRequest, group_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
requester_user_id = requester.user.to_string()

content = parse_json_object_from_request(request)
assert isinstance(
self.groups_handler, GroupsLocalHandler
), "Workers cannot join a user to a group."
result = await self.groups_handler.join_group(
group_id, requester_user_id, content
)

return 200, result


class GroupSelfAcceptInviteServlet(RestServlet):
"""Accept a group invite"""

PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/self/accept_invite$")

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.groups_handler = hs.get_groups_local_handler()

@_validate_group_id
async def on_PUT(
self, request: SynapseRequest, group_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
requester_user_id = requester.user.to_string()

content = parse_json_object_from_request(request)
assert isinstance(
self.groups_handler, GroupsLocalHandler
), "Workers cannot accept an invite to a group."
result = await self.groups_handler.accept_invite(
group_id, requester_user_id, content
)

return 200, result


class GroupSelfUpdatePublicityServlet(RestServlet):
"""Update whether we publicise a users membership of a group"""

PATTERNS = client_patterns("/groups/(?P<group_id>[^/]*)/self/update_publicity$")

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.store = hs.get_datastores().main

@_validate_group_id
async def on_PUT(
self, request: SynapseRequest, group_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
requester_user_id = requester.user.to_string()

content = parse_json_object_from_request(request)
publicise = content["publicise"]
await self.store.update_group_publicity(group_id, requester_user_id, publicise)

return 200, {}


class PublicisedGroupsForUserServlet(RestServlet):
"""Get the list of groups a user is advertising"""

PATTERNS = client_patterns("/publicised_groups/(?P<user_id>[^/]*)$")

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.store = hs.get_datastores().main
self.groups_handler = hs.get_groups_local_handler()

async def on_GET(
self, request: SynapseRequest, user_id: str
) -> Tuple[int, JsonDict]:
await self.auth.get_user_by_req(request, allow_guest=True)

result = await self.groups_handler.get_publicised_groups_for_user(user_id)

return 200, result


class PublicisedGroupsForUsersServlet(RestServlet):
"""Get the list of groups a user is advertising"""

PATTERNS = client_patterns("/publicised_groups$")

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.store = hs.get_datastores().main
self.groups_handler = hs.get_groups_local_handler()

async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
await self.auth.get_user_by_req(request, allow_guest=True)

content = parse_json_object_from_request(request)
user_ids = content["user_ids"]

result = await self.groups_handler.bulk_get_publicised_groups(user_ids)

return 200, result


class GroupsForUserServlet(RestServlet):
"""Get all groups the logged in user is joined to"""

PATTERNS = client_patterns("/joined_groups$")

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.groups_handler = hs.get_groups_local_handler()

async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request, allow_guest=True)
requester_user_id = requester.user.to_string()

result = await self.groups_handler.get_joined_groups(requester_user_id)

return 200, result


def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
GroupServlet(hs).register(http_server)
GroupSummaryServlet(hs).register(http_server)
GroupInvitedUsersServlet(hs).register(http_server)
GroupUsersServlet(hs).register(http_server)
GroupRoomServlet(hs).register(http_server)
GroupSettingJoinPolicyServlet(hs).register(http_server)
GroupCreateServlet(hs).register(http_server)
GroupAdminRoomsServlet(hs).register(http_server)
GroupAdminRoomsConfigServlet(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)
GroupSelfUpdatePublicityServlet(hs).register(http_server)
GroupSummaryUsersRoleServlet(hs).register(http_server)
PublicisedGroupsForUserServlet(hs).register(http_server)
PublicisedGroupsForUsersServlet(hs).register(http_server)

+ 0
- 8
synapse/rest/client/sync.py Dosyayı Görüntüle

@@ -298,14 +298,6 @@ class SyncRestServlet(RestServlet):
if archived:
response["rooms"][Membership.LEAVE] = archived

if sync_result.groups is not None:
if sync_result.groups.join:
response["groups"][Membership.JOIN] = sync_result.groups.join
if sync_result.groups.invite:
response["groups"][Membership.INVITE] = sync_result.groups.invite
if sync_result.groups.leave:
response["groups"][Membership.LEAVE] = sync_result.groups.leave

return response

@staticmethod


+ 1
- 89
tests/rest/admin/test_admin.py Dosyayı Görüntüle

@@ -14,7 +14,6 @@

import urllib.parse
from http import HTTPStatus
from typing import List

from parameterized import parameterized

@@ -23,7 +22,7 @@ from twisted.test.proto_helpers import MemoryReactor
import synapse.rest.admin
from synapse.http.server import JsonResource
from synapse.rest.admin import VersionServlet
from synapse.rest.client import groups, login, room
from synapse.rest.client import login, room
from synapse.server import HomeServer
from synapse.util import Clock

@@ -49,93 +48,6 @@ class VersionTestCase(unittest.HomeserverTestCase):
)


class DeleteGroupTestCase(unittest.HomeserverTestCase):
servlets = [
synapse.rest.admin.register_servlets_for_client_rest_resource,
login.register_servlets,
groups.register_servlets,
]

def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
self.admin_user = self.register_user("admin", "pass", admin=True)
self.admin_user_tok = self.login("admin", "pass")

self.other_user = self.register_user("user", "pass")
self.other_user_token = self.login("user", "pass")

@unittest.override_config({"experimental_features": {"groups_enabled": True}})
def test_delete_group(self) -> None:
# Create a new group
channel = self.make_request(
"POST",
b"/create_group",
access_token=self.admin_user_tok,
content={"localpart": "test"},
)

self.assertEqual(HTTPStatus.OK, channel.code, msg=channel.json_body)

group_id = channel.json_body["group_id"]

self._check_group(group_id, expect_code=HTTPStatus.OK)

# Invite/join another user

url = "/groups/%s/admin/users/invite/%s" % (group_id, self.other_user)
channel = self.make_request(
"PUT", url.encode("ascii"), access_token=self.admin_user_tok, content={}
)
self.assertEqual(HTTPStatus.OK, channel.code, msg=channel.json_body)

url = "/groups/%s/self/accept_invite" % (group_id,)
channel = self.make_request(
"PUT", url.encode("ascii"), access_token=self.other_user_token, content={}
)
self.assertEqual(HTTPStatus.OK, channel.code, msg=channel.json_body)

# Check other user knows they're in the group
self.assertIn(group_id, self._get_groups_user_is_in(self.admin_user_tok))
self.assertIn(group_id, self._get_groups_user_is_in(self.other_user_token))

# Now delete the group
url = "/_synapse/admin/v1/delete_group/" + group_id
channel = self.make_request(
"POST",
url.encode("ascii"),
access_token=self.admin_user_tok,
content={"localpart": "test"},
)

self.assertEqual(HTTPStatus.OK, channel.code, msg=channel.json_body)

# Check group returns HTTPStatus.NOT_FOUND
self._check_group(group_id, expect_code=HTTPStatus.NOT_FOUND)

# Check users don't think they're in the group
self.assertNotIn(group_id, self._get_groups_user_is_in(self.admin_user_tok))
self.assertNotIn(group_id, self._get_groups_user_is_in(self.other_user_token))

def _check_group(self, group_id: str, expect_code: int) -> None:
"""Assert that trying to fetch the given group results in the given
HTTP status code
"""

url = "/groups/%s/profile" % (group_id,)
channel = self.make_request(
"GET", url.encode("ascii"), access_token=self.admin_user_tok
)

self.assertEqual(expect_code, channel.code, msg=channel.json_body)

def _get_groups_user_is_in(self, access_token: str) -> List[str]:
"""Returns the list of groups the user is in (given their access token)"""
channel = self.make_request("GET", b"/joined_groups", access_token=access_token)

self.assertEqual(HTTPStatus.OK, channel.code, msg=channel.json_body)

return channel.json_body["groups"]


class QuarantineMediaTestCase(unittest.HomeserverTestCase):
"""Test /quarantine_media admin API."""



+ 0
- 56
tests/rest/client/test_groups.py Dosyayı Görüntüle

@@ -1,56 +0,0 @@
# Copyright 2021 The Matrix.org Foundation C.I.C.
#
# 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 synapse.rest.client import groups, room

from tests import unittest
from tests.unittest import override_config


class GroupsTestCase(unittest.HomeserverTestCase):
user_id = "@alice:test"
room_creator_user_id = "@bob:test"

servlets = [room.register_servlets, groups.register_servlets]

@override_config({"enable_group_creation": True})
def test_rooms_limited_by_visibility(self) -> None:
group_id = "+spqr:test"

# Alice creates a group
channel = self.make_request("POST", "/create_group", {"localpart": "spqr"})
self.assertEqual(channel.code, 200, msg=channel.text_body)
self.assertEqual(channel.json_body, {"group_id": group_id})

# Bob creates a private room
room_id = self.helper.create_room_as(self.room_creator_user_id, is_public=False)
self.helper.auth_user_id = self.room_creator_user_id
self.helper.send_state(
room_id, "m.room.name", {"name": "bob's secret room"}, tok=None
)
self.helper.auth_user_id = self.user_id

# Alice adds the room to her group.
channel = self.make_request(
"PUT", f"/groups/{group_id}/admin/rooms/{room_id}", {}
)
self.assertEqual(channel.code, 200, msg=channel.text_body)
self.assertEqual(channel.json_body, {})

# Alice now tries to retrieve the room list of the space.
channel = self.make_request("GET", f"/groups/{group_id}/rooms")
self.assertEqual(channel.code, 200, msg=channel.text_body)
self.assertEqual(
channel.json_body, {"chunk": [], "total_room_count_estimate": 0}
)

Yükleniyor…
İptal
Kaydet