Beispiel #1
0
    def on_DELETE(self, request, room_alias):
        dir_handler = self.handlers.directory_handler

        try:
            service = yield self.auth.get_appservice_by_req(request)
            room_alias = RoomAlias.from_string(room_alias)
            yield dir_handler.delete_appservice_association(
                service, room_alias
            )
            logger.info(
                "Application service at %s deleted alias %s",
                service.url,
                room_alias.to_string()
            )
            defer.returnValue((200, {}))
        except AuthError:
            # fallback to default user behaviour if they aren't an AS
            pass

        requester = yield self.auth.get_user_by_req(request)
        user = requester.user

        room_alias = RoomAlias.from_string(room_alias)

        yield dir_handler.delete_association(
            requester, user.to_string(), room_alias
        )

        logger.info(
            "User %s deleted alias %s",
            user.to_string(),
            room_alias.to_string()
        )

        defer.returnValue((200, {}))
Beispiel #2
0
    def on_DELETE(self, request, room_alias):
        dir_handler = self.handlers.directory_handler

        try:
            service = yield self.auth.get_appservice_by_req(request)
            room_alias = RoomAlias.from_string(room_alias)
            yield dir_handler.delete_appservice_association(service, room_alias)
            logger.info("Application service at %s deleted alias %s", service.url, room_alias.to_string())
            defer.returnValue((200, {}))
        except AuthError:
            # fallback to default user behaviour if they aren't an AS
            pass

        user, _, _ = yield self.auth.get_user_by_req(request)

        is_admin = yield self.auth.is_server_admin(user)
        if not is_admin:
            raise AuthError(403, "You need to be a server admin")

        room_alias = RoomAlias.from_string(room_alias)

        yield dir_handler.delete_association(user.to_string(), room_alias)
        logger.info("User %s deleted alias %s", user.to_string(), room_alias.to_string())

        defer.returnValue((200, {}))
Beispiel #3
0
    async def on_DELETE(self, request, room_alias):
        dir_handler = self.handlers.directory_handler

        try:
            service = self.auth.get_appservice_by_req(request)
            room_alias = RoomAlias.from_string(room_alias)
            await dir_handler.delete_appservice_association(
                service, room_alias)
            logger.info(
                "Application service at %s deleted alias %s",
                service.url,
                room_alias.to_string(),
            )
            return 200, {}
        except InvalidClientCredentialsError:
            # fallback to default user behaviour if they aren't an AS
            pass

        requester = await self.auth.get_user_by_req(request)
        user = requester.user

        room_alias = RoomAlias.from_string(room_alias)

        await dir_handler.delete_association(requester, room_alias)

        logger.info("User %s deleted alias %s", user.to_string(),
                    room_alias.to_string())

        return 200, {}
Beispiel #4
0
    def on_DELETE(self, request, room_alias):
        dir_handler = self.handlers.directory_handler

        try:
            service = yield self.auth.get_appservice_by_req(request)
            room_alias = RoomAlias.from_string(room_alias)
            yield dir_handler.delete_appservice_association(
                service, room_alias)
            logger.info("Application service at %s deleted alias %s",
                        service.url, room_alias.to_string())
            defer.returnValue((200, {}))
        except AuthError:
            # fallback to default user behaviour if they aren't an AS
            pass

        requester = yield self.auth.get_user_by_req(request)
        user = requester.user

        room_alias = RoomAlias.from_string(room_alias)

        yield dir_handler.delete_association(requester, user.to_string(),
                                             room_alias)

        logger.info("User %s deleted alias %s", user.to_string(),
                    room_alias.to_string())

        defer.returnValue((200, {}))
    def setUp(self):
        self.mock_federation = Mock()
        self.mock_registry = Mock()

        self.query_handlers = {}

        def register_query_handler(query_type, handler):
            self.query_handlers[query_type] = handler

        self.mock_registry.register_query_handler = register_query_handler

        hs = yield setup_test_homeserver(
            self.addCleanup,
            http_client=None,
            resource_for_federation=Mock(),
            federation_client=self.mock_federation,
            federation_registry=self.mock_registry,
        )
        hs.handlers = DirectoryHandlers(hs)

        self.handler = hs.get_handlers().directory_handler

        self.store = hs.get_datastore()

        self.my_room = RoomAlias.from_string("#my-room:test")
        self.your_room = RoomAlias.from_string("#your-room:test")
        self.remote_room = RoomAlias.from_string("#another:remote")
Beispiel #6
0
    def on_DELETE(self, request, room_alias):
        dir_handler = self.handlers.directory_handler

        try:
            service = yield self.auth.get_appservice_by_req(request)
            room_alias = RoomAlias.from_string(room_alias)
            yield dir_handler.delete_appservice_association(
                service, room_alias)
            logger.info("Application service at %s deleted alias %s",
                        service.url, room_alias.to_string())
            defer.returnValue((200, {}))
        except AuthError:
            # fallback to default user behaviour if they aren't an AS
            pass

        user, _, _ = yield self.auth.get_user_by_req(request)

        is_admin = yield self.auth.is_server_admin(user)
        if not is_admin:
            raise AuthError(403, "You need to be a server admin")

        room_alias = RoomAlias.from_string(room_alias)

        yield dir_handler.delete_association(user.to_string(), room_alias)
        logger.info("User %s deleted alias %s", user.to_string(),
                    room_alias.to_string())

        defer.returnValue((200, {}))
Beispiel #7
0
    def make_homeserver(self, reactor, clock):
        self.mock_federation = Mock()
        self.mock_registry = Mock()

        self.query_handlers = {}

        def register_query_handler(query_type, handler):
            self.query_handlers[query_type] = handler

        self.mock_registry.register_query_handler = register_query_handler

        hs = self.setup_test_homeserver(
            http_client=None,
            resource_for_federation=Mock(),
            federation_client=self.mock_federation,
            federation_registry=self.mock_registry,
        )

        self.handler = hs.get_handlers().directory_handler

        self.store = hs.get_datastore()

        self.my_room = RoomAlias.from_string("#my-room:test")
        self.your_room = RoomAlias.from_string("#your-room:test")
        self.remote_room = RoomAlias.from_string("#another:remote")

        return hs
Beispiel #8
0
    def setUp(self):
        self.mock_federation = Mock(spec=[
            "make_query",
        ])

        self.query_handlers = {}

        def register_query_handler(query_type, handler):
            self.query_handlers[query_type] = handler
        self.mock_federation.register_query_handler = register_query_handler

        hs = yield setup_test_homeserver(
            http_client=None,
            resource_for_federation=Mock(),
            replication_layer=self.mock_federation,
        )
        hs.handlers = DirectoryHandlers(hs)

        self.handler = hs.get_handlers().directory_handler

        self.store = hs.get_datastore()

        self.my_room = RoomAlias.from_string("#my-room:test")
        self.your_room = RoomAlias.from_string("#your-room:test")
        self.remote_room = RoomAlias.from_string("#another:remote")
Beispiel #9
0
    async def on_PUT(self, request: SynapseRequest,
                     room_alias: str) -> Tuple[int, JsonDict]:
        if not RoomAlias.is_valid(room_alias):
            raise SynapseError(400,
                               "Room alias invalid",
                               errcode=Codes.INVALID_PARAM)
        room_alias_obj = RoomAlias.from_string(room_alias)

        content = parse_json_object_from_request(request)
        if "room_id" not in content:
            raise SynapseError(400,
                               'Missing params: ["room_id"]',
                               errcode=Codes.BAD_JSON)

        logger.debug("Got content: %s", content)
        logger.debug("Got room name: %s", room_alias_obj.to_string())

        room_id = content["room_id"]
        servers = content["servers"] if "servers" in content else None

        logger.debug("Got room_id: %s", room_id)
        logger.debug("Got servers: %s", servers)

        # TODO(erikj): Check types.

        room = await self.store.get_room(room_id)
        if room is None:
            raise SynapseError(400, "Room does not exist")

        requester = await self.auth.get_user_by_req(request)

        await self.directory_handler.create_association(
            requester, room_alias_obj, room_id, servers)

        return 200, {}
Beispiel #10
0
    def setUp(self):
        hs = yield setup_test_homeserver(self.addCleanup)

        self.store = hs.get_datastore()

        self.room = RoomID.from_string("!abcde:test")
        self.alias = RoomAlias.from_string("#my-room:test")
Beispiel #11
0
    async def on_GET(self, request: Request,
                     room_alias: str) -> Tuple[int, JsonDict]:
        room_alias_obj = RoomAlias.from_string(room_alias)

        res = await self.directory_handler.get_association(room_alias_obj)

        return 200, res
Beispiel #12
0
    def setUp(self):
        hs = yield setup_test_homeserver(self.addCleanup)

        self.store = hs.get_datastore()

        self.room = RoomID.from_string("!abcde:test")
        self.alias = RoomAlias.from_string("#my-room:test")
Beispiel #13
0
    async def on_DELETE(self, request: SynapseRequest,
                        room_alias: str) -> Tuple[int, JsonDict]:
        if not RoomAlias.is_valid(room_alias):
            raise SynapseError(400,
                               "Room alias invalid",
                               errcode=Codes.INVALID_PARAM)
        room_alias_obj = RoomAlias.from_string(room_alias)
        requester = await self.auth.get_user_by_req(request)

        if requester.app_service:
            await self.directory_handler.delete_appservice_association(
                requester.app_service, room_alias_obj)

            logger.info(
                "Application service at %s deleted alias %s",
                requester.app_service.url,
                room_alias_obj.to_string(),
            )

        else:
            await self.directory_handler.delete_association(
                requester, room_alias_obj)

            logger.info(
                "User %s deleted alias %s",
                requester.user.to_string(),
                room_alias_obj.to_string(),
            )

        return 200, {}
Beispiel #14
0
    def on_GET(self, request, room_alias):
        room_alias = RoomAlias.from_string(room_alias)

        dir_handler = self.handlers.directory_handler
        res = yield dir_handler.get_association(room_alias)

        defer.returnValue((200, res))
Beispiel #15
0
    def _validate_canonical_alias(self, directory_handler, room_alias_str,
                                  expected_room_id):
        """
        Ensure that the given room alias points to the expected room ID.

        Args:
            directory_handler: The directory handler object.
            room_alias_str: The room alias to check.
            expected_room_id: The room ID that the alias should point to.
        """
        room_alias = RoomAlias.from_string(room_alias_str)
        try:
            mapping = yield defer.ensureDeferred(
                directory_handler.get_association(room_alias))
        except SynapseError as e:
            # Turn M_NOT_FOUND errors into M_BAD_ALIAS errors.
            if e.errcode == Codes.NOT_FOUND:
                raise SynapseError(
                    400,
                    "Room alias %s does not point to the room" %
                    (room_alias_str, ),
                    Codes.BAD_ALIAS,
                )
            raise

        if mapping["room_id"] != expected_room_id:
            raise SynapseError(
                400,
                "Room alias %s does not point to the room" %
                (room_alias_str, ),
                Codes.BAD_ALIAS,
            )
Beispiel #16
0
    def setUp(self):
        hs = yield setup_test_homeserver()

        self.store = DirectoryStore(hs)

        self.room = RoomID.from_string("!abcde:test")
        self.alias = RoomAlias.from_string("#my-room:test")
Beispiel #17
0
    def setUp(self):
        hs = yield setup_test_homeserver(self.addCleanup)

        self.store = DirectoryStore(None, hs)

        self.room = RoomID.from_string("!abcde:test")
        self.alias = RoomAlias.from_string("#my-room:test")
Beispiel #18
0
    def on_GET(self, request, room_alias):
        room_alias = RoomAlias.from_string(room_alias)

        dir_handler = self.handlers.directory_handler
        res = yield dir_handler.get_association(room_alias)

        defer.returnValue((200, res))
Beispiel #19
0
    def on_PUT(self, request, room_alias):
        room_alias = RoomAlias.from_string(room_alias)

        content = parse_json_object_from_request(request)
        if "room_id" not in content:
            raise SynapseError(400, 'Missing params: ["room_id"]',
                               errcode=Codes.BAD_JSON)

        logger.debug("Got content: %s", content)
        logger.debug("Got room name: %s", room_alias.to_string())

        room_id = content["room_id"]
        servers = content["servers"] if "servers" in content else None

        logger.debug("Got room_id: %s", room_id)
        logger.debug("Got servers: %s", servers)

        # TODO(erikj): Check types.

        room = yield self.store.get_room(room_id)
        if room is None:
            raise SynapseError(400, "Room does not exist")

        requester = yield self.auth.get_user_by_req(request)

        yield self.handlers.directory_handler.create_association(
            requester, room_alias, room_id, servers
        )

        defer.returnValue((200, {}))
Beispiel #20
0
    def on_PUT(self, request, room_alias):
        room_alias = RoomAlias.from_string(room_alias)

        content = parse_json_object_from_request(request)
        if "room_id" not in content:
            raise SynapseError(400,
                               'Missing params: ["room_id"]',
                               errcode=Codes.BAD_JSON)

        logger.debug("Got content: %s", content)
        logger.debug("Got room name: %s", room_alias.to_string())

        room_id = content["room_id"]
        servers = content["servers"] if "servers" in content else None

        logger.debug("Got room_id: %s", room_id)
        logger.debug("Got servers: %s", servers)

        # TODO(erikj): Check types.

        room = yield self.store.get_room(room_id)
        if room is None:
            raise SynapseError(400, "Room does not exist")

        requester = yield self.auth.get_user_by_req(request)

        yield self.handlers.directory_handler.create_association(
            requester, room_alias, room_id, servers)

        defer.returnValue((200, {}))
Beispiel #21
0
    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_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)
Beispiel #22
0
    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_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)
Beispiel #23
0
    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_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)
Beispiel #24
0
    async def on_GET(self, request, room_alias):
        room_alias = RoomAlias.from_string(room_alias)

        dir_handler = self.handlers.directory_handler
        res = await dir_handler.get_association(room_alias)

        return 200, res
Beispiel #25
0
    async def on_POST(self, request, room_identifier):
        requester = await self.auth.get_user_by_req(request)
        await assert_user_is_admin(self.auth, requester.user)

        content = parse_json_object_from_request(request)

        assert_params_in_dict(content, ["user_id"])
        target_user = UserID.from_string(content["user_id"])

        if not self.hs.is_mine(target_user):
            raise SynapseError(400, "This endpoint can only be used with local users")

        if not await self.admin_handler.get_user(target_user):
            raise NotFoundError("User not found")

        if RoomID.is_valid(room_identifier):
            room_id = room_identifier
            try:
                remote_room_hosts = [
                    x.decode("ascii") for x in request.args[b"server_name"]
                ]  # type: Optional[List[str]]
            except Exception:
                remote_room_hosts = None
        elif RoomAlias.is_valid(room_identifier):
            handler = self.room_member_handler
            room_alias = RoomAlias.from_string(room_identifier)
            room_id, remote_room_hosts = await 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,)
            )

        fake_requester = create_requester(target_user)

        # send invite if room has "JoinRules.INVITE"
        room_state = await self.state_handler.get_current_state(room_id)
        join_rules_event = room_state.get((EventTypes.JoinRules, ""))
        if join_rules_event:
            if not (join_rules_event.content.get("join_rule") == JoinRules.PUBLIC):
                await self.room_member_handler.update_membership(
                    requester=requester,
                    target=fake_requester.user,
                    room_id=room_id,
                    action="invite",
                    remote_room_hosts=remote_room_hosts,
                    ratelimit=False,
                )

        await self.room_member_handler.update_membership(
            requester=fake_requester,
            target=fake_requester.user,
            room_id=room_id,
            action="join",
            remote_room_hosts=remote_room_hosts,
            ratelimit=False,
        )

        return 200, {"room_id": room_id}
Beispiel #26
0
    def on_PUT(self, request, room_alias):
        content = parse_json_object_from_request(request)
        if "room_id" not in content:
            raise SynapseError(400, "Missing room_id key",
                               errcode=Codes.BAD_JSON)

        logger.debug("Got content: %s", content)

        room_alias = RoomAlias.from_string(room_alias)

        logger.debug("Got room name: %s", room_alias.to_string())

        room_id = content["room_id"]
        servers = content["servers"] if "servers" in content else None

        logger.debug("Got room_id: %s", room_id)
        logger.debug("Got servers: %s", servers)

        # TODO(erikj): Check types.

        room = yield self.store.get_room(room_id)
        if room is None:
            raise SynapseError(400, "Room does not exist")

        dir_handler = self.handlers.directory_handler

        try:
            # try to auth as a user
            requester = yield self.auth.get_user_by_req(request)
            try:
                user_id = requester.user.to_string()
                yield dir_handler.create_association(
                    user_id, room_alias, room_id, servers
                )
                yield dir_handler.send_room_alias_update_event(
                    requester,
                    user_id,
                    room_id
                )
            except SynapseError as e:
                raise e
            except Exception:
                logger.exception("Failed to create association")
                raise
        except AuthError:
            # try to auth as an application service
            service = yield self.auth.get_appservice_by_req(request)
            yield dir_handler.create_appservice_association(
                service, room_alias, room_id, servers
            )
            logger.info(
                "Application service at %s created alias %s pointing to %s",
                service.url,
                room_alias.to_string(),
                room_id
            )

        defer.returnValue((200, {}))
Beispiel #27
0
 def test_auto_create_auto_join_rooms_when_user_is_not_a_real_user(self):
     room_alias_str = "#room:test"
     self.store.is_real_user = Mock(return_value=make_awaitable(False))
     user_id = self.get_success(self.handler.register_user(localpart="support"))
     rooms = self.get_success(self.store.get_rooms_for_user(user_id))
     self.assertEqual(len(rooms), 0)
     directory_handler = self.hs.get_directory_handler()
     room_alias = RoomAlias.from_string(room_alias_str)
     self.get_failure(directory_handler.get_association(room_alias), SynapseError)
Beispiel #28
0
    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_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.handle_new_client_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, [])
Beispiel #29
0
    def test_auto_create_auto_join_rooms(self):
        room_alias_str = "#room:test"
        user_id = self.get_success(self.handler.register_user(localpart="jeff"))
        rooms = self.get_success(self.store.get_rooms_for_user(user_id))
        directory_handler = self.hs.get_directory_handler()
        room_alias = RoomAlias.from_string(room_alias_str)
        room_id = self.get_success(directory_handler.get_association(room_alias))

        self.assertTrue(room_id["room_id"] in rooms)
        self.assertEqual(len(rooms), 1)
Beispiel #30
0
    def _add_alias(self, alias: str) -> RoomAlias:
        """Add an alias to the test room."""
        room_alias = RoomAlias.from_string(alias)

        # Create a new alias to this room.
        self.get_success(
            self.store.create_room_alias_association(room_alias, self.room_id,
                                                     ["test"],
                                                     self.admin_user))
        return room_alias
Beispiel #31
0
    def _auto_join_rooms(self, user_id):
        """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(str): The user to join
        """
        # auto-join the user to any rooms we're supposed to dump them into
        fake_requester = create_requester(user_id)

        # try to create the room if we're the first real user on the server. Note
        # that an auto-generated support user is not a real user and will never be
        # the user to create the room
        should_auto_create_rooms = False
        is_support = yield self.store.is_support_user(user_id)
        # There is an edge case where the first user is the support user, then
        # the room is never created, though this seems unlikely and
        # recoverable from given the support user being involved in the first
        # place.
        if self.hs.config.autocreate_auto_join_rooms and not is_support:
            count = yield self.store.count_all_users()
            should_auto_create_rooms = count == 1
        for r in self.hs.config.auto_join_rooms:
            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
                        # loop
                        yield self.hs.get_room_creation_handler().create_room(
                            fake_requester,
                            config={
                                "preset": "public_chat",
                                "room_alias_name": room_alias_localpart,
                            },
                            ratelimit=False,
                        )
                else:
                    yield self._join_user_to_room(fake_requester, r)
            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)
Beispiel #32
0
    def on_PUT(self, request, room_alias):
        content = parse_json_object_from_request(request)
        if "room_id" not in content:
            raise SynapseError(400, "Missing room_id key",
                               errcode=Codes.BAD_JSON)

        logger.debug("Got content: %s", content)

        room_alias = RoomAlias.from_string(room_alias)

        logger.debug("Got room name: %s", room_alias.to_string())

        room_id = content["room_id"]
        servers = content["servers"] if "servers" in content else None

        logger.debug("Got room_id: %s", room_id)
        logger.debug("Got servers: %s", servers)

        # TODO(erikj): Check types.
        # TODO(erikj): Check that room exists

        dir_handler = self.handlers.directory_handler

        try:
            # try to auth as a user
            requester = yield self.auth.get_user_by_req(request)
            try:
                user_id = requester.user.to_string()
                yield dir_handler.create_association(
                    user_id, room_alias, room_id, servers
                )
                yield dir_handler.send_room_alias_update_event(
                    requester,
                    user_id,
                    room_id
                )
            except SynapseError as e:
                raise e
            except:
                logger.exception("Failed to create association")
                raise
        except AuthError:
            # try to auth as an application service
            service = yield self.auth.get_appservice_by_req(request)
            yield dir_handler.create_appservice_association(
                service, room_alias, room_id, servers
            )
            logger.info(
                "Application service at %s created alias %s pointing to %s",
                service.url,
                room_alias.to_string(),
                room_id
            )

        defer.returnValue((200, {}))
Beispiel #33
0
    def test_auto_create_auto_join_rooms_when_support_user_exists(self):
        room_alias_str = "#room:test"
        self.hs.config.auto_join_rooms = [room_alias_str]

        self.store.is_support_user = Mock(return_value=True)
        res = self.get_success(self.handler.register(localpart='support'))
        rooms = self.get_success(self.store.get_rooms_for_user(res[0]))
        self.assertEqual(len(rooms), 0)
        directory_handler = self.hs.get_handlers().directory_handler
        room_alias = RoomAlias.from_string(room_alias_str)
        self.get_failure(directory_handler.get_association(room_alias), SynapseError)
Beispiel #34
0
    def _auto_join_rooms(self, user_id):
        """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(str): The user to join
        """
        # auto-join the user to any rooms we're supposed to dump them into
        fake_requester = create_requester(user_id)

        # try to create the room if we're the first real user on the server. Note
        # that an auto-generated support user is not a real user and will never be
        # the user to create the room
        should_auto_create_rooms = False
        is_support = yield self.store.is_support_user(user_id)
        # There is an edge case where the first user is the support user, then
        # the room is never created, though this seems unlikely and
        # recoverable from given the support user being involved in the first
        # place.
        if self.hs.config.autocreate_auto_join_rooms and not is_support:
            count = yield self.store.count_all_users()
            should_auto_create_rooms = count == 1
        for r in self.hs.config.auto_join_rooms:
            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
                        # loop
                        yield self.hs.get_room_creation_handler().create_room(
                            fake_requester,
                            config={
                                "preset": "public_chat",
                                "room_alias_name": room_alias_localpart
                            },
                            ratelimit=False,
                        )
                else:
                    yield self._join_user_to_room(fake_requester, r)
            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)
Beispiel #35
0
    def test_auto_create_auto_join_rooms(self):
        room_alias_str = "#room:test"
        self.hs.config.auto_join_rooms = [room_alias_str]
        res = self.get_success(self.handler.register(localpart='jeff'))
        rooms = self.get_success(self.store.get_rooms_for_user(res[0]))
        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))

        self.assertTrue(room_id['room_id'] in rooms)
        self.assertEqual(len(rooms), 1)
Beispiel #36
0
    def test_auto_create_auto_join_rooms_when_support_user_exists(self):
        room_alias_str = "#room:test"
        self.hs.config.auto_join_rooms = [room_alias_str]

        self.store.is_support_user = Mock(return_value=True)
        res = self.get_success(self.handler.register(localpart='support'))
        rooms = self.get_success(self.store.get_rooms_for_user(res[0]))
        self.assertEqual(len(rooms), 0)
        directory_handler = self.hs.get_handlers().directory_handler
        room_alias = RoomAlias.from_string(room_alias_str)
        self.get_failure(directory_handler.get_association(room_alias), SynapseError)
Beispiel #37
0
    async def on_GET(self, request: Request,
                     room_alias: str) -> Tuple[int, JsonDict]:
        if not RoomAlias.is_valid(room_alias):
            raise SynapseError(400,
                               "Room alias invalid",
                               errcode=Codes.INVALID_PARAM)
        room_alias_obj = RoomAlias.from_string(room_alias)

        res = await self.directory_handler.get_association(room_alias_obj)

        return 200, res
    def test_auto_create_auto_join_rooms(self):
        room_alias_str = "#room:test"
        self.hs.config.auto_join_rooms = [room_alias_str]
        res = yield self.handler.register(localpart='jeff')
        rooms = yield self.store.get_rooms_for_user(res[0])
        directory_handler = self.hs.get_handlers().directory_handler
        room_alias = RoomAlias.from_string(room_alias_str)
        room_id = yield directory_handler.get_association(room_alias)

        self.assertTrue(room_id['room_id'] in rooms)
        self.assertEqual(len(rooms), 1)
Beispiel #39
0
    def test_auto_create_auto_join_rooms_when_user_is_the_first_real_user(self):
        room_alias_str = "#room:test"

        self.store.count_real_users = Mock(return_value=make_awaitable(1))
        self.store.is_real_user = Mock(return_value=make_awaitable(True))
        user_id = self.get_success(self.handler.register_user(localpart="real"))
        rooms = self.get_success(self.store.get_rooms_for_user(user_id))
        directory_handler = self.hs.get_directory_handler()
        room_alias = RoomAlias.from_string(room_alias_str)
        room_id = self.get_success(directory_handler.get_association(room_alias))

        self.assertTrue(room_id["room_id"] in rooms)
        self.assertEqual(len(rooms), 1)
Beispiel #40
0
    async def on_directory_query(self, args):
        room_alias = RoomAlias.from_string(args["room_alias"])
        if not self.hs.is_mine(room_alias):
            raise SynapseError(400, "Room Alias is not hosted on this homeserver")

        result = await self.get_association_from_room_alias(room_alias)

        if result is not None:
            return {"room_id": result.room_id, "servers": result.servers}
        else:
            raise SynapseError(
                404,
                "Room alias %r not found" % (room_alias.to_string(),),
                Codes.NOT_FOUND,
            )
Beispiel #41
0
    def setUp(self):
        hs = yield setup_test_homeserver()

        # We can't test RoomStore on its own without the DirectoryStore, for
        # management of the 'room_aliases' table
        self.store = hs.get_datastore()

        self.room = RoomID.from_string("!abcde:test")
        self.alias = RoomAlias.from_string("#a-room-name:test")
        self.u_creator = UserID.from_string("@creator:test")

        yield self.store.store_room(
            self.room.to_string(),
            room_creator_user_id=self.u_creator.to_string(),
            is_public=True)
Beispiel #42
0
    def setUp(self):
        hs = yield setup_test_homeserver()

        # We can't test RoomStore on its own without the DirectoryStore, for
        # management of the 'room_aliases' table
        self.store = hs.get_datastore()

        self.room = RoomID.from_string("!abcde:test")
        self.alias = RoomAlias.from_string("#a-room-name:test")
        self.u_creator = UserID.from_string("@creator:test")

        yield self.store.store_room(self.room.to_string(),
            room_creator_user_id=self.u_creator.to_string(),
            is_public=True
        )
Beispiel #43
0
    def on_POST(self, request, room_identifier, txn_id=None):
        requester = yield self.auth.get_user_by_req(
            request,
            allow_guest=True,
        )

        # the identifier could be a room alias or a room id. Try one then the
        # other if it fails to parse, without swallowing other valid
        # SynapseErrors.

        identifier = None
        is_room_alias = False
        try:
            identifier = RoomAlias.from_string(room_identifier)
            is_room_alias = True
        except SynapseError:
            identifier = RoomID.from_string(room_identifier)

        # TODO: Support for specifying the home server to join with?

        if is_room_alias:
            handler = self.handlers.room_member_handler
            ret_dict = yield handler.join_room_alias(
                requester.user,
                identifier,
            )
            defer.returnValue((200, ret_dict))
        else:  # room id
            msg_handler = self.handlers.message_handler
            content = {"membership": Membership.JOIN}
            if requester.is_guest:
                content["kind"] = "guest"
            yield msg_handler.create_and_send_event(
                {
                    "type": EventTypes.Member,
                    "content": content,
                    "room_id": identifier.to_string(),
                    "sender": requester.user.to_string(),
                    "state_key": requester.user.to_string(),
                },
                token_id=requester.access_token_id,
                txn_id=txn_id,
                is_guest=requester.is_guest,
            )

            defer.returnValue((200, {"room_id": identifier.to_string()}))
Beispiel #44
0
    def on_POST(self, request, room_identifier, txn_id=None):
        requester = yield self.auth.get_user_by_req(
            request,
            allow_guest=True,
        )

        try:
            content = parse_json_object_from_request(request)
        except Exception:
            # Turns out we used to ignore the body entirely, and some clients
            # cheekily send invalid bodies.
            content = {}

        if RoomID.is_valid(room_identifier):
            room_id = room_identifier
            try:
                remote_room_hosts = [
                    x.decode('ascii') for x in request.args[b"server_name"]
                ]
            except Exception:
                remote_room_hosts = None
        elif RoomAlias.is_valid(room_identifier):
            handler = self.room_member_handler
            room_alias = RoomAlias.from_string(room_identifier)
            room_id, remote_room_hosts = yield 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,
            ))

        yield self.room_member_handler.update_membership(
            requester=requester,
            target=requester.user,
            room_id=room_id,
            action="join",
            txn_id=txn_id,
            remote_room_hosts=remote_room_hosts,
            content=content,
            third_party_signed=content.get("third_party_signed", None),
        )

        defer.returnValue((200, {"room_id": room_id}))
Beispiel #45
0
    def on_directory_query(self, args):
        room_alias = RoomAlias.from_string(args["room_alias"])
        if not self.hs.is_mine(room_alias):
            raise SynapseError(
                400, "Room Alias is not hosted on this Home Server"
            )

        result = yield self.get_association_from_room_alias(
            room_alias
        )

        if result is not None:
            defer.returnValue({
                "room_id": result.room_id,
                "servers": result.servers,
            })
        else:
            raise SynapseError(
                404,
                "Room alias %r not found" % (room_alias.to_string(),),
                Codes.NOT_FOUND
            )
Beispiel #46
0
    def _join_user_to_room(self, requester, room_identifier):
        room_id = None
        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 = (
                yield 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,
            ))

        yield room_member_handler.update_membership(
            requester=requester,
            target=requester.user,
            room_id=room_id,
            remote_room_hosts=remote_room_hosts,
            action="join",
        )
Beispiel #47
0
    def test_parse(self):
        room = RoomAlias.from_string("#channel:my.domain")

        self.assertEquals("channel", room.localpart)
        self.assertEquals("my.domain", room.domain)
        self.assertEquals(True, mock_homeserver.is_mine(room))
Beispiel #48
0
    def register(
        self,
        localpart=None,
        password=None,
        generate_token=True,
        guest_access_token=None,
        make_guest=False,
        admin=False,
        threepid=None,
    ):
        """Registers a new client on the server.

        Args:
            localpart : The local part of the user ID to register. If None,
              one will be generated.
            password (unicode) : The password to assign to this user so they can
              login again. This can be None which means they cannot login again
              via a password (e.g. the user is an application service user).
            generate_token (bool): Whether a new access token should be
              generated. Having this be True should be considered deprecated,
              since it offers no means of associating a device_id with the
              access_token. Instead you should call auth_handler.issue_access_token
              after registration.
        Returns:
            A tuple of (user_id, access_token).
        Raises:
            RegistrationError if there was a problem registering.
        """

        yield self.auth.check_auth_blocking(threepid=threepid)
        password_hash = None
        if password:
            password_hash = yield self.auth_handler().hash(password)

        if localpart:
            yield self.check_username(localpart, guest_access_token=guest_access_token)

            was_guest = guest_access_token is not None

            if not was_guest:
                try:
                    int(localpart)
                    raise RegistrationError(
                        400,
                        "Numeric user IDs are reserved for guest users."
                    )
                except ValueError:
                    pass

            user = UserID(localpart, self.hs.hostname)
            user_id = user.to_string()

            token = None
            if generate_token:
                token = self.macaroon_gen.generate_access_token(user_id)
            yield self.store.register(
                user_id=user_id,
                token=token,
                password_hash=password_hash,
                was_guest=was_guest,
                make_guest=make_guest,
                create_profile_with_localpart=(
                    # If the user was a guest then they already have a profile
                    None if was_guest else user.localpart
                ),
                admin=admin,
            )

            if self.hs.config.user_directory_search_all_users:
                profile = yield self.store.get_profileinfo(localpart)
                yield self.user_directory_handler.handle_local_profile_change(
                    user_id, profile
                )

        else:
            # autogen a sequential user ID
            attempts = 0
            token = None
            user = None
            while not user:
                localpart = yield self._generate_user_id(attempts > 0)
                user = UserID(localpart, self.hs.hostname)
                user_id = user.to_string()
                yield self.check_user_id_not_appservice_exclusive(user_id)
                if generate_token:
                    token = self.macaroon_gen.generate_access_token(user_id)
                try:
                    yield self.store.register(
                        user_id=user_id,
                        token=token,
                        password_hash=password_hash,
                        make_guest=make_guest,
                        create_profile_with_localpart=user.localpart,
                    )
                except SynapseError:
                    # if user id is taken, just generate another
                    user = None
                    user_id = None
                    token = None
                    attempts += 1

        # auto-join the user to any rooms we're supposed to dump them into
        fake_requester = create_requester(user_id)

        # try to create the room if we're the first user on the server
        should_auto_create_rooms = False
        if self.hs.config.autocreate_auto_join_rooms:
            count = yield self.store.count_all_users()
            should_auto_create_rooms = count == 1

        for r in self.hs.config.auto_join_rooms:
            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
                        # loop
                        yield self.hs.get_room_creation_handler().create_room(
                            fake_requester,
                            config={
                                "preset": "public_chat",
                                "room_alias_name": room_alias_localpart
                            },
                            ratelimit=False,
                        )
                else:
                    yield self._join_user_to_room(fake_requester, r)
            except Exception as e:
                logger.error("Failed to join new user to %r: %r", r, e)

        defer.returnValue((user_id, token))
Beispiel #49
0
    def handle_new_client_event(
        self,
        requester,
        event,
        context,
        ratelimit=True,
        extra_users=[]
    ):
        # We now need to go and hit out to wherever we need to hit out to.

        if ratelimit:
            self.ratelimit(requester)

        try:
            self.auth.check(event, auth_events=context.current_state)
        except AuthError as err:
            logger.warn("Denying new event %r because %s", event, err)
            raise err

        yield self.maybe_kick_guest_users(event, context.current_state.values())

        if event.type == EventTypes.CanonicalAlias:
            # Check the alias is acually valid (at this time at least)
            room_alias_str = event.content.get("alias", None)
            if room_alias_str:
                room_alias = RoomAlias.from_string(room_alias_str)
                directory_handler = self.hs.get_handlers().directory_handler
                mapping = yield directory_handler.get_association(room_alias)

                if mapping["room_id"] != event.room_id:
                    raise SynapseError(
                        400,
                        "Room alias %s does not point to the room" % (
                            room_alias_str,
                        )
                    )

        federation_handler = self.hs.get_handlers().federation_handler

        if event.type == EventTypes.Member:
            if event.content["membership"] == Membership.INVITE:
                def is_inviter_member_event(e):
                    return (
                        e.type == EventTypes.Member and
                        e.sender == event.sender
                    )

                event.unsigned["invite_room_state"] = [
                    {
                        "type": e.type,
                        "state_key": e.state_key,
                        "content": e.content,
                        "sender": e.sender,
                    }
                    for k, e in context.current_state.items()
                    if e.type in self.hs.config.room_invite_state_types
                    or is_inviter_member_event(e)
                ]

                invitee = UserID.from_string(event.state_key)
                if not self.hs.is_mine(invitee):
                    # TODO: Can we add signature from remote server in a nicer
                    # way? If we have been invited by a remote server, we need
                    # to get them to sign the event.

                    returned_invite = yield federation_handler.send_invite(
                        invitee.domain,
                        event,
                    )

                    event.unsigned.pop("room_state", None)

                    # TODO: Make sure the signatures actually are correct.
                    event.signatures.update(
                        returned_invite.signatures
                    )

        if event.type == EventTypes.Redaction:
            if self.auth.check_redaction(event, auth_events=context.current_state):
                original_event = yield self.store.get_event(
                    event.redacts,
                    check_redacted=False,
                    get_prev_content=False,
                    allow_rejected=False,
                    allow_none=False
                )
                if event.user_id != original_event.user_id:
                    raise AuthError(
                        403,
                        "You don't have permission to redact events"
                    )

        if event.type == EventTypes.Create and context.current_state:
            raise AuthError(
                403,
                "Changing the room create event is forbidden",
            )

        action_generator = ActionGenerator(self.hs)
        yield action_generator.handle_push_actions_for_event(
            event, context
        )

        (event_stream_id, max_stream_id) = yield self.store.persist_event(
            event, context=context
        )

        # this intentionally does not yield: we don't care about the result
        # and don't need to wait for it.
        preserve_fn(self.hs.get_pusherpool().on_new_notifications)(
            event_stream_id, max_stream_id
        )

        destinations = set()
        for k, s in context.current_state.items():
            try:
                if k[0] == EventTypes.Member:
                    if s.content["membership"] == Membership.JOIN:
                        destinations.add(get_domain_from_id(s.state_key))
            except SynapseError:
                logger.warn(
                    "Failed to get destination from event %s", s.event_id
                )

        @defer.inlineCallbacks
        def _notify():
            yield run_on_reactor()
            self.notifier.on_new_room_event(
                event, event_stream_id, max_stream_id,
                extra_users=extra_users
            )

        preserve_fn(_notify)()

        # If invite, remove room_state from unsigned before sending.
        event.unsigned.pop("invite_room_state", None)

        federation_handler.handle_new_event(
            event, destinations=destinations,
        )
Beispiel #50
0
    def handle_new_client_event(self, event, context, extra_destinations=[],
                                extra_users=[], suppress_auth=False):
        # We now need to go and hit out to wherever we need to hit out to.

        if not suppress_auth:
            self.auth.check(event, auth_events=context.current_state)

        if event.type == EventTypes.CanonicalAlias:
            # Check the alias is acually valid (at this time at least)
            room_alias_str = event.content.get("alias", None)
            if room_alias_str:
                room_alias = RoomAlias.from_string(room_alias_str)
                directory_handler = self.hs.get_handlers().directory_handler
                mapping = yield directory_handler.get_association(room_alias)

                if mapping["room_id"] != event.room_id:
                    raise SynapseError(
                        400,
                        "Room alias %s does not point to the room" % (
                            room_alias_str,
                        )
                    )

        (event_stream_id, max_stream_id) = yield self.store.persist_event(
            event, context=context
        )

        federation_handler = self.hs.get_handlers().federation_handler

        if event.type == EventTypes.Member:
            if event.content["membership"] == Membership.INVITE:
                invitee = UserID.from_string(event.state_key)
                if not self.hs.is_mine(invitee):
                    # TODO: Can we add signature from remote server in a nicer
                    # way? If we have been invited by a remote server, we need
                    # to get them to sign the event.
                    returned_invite = yield federation_handler.send_invite(
                        invitee.domain,
                        event,
                    )

                    # TODO: Make sure the signatures actually are correct.
                    event.signatures.update(
                        returned_invite.signatures
                    )

        destinations = set(extra_destinations)
        for k, s in context.current_state.items():
            try:
                if k[0] == EventTypes.Member:
                    if s.content["membership"] == Membership.JOIN:
                        destinations.add(
                            UserID.from_string(s.state_key).domain
                        )
            except SynapseError:
                logger.warn(
                    "Failed to get destination from event %s", s.event_id
                )

        with PreserveLoggingContext():
            # Don't block waiting on waking up all the listeners.
            notify_d = self.notifier.on_new_room_event(
                event, event_stream_id, max_stream_id,
                extra_users=extra_users
            )

        def log_failure(f):
            logger.warn(
                "Failed to notify about %s: %s",
                event.event_id, f.value
            )

        notify_d.addErrback(log_failure)

        federation_handler.handle_new_event(
            event, destinations=destinations,
        )
Beispiel #51
0
    def persist_and_notify_client_event(
        self,
        requester,
        event,
        context,
        ratelimit=True,
        extra_users=[],
    ):
        """Called when we have fully built the event, have already
        calculated the push actions for the event, and checked auth.

        This should only be run on master.
        """
        assert not self.config.worker_app

        if ratelimit:
            yield self.base_handler.ratelimit(requester)

        yield self.base_handler.maybe_kick_guest_users(event, context)

        if event.type == EventTypes.CanonicalAlias:
            # Check the alias is acually valid (at this time at least)
            room_alias_str = event.content.get("alias", None)
            if room_alias_str:
                room_alias = RoomAlias.from_string(room_alias_str)
                directory_handler = self.hs.get_handlers().directory_handler
                mapping = yield directory_handler.get_association(room_alias)

                if mapping["room_id"] != event.room_id:
                    raise SynapseError(
                        400,
                        "Room alias %s does not point to the room" % (
                            room_alias_str,
                        )
                    )

        federation_handler = self.hs.get_handlers().federation_handler

        if event.type == EventTypes.Member:
            if event.content["membership"] == Membership.INVITE:
                def is_inviter_member_event(e):
                    return (
                        e.type == EventTypes.Member and
                        e.sender == event.sender
                    )

                current_state_ids = yield context.get_current_state_ids(self.store)

                state_to_include_ids = [
                    e_id
                    for k, e_id in iteritems(current_state_ids)
                    if k[0] in self.hs.config.room_invite_state_types
                    or k == (EventTypes.Member, event.sender)
                ]

                state_to_include = yield self.store.get_events(state_to_include_ids)

                event.unsigned["invite_room_state"] = [
                    {
                        "type": e.type,
                        "state_key": e.state_key,
                        "content": e.content,
                        "sender": e.sender,
                    }
                    for e in itervalues(state_to_include)
                ]

                invitee = UserID.from_string(event.state_key)
                if not self.hs.is_mine(invitee):
                    # TODO: Can we add signature from remote server in a nicer
                    # way? If we have been invited by a remote server, we need
                    # to get them to sign the event.

                    returned_invite = yield federation_handler.send_invite(
                        invitee.domain,
                        event,
                    )

                    event.unsigned.pop("room_state", None)

                    # TODO: Make sure the signatures actually are correct.
                    event.signatures.update(
                        returned_invite.signatures
                    )

        if event.type == EventTypes.Redaction:
            prev_state_ids = yield context.get_prev_state_ids(self.store)
            auth_events_ids = yield self.auth.compute_auth_events(
                event, prev_state_ids, for_verification=True,
            )
            auth_events = yield self.store.get_events(auth_events_ids)
            auth_events = {
                (e.type, e.state_key): e for e in auth_events.values()
            }
            room_version = yield self.store.get_room_version(event.room_id)
            if self.auth.check_redaction(room_version, event, auth_events=auth_events):
                original_event = yield self.store.get_event(
                    event.redacts,
                    check_redacted=False,
                    get_prev_content=False,
                    allow_rejected=False,
                    allow_none=False
                )
                if event.user_id != original_event.user_id:
                    raise AuthError(
                        403,
                        "You don't have permission to redact events"
                    )

                # We've already checked.
                event.internal_metadata.recheck_redaction = False

        if event.type == EventTypes.Create:
            prev_state_ids = yield context.get_prev_state_ids(self.store)
            if prev_state_ids:
                raise AuthError(
                    403,
                    "Changing the room create event is forbidden",
                )

        (event_stream_id, max_stream_id) = yield self.store.persist_event(
            event, context=context
        )

        yield self.pusher_pool.on_new_notifications(
            event_stream_id, max_stream_id,
        )

        def _notify():
            try:
                self.notifier.on_new_room_event(
                    event, event_stream_id, max_stream_id,
                    extra_users=extra_users
                )
            except Exception:
                logger.exception("Error notifying about new room event")

        run_in_background(_notify)

        if event.type == EventTypes.Message:
            # We don't want to block sending messages on any presence code. This
            # matters as sometimes presence code can take a while.
            run_in_background(self._bump_active_time, requester.user)
Beispiel #52
0
    def _move_aliases_to_new_room(
            self, requester, old_room_id, new_room_id, old_room_state,
    ):
        directory_handler = self.hs.get_handlers().directory_handler

        aliases = yield self.store.get_aliases_for_room(old_room_id)

        # check to see if we have a canonical alias.
        canonical_alias = None
        canonical_alias_event_id = old_room_state.get((EventTypes.CanonicalAlias, ""))
        if canonical_alias_event_id:
            canonical_alias_event = yield self.store.get_event(canonical_alias_event_id)
            if canonical_alias_event:
                canonical_alias = canonical_alias_event.content.get("alias", "")

        # first we try to remove the aliases from the old room (we suppress sending
        # the room_aliases event until the end).
        #
        # Note that we'll only be able to remove aliases that (a) aren't owned by an AS,
        # and (b) unless the user is a server admin, which the user created.
        #
        # This is probably correct - given we don't allow such aliases to be deleted
        # normally, it would be odd to allow it in the case of doing a room upgrade -
        # but it makes the upgrade less effective, and you have to wonder why a room
        # admin can't remove aliases that point to that room anyway.
        # (cf https://github.com/matrix-org/synapse/issues/2360)
        #
        removed_aliases = []
        for alias_str in aliases:
            alias = RoomAlias.from_string(alias_str)
            try:
                yield directory_handler.delete_association(
                    requester, alias, send_event=False,
                )
                removed_aliases.append(alias_str)
            except SynapseError as e:
                logger.warning(
                    "Unable to remove alias %s from old room: %s",
                    alias, e,
                )

        # if we didn't find any aliases, or couldn't remove anyway, we can skip the rest
        # of this.
        if not removed_aliases:
            return

        try:
            # this can fail if, for some reason, our user doesn't have perms to send
            # m.room.aliases events in the old room (note that we've already checked that
            # they have perms to send a tombstone event, so that's not terribly likely).
            #
            # If that happens, it's regrettable, but we should carry on: it's the same
            # as when you remove an alias from the directory normally - it just means that
            # the aliases event gets out of sync with the directory
            # (cf https://github.com/vector-im/riot-web/issues/2369)
            yield directory_handler.send_room_alias_update_event(
                requester, old_room_id,
            )
        except AuthError as e:
            logger.warning(
                "Failed to send updated alias event on old room: %s", e,
            )

        # we can now add any aliases we successfully removed to the new room.
        for alias in removed_aliases:
            try:
                yield directory_handler.create_association(
                    requester, RoomAlias.from_string(alias),
                    new_room_id, servers=(self.hs.hostname, ),
                    send_event=False, check_membership=False,
                )
                logger.info("Moved alias %s to new room", alias)
            except SynapseError as e:
                # I'm not really expecting this to happen, but it could if the spam
                # checking module decides it shouldn't, or similar.
                logger.error(
                    "Error adding alias %s to new room: %s",
                    alias, e,
                )

        try:
            if canonical_alias and (canonical_alias in removed_aliases):
                yield self.event_creation_handler.create_and_send_nonmember_event(
                    requester,
                    {
                        "type": EventTypes.CanonicalAlias,
                        "state_key": "",
                        "room_id": new_room_id,
                        "sender": requester.user.to_string(),
                        "content": {"alias": canonical_alias, },
                    },
                    ratelimit=False
                )

            yield directory_handler.send_room_alias_update_event(
                requester, new_room_id,
            )
        except SynapseError as e:
            # again I'm not really expecting this to fail, but if it does, I'd rather
            # we returned the new room to the client at this point.
            logger.error(
                "Unable to send updated alias events in new room: %s", e,
            )
Beispiel #53
0
 def parse_roomalias(self, s):
     """Parse the string given by 's' as a Room Alias and return a RoomAlias
     object."""
     return RoomAlias.from_string(s, hs=self)
Beispiel #54
0
    def handle_new_client_event(self, event, context, extra_users=[]):
        # We now need to go and hit out to wherever we need to hit out to.

        self.auth.check(event, auth_events=context.current_state)

        yield self.maybe_kick_guest_users(event, context.current_state.values())

        if event.type == EventTypes.CanonicalAlias:
            # Check the alias is acually valid (at this time at least)
            room_alias_str = event.content.get("alias", None)
            if room_alias_str:
                room_alias = RoomAlias.from_string(room_alias_str)
                directory_handler = self.hs.get_handlers().directory_handler
                mapping = yield directory_handler.get_association(room_alias)

                if mapping["room_id"] != event.room_id:
                    raise SynapseError(
                        400,
                        "Room alias %s does not point to the room" % (
                            room_alias_str,
                        )
                    )

        federation_handler = self.hs.get_handlers().federation_handler

        if event.type == EventTypes.Member:
            if event.content["membership"] == Membership.INVITE:
                event.unsigned["invite_room_state"] = [
                    {
                        "type": e.type,
                        "state_key": e.state_key,
                        "content": e.content,
                        "sender": e.sender,
                    }
                    for k, e in context.current_state.items()
                    if e.type in (
                        EventTypes.JoinRules,
                        EventTypes.CanonicalAlias,
                        EventTypes.RoomAvatar,
                        EventTypes.Name,
                    )
                ]

                invitee = UserID.from_string(event.state_key)
                if not self.hs.is_mine(invitee):
                    # TODO: Can we add signature from remote server in a nicer
                    # way? If we have been invited by a remote server, we need
                    # to get them to sign the event.

                    returned_invite = yield federation_handler.send_invite(
                        invitee.domain,
                        event,
                    )

                    event.unsigned.pop("room_state", None)

                    # TODO: Make sure the signatures actually are correct.
                    event.signatures.update(
                        returned_invite.signatures
                    )

        if event.type == EventTypes.Redaction:
            if self.auth.check_redaction(event, auth_events=context.current_state):
                original_event = yield self.store.get_event(
                    event.redacts,
                    check_redacted=False,
                    get_prev_content=False,
                    allow_rejected=False,
                    allow_none=False
                )
                if event.user_id != original_event.user_id:
                    raise AuthError(
                        403,
                        "You don't have permission to redact events"
                    )

        action_generator = ActionGenerator(self.hs)
        yield action_generator.handle_push_actions_for_event(
            event, context, self
        )

        (event_stream_id, max_stream_id) = yield self.store.persist_event(
            event, context=context
        )

        destinations = set()
        for k, s in context.current_state.items():
            try:
                if k[0] == EventTypes.Member:
                    if s.content["membership"] == Membership.JOIN:
                        destinations.add(
                            UserID.from_string(s.state_key).domain
                        )
            except SynapseError:
                logger.warn(
                    "Failed to get destination from event %s", s.event_id
                )

        with PreserveLoggingContext():
            # Don't block waiting on waking up all the listeners.
            self.notifier.on_new_room_event(
                event, event_stream_id, max_stream_id,
                extra_users=extra_users
            )

        # If invite, remove room_state from unsigned before sending.
        event.unsigned.pop("invite_room_state", None)

        federation_handler.handle_new_event(
            event, destinations=destinations,
        )