mirror of
https://git.anonymousland.org/anonymousland/synapse.git
synced 2025-01-08 05:58:26 -05:00
Additional configuration options for auto-join rooms (#7763)
This commit is contained in:
parent
a99658074d
commit
71cccf1593
1
changelog.d/7763.feature
Normal file
1
changelog.d/7763.feature
Normal file
@ -0,0 +1 @@
|
|||||||
|
Expand the configuration options for auto-join rooms.
|
@ -1210,7 +1210,11 @@ account_threepid_delegates:
|
|||||||
#enable_3pid_changes: false
|
#enable_3pid_changes: false
|
||||||
|
|
||||||
# Users who register on this homeserver will automatically be joined
|
# Users who register on this homeserver will automatically be joined
|
||||||
# to these rooms
|
# to these rooms.
|
||||||
|
#
|
||||||
|
# By default, any room aliases included in this list will be created
|
||||||
|
# as a publicly joinable room when the first user registers for the
|
||||||
|
# homeserver. This behaviour can be customised with the settings below.
|
||||||
#
|
#
|
||||||
#auto_join_rooms:
|
#auto_join_rooms:
|
||||||
# - "#example:example.com"
|
# - "#example:example.com"
|
||||||
@ -1218,10 +1222,62 @@ account_threepid_delegates:
|
|||||||
# Where auto_join_rooms are specified, setting this flag ensures that the
|
# Where auto_join_rooms are specified, setting this flag ensures that the
|
||||||
# the rooms exist by creating them when the first user on the
|
# the rooms exist by creating them when the first user on the
|
||||||
# homeserver registers.
|
# homeserver registers.
|
||||||
|
#
|
||||||
|
# By default the auto-created rooms are publicly joinable from any federated
|
||||||
|
# server. Use the autocreate_auto_join_rooms_federated and
|
||||||
|
# autocreate_auto_join_room_preset settings below to customise this behaviour.
|
||||||
|
#
|
||||||
# Setting to false means that if the rooms are not manually created,
|
# Setting to false means that if the rooms are not manually created,
|
||||||
# users cannot be auto-joined since they do not exist.
|
# users cannot be auto-joined since they do not exist.
|
||||||
#
|
#
|
||||||
#autocreate_auto_join_rooms: true
|
# Defaults to true. Uncomment the following line to disable automatically
|
||||||
|
# creating auto-join rooms.
|
||||||
|
#
|
||||||
|
#autocreate_auto_join_rooms: false
|
||||||
|
|
||||||
|
# Whether the auto_join_rooms that are auto-created are available via
|
||||||
|
# federation. Only has an effect if autocreate_auto_join_rooms is true.
|
||||||
|
#
|
||||||
|
# Note that whether a room is federated cannot be modified after
|
||||||
|
# creation.
|
||||||
|
#
|
||||||
|
# Defaults to true: the room will be joinable from other servers.
|
||||||
|
# Uncomment the following to prevent users from other homeservers from
|
||||||
|
# joining these rooms.
|
||||||
|
#
|
||||||
|
#autocreate_auto_join_rooms_federated: false
|
||||||
|
|
||||||
|
# The room preset to use when auto-creating one of auto_join_rooms. Only has an
|
||||||
|
# effect if autocreate_auto_join_rooms is true.
|
||||||
|
#
|
||||||
|
# This can be one of "public_chat", "private_chat", or "trusted_private_chat".
|
||||||
|
# If a value of "private_chat" or "trusted_private_chat" is used then
|
||||||
|
# auto_join_mxid_localpart must also be configured.
|
||||||
|
#
|
||||||
|
# Defaults to "public_chat", meaning that the room is joinable by anyone, including
|
||||||
|
# federated servers if autocreate_auto_join_rooms_federated is true (the default).
|
||||||
|
# Uncomment the following to require an invitation to join these rooms.
|
||||||
|
#
|
||||||
|
#autocreate_auto_join_room_preset: private_chat
|
||||||
|
|
||||||
|
# The local part of the user id which is used to create auto_join_rooms if
|
||||||
|
# autocreate_auto_join_rooms is true. If this is not provided then the
|
||||||
|
# initial user account that registers will be used to create the rooms.
|
||||||
|
#
|
||||||
|
# The user id is also used to invite new users to any auto-join rooms which
|
||||||
|
# are set to invite-only.
|
||||||
|
#
|
||||||
|
# It *must* be configured if autocreate_auto_join_room_preset is set to
|
||||||
|
# "private_chat" or "trusted_private_chat".
|
||||||
|
#
|
||||||
|
# Note that this must be specified in order for new users to be correctly
|
||||||
|
# invited to any auto-join rooms which have been set to invite-only (either
|
||||||
|
# at the time of creation or subsequently).
|
||||||
|
#
|
||||||
|
# Note that, if the room already exists, this user must be joined and
|
||||||
|
# have the appropriate permissions to invite new members.
|
||||||
|
#
|
||||||
|
#auto_join_mxid_localpart: system
|
||||||
|
|
||||||
# When auto_join_rooms is specified, setting this flag to false prevents
|
# When auto_join_rooms is specified, setting this flag to false prevents
|
||||||
# guest accounts from being automatically joined to the rooms.
|
# guest accounts from being automatically joined to the rooms.
|
||||||
|
@ -18,8 +18,9 @@ from distutils.util import strtobool
|
|||||||
|
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
|
||||||
|
from synapse.api.constants import RoomCreationPreset
|
||||||
from synapse.config._base import Config, ConfigError
|
from synapse.config._base import Config, ConfigError
|
||||||
from synapse.types import RoomAlias
|
from synapse.types import RoomAlias, UserID
|
||||||
from synapse.util.stringutils import random_string_with_symbols
|
from synapse.util.stringutils import random_string_with_symbols
|
||||||
|
|
||||||
|
|
||||||
@ -127,7 +128,50 @@ class RegistrationConfig(Config):
|
|||||||
for room_alias in self.auto_join_rooms:
|
for room_alias in self.auto_join_rooms:
|
||||||
if not RoomAlias.is_valid(room_alias):
|
if not RoomAlias.is_valid(room_alias):
|
||||||
raise ConfigError("Invalid auto_join_rooms entry %s" % (room_alias,))
|
raise ConfigError("Invalid auto_join_rooms entry %s" % (room_alias,))
|
||||||
|
|
||||||
|
# Options for creating auto-join rooms if they do not exist yet.
|
||||||
self.autocreate_auto_join_rooms = config.get("autocreate_auto_join_rooms", True)
|
self.autocreate_auto_join_rooms = config.get("autocreate_auto_join_rooms", True)
|
||||||
|
self.autocreate_auto_join_rooms_federated = config.get(
|
||||||
|
"autocreate_auto_join_rooms_federated", True
|
||||||
|
)
|
||||||
|
self.autocreate_auto_join_room_preset = (
|
||||||
|
config.get("autocreate_auto_join_room_preset")
|
||||||
|
or RoomCreationPreset.PUBLIC_CHAT
|
||||||
|
)
|
||||||
|
self.auto_join_room_requires_invite = self.autocreate_auto_join_room_preset in {
|
||||||
|
RoomCreationPreset.PRIVATE_CHAT,
|
||||||
|
RoomCreationPreset.TRUSTED_PRIVATE_CHAT,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Pull the creater/inviter from the configuration, this gets used to
|
||||||
|
# send invites for invite-only rooms.
|
||||||
|
mxid_localpart = config.get("auto_join_mxid_localpart")
|
||||||
|
self.auto_join_user_id = None
|
||||||
|
if mxid_localpart:
|
||||||
|
# Convert the localpart to a full mxid.
|
||||||
|
self.auto_join_user_id = UserID(
|
||||||
|
mxid_localpart, self.server_name
|
||||||
|
).to_string()
|
||||||
|
|
||||||
|
if self.autocreate_auto_join_rooms:
|
||||||
|
# Ensure the preset is a known value.
|
||||||
|
if self.autocreate_auto_join_room_preset not in {
|
||||||
|
RoomCreationPreset.PUBLIC_CHAT,
|
||||||
|
RoomCreationPreset.PRIVATE_CHAT,
|
||||||
|
RoomCreationPreset.TRUSTED_PRIVATE_CHAT,
|
||||||
|
}:
|
||||||
|
raise ConfigError("Invalid value for autocreate_auto_join_room_preset")
|
||||||
|
# If the preset requires invitations to be sent, ensure there's a
|
||||||
|
# configured user to send them from.
|
||||||
|
if self.auto_join_room_requires_invite:
|
||||||
|
if not mxid_localpart:
|
||||||
|
raise ConfigError(
|
||||||
|
"The configuration option `auto_join_mxid_localpart` is required if "
|
||||||
|
"`autocreate_auto_join_room_preset` is set to private_chat or trusted_private_chat, such that "
|
||||||
|
"Synapse knows who to send invitations from. Please "
|
||||||
|
"configure `auto_join_mxid_localpart`."
|
||||||
|
)
|
||||||
|
|
||||||
self.auto_join_rooms_for_guests = config.get("auto_join_rooms_for_guests", True)
|
self.auto_join_rooms_for_guests = config.get("auto_join_rooms_for_guests", True)
|
||||||
|
|
||||||
self.enable_set_displayname = config.get("enable_set_displayname", True)
|
self.enable_set_displayname = config.get("enable_set_displayname", True)
|
||||||
@ -357,7 +401,11 @@ class RegistrationConfig(Config):
|
|||||||
#enable_3pid_changes: false
|
#enable_3pid_changes: false
|
||||||
|
|
||||||
# Users who register on this homeserver will automatically be joined
|
# Users who register on this homeserver will automatically be joined
|
||||||
# to these rooms
|
# to these rooms.
|
||||||
|
#
|
||||||
|
# By default, any room aliases included in this list will be created
|
||||||
|
# as a publicly joinable room when the first user registers for the
|
||||||
|
# homeserver. This behaviour can be customised with the settings below.
|
||||||
#
|
#
|
||||||
#auto_join_rooms:
|
#auto_join_rooms:
|
||||||
# - "#example:example.com"
|
# - "#example:example.com"
|
||||||
@ -365,10 +413,62 @@ class RegistrationConfig(Config):
|
|||||||
# Where auto_join_rooms are specified, setting this flag ensures that the
|
# Where auto_join_rooms are specified, setting this flag ensures that the
|
||||||
# the rooms exist by creating them when the first user on the
|
# the rooms exist by creating them when the first user on the
|
||||||
# homeserver registers.
|
# homeserver registers.
|
||||||
|
#
|
||||||
|
# By default the auto-created rooms are publicly joinable from any federated
|
||||||
|
# server. Use the autocreate_auto_join_rooms_federated and
|
||||||
|
# autocreate_auto_join_room_preset settings below to customise this behaviour.
|
||||||
|
#
|
||||||
# Setting to false means that if the rooms are not manually created,
|
# Setting to false means that if the rooms are not manually created,
|
||||||
# users cannot be auto-joined since they do not exist.
|
# users cannot be auto-joined since they do not exist.
|
||||||
#
|
#
|
||||||
#autocreate_auto_join_rooms: true
|
# Defaults to true. Uncomment the following line to disable automatically
|
||||||
|
# creating auto-join rooms.
|
||||||
|
#
|
||||||
|
#autocreate_auto_join_rooms: false
|
||||||
|
|
||||||
|
# Whether the auto_join_rooms that are auto-created are available via
|
||||||
|
# federation. Only has an effect if autocreate_auto_join_rooms is true.
|
||||||
|
#
|
||||||
|
# Note that whether a room is federated cannot be modified after
|
||||||
|
# creation.
|
||||||
|
#
|
||||||
|
# Defaults to true: the room will be joinable from other servers.
|
||||||
|
# Uncomment the following to prevent users from other homeservers from
|
||||||
|
# joining these rooms.
|
||||||
|
#
|
||||||
|
#autocreate_auto_join_rooms_federated: false
|
||||||
|
|
||||||
|
# The room preset to use when auto-creating one of auto_join_rooms. Only has an
|
||||||
|
# effect if autocreate_auto_join_rooms is true.
|
||||||
|
#
|
||||||
|
# This can be one of "public_chat", "private_chat", or "trusted_private_chat".
|
||||||
|
# If a value of "private_chat" or "trusted_private_chat" is used then
|
||||||
|
# auto_join_mxid_localpart must also be configured.
|
||||||
|
#
|
||||||
|
# Defaults to "public_chat", meaning that the room is joinable by anyone, including
|
||||||
|
# federated servers if autocreate_auto_join_rooms_federated is true (the default).
|
||||||
|
# Uncomment the following to require an invitation to join these rooms.
|
||||||
|
#
|
||||||
|
#autocreate_auto_join_room_preset: private_chat
|
||||||
|
|
||||||
|
# The local part of the user id which is used to create auto_join_rooms if
|
||||||
|
# autocreate_auto_join_rooms is true. If this is not provided then the
|
||||||
|
# initial user account that registers will be used to create the rooms.
|
||||||
|
#
|
||||||
|
# The user id is also used to invite new users to any auto-join rooms which
|
||||||
|
# are set to invite-only.
|
||||||
|
#
|
||||||
|
# It *must* be configured if autocreate_auto_join_room_preset is set to
|
||||||
|
# "private_chat" or "trusted_private_chat".
|
||||||
|
#
|
||||||
|
# Note that this must be specified in order for new users to be correctly
|
||||||
|
# invited to any auto-join rooms which have been set to invite-only (either
|
||||||
|
# at the time of creation or subsequently).
|
||||||
|
#
|
||||||
|
# Note that, if the room already exists, this user must be joined and
|
||||||
|
# have the appropriate permissions to invite new members.
|
||||||
|
#
|
||||||
|
#auto_join_mxid_localpart: system
|
||||||
|
|
||||||
# When auto_join_rooms is specified, setting this flag to false prevents
|
# When auto_join_rooms is specified, setting this flag to false prevents
|
||||||
# guest accounts from being automatically joined to the rooms.
|
# guest accounts from being automatically joined to the rooms.
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from synapse import types
|
from synapse import types
|
||||||
from synapse.api.constants import MAX_USERID_LENGTH, LoginType
|
from synapse.api.constants import MAX_USERID_LENGTH, EventTypes, JoinRules, LoginType
|
||||||
from synapse.api.errors import AuthError, Codes, ConsentNotGivenError, SynapseError
|
from synapse.api.errors import AuthError, Codes, ConsentNotGivenError, SynapseError
|
||||||
from synapse.config.server import is_threepid_reserved
|
from synapse.config.server import is_threepid_reserved
|
||||||
from synapse.http.servlet import assert_params_in_dict
|
from synapse.http.servlet import assert_params_in_dict
|
||||||
@ -26,7 +26,8 @@ from synapse.replication.http.register import (
|
|||||||
ReplicationPostRegisterActionsServlet,
|
ReplicationPostRegisterActionsServlet,
|
||||||
ReplicationRegisterServlet,
|
ReplicationRegisterServlet,
|
||||||
)
|
)
|
||||||
from synapse.types import RoomAlias, RoomID, UserID, create_requester
|
from synapse.storage.state import StateFilter
|
||||||
|
from synapse.types import RoomAlias, UserID, create_requester
|
||||||
from synapse.util.async_helpers import Linearizer
|
from synapse.util.async_helpers import Linearizer
|
||||||
|
|
||||||
from ._base import BaseHandler
|
from ._base import BaseHandler
|
||||||
@ -270,51 +271,83 @@ class RegistrationHandler(BaseHandler):
|
|||||||
|
|
||||||
return user_id
|
return user_id
|
||||||
|
|
||||||
async def _auto_join_rooms(self, user_id):
|
async def _create_and_join_rooms(self, user_id: str):
|
||||||
"""Automatically joins users to auto join rooms - creating the room in the first place
|
"""
|
||||||
if the user is the first to be created.
|
Create the auto-join rooms and join or invite the user to them.
|
||||||
|
|
||||||
|
This should only be called when the first "real" user registers.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
user_id(str): The user to join
|
user_id: The user to join
|
||||||
"""
|
"""
|
||||||
# auto-join the user to any rooms we're supposed to dump them into
|
# Getting the handlers during init gives a dependency loop.
|
||||||
fake_requester = create_requester(user_id)
|
room_creation_handler = self.hs.get_room_creation_handler()
|
||||||
|
room_member_handler = self.hs.get_room_member_handler()
|
||||||
|
|
||||||
# try to create the room if we're the first real user on the server. Note
|
# Generate a stub for how the rooms will be configured.
|
||||||
# that an auto-generated support or bot user is not a real user and will never be
|
stub_config = {
|
||||||
# the user to create the room
|
"preset": self.hs.config.registration.autocreate_auto_join_room_preset,
|
||||||
should_auto_create_rooms = False
|
}
|
||||||
is_real_user = await self.store.is_real_user(user_id)
|
|
||||||
if self.hs.config.autocreate_auto_join_rooms and is_real_user:
|
# If the configuration providers a user ID to create rooms with, use
|
||||||
count = await self.store.count_real_users()
|
# that instead of the first user registered.
|
||||||
should_auto_create_rooms = count == 1
|
requires_join = False
|
||||||
for r in self.hs.config.auto_join_rooms:
|
if self.hs.config.registration.auto_join_user_id:
|
||||||
|
fake_requester = create_requester(
|
||||||
|
self.hs.config.registration.auto_join_user_id
|
||||||
|
)
|
||||||
|
|
||||||
|
# If the room requires an invite, add the user to the list of invites.
|
||||||
|
if self.hs.config.registration.auto_join_room_requires_invite:
|
||||||
|
stub_config["invite"] = [user_id]
|
||||||
|
|
||||||
|
# If the room is being created by a different user, the first user
|
||||||
|
# registered needs to join it. Note that in the case of an invitation
|
||||||
|
# being necessary this will occur after the invite was sent.
|
||||||
|
requires_join = True
|
||||||
|
else:
|
||||||
|
fake_requester = create_requester(user_id)
|
||||||
|
|
||||||
|
# Choose whether to federate the new room.
|
||||||
|
if not self.hs.config.registration.autocreate_auto_join_rooms_federated:
|
||||||
|
stub_config["creation_content"] = {"m.federate": False}
|
||||||
|
|
||||||
|
for r in self.hs.config.registration.auto_join_rooms:
|
||||||
logger.info("Auto-joining %s to %s", user_id, r)
|
logger.info("Auto-joining %s to %s", user_id, r)
|
||||||
try:
|
|
||||||
if should_auto_create_rooms:
|
|
||||||
room_alias = RoomAlias.from_string(r)
|
|
||||||
if self.hs.hostname != room_alias.domain:
|
|
||||||
logger.warning(
|
|
||||||
"Cannot create room alias %s, "
|
|
||||||
"it does not match server domain",
|
|
||||||
r,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# create room expects the localpart of the room alias
|
|
||||||
room_alias_localpart = room_alias.localpart
|
|
||||||
|
|
||||||
# getting the RoomCreationHandler during init gives a dependency
|
try:
|
||||||
# loop
|
room_alias = RoomAlias.from_string(r)
|
||||||
await self.hs.get_room_creation_handler().create_room(
|
|
||||||
fake_requester,
|
if self.hs.hostname != room_alias.domain:
|
||||||
config={
|
logger.warning(
|
||||||
"preset": "public_chat",
|
"Cannot create room alias %s, "
|
||||||
"room_alias_name": room_alias_localpart,
|
"it does not match server domain",
|
||||||
},
|
r,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# A shallow copy is OK here since the only key that is
|
||||||
|
# modified is room_alias_name.
|
||||||
|
config = stub_config.copy()
|
||||||
|
# create room expects the localpart of the room alias
|
||||||
|
config["room_alias_name"] = room_alias.localpart
|
||||||
|
|
||||||
|
info, _ = await room_creation_handler.create_room(
|
||||||
|
fake_requester, config=config, ratelimit=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# If the room does not require an invite, but another user
|
||||||
|
# created it, then ensure the first user joins it.
|
||||||
|
if requires_join:
|
||||||
|
await room_member_handler.update_membership(
|
||||||
|
requester=create_requester(user_id),
|
||||||
|
target=UserID.from_string(user_id),
|
||||||
|
room_id=info["room_id"],
|
||||||
|
# Since it was just created, there are no remote hosts.
|
||||||
|
remote_room_hosts=[],
|
||||||
|
action="join",
|
||||||
ratelimit=False,
|
ratelimit=False,
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
await self._join_user_to_room(fake_requester, r)
|
|
||||||
except ConsentNotGivenError as e:
|
except ConsentNotGivenError as e:
|
||||||
# Technically not necessary to pull out this error though
|
# Technically not necessary to pull out this error though
|
||||||
# moving away from bare excepts is a good thing to do.
|
# moving away from bare excepts is a good thing to do.
|
||||||
@ -322,6 +355,103 @@ class RegistrationHandler(BaseHandler):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Failed to join new user to %r: %r", r, e)
|
logger.error("Failed to join new user to %r: %r", r, e)
|
||||||
|
|
||||||
|
async def _join_rooms(self, user_id: str):
|
||||||
|
"""
|
||||||
|
Join or invite the user to the auto-join rooms.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id: The user to join
|
||||||
|
"""
|
||||||
|
room_member_handler = self.hs.get_room_member_handler()
|
||||||
|
|
||||||
|
for r in self.hs.config.registration.auto_join_rooms:
|
||||||
|
logger.info("Auto-joining %s to %s", user_id, r)
|
||||||
|
|
||||||
|
try:
|
||||||
|
room_alias = RoomAlias.from_string(r)
|
||||||
|
|
||||||
|
if RoomAlias.is_valid(r):
|
||||||
|
(
|
||||||
|
room_id,
|
||||||
|
remote_room_hosts,
|
||||||
|
) = await room_member_handler.lookup_room_alias(room_alias)
|
||||||
|
room_id = room_id.to_string()
|
||||||
|
else:
|
||||||
|
raise SynapseError(
|
||||||
|
400, "%s was not legal room ID or room alias" % (r,)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Calculate whether the room requires an invite or can be
|
||||||
|
# joined directly. Note that unless a join rule of public exists,
|
||||||
|
# it is treated as requiring an invite.
|
||||||
|
requires_invite = True
|
||||||
|
|
||||||
|
state = await self.store.get_filtered_current_state_ids(
|
||||||
|
room_id, StateFilter.from_types([(EventTypes.JoinRules, "")])
|
||||||
|
)
|
||||||
|
|
||||||
|
event_id = state.get((EventTypes.JoinRules, ""))
|
||||||
|
if event_id:
|
||||||
|
join_rules_event = await self.store.get_event(
|
||||||
|
event_id, allow_none=True
|
||||||
|
)
|
||||||
|
if join_rules_event:
|
||||||
|
join_rule = join_rules_event.content.get("join_rule", None)
|
||||||
|
requires_invite = join_rule and join_rule != JoinRules.PUBLIC
|
||||||
|
|
||||||
|
# Send the invite, if necessary.
|
||||||
|
if requires_invite:
|
||||||
|
await room_member_handler.update_membership(
|
||||||
|
requester=create_requester(
|
||||||
|
self.hs.config.registration.auto_join_user_id
|
||||||
|
),
|
||||||
|
target=UserID.from_string(user_id),
|
||||||
|
room_id=room_id,
|
||||||
|
remote_room_hosts=remote_room_hosts,
|
||||||
|
action="invite",
|
||||||
|
ratelimit=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Send the join.
|
||||||
|
await room_member_handler.update_membership(
|
||||||
|
requester=create_requester(user_id),
|
||||||
|
target=UserID.from_string(user_id),
|
||||||
|
room_id=room_id,
|
||||||
|
remote_room_hosts=remote_room_hosts,
|
||||||
|
action="join",
|
||||||
|
ratelimit=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
except ConsentNotGivenError as e:
|
||||||
|
# Technically not necessary to pull out this error though
|
||||||
|
# moving away from bare excepts is a good thing to do.
|
||||||
|
logger.error("Failed to join new user to %r: %r", r, e)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("Failed to join new user to %r: %r", r, e)
|
||||||
|
|
||||||
|
async def _auto_join_rooms(self, user_id: str):
|
||||||
|
"""Automatically joins users to auto join rooms - creating the room in the first place
|
||||||
|
if the user is the first to be created.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id: The user to join
|
||||||
|
"""
|
||||||
|
# auto-join the user to any rooms we're supposed to dump them into
|
||||||
|
|
||||||
|
# try to create the room if we're the first real user on the server. Note
|
||||||
|
# that an auto-generated support or bot user is not a real user and will never be
|
||||||
|
# the user to create the room
|
||||||
|
should_auto_create_rooms = False
|
||||||
|
is_real_user = await self.store.is_real_user(user_id)
|
||||||
|
if self.hs.config.registration.autocreate_auto_join_rooms and is_real_user:
|
||||||
|
count = await self.store.count_real_users()
|
||||||
|
should_auto_create_rooms = count == 1
|
||||||
|
|
||||||
|
if should_auto_create_rooms:
|
||||||
|
await self._create_and_join_rooms(user_id)
|
||||||
|
else:
|
||||||
|
await self._join_rooms(user_id)
|
||||||
|
|
||||||
async def post_consent_actions(self, user_id):
|
async def post_consent_actions(self, user_id):
|
||||||
"""A series of registration actions that can only be carried out once consent
|
"""A series of registration actions that can only be carried out once consent
|
||||||
has been granted
|
has been granted
|
||||||
@ -392,30 +522,6 @@ class RegistrationHandler(BaseHandler):
|
|||||||
self._next_generated_user_id += 1
|
self._next_generated_user_id += 1
|
||||||
return str(id)
|
return str(id)
|
||||||
|
|
||||||
async def _join_user_to_room(self, requester, room_identifier):
|
|
||||||
room_member_handler = self.hs.get_room_member_handler()
|
|
||||||
if RoomID.is_valid(room_identifier):
|
|
||||||
room_id = room_identifier
|
|
||||||
elif RoomAlias.is_valid(room_identifier):
|
|
||||||
room_alias = RoomAlias.from_string(room_identifier)
|
|
||||||
room_id, remote_room_hosts = await room_member_handler.lookup_room_alias(
|
|
||||||
room_alias
|
|
||||||
)
|
|
||||||
room_id = room_id.to_string()
|
|
||||||
else:
|
|
||||||
raise SynapseError(
|
|
||||||
400, "%s was not legal room ID or room alias" % (room_identifier,)
|
|
||||||
)
|
|
||||||
|
|
||||||
await room_member_handler.update_membership(
|
|
||||||
requester=requester,
|
|
||||||
target=requester.user,
|
|
||||||
room_id=room_id,
|
|
||||||
remote_room_hosts=remote_room_hosts,
|
|
||||||
action="join",
|
|
||||||
ratelimit=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
def check_registration_ratelimit(self, address):
|
def check_registration_ratelimit(self, address):
|
||||||
"""A simple helper method to check whether the registration rate limit has been hit
|
"""A simple helper method to check whether the registration rate limit has been hit
|
||||||
for a given IP address
|
for a given IP address
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
from synapse.api.constants import EventTypes, JoinRules, Membership
|
from synapse.api.constants import EventTypes, JoinRules, Membership, RoomCreationPreset
|
||||||
from synapse.api.errors import Codes, NotFoundError, SynapseError
|
from synapse.api.errors import Codes, NotFoundError, SynapseError
|
||||||
from synapse.http.servlet import (
|
from synapse.http.servlet import (
|
||||||
RestServlet,
|
RestServlet,
|
||||||
@ -77,7 +77,7 @@ class ShutdownRoomRestServlet(RestServlet):
|
|||||||
info, stream_id = await self._room_creation_handler.create_room(
|
info, stream_id = await self._room_creation_handler.create_room(
|
||||||
room_creator_requester,
|
room_creator_requester,
|
||||||
config={
|
config={
|
||||||
"preset": "public_chat",
|
"preset": RoomCreationPreset.PUBLIC_CHAT,
|
||||||
"name": room_name,
|
"name": room_name,
|
||||||
"power_level_content_override": {"users_default": -10},
|
"power_level_content_override": {"users_default": -10},
|
||||||
},
|
},
|
||||||
|
@ -22,6 +22,8 @@ from synapse.api.errors import Codes, ResourceLimitError, SynapseError
|
|||||||
from synapse.handlers.register import RegistrationHandler
|
from synapse.handlers.register import RegistrationHandler
|
||||||
from synapse.types import RoomAlias, UserID, create_requester
|
from synapse.types import RoomAlias, UserID, create_requester
|
||||||
|
|
||||||
|
from tests.unittest import override_config
|
||||||
|
|
||||||
from .. import unittest
|
from .. import unittest
|
||||||
|
|
||||||
|
|
||||||
@ -145,9 +147,9 @@ class RegistrationTestCase(unittest.HomeserverTestCase):
|
|||||||
rooms = self.get_success(self.store.get_rooms_for_user(user_id))
|
rooms = self.get_success(self.store.get_rooms_for_user(user_id))
|
||||||
self.assertEqual(len(rooms), 0)
|
self.assertEqual(len(rooms), 0)
|
||||||
|
|
||||||
|
@override_config({"auto_join_rooms": ["#room:test"]})
|
||||||
def test_auto_create_auto_join_rooms(self):
|
def test_auto_create_auto_join_rooms(self):
|
||||||
room_alias_str = "#room:test"
|
room_alias_str = "#room:test"
|
||||||
self.hs.config.auto_join_rooms = [room_alias_str]
|
|
||||||
user_id = self.get_success(self.handler.register_user(localpart="jeff"))
|
user_id = self.get_success(self.handler.register_user(localpart="jeff"))
|
||||||
rooms = self.get_success(self.store.get_rooms_for_user(user_id))
|
rooms = self.get_success(self.store.get_rooms_for_user(user_id))
|
||||||
directory_handler = self.hs.get_handlers().directory_handler
|
directory_handler = self.hs.get_handlers().directory_handler
|
||||||
@ -193,9 +195,9 @@ class RegistrationTestCase(unittest.HomeserverTestCase):
|
|||||||
room_alias = RoomAlias.from_string(room_alias_str)
|
room_alias = RoomAlias.from_string(room_alias_str)
|
||||||
self.get_failure(directory_handler.get_association(room_alias), SynapseError)
|
self.get_failure(directory_handler.get_association(room_alias), SynapseError)
|
||||||
|
|
||||||
|
@override_config({"auto_join_rooms": ["#room:test"]})
|
||||||
def test_auto_create_auto_join_rooms_when_user_is_the_first_real_user(self):
|
def test_auto_create_auto_join_rooms_when_user_is_the_first_real_user(self):
|
||||||
room_alias_str = "#room:test"
|
room_alias_str = "#room:test"
|
||||||
self.hs.config.auto_join_rooms = [room_alias_str]
|
|
||||||
|
|
||||||
self.store.count_real_users = Mock(return_value=defer.succeed(1))
|
self.store.count_real_users = Mock(return_value=defer.succeed(1))
|
||||||
self.store.is_real_user = Mock(return_value=defer.succeed(True))
|
self.store.is_real_user = Mock(return_value=defer.succeed(True))
|
||||||
@ -218,6 +220,212 @@ class RegistrationTestCase(unittest.HomeserverTestCase):
|
|||||||
rooms = self.get_success(self.store.get_rooms_for_user(user_id))
|
rooms = self.get_success(self.store.get_rooms_for_user(user_id))
|
||||||
self.assertEqual(len(rooms), 0)
|
self.assertEqual(len(rooms), 0)
|
||||||
|
|
||||||
|
@override_config(
|
||||||
|
{
|
||||||
|
"auto_join_rooms": ["#room:test"],
|
||||||
|
"autocreate_auto_join_rooms_federated": False,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
def test_auto_create_auto_join_rooms_federated(self):
|
||||||
|
"""
|
||||||
|
Auto-created rooms that are private require an invite to go to the user
|
||||||
|
(instead of directly joining it).
|
||||||
|
"""
|
||||||
|
room_alias_str = "#room:test"
|
||||||
|
user_id = self.get_success(self.handler.register_user(localpart="jeff"))
|
||||||
|
|
||||||
|
# Ensure the room was created.
|
||||||
|
directory_handler = self.hs.get_handlers().directory_handler
|
||||||
|
room_alias = RoomAlias.from_string(room_alias_str)
|
||||||
|
room_id = self.get_success(directory_handler.get_association(room_alias))
|
||||||
|
|
||||||
|
# Ensure the room is properly not federated.
|
||||||
|
room = self.get_success(self.store.get_room_with_stats(room_id["room_id"]))
|
||||||
|
self.assertFalse(room["federatable"])
|
||||||
|
self.assertFalse(room["public"])
|
||||||
|
self.assertEqual(room["join_rules"], "public")
|
||||||
|
self.assertIsNone(room["guest_access"])
|
||||||
|
|
||||||
|
# The user should be in the room.
|
||||||
|
rooms = self.get_success(self.store.get_rooms_for_user(user_id))
|
||||||
|
self.assertIn(room_id["room_id"], rooms)
|
||||||
|
|
||||||
|
@override_config(
|
||||||
|
{"auto_join_rooms": ["#room:test"], "auto_join_mxid_localpart": "support"}
|
||||||
|
)
|
||||||
|
def test_auto_join_mxid_localpart(self):
|
||||||
|
"""
|
||||||
|
Ensure the user still needs up in the room created by a different user.
|
||||||
|
"""
|
||||||
|
# Ensure the support user exists.
|
||||||
|
inviter = "@support:test"
|
||||||
|
|
||||||
|
room_alias_str = "#room:test"
|
||||||
|
user_id = self.get_success(self.handler.register_user(localpart="jeff"))
|
||||||
|
|
||||||
|
# Ensure the room was created.
|
||||||
|
directory_handler = self.hs.get_handlers().directory_handler
|
||||||
|
room_alias = RoomAlias.from_string(room_alias_str)
|
||||||
|
room_id = self.get_success(directory_handler.get_association(room_alias))
|
||||||
|
|
||||||
|
# Ensure the room is properly a public room.
|
||||||
|
room = self.get_success(self.store.get_room_with_stats(room_id["room_id"]))
|
||||||
|
self.assertEqual(room["join_rules"], "public")
|
||||||
|
|
||||||
|
# Both users should be in the room.
|
||||||
|
rooms = self.get_success(self.store.get_rooms_for_user(inviter))
|
||||||
|
self.assertIn(room_id["room_id"], rooms)
|
||||||
|
rooms = self.get_success(self.store.get_rooms_for_user(user_id))
|
||||||
|
self.assertIn(room_id["room_id"], rooms)
|
||||||
|
|
||||||
|
# Register a second user, which should also end up in the room.
|
||||||
|
user_id = self.get_success(self.handler.register_user(localpart="bob"))
|
||||||
|
rooms = self.get_success(self.store.get_rooms_for_user(user_id))
|
||||||
|
self.assertIn(room_id["room_id"], rooms)
|
||||||
|
|
||||||
|
@override_config(
|
||||||
|
{
|
||||||
|
"auto_join_rooms": ["#room:test"],
|
||||||
|
"autocreate_auto_join_room_preset": "private_chat",
|
||||||
|
"auto_join_mxid_localpart": "support",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
def test_auto_create_auto_join_room_preset(self):
|
||||||
|
"""
|
||||||
|
Auto-created rooms that are private require an invite to go to the user
|
||||||
|
(instead of directly joining it).
|
||||||
|
"""
|
||||||
|
# Ensure the support user exists.
|
||||||
|
inviter = "@support:test"
|
||||||
|
|
||||||
|
room_alias_str = "#room:test"
|
||||||
|
user_id = self.get_success(self.handler.register_user(localpart="jeff"))
|
||||||
|
|
||||||
|
# Ensure the room was created.
|
||||||
|
directory_handler = self.hs.get_handlers().directory_handler
|
||||||
|
room_alias = RoomAlias.from_string(room_alias_str)
|
||||||
|
room_id = self.get_success(directory_handler.get_association(room_alias))
|
||||||
|
|
||||||
|
# Ensure the room is properly a private room.
|
||||||
|
room = self.get_success(self.store.get_room_with_stats(room_id["room_id"]))
|
||||||
|
self.assertFalse(room["public"])
|
||||||
|
self.assertEqual(room["join_rules"], "invite")
|
||||||
|
self.assertEqual(room["guest_access"], "can_join")
|
||||||
|
|
||||||
|
# Both users should be in the room.
|
||||||
|
rooms = self.get_success(self.store.get_rooms_for_user(inviter))
|
||||||
|
self.assertIn(room_id["room_id"], rooms)
|
||||||
|
rooms = self.get_success(self.store.get_rooms_for_user(user_id))
|
||||||
|
self.assertIn(room_id["room_id"], rooms)
|
||||||
|
|
||||||
|
# Register a second user, which should also end up in the room.
|
||||||
|
user_id = self.get_success(self.handler.register_user(localpart="bob"))
|
||||||
|
rooms = self.get_success(self.store.get_rooms_for_user(user_id))
|
||||||
|
self.assertIn(room_id["room_id"], rooms)
|
||||||
|
|
||||||
|
@override_config(
|
||||||
|
{
|
||||||
|
"auto_join_rooms": ["#room:test"],
|
||||||
|
"autocreate_auto_join_room_preset": "private_chat",
|
||||||
|
"auto_join_mxid_localpart": "support",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
def test_auto_create_auto_join_room_preset_guest(self):
|
||||||
|
"""
|
||||||
|
Auto-created rooms that are private require an invite to go to the user
|
||||||
|
(instead of directly joining it).
|
||||||
|
|
||||||
|
This should also work for guests.
|
||||||
|
"""
|
||||||
|
inviter = "@support:test"
|
||||||
|
|
||||||
|
room_alias_str = "#room:test"
|
||||||
|
user_id = self.get_success(
|
||||||
|
self.handler.register_user(localpart="jeff", make_guest=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Ensure the room was created.
|
||||||
|
directory_handler = self.hs.get_handlers().directory_handler
|
||||||
|
room_alias = RoomAlias.from_string(room_alias_str)
|
||||||
|
room_id = self.get_success(directory_handler.get_association(room_alias))
|
||||||
|
|
||||||
|
# Ensure the room is properly a private room.
|
||||||
|
room = self.get_success(self.store.get_room_with_stats(room_id["room_id"]))
|
||||||
|
self.assertFalse(room["public"])
|
||||||
|
self.assertEqual(room["join_rules"], "invite")
|
||||||
|
self.assertEqual(room["guest_access"], "can_join")
|
||||||
|
|
||||||
|
# Both users should be in the room.
|
||||||
|
rooms = self.get_success(self.store.get_rooms_for_user(inviter))
|
||||||
|
self.assertIn(room_id["room_id"], rooms)
|
||||||
|
rooms = self.get_success(self.store.get_rooms_for_user(user_id))
|
||||||
|
self.assertIn(room_id["room_id"], rooms)
|
||||||
|
|
||||||
|
@override_config(
|
||||||
|
{
|
||||||
|
"auto_join_rooms": ["#room:test"],
|
||||||
|
"autocreate_auto_join_room_preset": "private_chat",
|
||||||
|
"auto_join_mxid_localpart": "support",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
def test_auto_create_auto_join_room_preset_invalid_permissions(self):
|
||||||
|
"""
|
||||||
|
Auto-created rooms that are private require an invite, check that
|
||||||
|
registration doesn't completely break if the inviter doesn't have proper
|
||||||
|
permissions.
|
||||||
|
"""
|
||||||
|
inviter = "@support:test"
|
||||||
|
|
||||||
|
# Register an initial user to create the room and such (essentially this
|
||||||
|
# is a subset of test_auto_create_auto_join_room_preset).
|
||||||
|
room_alias_str = "#room:test"
|
||||||
|
user_id = self.get_success(self.handler.register_user(localpart="jeff"))
|
||||||
|
|
||||||
|
# Ensure the room was created.
|
||||||
|
directory_handler = self.hs.get_handlers().directory_handler
|
||||||
|
room_alias = RoomAlias.from_string(room_alias_str)
|
||||||
|
room_id = self.get_success(directory_handler.get_association(room_alias))
|
||||||
|
|
||||||
|
# Ensure the room exists.
|
||||||
|
self.get_success(self.store.get_room_with_stats(room_id["room_id"]))
|
||||||
|
|
||||||
|
# Both users should be in the room.
|
||||||
|
rooms = self.get_success(self.store.get_rooms_for_user(inviter))
|
||||||
|
self.assertIn(room_id["room_id"], rooms)
|
||||||
|
rooms = self.get_success(self.store.get_rooms_for_user(user_id))
|
||||||
|
self.assertIn(room_id["room_id"], rooms)
|
||||||
|
|
||||||
|
# Lower the permissions of the inviter.
|
||||||
|
event_creation_handler = self.hs.get_event_creation_handler()
|
||||||
|
requester = create_requester(inviter)
|
||||||
|
event, context = self.get_success(
|
||||||
|
event_creation_handler.create_event(
|
||||||
|
requester,
|
||||||
|
{
|
||||||
|
"type": "m.room.power_levels",
|
||||||
|
"state_key": "",
|
||||||
|
"room_id": room_id["room_id"],
|
||||||
|
"content": {"invite": 100, "users": {inviter: 0}},
|
||||||
|
"sender": inviter,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.get_success(
|
||||||
|
event_creation_handler.send_nonmember_event(requester, event, context)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Register a second user, which won't be be in the room (or even have an invite)
|
||||||
|
# since the inviter no longer has the proper permissions.
|
||||||
|
user_id = self.get_success(self.handler.register_user(localpart="bob"))
|
||||||
|
|
||||||
|
# This user should not be in any rooms.
|
||||||
|
rooms = self.get_success(self.store.get_rooms_for_user(user_id))
|
||||||
|
invited_rooms = self.get_success(
|
||||||
|
self.store.get_invited_rooms_for_local_user(user_id)
|
||||||
|
)
|
||||||
|
self.assertEqual(rooms, set())
|
||||||
|
self.assertEqual(invited_rooms, [])
|
||||||
|
|
||||||
def test_auto_create_auto_join_where_no_consent(self):
|
def test_auto_create_auto_join_where_no_consent(self):
|
||||||
"""Test to ensure that the first user is not auto-joined to a room if
|
"""Test to ensure that the first user is not auto-joined to a room if
|
||||||
they have not given general consent.
|
they have not given general consent.
|
||||||
|
Loading…
Reference in New Issue
Block a user