diff --git a/bumper/__init__.py b/bumper/__init__.py index 31cff7e..ad3e3be 100644 --- a/bumper/__init__.py +++ b/bumper/__init__.py @@ -109,7 +109,7 @@ async def start() -> None: global web_server web_server = WebServer(web_server_bindings) global xmpp_server - xmpp_server = XMPPServer((bumper_listen, xmpp_listen_port)) + xmpp_server = XMPPServer(bumper_listen, xmpp_listen_port) # Start XMPP Server asyncio.create_task(xmpp_server.start_async_server()) @@ -151,7 +151,7 @@ async def shutdown() -> None: if mqtt_server.state == "started": await mqtt_server.shutdown() if xmpp_server.server: - if xmpp_server.server._serving: + if xmpp_server.server.is_serving: xmpp_server.server.close() await xmpp_server.server.wait_closed() diff --git a/bumper/web/auth_util.py b/bumper/web/auth_util.py index 2924941..d683b2b 100644 --- a/bumper/web/auth_util.py +++ b/bumper/web/auth_util.py @@ -275,6 +275,7 @@ def _auth_any( checkToken = _check_token( apptype, countrycode, tmpuser, request.query["accessToken"] ) + assert checkToken.text isGood: dict[str, Any] = json.loads(checkToken.text) if isGood["code"] == "0000": return isGood diff --git a/bumper/web/server.py b/bumper/web/server.py index bf30703..cc231cb 100644 --- a/bumper/web/server.py +++ b/bumper/web/server.py @@ -208,7 +208,9 @@ class WebServer: response = await handler(request) if response is None: - confserverlog.warning("Response was null!") + confserverlog.warning( # type:ignore[unreachable] + "Response was null!" + ) confserverlog.warning(json.dumps(to_log)) raise HTTPNoContent @@ -218,7 +220,9 @@ class WebServer: if ( "application/octet-stream" not in response.content_type and isinstance(response, Response) + and response.body ): + assert isinstance(response.body, bytes) to_log["response"]["body"] = f"{json.loads(response.body)}" confserverlog.debug(json.dumps(to_log)) diff --git a/bumper/xmppserver.py b/bumper/xmppserver.py index 648b43d..af16cde 100644 --- a/bumper/xmppserver.py +++ b/bumper/xmppserver.py @@ -4,6 +4,8 @@ import re import ssl import uuid import xml.etree.ElementTree as ET +from asyncio import transports +from typing import Optional import bumper from bumper.db import ( @@ -22,25 +24,24 @@ boterrorlog = bumper.get_logger("boterror") class XMPPServer: server_id = "ecouser.net" - clients = [] + clients: list["XMPPAsyncClient"] = [] exit_flag = False server = None - def __init__(self, address): + def __init__(self, host: str, port: int): # Initialize bot server - self.address = address + self._host = host + self._port = port self.xmpp_protocol = lambda: XMPPServer_Protocol() async def start_async_server(self) -> None: try: - xmppserverlog.info( - f"Starting XMPP Server at {self.address[0]}:{self.address[1]}" - ) + xmppserverlog.info(f"Starting XMPP Server at {self._host}:{self._port}") loop = asyncio.get_running_loop() self.server = await loop.create_server( - self.xmpp_protocol, host=self.address[0], port=self.address[1] + self.xmpp_protocol, host=self._host, port=self._port ) self.server_coro = loop.create_task(self.server.serve_forever()) @@ -63,32 +64,34 @@ class XMPPServer: class XMPPServer_Protocol(asyncio.Protocol): client_id = None exit_flag = False - aclient = None + _client: Optional["XMPPAsyncClient"] = None - def connection_made(self, transport): - if self.aclient: # Existing client... upgrading to TLS - xmppserverlog.debug(f"Upgraded connection for {self.aclient.address}") - self.aclient.transport = transport + def connection_made(self, transport: transports.BaseTransport) -> None: + if self._client: # Existing client... upgrading to TLS + xmppserverlog.debug(f"Upgraded connection for {self._client.address}") + self._client.transport = transport else: - aclient = XMPPAsyncClient(transport) - self.aclient = aclient - XMPPServer.clients.append(aclient) - self.aclient.state = getattr(aclient, "CONNECT") - xmppserverlog.debug(f"New Connection from {aclient.address}") + client = XMPPAsyncClient(transport) + self._client = client + XMPPServer.clients.append(client) + self._client.state = getattr(client, "CONNECT") + xmppserverlog.debug(f"New Connection from {client.address}") - def connection_lost(self, error): - XMPPServer.clients.remove(self.aclient) - self.aclient._set_state("DISCONNECT") - xmppserverlog.debug( - "End Connection for ({}:{} | {})".format( - self.aclient.address[0], - self.aclient.address[1], - self.aclient.bumper_jid, + def connection_lost(self, exc: Exception | None) -> None: + if self._client: + XMPPServer.clients.remove(self._client) + self._client.set_state("DISCONNECT") + xmppserverlog.debug( + "End Connection for ({}:{} | {})".format( + self._client.address[0], + self._client.address[1], + self._client.bumper_jid, + ) ) - ) - def data_received(self, data): - self.aclient._parse_data(data) + def data_received(self, data: bytes) -> None: + if self._client: + self._client.parse_data(data) class XMPPAsyncClient: @@ -103,7 +106,7 @@ class XMPPAsyncClient: CONTROLLER = 2 TLSUpgraded = False - def __init__(self, transport): + def __init__(self, transport: transports.BaseTransport): self.type = self.UNKNOWN self.state = self.IDLE self.address = transport.get_extra_info("peername") @@ -116,7 +119,7 @@ class XMPPAsyncClient: self.log_incoming_data = True # Set to true to log sends xmppserverlog.debug(f"new client with ip {self.address}") - def send(self, command): + def send(self, command: str) -> None: try: if self.log_sent_message: xmppserverlog.debug( @@ -124,13 +127,13 @@ class XMPPAsyncClient: self.address[0], self.address[1], self.bumper_jid, command ) ) - - self.transport.write(command.encode()) + if isinstance(self.transport, transports.WriteTransport): + self.transport.write(command.encode()) except Exception as e: xmppserverlog.exception(f"{e}") - def _disconnect(self): + def _disconnect(self) -> None: try: bot = bot_get(self.uid) @@ -146,16 +149,15 @@ class XMPPAsyncClient: except Exception as e: xmppserverlog.error(f"{e}") - def _tag_strip_uri(self, tag): + def _tag_strip_uri(self, tag: str) -> str: try: if tag[0] == "{": _, _, tag = tag[1:].partition("}") - return tag - except Exception as e: xmppserverlog.error(f"{e}") + return tag - def _set_state(self, state): + def set_state(self, state: str) -> None: try: new_state = getattr(XMPPAsyncClient, state) if self.state > new_state: @@ -179,7 +181,7 @@ class XMPPAsyncClient: except Exception as e: xmppserverlog.error(f"{e}") - def _handle_ctl(self, xml, data): + def _handle_ctl(self, xml: ET.Element, data: str) -> None: try: if "roster" in data: @@ -228,7 +230,7 @@ class XMPPAsyncClient: xmppserverlog.debug( "admin username received from bot: {}".format(ctl.get("admin")) ) - XMPPServer.client_id = ctl.get("admin") + #XMPPServer.client_id = ctl.get("admin") return # forward @@ -247,7 +249,7 @@ class XMPPAsyncClient: rxmlstring = rxmlstring.replace('iq xmlns="com:ctl"', "iq") rxmlstring = rxmlstring.replace(" None: try: - if xml.get("to").find("@") == -1: # No to address + pingto = xml.get("to") + if pingto and pingto.find("@") == -1: # No to address # Ping to server - respond pingresp = ''.format( xml.get("id"), xml.get("to") @@ -266,7 +269,6 @@ class XMPPAsyncClient: self.send(pingresp) else: - pingto = xml.get("to") pingfrom = self.bumper_jid if not "from" in xml.attrib: xml.attrib["from"] = f"{pingfrom}" @@ -281,6 +283,7 @@ class XMPPAsyncClient: if ( client.bumper_jid != self.bumper_jid and client.state == client.READY + and pingto ): if client.uid.lower() in pingto.lower(): client.send(pingstring) @@ -288,7 +291,7 @@ class XMPPAsyncClient: except Exception as e: xmppserverlog.exception(f"{e}") - async def schedule_ping(self, time): + async def schedule_ping(self, time: int) -> None: if not self.state == 5: # disconnected pingstring = "".format( XMPPServer.server_id, self.bumper_jid @@ -297,7 +300,7 @@ class XMPPAsyncClient: await asyncio.sleep(time) asyncio.Task(self.schedule_ping(time)) - def _handle_result(self, xml, data): + def _handle_result(self, xml: ET.Element, data: str) -> None: try: ctl_to = xml.get("to") if not "from" in xml.attrib: @@ -321,7 +324,7 @@ class XMPPAsyncClient: adminuser = adminuser.replace(" ", "") elif "admin" in ctl[0].attrib: adminuser = ctl[0].attrib["admin"] - if not ( + if ctl_to and not ( adminuser.startswith("fuid_") or adminuser.startswith("fusername_") or bumper.use_auth @@ -367,9 +370,11 @@ class XMPPAsyncClient: for client in XMPPServer.clients: client.send(rxmlstring) - if xml.get("to").find("@") == -1: # No to address - ctl_to = xml.get("to") + to = xml.get("to") + if to and to.find("@") == -1: # No to address + ctl_to = to else: + assert ctl_to ctl_to = "{}@ecouser.net".format(ctl_to.split("@")[0]) for client in XMPPServer.clients: @@ -394,11 +399,11 @@ class XMPPAsyncClient: except Exception as e: xmppserverlog.exception(f"{e}") - def _handle_connect(self, data, xml=None): + def _handle_connect(self, data: bytes, xml: None | ET.Element = None) -> None: try: if self.state == self.CONNECT: - if xml == None: + if xml is None: # Client first connecting, send our features if data.decode("utf-8").find("jabber:client") > -1: sc = data.decode("utf-8").find("to=") @@ -414,7 +419,7 @@ class XMPPAsyncClient: ) # Send STARTTLS to client with auth mechanisms - if self.TLSUpgraded == False: + if not self.TLSUpgraded: # With STARTTLS #https://xmpp.org/rfcs/rfc3920.html self.send( 'PLAIN' @@ -438,7 +443,7 @@ class XMPPAsyncClient: xmppserverlog.error(f"Couldn't handle: {xml}") elif self.state == self.INIT: - if xml == None: + if xml is None: # Client getting session after authentication if data.decode("utf-8").find("jabber:client") > -1: # ack jabbr:client @@ -467,9 +472,9 @@ class XMPPAsyncClient: except Exception as e: xmppserverlog.exception(f"{e}") - async def _handle_starttls(self, data): + async def _handle_starttls(self, _: bytes) -> None: try: - if self.TLSUpgraded == False: + if not self.TLSUpgraded: self.TLSUpgraded = True # Set TLSUpgraded true to prevent further attempts to upgrade connection xmppserverlog.debug( "Upgrading connection with STARTTLS for {}:{}".format( @@ -497,9 +502,9 @@ class XMPPAsyncClient: except Exception as e: xmppserverlog.exception(f"{e}") - def _handle_sasl_auth(self, xml): + def _handle_sasl_auth(self, xml: ET.Element) -> None: try: - + assert xml.text saslauth = base64.b64decode(xml.text).decode("utf-8").split("/") username = saslauth[0] username = saslauth[0].split("\x00")[1] @@ -525,7 +530,7 @@ class XMPPAsyncClient: ) # Success # Client authenticated, move to next state - self._set_state("INIT") + self.set_state("INIT") else: auth = False @@ -540,7 +545,7 @@ class XMPPAsyncClient: xmppserverlog.info(f"client authenticated {self.uid}") # Client authenticated, move to next state - self._set_state("INIT") + self.set_state("INIT") # Send response self.send( @@ -556,7 +561,7 @@ class XMPPAsyncClient: except Exception as e: xmppserverlog.exception(f"{e}") - def _handle_bind(self, xml): + def _handle_bind(self, xml: ET.Element) -> None: try: bot = bot_get(self.uid) @@ -583,6 +588,7 @@ class XMPPAsyncClient: xml.get("id"), self.bumper_jid ) elif len(clientresourcexml) > 0: + assert clientresourcexml[0].text self.clientresource = clientresourcexml[0].text self.name = f"XMPP_Client_{self.clientresource}" self.bumper_jid = "{}@{}/{}".format( @@ -608,19 +614,19 @@ class XMPPAsyncClient: xml.get("id"), self.bumper_jid ) - self._set_state("BIND") + self.set_state("BIND") self.send(res) except Exception as e: xmppserverlog.exception(f"{e}") - def _handle_session(self, xml): + def _handle_session(self, xml: ET.Element) -> None: res = ''.format(xml.get("id")) - self._set_state("READY") + self.set_state("READY") self.send(res) asyncio.Task(self.schedule_ping(30)) - def _handle_presence(self, xml): + def _handle_presence(self, xml: ET.Element) -> None: if len(xml) and xml[0].tag == "status": xmppserverlog.debug( @@ -665,7 +671,7 @@ class XMPPAsyncClient: ) ) - self._set_state("DISCONNECT") + self.set_state("DISCONNECT") else: # Sometimes the android app sends these xmppserverlog.debug( @@ -676,7 +682,7 @@ class XMPPAsyncClient: # Send dummy return self.send(f' dummy ') - def _parse_data(self, data): + def parse_data(self, data: bytes) -> None: if data.decode("utf-8").startswith( "") # Close stream - self._set_state("DISCONNECT") + self.set_state("DISCONNECT") except Exception as e: xmppserverlog.exception(f"{e}") - def _handle_iq(self, xml, data): + def _handle_iq(self, xml: ET.Element, data: str) -> None: if len(xml): child = self._tag_strip_uri(xml[0].tag) @@ -796,7 +802,7 @@ class XMPPAsyncClient: elif child == "session": self._handle_session(xml) elif child == "ping": - self._handle_ping(xml, data) + self._handle_ping(xml) elif child == "query": if self.type == self.BOT: self._handle_result(xml, data) diff --git a/tests/test_xmppserver.py b/tests/test_xmppserver.py index 7f3ab62..02c9ad9 100644 --- a/tests/test_xmppserver.py +++ b/tests/test_xmppserver.py @@ -17,8 +17,7 @@ def mock_transport_extra_info(): async def test_xmpp_server(): - xmpp_address = ("127.0.0.1", 5223) - xmpp_server = XMPPServer(xmpp_address) + xmpp_server = XMPPServer("127.0.0.1", 5223) await xmpp_server.start_async_server() with LogCapture("xmppserver") as l: @@ -63,7 +62,7 @@ async def test_client_connect_no_starttls(): # Send connect stream from "client" test_data = b"" - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) # Expect 2 calls to send assert mock_send.call_count == 2 @@ -83,7 +82,7 @@ async def test_client_connect_no_starttls(): # Client sendss auth - Ignoring the starttls, we don't force this with bumper test_data = b'AGZ1aWRfdG1wdXNlcgAwL0lPU0Y1M0QwN0JBL3VzXzg5ODgwMmZkYmM0NDQxYjBiYzgxNWIxZDFjNjgzMDJl' - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) assert ( mock_send.mock_calls[0][1][0] @@ -102,7 +101,7 @@ async def test_client_end_stream(): # Send end stream from "client" test_data = b"" - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) # Expect 2 calls to send assert mock_send.call_count == 1 @@ -114,14 +113,14 @@ async def test_client_end_stream(): # Send abnormal stream from "client" test_data = b"" - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) # Reset mock calls mock_send.reset_mock() # Send blank from "client" test_data = b"" - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) async def test_client_connect_starttls_called(): @@ -134,7 +133,7 @@ async def test_client_connect_starttls_called(): # Send connect stream from "client" test_data = b"" - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) # Expect 2 calls to send assert mock_send.call_count == 2 @@ -156,7 +155,7 @@ async def test_client_connect_starttls_called(): # Send start tls from "client" test_data = b"" - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) # After upgrading connection, server tells client to proceed with auth again assert mock_tls.called @@ -165,7 +164,7 @@ async def test_client_connect_starttls_called(): # After TLS is upgraded, Client establishes session again and will auth this time # Send connect stream from "client" test_data = b"" - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) # Expect 2 calls to send assert mock_send.call_count == 2 @@ -184,7 +183,7 @@ async def test_client_connect_starttls_called(): # Client sends auth test_data = b'AGZ1aWRfdG1wdXNlcgAwL0lPU0Y1M0QwN0JBL3VzXzg5ODgwMmZkYmM0NDQxYjBiYzgxNWIxZDFjNjgzMDJl' - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) assert ( mock_send.mock_calls[0][1][0] @@ -207,7 +206,7 @@ async def test_client_init(): # Send connect stream from "client" test_data = b"" - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) # Expect 2 calls to send assert mock_send.call_count == 2 @@ -227,7 +226,7 @@ async def test_client_init(): # Send bind from "client" test_data = b'IOSF53D07BA' - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) assert ( mock_send.mock_calls[0][1][0] @@ -240,7 +239,7 @@ async def test_client_init(): # Send set session from client test_data = b'' - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) assert xmppclient.state == xmppclient.READY # client moved to READY state assert ( @@ -253,7 +252,7 @@ async def test_client_init(): # Send presence from client test_data = b'' - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) assert ( mock_send.mock_calls[0][1][0] @@ -271,7 +270,7 @@ async def test_bot_connect(): # Send connect stream from "bot" test_data = b"" - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) # Expect 2 calls to send assert mock_send.call_count == 2 @@ -291,7 +290,7 @@ async def test_bot_connect(): # Send auth from "bot" test_data = b"AEUwMDAwMDAwMDAwMDAwMDAxMjM0AGVuY3J5cHRlZF9wYXNz" - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) assert ( mock_send.mock_calls[0][1][0] @@ -314,7 +313,7 @@ async def test_bot_init(): # Send connect stream from "bot" test_data = b"" - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) # Expect 2 calls to send assert mock_send.call_count == 2 @@ -334,7 +333,7 @@ async def test_bot_init(): # Send bind from "bot" test_data = b"atom" - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) assert ( mock_send.mock_calls[0][1][0] @@ -347,7 +346,7 @@ async def test_bot_init(): # Send set session from bot test_data = b"" - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) assert xmppclient.state == xmppclient.READY # Bot moved to READY state assert ( @@ -359,7 +358,7 @@ async def test_bot_init(): # Send presence from bot test_data = b"hello world" - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) assert ( mock_send.mock_calls[0][1][0] @@ -379,7 +378,7 @@ async def test_ping_server(): # Ping from bot test_data = b'' - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) assert ( mock_send.mock_calls[0][1][0] @@ -410,7 +409,7 @@ async def test_ping_client_to_client(): # Ping from user to bot test_data = b'' - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) assert ( mock_send2.mock_calls[0][1][0] @@ -419,7 +418,7 @@ async def test_ping_client_to_client(): # Ping response from bot to user test_data = b"" - xmppclient2._parse_data(test_data) + xmppclient2.parse_data(test_data) assert ( mock_send.mock_calls[0][1][0] @@ -454,7 +453,7 @@ async def test_client_send_iq(): test_data = ( b'' ) - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) assert ( mock_send.mock_calls[0][1][0] @@ -466,7 +465,7 @@ async def test_client_send_iq(): # Bot Command test_data = b'' - xmppclient._parse_data(test_data) + xmppclient.parse_data(test_data) assert ( mock_send2.mock_calls[0][1][0] @@ -478,7 +477,7 @@ async def test_client_send_iq(): # Bot response to query test_data = b'' - xmppclient2._parse_data(test_data) + xmppclient2.parse_data(test_data) assert ( mock_send.mock_calls[0][1][0] @@ -490,7 +489,7 @@ async def test_client_send_iq(): # Bot result test_data = b"" - xmppclient2._parse_data(test_data) + xmppclient2.parse_data(test_data) assert ( mock_send.mock_calls[0][1][0] @@ -502,7 +501,7 @@ async def test_client_send_iq(): # Bot iq set test_data = b"" - xmppclient2._parse_data(test_data) + xmppclient2.parse_data(test_data) assert ( mock_send.mock_calls[0][1][0] @@ -514,7 +513,7 @@ async def test_client_send_iq(): # Bot error report test_data = b"" - xmppclient2._parse_data(test_data) + xmppclient2.parse_data(test_data) assert ( mock_send.mock_calls[0][1][0] @@ -526,7 +525,7 @@ async def test_client_send_iq(): # Bot "DorpError" to all test_data = b"" - xmppclient2._parse_data(test_data) + xmppclient2.parse_data(test_data) assert mock_send.mock_calls[0][1][0] == ( '' ) # result sent to ecouser.net diff --git a/tests/web/test_server.py b/tests/web/test_server.py index 48ac055..943b056 100644 --- a/tests/web/test_server.py +++ b/tests/web/test_server.py @@ -41,8 +41,7 @@ async def test_base(webserver_client): remove_existing_db() # Start XMPP - xmpp_address = (HOST, 5223) - xmpp_server = XMPPServer(xmpp_address) + xmpp_server = XMPPServer(HOST, 5223) bumper.xmpp_server = xmpp_server await xmpp_server.start_async_server() @@ -57,8 +56,7 @@ async def test_restartService(webserver_client): remove_existing_db() # Start XMPP - xmpp_address = (HOST, 5223) - xmpp_server = XMPPServer(xmpp_address) + xmpp_server = XMPPServer(HOST, 5223) bumper.xmpp_server = xmpp_server await xmpp_server.start_async_server()