async def test_authenticated_handshake_no_longer_supported(
        backend, server_factory, alice):
    async with server_factory(backend.handle_client) as server:
        stream = await server.connection_factory()
        transport = await Transport.init_for_client(stream, "127.0.0.1")

        challenge_req = await transport.recv()
        challenge = unpackb(challenge_req)["challenge"]
        answer = alice.signing_key.sign(challenge)
        answer_req = {
            "handshake": "answer",
            "client_api_version": ApiVersion(1, 3),
            "type": "AUTHENTICATED",
            "organization_id": str(alice.organization_id),
            "device_id": str(alice.device_id),
            "rvk": alice.root_verify_key.encode(),
            "answer": answer,
        }

        await transport.send(packb(answer_req))
        result_req = await transport.recv()
        assert unpackb(result_req) == {
            "handshake": "result",
            "result": "bad_protocol",
            "help": "{'type': ['Unsupported value: AUTHENTICATED']}",
        }
示例#2
0
async def test_authenticated_handshake_bad_versions(
    backend, server_factory, alice, mock_api_versions
):
    ch = APIV1_AuthenticatedClientHandshake(
        alice.organization_id, alice.device_id, alice.signing_key, alice.root_verify_key
    )

    async with server_factory(backend.handle_client) as server:
        stream = server.connection_factory()
        transport = await Transport.init_for_client(stream, server.addr.hostname)

        challenge_req = await transport.recv()
        answer_req = ch.process_challenge_req(challenge_req)

        # Alter answer
        answer_dict = unpackb(answer_req)
        answer_dict["client_api_version"] = ApiVersion(3, 22)
        answer_req = packb(answer_dict)

        await transport.send(answer_req)
        result_req = await transport.recv()

        with pytest.raises(InvalidMessageError) as context:
            ch.process_result_req(result_req)
        assert "bad_protocol" in str(context.value)
        assert "{1.22}" in str(context.value)
示例#3
0
async def test_handshake_incompatible_version(backend, server_factory):
    async with server_factory(backend.handle_client) as server:
        stream = server.connection_factory()
        transport = await Transport.init_for_client(stream,
                                                    server.addr.hostname)

        incompatible_version = ApiVersion(API_VERSION.version + 1, 0)
        await transport.recv()  # Get challenge
        req = {
            "handshake": "answer",
            "type": "anonymous",
            "client_api_version": incompatible_version,
            "organization_id": OrganizationID("Org"),
            "token": "whatever",
        }
        await transport.send(packb(req))
        result_req = await transport.recv()
        assert unpackb(result_req) == {
            "handshake":
            "result",
            "result":
            "bad_protocol",
            "help":
            "No overlap between client API versions {3.0} and backend API versions {"
            + str(API_VERSION) + ", 1.3}",
        }
示例#4
0
    async def _handle_client_loop(self, transport, client_ctx):
        raw_req = None
        while True:
            # raw_req can be already defined if we received a new request
            # while processing a command
            raw_req = raw_req or await transport.recv()
            req = unpackb(raw_req)
            if get_log_level() <= LOG_LEVEL_DEBUG:
                client_ctx.logger.debug("Request",
                                        req=_filter_binary_fields(req))
            try:
                cmd = req.get("cmd", "<missing>")
                if not isinstance(cmd, str):
                    raise KeyError()

                if isinstance(client_ctx, AdministrationClientContext):
                    cmd_func = self.administration_cmds[cmd]

                elif isinstance(client_ctx, LoggedClientContext):
                    cmd_func = self.logged_cmds[cmd]

                else:
                    cmd_func = self.anonymous_cmds[cmd]

            except KeyError:
                rep = {
                    "status": "unknown_command",
                    "reason": "Unknown command"
                }

            else:
                try:
                    rep = await cmd_func(client_ctx, req)

                except InvalidMessageError as exc:
                    rep = {
                        "status": "bad_message",
                        "errors": exc.errors,
                        "reason": "Invalid message.",
                    }

                except ProtocolError as exc:
                    rep = {"status": "bad_message", "reason": str(exc)}

                except CancelledByNewRequest as exc:
                    # Long command handling such as message_get can be cancelled
                    # when the peer send a new request
                    raw_req = exc.new_raw_req
                    continue

            if get_log_level() <= LOG_LEVEL_DEBUG:
                client_ctx.logger.debug("Response",
                                        rep=_filter_binary_fields(req))
            else:
                client_ctx.logger.info("Request",
                                       cmd=cmd,
                                       status=rep["status"])
            raw_rep = packb(rep)
            await transport.send(raw_rep)
            raw_req = None
示例#5
0
async def test_bad_cmd(alice_backend_sock):
    await alice_backend_sock.send(packb({"cmd": "dummy"}))
    rep = await alice_backend_sock.recv()
    assert unpackb(rep) == {
        "status": "unknown_command",
        "reason": "Unknown command"
    }
示例#6
0
async def check_allowed_cmds(backend_sock, cmds):
    for cmd in cmds:
        if cmd == "events_listen":
            # Must pass wait option otherwise backend will hang forever
            await backend_sock.send(packb({"cmd": cmd, "wait": False}))
        else:
            await backend_sock.send(packb({"cmd": cmd}))
        rep = await backend_sock.recv()
        assert unpackb(rep)["status"] != "unknown_command"
示例#7
0
async def test_handshake_invalid_format(backend, server_factory):
    async with server_factory(backend.handle_client) as server:
        stream = await server.connection_factory()
        transport = await Transport.init_for_client(stream, "127.0.0.1")

        await transport.recv()  # Get challenge
        req = {"handshake": "dummy", "client_api_version": API_VERSION}
        await transport.send(packb(req))
        result_req = await transport.recv()
        assert unpackb(result_req) == {
            "handshake": "result",
            "result": "bad_protocol",
            "help": "{'handshake': ['Invalid value, should be `answer`']}",
        }
示例#8
0
def test_build_bad_outcomes(alice, method, expected_result):
    sh = ServerHandshake()
    sh.build_challenge_req()
    answer = {
        "handshake": "answer",
        "type": HandshakeType.AUTHENTICATED.value,
        "client_api_version": API_V2_VERSION,
        "organization_id": alice.organization_id,
        "device_id": alice.device_id,
        "rvk": alice.root_verify_key.encode(),
        "answer": alice.signing_key.sign(sh.challenge),
    }
    sh.process_answer_req(packb(answer))
    req = getattr(sh, method)()
    assert unpackb(req) == {"handshake": "result", "result": expected_result, "help": ANY}
示例#9
0
    async def _handle_client_websocket_loop(self, transport: Transport,
                                            client_ctx) -> NoReturn:
        # Retrieve the allowed commands according to api version and auth type
        api_cmds = self.apis[client_ctx.TYPE]

        raw_req = None
        while True:
            # raw_req can be already defined if we received a new request
            # while processing a command
            raw_req = raw_req or await transport.recv()
            rep: dict
            req = unpackb(raw_req)
            try:
                cmd = req.get("cmd", "<missing>")
                if not isinstance(cmd, str):
                    raise KeyError()

                cmd_func = api_cmds[cmd]

            except KeyError:
                rep = {
                    "status": "unknown_command",
                    "reason": "Unknown command"
                }

            else:
                try:
                    rep = await cmd_func(client_ctx, req)

                except InvalidMessageError as exc:
                    rep = {
                        "status": "bad_message",
                        "errors": exc.errors,
                        "reason": "Invalid message.",
                    }

                except ProtocolError as exc:
                    rep = {"status": "bad_message", "reason": str(exc)}

                except CancelledByNewRequest as exc:
                    # Long command handling such as message_get can be cancelled
                    # when the peer send a new request
                    raw_req = exc.new_raw_req
                    continue
            client_ctx.logger.info("Request", cmd=cmd, status=rep["status"])
            raw_rep = packb(rep)
            await transport.send(raw_rep)
            raw_req = None
示例#10
0
async def test_anonymous_handshake_invalid_format(backend, server_factory):
    async with server_factory(backend.handle_client) as server:
        stream = server.connection_factory()
        transport = await Transport.init_for_client(stream, server.addr.hostname)

        await transport.recv()  # Get challenge
        req = {
            "handshake": "foo",
            "type": "anonymous",
            "client_api_version": ApiVersion(1, 1),
            "organization_id": "zob",
        }
        await transport.send(packb(req))
        result_req = await transport.recv()
        assert unpackb(result_req) == {
            "handshake": "result",
            "result": "bad_protocol",
            "help": "{'handshake': ['Invalid value, should be `answer`']}",
        }
示例#11
0
async def test_administration_handshake_no_longer_supported(
        backend, server_factory):
    async with server_factory(backend.handle_client) as server:
        stream = await server.connection_factory()
        transport = await Transport.init_for_client(stream, "127.0.0.1")

        await transport.recv()
        answer_req = {
            "handshake": "answer",
            "client_api_version": ApiVersion(1, 3),
            "type": "ADMINISTRATION",
            "token": backend.config.administration_token,
        }

        await transport.send(packb(answer_req))
        result_req = await transport.recv()
        assert unpackb(result_req) == {
            "handshake": "result",
            "result": "bad_protocol",
            "help": "{'type': ['Unsupported value: ADMINISTRATION']}",
        }
示例#12
0
async def test_handshake_incompatible_version(backend, server_factory):
    async with server_factory(backend.handle_client) as server:
        stream = await server.connection_factory()
        transport = await Transport.init_for_client(stream, "127.0.0.1")

        incompatible_version = ApiVersion(API_VERSION.version + 1, 0)
        await transport.recv()  # Get challenge
        req = {
            "handshake": "answer",
            "type": "anonymous",
            "client_api_version": incompatible_version,
            "organization_id": "Org",
            "token": "whatever",
        }
        await transport.send(packb(req))
        result_req = await transport.recv()
        assert unpackb(result_req) == {
            "handshake":
            "result",
            "result":
            "bad_protocol",
            "help":
            f"No overlap between client API versions {{{incompatible_version}}} and backend API versions {{{', '.join(map(str, ServerHandshake.SUPPORTED_API_VERSIONS))}}}",
        }
示例#13
0
async def test_connection(alice_backend_sock):
    await alice_backend_sock.send(packb({"cmd": "ping", "ping": "42"}))
    rep = await alice_backend_sock.recv()
    assert unpackb(rep) == {"status": "ok", "pong": "42"}