async def test_api_user_get_bad_msg(alice_backend_sock, bad_msg):
    await alice_backend_sock.send(packb({"cmd": "user_get", **bad_msg}))
    raw_rep = await alice_backend_sock.recv()
    rep = user_get_serializer.rep_loads(raw_rep)
    assert rep["status"] == "bad_message"
示例#2
0
    async def _handle_client_websocket(self, stream, request):
        selected_logger = logger

        try:
            transport = await Transport.init_for_server(stream, upgrade_request=request)

        except TransportClosedByPeer as exc:
            selected_logger.info("Connection dropped: client has left", reason=str(exc))
            return

        except TransportError as exc:
            selected_logger.info("Connection dropped: websocket error", reason=str(exc))
            return

        selected_logger = transport.logger

        try:
            client_ctx, error_infos = await do_handshake(self, transport)
            if not client_ctx:
                # Invalid handshake
                # TODO Fragile test based on reason, make it more robust
                if error_infos and error_infos.get("reason", "") == "Expired organization":
                    organization_id = error_infos["organization_id"]
                    await self.events.send(
                        BackendEvent.ORGANIZATION_EXPIRED, organization_id=organization_id
                    )
                selected_logger.info("Connection dropped: bad handshake", **error_infos)
                return

            selected_logger = client_ctx.logger
            selected_logger.info("Connection established")

            if isinstance(client_ctx, AuthenticatedClientContext):
                with trio.CancelScope() as cancel_scope:
                    with self.event_bus.connection_context() as client_ctx.event_bus_ctx:

                        def _on_revoked(event, organization_id, user_id):
                            if (
                                organization_id == client_ctx.organization_id
                                and user_id == client_ctx.user_id
                            ):
                                cancel_scope.cancel()

                        def _on_expired(event, organization_id):
                            if organization_id == client_ctx.organization_id:
                                cancel_scope.cancel()

                        client_ctx.event_bus_ctx.connect(BackendEvent.USER_REVOKED, _on_revoked)
                        client_ctx.event_bus_ctx.connect(
                            BackendEvent.ORGANIZATION_EXPIRED, _on_expired
                        )
                        await self._handle_client_websocket_loop(transport, client_ctx)

            elif isinstance(client_ctx, InvitedClientContext):
                await self.invite.claimer_joined(
                    organization_id=client_ctx.organization_id,
                    greeter=client_ctx.invitation.greeter_user_id,
                    token=client_ctx.invitation.token,
                )
                try:
                    with trio.CancelScope() as cancel_scope:
                        with self.event_bus.connection_context() as event_bus_ctx:

                            def _on_invite_status_changed(
                                event, organization_id, greeter, token, status
                            ):
                                if (
                                    status == InvitationStatus.DELETED
                                    and organization_id == client_ctx.organization_id
                                    and token == client_ctx.invitation.token
                                ):
                                    cancel_scope.cancel()

                            event_bus_ctx.connect(
                                BackendEvent.INVITE_STATUS_CHANGED, _on_invite_status_changed
                            )
                            await self._handle_client_websocket_loop(transport, client_ctx)

                except CloseInviteConnection:
                    # If the invitation has been deleted after the invited handshake,
                    # invitation commands can raise an InvitationAlreadyDeletedError.
                    # This error is converted to a CloseInviteConnection
                    # The connection shall be closed due to a BackendEvent.INVITE_STATUS_CHANGED
                    # but nothing garantie that the event will be handled before cmd_func
                    # errors returns. If this happen, let's also close the connection
                    pass

                finally:
                    with trio.CancelScope(shield=True):
                        await self.invite.claimer_left(
                            organization_id=client_ctx.organization_id,
                            greeter=client_ctx.invitation.greeter_user_id,
                            token=client_ctx.invitation.token,
                        )

            else:
                await self._handle_client_websocket_loop(transport, client_ctx)

            await transport.aclose()

        except TransportClosedByPeer as exc:
            selected_logger.info("Connection dropped: client has left", reason=str(exc))

        except (TransportError, MessageSerializationError) as exc:
            rep = {"status": "invalid_msg_format", "reason": "Invalid message format"}
            try:
                await transport.send(packb(rep))
            except TransportError:
                pass
            await transport.aclose()
            selected_logger.info("Connection dropped: invalid data", reason=str(exc))
示例#3
0
async def test_api_user_find(access_testbed, organization_factory,
                             local_device_factory):
    binder, org, godfrey1, sock = access_testbed

    # Populate with cool guys
    for name in [
            "Philippe@p1", "Philippe@p2", "Mike@p1", "Blacky@p1",
            "Philip_J_Fry@p1"
    ]:
        device = local_device_factory(name, org)
        await binder.bind_device(device, certifier=godfrey1)

    await binder.bind_revocation("Philip_J_Fry", certifier=godfrey1)

    # Also create homonyme in different organization, just to be sure...
    other_org = organization_factory("FilmMark")
    other_device = local_device_factory("Philippe@p1", other_org)
    await binder.bind_organization(other_org, other_device)

    # Test exact match
    rep = await user_find(sock, query="Mike")
    assert rep == {
        "status": "ok",
        "results": ["Mike"],
        "per_page": 100,
        "page": 1,
        "total": 1
    }

    # Test partial search
    rep = await user_find(sock, query="Phil")
    assert rep == {
        "status": "ok",
        "results": ["Philip_J_Fry", "Philippe"],
        "per_page": 100,
        "page": 1,
        "total": 2,
    }

    # Test case insensitivity
    rep = await user_find(sock, query="phil")
    assert rep == {
        "status": "ok",
        "results": ["Philip_J_Fry", "Philippe"],
        "per_page": 100,
        "page": 1,
        "total": 2,
    }

    # Test partial search while omitting revoked users
    rep = await user_find(sock, query="Phil", omit_revoked=True)
    assert rep == {
        "status": "ok",
        "results": ["Philippe"],
        "per_page": 100,
        "page": 1,
        "total": 1
    }

    # Test partial search with invalid query
    rep = await user_find(sock, query="p*", omit_revoked=True)
    assert rep == {
        "status": "ok",
        "results": [],
        "per_page": 100,
        "page": 1,
        "total": 0
    }

    # Test pagination
    rep = await user_find(sock, query="Phil", page=1, per_page=1)
    assert rep == {
        "status": "ok",
        "results": ["Philip_J_Fry"],
        "per_page": 1,
        "page": 1,
        "total": 2,
    }

    # Test out of pagination
    rep = await user_find(sock, query="Phil", page=2, per_page=5)
    assert rep == {
        "status": "ok",
        "results": [],
        "per_page": 5,
        "page": 2,
        "total": 2
    }

    # Test no params
    rep = await user_find(sock)
    assert rep == {
        "status": "ok",
        "results": ["Blacky", "Godfrey", "Mike", "Philip_J_Fry", "Philippe"],
        "per_page": 100,
        "page": 1,
        "total": 5,
    }

    # Test omit revoked users
    rep = await user_find(sock, omit_revoked=True)
    assert rep == {
        "status": "ok",
        "results": ["Blacky", "Godfrey", "Mike", "Philippe"],
        "per_page": 100,
        "page": 1,
        "total": 4,
    }

    # Test bad params
    for bad in [{
            "query": 42
    }, {
            "page": 0
    }, {
            "per_page": 0
    }, {
            "per_page": 101
    }]:
        await sock.send(packb({"cmd": "user_find", **bad}))
        raw_rep = await sock.recv()
        rep = apiv1_user_find_serializer.rep_loads(raw_rep)
        assert rep["status"] == "bad_message"
示例#4
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"}
示例#5
0
def test_process_result_req_bad_outcome(result, exc_cls):
    ch = BaseClientHandshake()
    with pytest.raises(exc_cls):
        ch.process_result_req(packb({"handshake": "result", "result": result}))
示例#6
0
def test_process_result_req_bad_format(req):
    ch = BaseClientHandshake()
    with pytest.raises(InvalidMessageError):
        ch.process_result_req(packb(req))
示例#7
0
def test_process_challenge_req_bad_format(alice, req):
    ch = AuthenticatedClientHandshake(
        alice.organization_id, alice.device_id, alice.signing_key, alice.root_verify_key
    )
    with pytest.raises(InvalidMessageError):
        ch.process_challenge_req(packb(req))