def test_validate_userid_signature(): ownserver = "https://ownserver.com" api = Mock() api.base_url = ownserver server_name = urlparse(ownserver).netloc signer = make_signer() user = Mock(spec=User) user.api = api user.user_id = f"@{to_normalized_address(signer.address)}:{server_name}" user.displayname = None user.get_display_name = Mock(side_effect=lambda: user.displayname) # displayname is None, get_display_name will be called but continue to give None with pytest.raises(AssertionError): assert validate_userid_signature(user) assert user.get_display_name.call_count == 0 # successfuly recover valid displayname user.displayname = encode_hex(signer.sign(user.user_id.encode())) assert validate_userid_signature(user) == signer.address assert user.get_display_name.call_count == 0 # assert another call will cache the result and avoid wasteful get_display_name call assert validate_userid_signature(user) == signer.address assert user.get_display_name.call_count == 0 # non-hex displayname should be gracefully handled user.displayname = "random gibberish" assert validate_userid_signature(user) is None assert user.get_display_name.call_count == 0 # valid signature but from another user should also return None user.displayname = encode_hex(make_signer().sign(user.user_id.encode())) assert validate_userid_signature(user) is None assert user.get_display_name.call_count == 0 # same address, but different user_id, even if valid, should be rejected # (prevent personification) user.displayname = encode_hex(signer.sign(user.user_id.encode())) user.user_id = f"@{to_normalized_address(signer.address)}.deadbeef:{server_name}" assert validate_userid_signature(user) is None assert user.get_display_name.call_count == 0 # but non-default but valid user_id should be accepted user.displayname = encode_hex(signer.sign(user.user_id.encode())) assert validate_userid_signature(user) == signer.address assert user.get_display_name.call_count == 0 # non-compliant user_id shouldn't even call get_display_name user.user_id = f"@my_user:{server_name}" assert validate_userid_signature(user) is None assert user.get_display_name.call_count == 0
def test_leave_after_member_join(mock_matrix, room_with_members): # create a valid room with one external member room = room_with_members[0] user = create_new_users_for_address(make_signer())[0] room.client = mock_matrix._client mock_matrix._client.rooms[room.room_id] = room # response showing that user from another address joins the room response_list = list() member_join = { "room_id": 9, "type": "m.room.member", "state_key": user.user_id, "content": {"membership": "join", "displayname": user.displayname}, } response = { "presence": {"events": {}}, "to_device": {"events": {}}, "rooms": { "invite": {}, "leave": {}, "join": { room.room_id: { "state": {"events": {}}, "ephemeral": {"events": {}}, "timeline": {"prev_batch": "PREV_SYNC_TOKEN", "events": [member_join]}, } }, }, } response_list.append(response) mock_matrix._client._handle_responses(response_list)
def create_logged_in_client(server: str) -> Tuple[GMatrixClient, Signer]: client = make_client(ignore_messages, ignore_member_join, [server]) signer = factories.make_signer() login(client, signer) return client, signer
def _connect(self, server_name: str, server_url: str) -> Optional[Tuple[str, GMatrixHttpApi]]: log.debug("Connecting", server=server_name) api = GMatrixHttpApi(server_url) username = self._username password = self._password if server_name != self._own_server_name: signer = make_signer() username = str(to_normalized_address(signer.address)) password = encode_hex(signer.sign(server_name.encode())) try: response = api.login( "m.login.password", user=username, password=password, device_id="room_ensurer" ) api.token = response["access_token"] except MatrixHttpLibError: log.warning("Could not connect to server", server_url=server_url) return None except MatrixRequestError: log.warning("Failed to login to server", server_url=server_url) return None log.debug("Connected", server=server_name) return server_name, api
def create_logged_in_client(server: str) -> Tuple[GMatrixClient, Signer]: client = make_client([server]) signer = factories.make_signer() login(client, signer) return client, signer
def test_get_offline_address_metadata(api_url: str): address = make_signer().address checksummed_address = to_checksum_address(address) url = f"{api_url}/v1/address/{checksummed_address}/metadata" response = requests.get(url) assert response.status_code == 404 response_body = response.json() assert response_body["error_details"]["seen_offline_since"] is not None
def new_user(matrix_server_url: str) -> LoggedUser: client = GMatrixClient(time_messages, ignore_member_join, matrix_server_url) signer = factories.make_signer() with logtime(USER) as details: user = login(client, signer) details["user_id"] = user.user_id return LoggedUser(client, signer, user)
def new_client(server: "ParsedURL") -> GMatrixClient: server_name = server.netloc signer = make_signer() username = str(to_normalized_address(signer.address)) password = encode_hex(signer.sign(server_name.encode())) client = GMatrixClient(server) client.login(username, password, sync=False) return client
def create_new_users_for_address(signer=None, number_of_users=1): users = list() if signer is None: signer = make_signer() for i in range(number_of_users): user_id = f"@{signer.address_hex.lower()}:server{i}" signature_bytes = signer.sign(user_id.encode()) signature_hex = encode_hex(signature_bytes) user = User(api=None, user_id=user_id, displayname=signature_hex) users.append(user) return users
def new_client(handle_messages_callback: Callable[[MatrixSyncMessages], bool], server: "ParsedURL") -> GMatrixClient: server_name = server.netloc signer = make_signer() username = str(to_normalized_address(signer.address)) password = encode_hex(signer.sign(server_name.encode())) client = GMatrixClient(handle_messages_callback, server) client.login(username, password, sync=False) return client
def test_get_address_metadata(api_url: str, api_sut: PFSApi): address = make_signer().address checksummed_address = to_checksum_address(address) url = f"{api_url}/v1/address/{checksummed_address}/metadata" response = requests.get(url) assert response.status_code == 404 user_manager = api_sut.pathfinding_service.matrix_listener.user_manager user_manager.reachabilities[address] = AddressReachability.REACHABLE response = requests.get(url) assert response.status_code == 200 assert response.json()["user_id"] == get_user_id_from_address(address)
def test_get_address_metadata(api_url: str, api_sut: PFSApi): address = make_signer().address checksummed_address = to_checksum_address(address) url = f"{api_url}/v1/address/{checksummed_address}/metadata" user_manager = api_sut.pathfinding_service.matrix_listener.user_manager user_manager.reachabilities[address] = AddressReachability.REACHABLE response = requests.get(url) response_body = response.json() assert response.status_code == 200 assert response_body["displayname"] is None assert response_body["user_id"] == get_user_id_from_address(address) assert response_body["capabilities"].startswith("mxc://")
def _connect(self, server_name: str, server_url: str) -> Tuple[str, GMatrixHttpApi]: log.debug("Connecting", server=server_name) api = GMatrixHttpApi(server_url) username = self._username password = self._password if server_name != self._own_server_name: signer = make_signer() username = str(to_normalized_address(signer.address)) password = encode_hex(signer.sign(server_name.encode())) response = api.login("m.login.password", user=username, password=password) api.token = response["access_token"] log.debug("Connected", server=server_name) return server_name, api
def test_login_or_register_default_user(): ownserver = 'https://ownserver.com' api = Mock() api.base_url = ownserver server_name = urlparse(ownserver).netloc client = Mock() client.api = api # login will assert user is hex-encoded address and pw is server_name signed with that address def mock_login(user, pw, sync=True): recovered = recover(data=server_name.encode(), signature=decode_hex(pw)) if recovered != to_canonical_address(user): raise MatrixRequestError(403) client.user_id = f'@{user}:{server_name}' client.login = Mock(side_effect=mock_login) MockUser = create_autospec(User) client.get_user = Mock(side_effect=lambda user_id: MockUser(api, user_id)) signer = make_signer() user = login_or_register( client=client, signer=signer, ) # client.user_id will be set by login assert client.user_id.startswith( f'@{to_normalized_address(signer.address)}') # login_or_register returns our own user object assert isinstance(user, User) # get_user must have been called once to generate above user client.get_user.assert_called_once_with(client.user_id) # assert set_display_name was called once on ourselves assert user.set_display_name.call_count == 1 # assert the user.set_display_name was called with the signature of the user_id assert recover( data=client.user_id.encode(), signature=decode_hex(user.set_display_name.call_args[0][0]), ) == signer.address
"displayname": "Alice Margatroid", "membership": "join", }, "event_id": "$143273582443PhrSn:example.org", "origin_server_ts": 1432735824653, "room_id": "!someroom:invalidserver", "sender": invite_user.user_id, "state_key": invite_user.user_id, "type": "m.room.member", "unsigned": {"age": 1234}, }, ] } @pytest.mark.parametrize("signer", [make_signer()]) def test_reject_invite_of_invalid_room( mock_matrix: MatrixTransport, monkeypatch, signer, invite_state ): invalid_room_id = RoomID("!someroom:invalidserver") user = create_new_users_for_address(signer)[0] mock_matrix._displayname_cache.warm_users([user]) leave_room_called = False def mock_leave_room(room_id): nonlocal leave_room_called if room_id == invalid_room_id: leave_room_called = True