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"
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))
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"
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"}
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}))
def test_process_result_req_bad_format(req): ch = BaseClientHandshake() with pytest.raises(InvalidMessageError): ch.process_result_req(packb(req))
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))