Пример #1
0
    def __init__(
        self,
        private_key: PrivateKey,
        chain_id: ChainID,
        device_id: DeviceIDs,
        message_received_callback: Callable[[Message], None],
        servers: Optional[List[str]] = None,
    ) -> None:
        super().__init__()

        self.chain_id = chain_id
        self.device_id = device_id
        self.message_received_callback = message_received_callback
        self._displayname_cache = DisplayNameCache()
        self.startup_finished = AsyncResult()
        self._client_manager = ClientManager(
            available_servers=servers,
            device_id=self.device_id,
            chain_id=self.chain_id,
            private_key=private_key,
            handle_matrix_sync=self._handle_matrix_sync,
        )

        self.base_url = self._client.api.base_url
        self.user_manager = MultiClientUserAddressManager(
            client=self._client,
            displayname_cache=self._displayname_cache,
        )

        self._rate_limiter = RateLimiter(
            allowed_bytes=MATRIX_RATE_LIMIT_ALLOWED_BYTES,
            reset_interval=MATRIX_RATE_LIMIT_RESET_INTERVAL,
        )
Пример #2
0
def test_client_manager_start(get_accounts, get_private_key):
    server_urls = [f"https://example0{i}.com" for i in range(5)]

    (c1, ) = get_accounts(1)
    private_key = get_private_key(c1)
    client_mock = Mock()
    client_mock.api.base_url = "https://example00.com"
    client_mock.user_id = "1"
    client_mock.sync_worker = AsyncResult()
    start_client_counter = 0

    def mock_start_client(server_url: str):  # pylint: disable=unused-argument
        nonlocal start_client_counter
        client_mock.sync_worker = AsyncResult()
        start_client_counter += 1
        return client_mock

    with patch.multiple(
            "raiden_libs.matrix",
            make_client=Mock(return_value=client_mock),
            get_matrix_servers=Mock(return_value=server_urls),
            login=Mock(),
            join_broadcast_room=Mock(),
    ):
        client_manager = ClientManager(
            available_servers=[f"https://example0{i}.com" for i in range(5)],
            broadcast_room_alias_prefix="_service",
            chain_id=ChainID(1),
            device_id=DeviceIDs.PFS,
            private_key=private_key,
            handle_matrix_sync=lambda s: True,
        )

        client_manager._start_client = mock_start_client  # pylint: disable=protected-access

        assert client_manager.known_servers == server_urls

        uam = MultiClientUserAddressManager(
            client=client_manager.main_client,
            displayname_cache=DisplayNameCache(),
        )
        uam.start()
        client_manager.user_manager = uam
        client_manager.stop_event.clear()

        gevent.spawn(client_manager.connect_client_forever,
                     client_mock.api.base_url)
        gevent.sleep(2)
        client_mock.sync_worker.set(True)
        gevent.sleep(2)
        client_manager.stop_event.set()
        client_mock.sync_worker.set(True)

        assert start_client_counter == 2
Пример #3
0
def test_filter_presences_by_client(presence_event, server_index):

    server_urls = [f"https://example0{i}.com" for i in range(5)]

    uuids_to_callbacks = {}
    server_url_to_clients = {}
    server_url_to_processed_presence = []

    def _mock_add_presence_listener(listener):
        uuid = uuid1()
        uuids_to_callbacks[uuid] = listener
        return uuid

    def _mock_remove_presence_listener(listener_id):
        uuids_to_callbacks.pop(listener_id)

    def _mock_presence_listener(
            self,
            event: Dict[str, Any],
            presence_update_id: int  # pylint: disable=unused-argument
    ) -> None:
        server_url_to_processed_presence.append(event)

    for server_url in server_urls:
        client = Mock()
        client.api.base_url = server_url
        client.add_presence_listener = _mock_add_presence_listener
        client.remove_presence_listener = _mock_remove_presence_listener
        server_url_to_clients[server_url] = client

    main_client = server_url_to_clients.pop(server_urls[0])

    with mock.patch.object(MultiClientUserAddressManager,
                           "_presence_listener",
                           new=_mock_presence_listener):
        uam = MultiClientUserAddressManager(
            client=main_client,
            displayname_cache=DisplayNameCache(),
        )
        uam.start()

        for client in server_url_to_clients.values():
            uam.add_client(client)

        # call presence listener on all clients
        for listener in uuids_to_callbacks.values():
            listener(presence_event, 0)

        # only the respective client should forward the presence to the uam
        expected_presences = 1

        assert len(server_url_to_processed_presence) == expected_presences

        # drop the client of the last homeserver in the list
        # and remove listener
        client = server_url_to_clients[server_urls[-1]]
        uam.remove_client(client)

        # only call the listener of the main client
        # if the presence event comes from the server with the dropped client
        # it should also consume the presence, otherwise not
        uuids_to_callbacks[uam._listener_id](presence_event, 0)  # pylint: disable=protected-access

        if server_index in [0, len(server_urls) - 1]:
            expected_presences += 1

        assert len(server_url_to_processed_presence) == expected_presences
Пример #4
0
class MatrixListener(gevent.Greenlet):
    # pylint: disable=too-many-instance-attributes
    def __init__(
        self,
        private_key: PrivateKey,
        chain_id: ChainID,
        device_id: DeviceIDs,
        message_received_callback: Callable[[Message], None],
        servers: Optional[List[str]] = None,
    ) -> None:
        super().__init__()

        self.chain_id = chain_id
        self.device_id = device_id
        self.message_received_callback = message_received_callback
        self._displayname_cache = DisplayNameCache()
        self.startup_finished = AsyncResult()
        self._client_manager = ClientManager(
            available_servers=servers,
            device_id=self.device_id,
            chain_id=self.chain_id,
            private_key=private_key,
            handle_matrix_sync=self._handle_matrix_sync,
        )

        self.base_url = self._client.api.base_url
        self.user_manager = MultiClientUserAddressManager(
            client=self._client,
            displayname_cache=self._displayname_cache,
        )

        self._rate_limiter = RateLimiter(
            allowed_bytes=MATRIX_RATE_LIMIT_ALLOWED_BYTES,
            reset_interval=MATRIX_RATE_LIMIT_RESET_INTERVAL,
        )

    @property
    def _client(self) -> GMatrixClient:
        return self._client_manager.main_client

    @property
    def server_url_to_other_clients(self) -> Dict[str, GMatrixClient]:
        return self._client_manager.server_url_to_other_clients

    def _run(self) -> None:  # pylint: disable=method-hidden

        self.user_manager.start()
        self._client_manager.start(self.user_manager)

        def set_startup_finished() -> None:
            self._client.processed.wait()
            self.startup_finished.set()

        startup_finished_greenlet = gevent.spawn(set_startup_finished)
        try:
            assert self._client.sync_worker
            self._client.sync_worker.get()
        finally:
            self._client_manager.stop()
            gevent.joinall({startup_finished_greenlet},
                           raise_error=True,
                           timeout=0)

    def _handle_matrix_sync(self, messages: MatrixSyncMessages) -> bool:
        all_messages: List[Message] = list()
        for room, room_messages in messages:
            if room is not None:
                # Ignore room messages
                # This will only handle to-device messages
                continue

            for text in room_messages:
                all_messages.extend(self._handle_message(room, text))

        log.debug("Incoming messages", messages=all_messages)

        for message in all_messages:
            self.message_received_callback(message)

        return True

    def _handle_message(self, room: Optional[Room],
                        message: MatrixMessage) -> List[SignedMessage]:
        """Handle a single Matrix message.

        The matrix message is expected to be a NDJSON, and each entry should be
        a valid JSON encoded Raiden message.

        If `room` is None this means we are processing a `to_device` message
        """
        is_valid_type = (message["type"] == "m.room.message"
                         and message["content"]["msgtype"]
                         == MatrixMessageType.TEXT.value)
        if not is_valid_type:
            return []

        sender_id = message["sender"]
        self._displayname_cache.warm_users([User(self._client.api, sender_id)])
        # handles the "Could not get 'display_name' for user" case

        try:
            displayname = self._displayname_cache.userid_to_displayname[
                sender_id]
        except KeyError:
            log.exception("Could not warm display cache", peer_user=sender_id)
            return []

        peer_address = validate_user_id_signature(sender_id, displayname)

        if not peer_address:
            log.debug(
                "Message from invalid user displayName signature",
                peer_user=sender_id,
                room=room,
            )
            return []

        data = message["content"]["body"]
        if not isinstance(data, str):
            log.warning(
                "Received message body not a string",
                peer_user=sender_id,
                peer_address=to_checksum_address(peer_address),
                room=room,
            )
            return []

        messages = deserialize_messages(data=data,
                                        peer_address=peer_address,
                                        rate_limiter=self._rate_limiter)
        if not messages:
            return []

        return messages