diff --git a/bumper/xmppserver.py b/bumper/xmppserver.py index f012f6f..0dabdc9 100644 --- a/bumper/xmppserver.py +++ b/bumper/xmppserver.py @@ -402,22 +402,20 @@ class XMPPAsyncClient: if self.TLSUpgraded == False: # With STARTTLS #https://xmpp.org/rfcs/rfc3920.html self.send( - 'PLAIN' + 'PLAIN' ) else: - # Already using TLS send authentication support for iq-auth (fallback) and SASL + # Already using TLS send authentication support for SASL self.send( - 'PLAIN' + 'PLAIN' ) else: self.send("") else: - if "jabber:iq:auth" in xml.tag: # Handle iq-auth - self._handle_iq_auth(xml) - elif ( + if ( "urn:ietf:params:xml:ns:xmpp-sasl" in xml.tag ): # Handle SASL Auth self._handle_sasl_auth(xml) @@ -486,98 +484,6 @@ class XMPPAsyncClient: except Exception as e: xmppserverlog.exception("{}".format(e)) - def _handle_iq_auth(self, data): - try: - xml = ET.fromstring(data.decode("utf-8")) - ctl = xml[0][0] - xmppserverlog.info("IQ AUTH XML: {}".format(xml)) - # Received username and auth tag, send username/password requirement - if ( - xml.get("type") == "get" - and "auth}username" in ctl.tag - and self.type == self.UNKNOWN - ): - self.send( - ''.format( - xml.get("id") - ) - ) - - # Received username, password, resource - Handle auth here and return pass or fail - if ( - xml.get("type") == "set" - and "auth}username" in ctl.tag - and self.type == self.UNKNOWN - ): - xmlauth = xml[0].getchildren() - # uid = "" - password = "" - authcode = "" - resource = "" - for aitem in xmlauth: - if "username" in aitem.tag: - self.uid = aitem.text - - elif "password" in aitem.tag: - password = aitem.text.split("/")[2] - authcode = password - - elif "resource" in aitem.tag: - self.clientresource = aitem.text - resource = self.clientresource - - if self.devclass: # if there is a devclass it is a bot - bumper.bot_add("", self.uid, "", resource, "eco-legacy") - xmppserverlog.debug("bot authenticated {}".format(self.uid)) - - # Client authenticated, move to next state - self._set_state("INIT") - - # Successful auth - self.send(''.format(xml.get("id"))) - - else: - auth = False - if bumper.check_authcode(self.uid, authcode): - auth = True - elif bumper.use_auth == False: - auth = True - - if auth: - bumper.client_add(self.uid, "bumper", self.clientresource) - xmppserverlog.debug("client authenticated {}".format(self.uid)) - - # Client authenticated, move to next state - self._set_state("INIT") - - # Successful auth - self.send(''.format(xml.get("id"))) - - else: - # Failed auth - self.send( - ''.format( - xml.get("id") - ) - ) - - except ET.ParseError as e: - if "no element found" in e.msg: - xmppserverlog.debug( - "xml parse error - {} - {}".format(data.decode("utf-8"), e) - ) - elif "not well-formed (invalid token)" in e.msg: - xmppserverlog.debug( - "xml parse error - {} - {}".format(data.decode("utf-8"), e) - ) - else: - xmppserverlog.debug( - "xml parse error - {} - {}".format(data.decode("utf-8"), e) - ) - - except Exception as e: - xmppserverlog.exception("{}".format(e)) - def _handle_sasl_auth(self, xml): try: diff --git a/tests/test_xmppserver.py b/tests/test_xmppserver.py index 95fa8e1..e56fdcf 100644 --- a/tests/test_xmppserver.py +++ b/tests/test_xmppserver.py @@ -41,7 +41,7 @@ async def test_client_connect_no_starttls(*args, **kwargs): # Server tells client available features assert ( mock_send.mock_calls[1].args[0] - == 'PLAIN' + == 'PLAIN' ) # Reset mock calls @@ -60,6 +60,38 @@ async def test_client_connect_no_starttls(*args, **kwargs): assert xmppclient.state == xmppclient.INIT # Client moved to INIT state +async def test_client_end_stream(*args, **kwargs): + test_transport = asyncio.Transport() + test_transport.get_extra_info = mock.Mock(return_value=mock_transport_extra_info()) + test_transport.write = mock.Mock(return_value=return_send_data) + xmppclient = bumper.xmppserver.XMPPAsyncClient(test_transport) + xmppclient.state = xmppclient.CONNECT # Set client state to CONNECT + mock_send = xmppclient.send = mock.Mock(side_effect=return_send_data) + + # Send end stream from "client" + test_data = "".encode("utf-8") + xmppclient._parse_data(test_data) + + # Expect 2 calls to send + assert mock_send.call_count == 1 + # Server opens stream + assert mock_send.mock_calls[0].args[0] == "" + + # Reset mock calls + mock_send.reset_mock() + + # Send abnormal stream from "client" + test_data = "".encode("utf-8") + xmppclient._parse_data(test_data) + + # Reset mock calls + mock_send.reset_mock() + + # Send blank from "client" + test_data = "".encode("utf-8") + xmppclient._parse_data(test_data) + + async def test_client_connect_starttls_called(*args, **kwargs): test_transport = asyncio.Transport() test_transport.get_extra_info = mock.Mock(return_value=mock_transport_extra_info()) @@ -84,7 +116,7 @@ async def test_client_connect_starttls_called(*args, **kwargs): # Server tells client available features assert ( mock_send.mock_calls[1].args[0] - == 'PLAIN' + == 'PLAIN' ) # Reset mock calls @@ -117,7 +149,7 @@ async def test_client_connect_starttls_called(*args, **kwargs): # Server tells client available features (without STARTTLS) assert ( mock_send.mock_calls[1].args[0] - == 'PLAIN' + == 'PLAIN' ) # Reset mock calls mock_send.reset_mock() @@ -135,6 +167,80 @@ async def test_client_connect_starttls_called(*args, **kwargs): assert xmppclient.state == xmppclient.INIT # Client moved to INIT state +async def test_client_init(*args, **kwargs): + test_transport = asyncio.Transport() + test_transport.get_extra_info = mock.Mock(return_value=mock_transport_extra_info()) + test_transport.write = mock.Mock(return_value=return_send_data) + xmppclient = bumper.xmppserver.XMPPAsyncClient(test_transport) + xmppclient.state = xmppclient.INIT # Set client state to INIT + xmppclient.uid = "fuid_tmpuser" + xmppclient.resource = "IOSF53D07BA" + xmppclient.bumper_jid = "fuid_tmpuser@ecouser.net/IOSF53D07BA" + xmppclient.type = xmppclient.CONTROLLER + mock_send = xmppclient.send = mock.Mock(side_effect=return_send_data) + + # Send connect stream from "client" + test_data = "".encode( + "utf-8" + ) + xmppclient._parse_data(test_data) + + # Expect 2 calls to send + assert mock_send.call_count == 2 + # Server opens stream + assert ( + mock_send.mock_calls[0].args[0] + == '' + ) + # Server tells client binds + assert ( + mock_send.mock_calls[1].args[0] + == '' + ) + + # Reset mock calls + mock_send.reset_mock() + + # Send bind from "client" + test_data = 'IOSF53D07BA'.encode( + "utf-8" + ) + xmppclient._parse_data(test_data) + + assert ( + mock_send.mock_calls[0].args[0] + == 'fuid_tmpuser@ecouser.net/IOSF53D07BA' + ) # client successfully binded + assert xmppclient.state == xmppclient.BIND # client moved to BIND state + + # Reset mock calls + mock_send.reset_mock() + + # Send set session from client + test_data = ''.encode( + "utf-8" + ) + xmppclient._parse_data(test_data) + + assert xmppclient.state == xmppclient.READY # client moved to READY state + assert ( + mock_send.mock_calls[0].args[0] + == '' + ) # client ready + + # Reset mock calls + mock_send.reset_mock() + + # Send presense from client + test_data = ''.encode("utf-8") + xmppclient._parse_data(test_data) + + assert ( + mock_send.mock_calls[0].args[0] + == ' dummy ' + ) # client presence - dummy response + + async def test_bot_connect(*args, **kwargs): test_transport = asyncio.Transport() test_transport.get_extra_info = mock.Mock(return_value=mock_transport_extra_info()) @@ -159,7 +265,7 @@ async def test_bot_connect(*args, **kwargs): # Server tells client available features assert ( mock_send.mock_calls[1].args[0] - == 'PLAIN' + == 'PLAIN' ) # Reset mock calls @@ -187,6 +293,7 @@ async def test_bot_init(*args, **kwargs): xmppclient.state = xmppclient.INIT # Set client state to INIT xmppclient.uid = "E0000000000000001234" xmppclient.devclass = "159" + xmppclient.type = xmppclient.BOT mock_send = xmppclient.send = mock.Mock(side_effect=return_send_data) # Send connect stream from "bot" @@ -302,12 +409,22 @@ async def test_ping_client_to_client(*args, **kwargs): ) xmppclient._parse_data(test_data) + assert ( + mock_send2.mock_calls[0].args[0] + == '' + ) # ping response + # Ping response from bot to user test_data = "".encode( "utf-8" ) xmppclient2._parse_data(test_data) + assert ( + mock_send.mock_calls[0].args[0] + == '' + ) # ping response + async def test_client_send_iq(*args, **kwargs): test_transport = asyncio.Transport() @@ -341,7 +458,7 @@ async def test_client_send_iq(*args, **kwargs): assert ( mock_send.mock_calls[0].args[0] == '' - ) # ping response + ) # feature not implemented response # Reset mock calls mock_send.reset_mock() @@ -373,3 +490,32 @@ async def test_client_send_iq(*args, **kwargs): # Reset mock calls mock_send.reset_mock() + + # Bot result + test_data = "".encode( + "utf-8" + ) + xmppclient2._parse_data(test_data) + + assert ( + mock_send.mock_calls[0].args[0] + == '' + ) # result sent to ecouser.net + + # Reset mock calls + mock_send.reset_mock() + + # Bot iq set + test_data = "".encode( + "utf-8" + ) + xmppclient2._parse_data(test_data) + + assert ( + mock_send.mock_calls[0].args[0] + == '' + ) # result sent to ecouser.net + + # Reset mock calls + mock_send.reset_mock() +