Exemplo n.º 1
0
    def _delete_room_alias_txn(
        self, txn: LoggingTransaction, room_alias: RoomAlias
    ) -> Optional[str]:
        txn.execute(
            "SELECT room_id FROM room_aliases WHERE room_alias = ?",
            (room_alias.to_string(),),
        )

        res = txn.fetchone()
        if res:
            room_id = res[0]
        else:
            return None

        txn.execute(
            "DELETE FROM room_aliases WHERE room_alias = ?", (room_alias.to_string(),)
        )

        txn.execute(
            "DELETE FROM room_alias_servers WHERE room_alias = ?",
            (room_alias.to_string(),),
        )

        self._invalidate_cache_and_stream(txn, self.get_aliases_for_room, (room_id,))

        return room_id
Exemplo n.º 2
0
    async def get_association_from_room_alias(
            self, room_alias: RoomAlias) -> Optional[RoomAliasMapping]:
        """Gets the room_id and server list for a given room_alias

        Args:
            room_alias: The alias to translate to an ID.

        Returns:
            The room alias mapping or None if no association can be found.
        """
        room_id = await self.db_pool.simple_select_one_onecol(
            "room_aliases",
            {"room_alias": room_alias.to_string()},
            "room_id",
            allow_none=True,
            desc="get_association_from_room_alias",
        )

        if not room_id:
            return None

        servers = await self.db_pool.simple_select_onecol(
            "room_alias_servers",
            {"room_alias": room_alias.to_string()},
            "server",
            desc="get_association_from_room_alias",
        )

        if not servers:
            return None

        return RoomAliasMapping(room_id, room_alias.to_string(), servers)
Exemplo n.º 3
0
    async def get_association(self, room_alias: RoomAlias) -> JsonDict:
        room_id = None
        if self.hs.is_mine(room_alias):
            result: Optional[
                RoomAliasMapping] = await self.get_association_from_room_alias(
                    room_alias)

            if result:
                room_id = result.room_id
                servers = result.servers
        else:
            try:
                fed_result: Optional[
                    JsonDict] = await self.federation.make_query(
                        destination=room_alias.domain,
                        query_type="directory",
                        args={"room_alias": room_alias.to_string()},
                        retry_on_dns_fail=False,
                        ignore_backoff=True,
                    )
            except RequestSendFailed:
                raise SynapseError(502, "Failed to fetch alias")
            except CodeMessageException as e:
                logging.warning("Error retrieving alias")
                if e.code == 404:
                    fed_result = None
                else:
                    raise SynapseError(502, "Failed to fetch alias")

            if fed_result and "room_id" in fed_result and "servers" in fed_result:
                room_id = fed_result["room_id"]
                servers = fed_result["servers"]

        if not room_id:
            raise SynapseError(
                404,
                "Room alias %s not found" % (room_alias.to_string(), ),
                Codes.NOT_FOUND,
            )

        users = await self.store.get_users_in_room(room_id)
        extra_servers = {get_domain_from_id(u) for u in users}
        servers = set(extra_servers) | set(servers)

        # If this server is in the list of servers, return it first.
        if self.server_name in servers:
            servers = [self.server_name
                       ] + [s for s in servers if s != self.server_name]
        else:
            servers = list(servers)

        return {"room_id": room_id, "servers": servers}
Exemplo n.º 4
0
    async def query_room_alias_exists(
        self, room_alias: RoomAlias
    ) -> Optional[RoomAliasMapping]:
        """Check if an application service knows this room alias exists.

        Args:
            room_alias: The room alias to query.
        Returns:
            namedtuple: with keys "room_id" and "servers" or None if no
            association can be found.
        """
        room_alias_str = room_alias.to_string()
        services = self.store.get_app_services()
        alias_query_services = [
            s for s in services if (s.is_interested_in_alias(room_alias_str))
        ]
        for alias_service in alias_query_services:
            is_known_alias = await self.appservice_api.query_alias(
                alias_service, room_alias_str
            )
            if is_known_alias:
                # the alias exists now so don't query more ASes.
                return await self.store.get_association_from_room_alias(room_alias)

        return None
Exemplo n.º 5
0
    async def _user_can_delete_alias(self, alias: RoomAlias, user_id: str):
        """Determine whether a user can delete an alias.

        One of the following must be true:

        1. The user created the alias.
        2. The user is a server administrator.
        3. The user has a power-level sufficient to send a canonical alias event
           for the current room.

        """
        creator = await self.store.get_room_alias_creator(alias.to_string())

        if creator is not None and creator == user_id:
            return True

        # Resolve the alias to the corresponding room.
        room_mapping = await self.get_association(alias)
        room_id = room_mapping["room_id"]
        if not room_id:
            return False

        res = await self.auth.check_can_change_room_list(
            room_id, UserID.from_string(user_id)
        )
        return res
Exemplo n.º 6
0
 async def delete_appservice_association(self, service: ApplicationService,
                                         room_alias: RoomAlias) -> None:
     if not service.is_interested_in_alias(room_alias.to_string()):
         raise SynapseError(
             400,
             "This application service has not reserved this kind of alias",
             errcode=Codes.EXCLUSIVE,
         )
     await self._delete_association(room_alias)
Exemplo n.º 7
0
    def can_modify_alias(self, alias: RoomAlias, user_id: Optional[str] = None) -> bool:
        # Any application service "interested" in an alias they are regexing on
        # can modify the alias.
        # Users can only modify the alias if ALL the interested services have
        # non-exclusive locks on the alias (or there are no interested services)
        services = self.store.get_app_services()
        interested_services = [
            s for s in services if s.is_interested_in_alias(alias.to_string())
        ]

        for service in interested_services:
            if user_id == service.sender:
                # this user IS the app service so they can do whatever they like
                return True
            elif service.is_exclusive_alias(alias.to_string()):
                # another service has an exclusive lock on this alias.
                return False
        # either no interested services, or no service with an exclusive lock
        return True
Exemplo n.º 8
0
    async def _update_canonical_alias(self, requester: Requester, user_id: str,
                                      room_id: str,
                                      room_alias: RoomAlias) -> None:
        """
        Send an updated canonical alias event if the removed alias was set as
        the canonical alias or listed in the alt_aliases field.

        Raises:
            ShadowBanError if the requester has been shadow-banned.
        """
        alias_event = await self.state.get_current_state(
            room_id, EventTypes.CanonicalAlias, "")

        # There is no canonical alias, nothing to do.
        if not alias_event:
            return

        # Obtain a mutable version of the event content.
        content = dict(alias_event.content)
        send_update = False

        # Remove the alias property if it matches the removed alias.
        alias_str = room_alias.to_string()
        if alias_event.content.get("alias", "") == alias_str:
            send_update = True
            content.pop("alias", "")

        # Filter the alt_aliases property for the removed alias. Note that the
        # value is not modified if alt_aliases is of an unexpected form.
        alt_aliases = content.get("alt_aliases")
        if isinstance(alt_aliases, (list, tuple)) and alias_str in alt_aliases:
            send_update = True
            alt_aliases = [
                alias for alias in alt_aliases if alias != alias_str
            ]

            if alt_aliases:
                content["alt_aliases"] = alt_aliases
            else:
                del content["alt_aliases"]

        if send_update:
            await self.event_creation_handler.create_and_send_nonmember_event(
                requester,
                {
                    "type": EventTypes.CanonicalAlias,
                    "state_key": "",
                    "room_id": room_id,
                    "sender": user_id,
                    "content": content,
                },
                ratelimit=False,
            )
Exemplo n.º 9
0
    async def create_room_alias_association(
        self,
        room_alias: RoomAlias,
        room_id: str,
        servers: Iterable[str],
        creator: Optional[str] = None,
    ) -> None:
        """Creates an association between a room alias and room_id/servers

        Args:
            room_alias: The alias to create.
            room_id: The target of the alias.
            servers: A list of servers through which it may be possible to join the room
            creator: Optional user_id of creator.
        """

        def alias_txn(txn: LoggingTransaction) -> None:
            self.db_pool.simple_insert_txn(
                txn,
                "room_aliases",
                {
                    "room_alias": room_alias.to_string(),
                    "room_id": room_id,
                    "creator": creator,
                },
            )

            self.db_pool.simple_insert_many_txn(
                txn,
                table="room_alias_servers",
                keys=("room_alias", "server"),
                values=[(room_alias.to_string(), server) for server in servers],
            )

            self._invalidate_cache_and_stream(
                txn, self.get_aliases_for_room, (room_id,)
            )

        try:
            await self.db_pool.runInteraction(
                "create_room_alias_association", alias_txn
            )
        except self.database_engine.module.IntegrityError:
            raise SynapseError(
                409, "Room alias %s already exists" % room_alias.to_string()
            )
Exemplo n.º 10
0
    def create_room(self,
                    requester,
                    config,
                    ratelimit=True,
                    creator_join_profile=None):
        """ Creates a new room.

        Args:
            requester (synapse.types.Requester):
                The user who requested the room creation.
            config (dict) : A dict of configuration options.
            ratelimit (bool): set to False to disable the rate limiter

            creator_join_profile (dict|None):
                Set to override the displayname and avatar for the creating
                user in this room. If unset, displayname and avatar will be
                derived from the user's profile. If set, should contain the
                values to go in the body of the 'join' event (typically
                `avatar_url` and/or `displayname`.

        Returns:
            Deferred[dict]:
                a dict containing the keys `room_id` and, if an alias was
                requested, `room_alias`.
        Raises:
            SynapseError if the room ID couldn't be stored, or something went
            horribly wrong.
            ResourceLimitError if server is blocked to some resource being
            exceeded
        """
        user_id = requester.user.to_string()

        yield self.auth.check_auth_blocking(user_id)

        if (self._server_notices_mxid is not None
                and requester.user.to_string() == self._server_notices_mxid):
            # allow the server notices mxid to create rooms
            is_requester_admin = True
        else:
            is_requester_admin = yield self.auth.is_server_admin(
                requester.user)

        # Check whether the third party rules allows/changes the room create
        # request.
        yield self.third_party_event_rules.on_create_room(
            requester, config, is_requester_admin=is_requester_admin)

        if not is_requester_admin and not self.spam_checker.user_may_create_room(
                user_id):
            raise SynapseError(403, "You are not permitted to create rooms")

        if ratelimit:
            yield self.ratelimit(requester)

        room_version = config.get("room_version",
                                  self.config.default_room_version.identifier)

        if not isinstance(room_version, string_types):
            raise SynapseError(400, "room_version must be a string",
                               Codes.BAD_JSON)

        if room_version not in KNOWN_ROOM_VERSIONS:
            raise SynapseError(
                400,
                "Your homeserver does not support this room version",
                Codes.UNSUPPORTED_ROOM_VERSION,
            )

        if "room_alias_name" in config:
            for wchar in string.whitespace:
                if wchar in config["room_alias_name"]:
                    raise SynapseError(400, "Invalid characters in room alias")

            room_alias = RoomAlias(config["room_alias_name"], self.hs.hostname)
            mapping = yield self.store.get_association_from_room_alias(
                room_alias)

            if mapping:
                raise SynapseError(400, "Room alias already taken",
                                   Codes.ROOM_IN_USE)
        else:
            room_alias = None

        invite_list = config.get("invite", [])
        for i in invite_list:
            try:
                uid = UserID.from_string(i)
                parse_and_validate_server_name(uid.domain)
            except Exception:
                raise SynapseError(400, "Invalid user_id: %s" % (i, ))

        yield self.event_creation_handler.assert_accepted_privacy_policy(
            requester)

        power_level_content_override = config.get(
            "power_level_content_override")
        if (power_level_content_override
                and "users" in power_level_content_override
                and user_id not in power_level_content_override["users"]):
            raise SynapseError(
                400,
                "Not a valid power_level_content_override: 'users' did not contain %s"
                % (user_id, ),
            )

        invite_3pid_list = config.get("invite_3pid", [])

        visibility = config.get("visibility", None)
        is_public = visibility == "public"

        room_id = yield self._generate_room_id(creator_id=user_id,
                                               is_public=is_public)

        directory_handler = self.hs.get_handlers().directory_handler
        if room_alias:
            yield directory_handler.create_association(
                requester=requester,
                room_id=room_id,
                room_alias=room_alias,
                servers=[self.hs.hostname],
                send_event=False,
                check_membership=False,
            )

        preset_config = config.get(
            "preset",
            RoomCreationPreset.PRIVATE_CHAT
            if visibility == "private" else RoomCreationPreset.PUBLIC_CHAT,
        )

        raw_initial_state = config.get("initial_state", [])

        initial_state = OrderedDict()
        for val in raw_initial_state:
            initial_state[(val["type"], val.get("state_key",
                                                ""))] = val["content"]

        creation_content = config.get("creation_content", {})

        # override any attempt to set room versions via the creation_content
        creation_content["room_version"] = room_version

        yield self._send_events_for_new_room(
            requester,
            room_id,
            preset_config=preset_config,
            invite_list=invite_list,
            initial_state=initial_state,
            creation_content=creation_content,
            room_alias=room_alias,
            power_level_content_override=power_level_content_override,
            creator_join_profile=creator_join_profile,
        )

        if "name" in config:
            name = config["name"]
            yield self.event_creation_handler.create_and_send_nonmember_event(
                requester,
                {
                    "type": EventTypes.Name,
                    "room_id": room_id,
                    "sender": user_id,
                    "state_key": "",
                    "content": {
                        "name": name
                    },
                },
                ratelimit=False,
            )

        if "topic" in config:
            topic = config["topic"]
            yield self.event_creation_handler.create_and_send_nonmember_event(
                requester,
                {
                    "type": EventTypes.Topic,
                    "room_id": room_id,
                    "sender": user_id,
                    "state_key": "",
                    "content": {
                        "topic": topic
                    },
                },
                ratelimit=False,
            )

        for invitee in invite_list:
            content = {}
            is_direct = config.get("is_direct", None)
            if is_direct:
                content["is_direct"] = is_direct

            yield self.room_member_handler.update_membership(
                requester,
                UserID.from_string(invitee),
                room_id,
                "invite",
                ratelimit=False,
                content=content,
            )

        for invite_3pid in invite_3pid_list:
            id_server = invite_3pid["id_server"]
            id_access_token = invite_3pid.get("id_access_token")  # optional
            address = invite_3pid["address"]
            medium = invite_3pid["medium"]
            yield self.hs.get_room_member_handler().do_3pid_invite(
                room_id,
                requester.user,
                medium,
                address,
                id_server,
                requester,
                txn_id=None,
                id_access_token=id_access_token,
            )

        result = {"room_id": room_id}

        if room_alias:
            result["room_alias"] = room_alias.to_string()
            yield directory_handler.send_room_alias_update_event(
                requester, room_id)

        return result
Exemplo n.º 11
0
    def test_build(self):
        room = RoomAlias("channel", "my.domain")

        self.assertEquals(room.to_string(), "#channel:my.domain")
Exemplo n.º 12
0
    async def create_association(
        self,
        requester: Requester,
        room_alias: RoomAlias,
        room_id: str,
        servers: Optional[List[str]] = None,
        check_membership: bool = True,
    ) -> None:
        """Attempt to create a new alias

        Args:
            requester
            room_alias
            room_id
            servers: Iterable of servers that others servers should try and join via
            check_membership: Whether to check if the user is in the room
                before the alias can be set (if the server's config requires it).
        """

        user_id = requester.user.to_string()

        if len(room_alias.to_string()) > MAX_ALIAS_LENGTH:
            raise SynapseError(
                400,
                "Can't create aliases longer than %s characters" % MAX_ALIAS_LENGTH,
                Codes.INVALID_PARAM,
            )

        service = requester.app_service
        if service:
            if not service.is_interested_in_alias(room_alias.to_string()):
                raise SynapseError(
                    400,
                    "This application service has not reserved this kind of alias.",
                    errcode=Codes.EXCLUSIVE,
                )
        else:
            # Server admins are not subject to the same constraints as normal
            # users when creating an alias (e.g. being in the room).
            is_admin = await self.auth.is_server_admin(requester.user)

            if (self.require_membership and check_membership) and not is_admin:
                rooms_for_user = await self.store.get_rooms_for_user(user_id)
                if room_id not in rooms_for_user:
                    raise AuthError(
                        403, "You must be in the room to create an alias for it"
                    )

            if not self.spam_checker.user_may_create_room_alias(user_id, room_alias):
                raise AuthError(403, "This user is not permitted to create this alias")

            if not self.config.is_alias_creation_allowed(
                user_id, room_id, room_alias.to_string()
            ):
                # Lets just return a generic message, as there may be all sorts of
                # reasons why we said no. TODO: Allow configurable error messages
                # per alias creation rule?
                raise SynapseError(403, "Not allowed to create alias")

            can_create = self.can_modify_alias(room_alias, user_id=user_id)
            if not can_create:
                raise AuthError(
                    400,
                    "This alias is reserved by an application service.",
                    errcode=Codes.EXCLUSIVE,
                )

        await self._create_association(room_alias, room_id, servers, creator=user_id)
Exemplo n.º 13
0
    def create_room(self, requester, config, ratelimit=True):
        """ Creates a new room.

        Args:
            requester (Requester): The user who requested the room creation.
            config (dict) : A dict of configuration options.
        Returns:
            The new room ID.
        Raises:
            SynapseError if the room ID couldn't be stored, or something went
            horribly wrong.
        """
        user_id = requester.user.to_string()

        if not self.spam_checker.user_may_create_room(user_id):
            raise SynapseError(403, "You are not permitted to create rooms")

        if ratelimit:
            yield self.ratelimit(requester)

        if "room_alias_name" in config:
            for wchar in string.whitespace:
                if wchar in config["room_alias_name"]:
                    raise SynapseError(400, "Invalid characters in room alias")

            room_alias = RoomAlias(
                config["room_alias_name"],
                self.hs.hostname,
            )
            mapping = yield self.store.get_association_from_room_alias(
                room_alias)

            if mapping:
                raise SynapseError(400, "Room alias already taken")
        else:
            room_alias = None

        invite_list = config.get("invite", [])
        for i in invite_list:
            try:
                UserID.from_string(i)
            except Exception:
                raise SynapseError(400, "Invalid user_id: %s" % (i, ))

        invite_3pid_list = config.get("invite_3pid", [])

        visibility = config.get("visibility", None)
        is_public = visibility == "public"

        # autogen room IDs and try to create it. We may clash, so just
        # try a few times till one goes through, giving up eventually.
        attempts = 0
        room_id = None
        while attempts < 5:
            try:
                random_string = stringutils.random_string(18)
                gen_room_id = RoomID(
                    random_string,
                    self.hs.hostname,
                )
                yield self.store.store_room(room_id=gen_room_id.to_string(),
                                            room_creator_user_id=user_id,
                                            is_public=is_public)
                room_id = gen_room_id.to_string()
                break
            except StoreError:
                attempts += 1
        if not room_id:
            raise StoreError(500, "Couldn't generate a room ID.")

        if room_alias:
            directory_handler = self.hs.get_handlers().directory_handler
            yield directory_handler.create_association(
                user_id=user_id,
                room_id=room_id,
                room_alias=room_alias,
                servers=[self.hs.hostname],
            )

        preset_config = config.get(
            "preset", RoomCreationPreset.PRIVATE_CHAT
            if visibility == "private" else RoomCreationPreset.PUBLIC_CHAT)

        raw_initial_state = config.get("initial_state", [])

        initial_state = OrderedDict()
        for val in raw_initial_state:
            initial_state[(val["type"], val.get("state_key",
                                                ""))] = val["content"]

        creation_content = config.get("creation_content", {})

        room_member_handler = self.hs.get_room_member_handler()

        yield self._send_events_for_new_room(
            requester,
            room_id,
            room_member_handler,
            preset_config=preset_config,
            invite_list=invite_list,
            initial_state=initial_state,
            creation_content=creation_content,
            room_alias=room_alias,
            power_level_content_override=config.get(
                "power_level_content_override", {}))

        if "name" in config:
            name = config["name"]
            yield self.event_creation_handler.create_and_send_nonmember_event(
                requester, {
                    "type": EventTypes.Name,
                    "room_id": room_id,
                    "sender": user_id,
                    "state_key": "",
                    "content": {
                        "name": name
                    },
                },
                ratelimit=False)

        if "topic" in config:
            topic = config["topic"]
            yield self.event_creation_handler.create_and_send_nonmember_event(
                requester, {
                    "type": EventTypes.Topic,
                    "room_id": room_id,
                    "sender": user_id,
                    "state_key": "",
                    "content": {
                        "topic": topic
                    },
                },
                ratelimit=False)

        for invitee in invite_list:
            content = {}
            is_direct = config.get("is_direct", None)
            if is_direct:
                content["is_direct"] = is_direct

            yield room_member_handler.update_membership(
                requester,
                UserID.from_string(invitee),
                room_id,
                "invite",
                ratelimit=False,
                content=content,
            )

        for invite_3pid in invite_3pid_list:
            id_server = invite_3pid["id_server"]
            address = invite_3pid["address"]
            medium = invite_3pid["medium"]
            yield self.hs.get_room_member_handler().do_3pid_invite(
                room_id,
                requester.user,
                medium,
                address,
                id_server,
                requester,
                txn_id=None,
            )

        result = {"room_id": room_id}

        if room_alias:
            result["room_alias"] = room_alias.to_string()
            yield directory_handler.send_room_alias_update_event(
                requester, user_id, room_id)

        defer.returnValue(result)
Exemplo n.º 14
0
    def create_room(self,
                    requester,
                    config,
                    ratelimit=True,
                    creator_join_profile=None):
        """ Creates a new room.

        Args:
            requester (synapse.types.Requester):
                The user who requested the room creation.
            config (dict) : A dict of configuration options.
            ratelimit (bool): set to False to disable the rate limiter

            creator_join_profile (dict|None):
                Set to override the displayname and avatar for the creating
                user in this room. If unset, displayname and avatar will be
                derived from the user's profile. If set, should contain the
                values to go in the body of the 'join' event (typically
                `avatar_url` and/or `displayname`.

        Returns:
            Deferred[dict]:
                a dict containing the keys `room_id` and, if an alias was
                requested, `room_alias`.
        Raises:
            SynapseError if the room ID couldn't be stored, or something went
            horribly wrong.
            ResourceLimitError if server is blocked to some resource being
            exceeded
        """
        user_id = requester.user.to_string()

        self.auth.check_auth_blocking(user_id)

        if not self.spam_checker.user_may_create_room(user_id):
            raise SynapseError(403, "You are not permitted to create rooms")

        if ratelimit:
            yield self.ratelimit(requester)

        room_version = config.get("room_version", DEFAULT_ROOM_VERSION)
        if not isinstance(room_version, string_types):
            raise SynapseError(
                400,
                "room_version must be a string",
                Codes.BAD_JSON,
            )

        if room_version not in KNOWN_ROOM_VERSIONS:
            raise SynapseError(
                400,
                "Your homeserver does not support this room version",
                Codes.UNSUPPORTED_ROOM_VERSION,
            )

        if "room_alias_name" in config:
            for wchar in string.whitespace:
                if wchar in config["room_alias_name"]:
                    raise SynapseError(400, "Invalid characters in room alias")

            room_alias = RoomAlias(
                config["room_alias_name"],
                self.hs.hostname,
            )
            mapping = yield self.store.get_association_from_room_alias(
                room_alias)

            if mapping:
                raise SynapseError(400, "Room alias already taken",
                                   Codes.ROOM_IN_USE)
        else:
            room_alias = None

        invite_list = config.get("invite", [])
        for i in invite_list:
            try:
                UserID.from_string(i)
            except Exception:
                raise SynapseError(400, "Invalid user_id: %s" % (i, ))

        yield self.event_creation_handler.assert_accepted_privacy_policy(
            requester, )

        invite_3pid_list = config.get("invite_3pid", [])

        visibility = config.get("visibility", None)
        is_public = visibility == "public"

        # autogen room IDs and try to create it. We may clash, so just
        # try a few times till one goes through, giving up eventually.
        attempts = 0
        room_id = None
        while attempts < 5:
            try:
                random_string = stringutils.random_string(18)
                gen_room_id = RoomID(
                    random_string,
                    self.hs.hostname,
                )
                yield self.store.store_room(room_id=gen_room_id.to_string(),
                                            room_creator_user_id=user_id,
                                            is_public=is_public)
                room_id = gen_room_id.to_string()
                break
            except StoreError:
                attempts += 1
        if not room_id:
            raise StoreError(500, "Couldn't generate a room ID.")

        if room_alias:
            directory_handler = self.hs.get_handlers().directory_handler
            yield directory_handler.create_association(
                user_id=user_id,
                room_id=room_id,
                room_alias=room_alias,
                servers=[self.hs.hostname],
            )

        preset_config = config.get(
            "preset", RoomCreationPreset.PRIVATE_CHAT
            if visibility == "private" else RoomCreationPreset.PUBLIC_CHAT)

        raw_initial_state = config.get("initial_state", [])

        initial_state = OrderedDict()
        for val in raw_initial_state:
            initial_state[(val["type"], val.get("state_key",
                                                ""))] = val["content"]

        creation_content = config.get("creation_content", {})

        # override any attempt to set room versions via the creation_content
        creation_content["room_version"] = room_version

        room_member_handler = self.hs.get_room_member_handler()

        yield self._send_events_for_new_room(
            requester,
            room_id,
            room_member_handler,
            preset_config=preset_config,
            invite_list=invite_list,
            initial_state=initial_state,
            creation_content=creation_content,
            room_alias=room_alias,
            power_level_content_override=config.get(
                "power_level_content_override", {}),
            creator_join_profile=creator_join_profile,
        )

        if "name" in config:
            name = config["name"]
            yield self.event_creation_handler.create_and_send_nonmember_event(
                requester, {
                    "type": EventTypes.Name,
                    "room_id": room_id,
                    "sender": user_id,
                    "state_key": "",
                    "content": {
                        "name": name
                    },
                },
                ratelimit=False)

        if "topic" in config:
            topic = config["topic"]
            yield self.event_creation_handler.create_and_send_nonmember_event(
                requester, {
                    "type": EventTypes.Topic,
                    "room_id": room_id,
                    "sender": user_id,
                    "state_key": "",
                    "content": {
                        "topic": topic
                    },
                },
                ratelimit=False)

        for invitee in invite_list:
            content = {}
            is_direct = config.get("is_direct", None)
            if is_direct:
                content["is_direct"] = is_direct

            yield room_member_handler.update_membership(
                requester,
                UserID.from_string(invitee),
                room_id,
                "invite",
                ratelimit=False,
                content=content,
            )

        for invite_3pid in invite_3pid_list:
            id_server = invite_3pid["id_server"]
            address = invite_3pid["address"]
            medium = invite_3pid["medium"]
            yield self.hs.get_room_member_handler().do_3pid_invite(
                room_id,
                requester.user,
                medium,
                address,
                id_server,
                requester,
                txn_id=None,
            )

        result = {"room_id": room_id}

        if room_alias:
            result["room_alias"] = room_alias.to_string()
            yield directory_handler.send_room_alias_update_event(
                requester, user_id, room_id)

        defer.returnValue(result)
Exemplo n.º 15
0
    def create_room(self, requester, config, ratelimit=True,
                    creator_join_profile=None):
        """ Creates a new room.

        Args:
            requester (synapse.types.Requester):
                The user who requested the room creation.
            config (dict) : A dict of configuration options.
            ratelimit (bool): set to False to disable the rate limiter

            creator_join_profile (dict|None):
                Set to override the displayname and avatar for the creating
                user in this room. If unset, displayname and avatar will be
                derived from the user's profile. If set, should contain the
                values to go in the body of the 'join' event (typically
                `avatar_url` and/or `displayname`.

        Returns:
            Deferred[dict]:
                a dict containing the keys `room_id` and, if an alias was
                requested, `room_alias`.
        Raises:
            SynapseError if the room ID couldn't be stored, or something went
            horribly wrong.
            ResourceLimitError if server is blocked to some resource being
            exceeded
        """
        user_id = requester.user.to_string()

        yield self.auth.check_auth_blocking(user_id)

        if not self.spam_checker.user_may_create_room(user_id):
            raise SynapseError(403, "You are not permitted to create rooms")

        if ratelimit:
            yield self.ratelimit(requester)

        room_version = config.get("room_version", DEFAULT_ROOM_VERSION.identifier)
        if not isinstance(room_version, string_types):
            raise SynapseError(
                400,
                "room_version must be a string",
                Codes.BAD_JSON,
            )

        if room_version not in KNOWN_ROOM_VERSIONS:
            raise SynapseError(
                400,
                "Your homeserver does not support this room version",
                Codes.UNSUPPORTED_ROOM_VERSION,
            )

        if "room_alias_name" in config:
            for wchar in string.whitespace:
                if wchar in config["room_alias_name"]:
                    raise SynapseError(400, "Invalid characters in room alias")

            room_alias = RoomAlias(
                config["room_alias_name"],
                self.hs.hostname,
            )
            mapping = yield self.store.get_association_from_room_alias(
                room_alias
            )

            if mapping:
                raise SynapseError(
                    400,
                    "Room alias already taken",
                    Codes.ROOM_IN_USE
                )
        else:
            room_alias = None

        invite_list = config.get("invite", [])
        for i in invite_list:
            try:
                UserID.from_string(i)
            except Exception:
                raise SynapseError(400, "Invalid user_id: %s" % (i,))

        yield self.event_creation_handler.assert_accepted_privacy_policy(
            requester,
        )

        invite_3pid_list = config.get("invite_3pid", [])

        visibility = config.get("visibility", None)
        is_public = visibility == "public"

        room_id = yield self._generate_room_id(creator_id=user_id, is_public=is_public)

        if room_alias:
            directory_handler = self.hs.get_handlers().directory_handler
            yield directory_handler.create_association(
                requester=requester,
                room_id=room_id,
                room_alias=room_alias,
                servers=[self.hs.hostname],
                send_event=False,
                check_membership=False,
            )

        preset_config = config.get(
            "preset",
            RoomCreationPreset.PRIVATE_CHAT
            if visibility == "private"
            else RoomCreationPreset.PUBLIC_CHAT
        )

        raw_initial_state = config.get("initial_state", [])

        initial_state = OrderedDict()
        for val in raw_initial_state:
            initial_state[(val["type"], val.get("state_key", ""))] = val["content"]

        creation_content = config.get("creation_content", {})

        # override any attempt to set room versions via the creation_content
        creation_content["room_version"] = room_version

        yield self._send_events_for_new_room(
            requester,
            room_id,
            preset_config=preset_config,
            invite_list=invite_list,
            initial_state=initial_state,
            creation_content=creation_content,
            room_alias=room_alias,
            power_level_content_override=config.get("power_level_content_override"),
            creator_join_profile=creator_join_profile,
        )

        if "name" in config:
            name = config["name"]
            yield self.event_creation_handler.create_and_send_nonmember_event(
                requester,
                {
                    "type": EventTypes.Name,
                    "room_id": room_id,
                    "sender": user_id,
                    "state_key": "",
                    "content": {"name": name},
                },
                ratelimit=False)

        if "topic" in config:
            topic = config["topic"]
            yield self.event_creation_handler.create_and_send_nonmember_event(
                requester,
                {
                    "type": EventTypes.Topic,
                    "room_id": room_id,
                    "sender": user_id,
                    "state_key": "",
                    "content": {"topic": topic},
                },
                ratelimit=False)

        for invitee in invite_list:
            content = {}
            is_direct = config.get("is_direct", None)
            if is_direct:
                content["is_direct"] = is_direct

            yield self.room_member_handler.update_membership(
                requester,
                UserID.from_string(invitee),
                room_id,
                "invite",
                ratelimit=False,
                content=content,
            )

        for invite_3pid in invite_3pid_list:
            id_server = invite_3pid["id_server"]
            address = invite_3pid["address"]
            medium = invite_3pid["medium"]
            yield self.hs.get_room_member_handler().do_3pid_invite(
                room_id,
                requester.user,
                medium,
                address,
                id_server,
                requester,
                txn_id=None,
            )

        result = {"room_id": room_id}

        if room_alias:
            result["room_alias"] = room_alias.to_string()
            yield directory_handler.send_room_alias_update_event(
                requester, room_id
            )

        defer.returnValue(result)
Exemplo n.º 16
0
    def create_room(self, requester, config, ratelimit=True):
        """ Creates a new room.

        Args:
            requester (Requester): The user who requested the room creation.
            config (dict) : A dict of configuration options.
        Returns:
            The new room ID.
        Raises:
            SynapseError if the room ID couldn't be stored, or something went
            horribly wrong.
        """
        user_id = requester.user.to_string()

        if not self.spam_checker.user_may_create_room(user_id):
            raise SynapseError(403, "You are not permitted to create rooms")

        if ratelimit:
            yield self.ratelimit(requester)

        if "room_alias_name" in config:
            for wchar in string.whitespace:
                if wchar in config["room_alias_name"]:
                    raise SynapseError(400, "Invalid characters in room alias")

            room_alias = RoomAlias(
                config["room_alias_name"],
                self.hs.hostname,
            )
            mapping = yield self.store.get_association_from_room_alias(
                room_alias
            )

            if mapping:
                raise SynapseError(400, "Room alias already taken")
        else:
            room_alias = None

        invite_list = config.get("invite", [])
        for i in invite_list:
            try:
                UserID.from_string(i)
            except Exception:
                raise SynapseError(400, "Invalid user_id: %s" % (i,))

        invite_3pid_list = config.get("invite_3pid", [])

        visibility = config.get("visibility", None)
        is_public = visibility == "public"

        # autogen room IDs and try to create it. We may clash, so just
        # try a few times till one goes through, giving up eventually.
        attempts = 0
        room_id = None
        while attempts < 5:
            try:
                random_string = stringutils.random_string(18)
                gen_room_id = RoomID(
                    random_string,
                    self.hs.hostname,
                )
                yield self.store.store_room(
                    room_id=gen_room_id.to_string(),
                    room_creator_user_id=user_id,
                    is_public=is_public
                )
                room_id = gen_room_id.to_string()
                break
            except StoreError:
                attempts += 1
        if not room_id:
            raise StoreError(500, "Couldn't generate a room ID.")

        if room_alias:
            directory_handler = self.hs.get_handlers().directory_handler
            yield directory_handler.create_association(
                user_id=user_id,
                room_id=room_id,
                room_alias=room_alias,
                servers=[self.hs.hostname],
            )

        preset_config = config.get(
            "preset",
            RoomCreationPreset.PRIVATE_CHAT
            if visibility == "private"
            else RoomCreationPreset.PUBLIC_CHAT
        )

        raw_initial_state = config.get("initial_state", [])

        initial_state = OrderedDict()
        for val in raw_initial_state:
            initial_state[(val["type"], val.get("state_key", ""))] = val["content"]

        creation_content = config.get("creation_content", {})

        room_member_handler = self.hs.get_room_member_handler()

        yield self._send_events_for_new_room(
            requester,
            room_id,
            room_member_handler,
            preset_config=preset_config,
            invite_list=invite_list,
            initial_state=initial_state,
            creation_content=creation_content,
            room_alias=room_alias,
            power_level_content_override=config.get("power_level_content_override", {})
        )

        if "name" in config:
            name = config["name"]
            yield self.event_creation_handler.create_and_send_nonmember_event(
                requester,
                {
                    "type": EventTypes.Name,
                    "room_id": room_id,
                    "sender": user_id,
                    "state_key": "",
                    "content": {"name": name},
                },
                ratelimit=False)

        if "topic" in config:
            topic = config["topic"]
            yield self.event_creation_handler.create_and_send_nonmember_event(
                requester,
                {
                    "type": EventTypes.Topic,
                    "room_id": room_id,
                    "sender": user_id,
                    "state_key": "",
                    "content": {"topic": topic},
                },
                ratelimit=False)

        for invitee in invite_list:
            content = {}
            is_direct = config.get("is_direct", None)
            if is_direct:
                content["is_direct"] = is_direct

            yield room_member_handler.update_membership(
                requester,
                UserID.from_string(invitee),
                room_id,
                "invite",
                ratelimit=False,
                content=content,
            )

        for invite_3pid in invite_3pid_list:
            id_server = invite_3pid["id_server"]
            address = invite_3pid["address"]
            medium = invite_3pid["medium"]
            yield self.hs.get_room_member_handler().do_3pid_invite(
                room_id,
                requester.user,
                medium,
                address,
                id_server,
                requester,
                txn_id=None,
            )

        result = {"room_id": room_id}

        if room_alias:
            result["room_alias"] = room_alias.to_string()
            yield directory_handler.send_room_alias_update_event(
                requester, user_id, room_id
            )

        defer.returnValue(result)