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
Beispiel #2
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)
Beispiel #3
0
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
Beispiel #4
0
    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
Beispiel #6
0
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)
Beispiel #8
0
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
Beispiel #9
0
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
Beispiel #10
0
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
Beispiel #11
0
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)
Beispiel #12
0
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://")
Beispiel #13
0
    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
Beispiel #14
0
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
Beispiel #15
0
                    "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