def _handle_sasl_auth(self, xml): try: saslauth = base64.b64decode(xml.text).decode("utf-8").split("/") username = saslauth[0] username = saslauth[0].split("\x00")[1] authcode = "" self.uid = username if len(saslauth) > 1: resource = saslauth[1] self.clientresource = resource elif len(saslauth[0].split("\x00")) > 2: resource = saslauth[0].split("\x00")[2] self.clientresource = resource if len(saslauth) > 2: authcode = saslauth[2] if self.devclass: # if there is a devclass it is a bot bumper.bot_add(self.uid, self.uid, self.devclass, "atom", "eco-legacy") self.type = self.BOT xmppserverlog.info("bot authenticated SN: {}".format(self.uid)) # Send response self.send('<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>' ) # Success # Client authenticated, move to next state self._set_state("INIT") else: auth = False if bumper.check_authcode(self.uid, authcode): auth = True elif bumper.use_auth == False: auth = True if auth: self.type = self.CONTROLLER bumper.client_add(self.uid, "bumper", self.clientresource) xmppserverlog.info("client authenticated {}".format( self.uid)) # Client authenticated, move to next state self._set_state("INIT") # Send response self.send( '<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>' ) # Success else: # Failed to authenticate self.send( '<response xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>' ) # Fail except Exception as e: xmppserverlog.exception("{}".format(e))
def test_client_db(): bumper.db = "tests/tmp.db" # Set db location for testing bumper.client_add("user_123", "realm_123", "resource_123") assert bumper.client_get("resource_123") # Test client was added bumper.client_set_mqtt("resource_123", True) assert bumper.client_get("resource_123")[ "mqtt_connection"] # Test that mqtt was set True for client bumper.client_set_xmpp("resource_123", False) assert (bumper.client_get("resource_123")["xmpp_connection"] == False ) # Test that xmpp was set False for client assert (len(bumper.get_disconnected_xmpp_clients()) == 1 ) # Test len of connected xmpp clients is 1
async def test_mqttserver(): if os.path.exists("tests/tmp.db"): os.remove("tests/tmp.db") # Remove existing db bumper.db = "tests/tmp.db" # Set db location for testing mqtt_address = ("127.0.0.1", 8883) mqtt_server = bumper.MQTTServer(mqtt_address) await mqtt_server.broker_coro() # Test helperbot connect mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address) await mqtt_helperbot.start_helper_bot() assert (mqtt_helperbot.Client._connected_state._value == True ) # Check helperbot is connected await mqtt_helperbot.Client.disconnect() # Test client connect bumper.user_add("user_123") # Add user to db bumper.client_add("user_123", "ecouser.net", "resource_123") # Add client to db test_client = bumper.MQTTHelperBot(mqtt_address) test_client.client_id = "[email protected]/resource_123" # await test_client.start_helper_bot() test_client.Client = hbmqtt.client.MQTTClient( client_id=test_client.client_id, config={"check_hostname": False}) await test_client.Client.connect( "mqtts://{}:{}/".format(test_client.address[0], test_client.address[1]), cafile=bumper.ca_cert, ) assert (test_client.Client._connected_state._value == True ) # Check client is connected await test_client.Client.disconnect() assert (test_client.Client._connected_state._value == False ) # Check client is disconnected # Test fake_bot connect fake_bot = bumper.MQTTHelperBot(mqtt_address) fake_bot.client_id = "bot_serial@ls1ok3/wC3g" await fake_bot.start_helper_bot() assert (fake_bot.Client._connected_state._value == True ) # Check fake_bot is connected await fake_bot.Client.disconnect() await asyncio.sleep(0.1) await mqtt_server.broker.shutdown()
async def authenticate(self, *args, **kwargs): authenticated = False try: session = kwargs.get("session", None) username = session.username password = session.password client_id = session.client_id if "@" in client_id: didsplit = str(client_id).split("@") if not ( # if ecouser or bumper aren't in details it is a bot "ecouser" in didsplit[1] or "bumper" in didsplit[1]): tmpbotdetail = str(didsplit[1]).split("/") bumper.bot_add( username, didsplit[0], tmpbotdetail[0], tmpbotdetail[1], "eco-ng", ) mqttserverlog.info( f"Bumper Authentication Success - Bot - SN: {username} - DID: {didsplit[0]} - Class: {tmpbotdetail[0]}" ) authenticated = True else: tmpclientdetail = str(didsplit[1]).split("/") userid = didsplit[0] realm = tmpclientdetail[0] resource = tmpclientdetail[1] if userid == "helperbot": mqttserverlog.info( f"Bumper Authentication Success - Helperbot: {client_id}" ) authenticated = True else: auth = False if bumper.check_authcode(didsplit[0], password): auth = True elif bumper.use_auth == False: auth = True if auth: bumper.client_add(userid, realm, resource) mqttserverlog.info( f"Bumper Authentication Success - Client - Username: {username} - ClientID: {client_id}" ) authenticated = True else: authenticated = False # Check for File Auth if username and not authenticated: # If there is a username and it isn't already authenticated hash = self._users.get(username, None) if hash: # If there is a matching entry in passwd, check hash authenticated = pwd_context.verify(password, hash) if authenticated: mqttserverlog.info( f"File Authentication Success - Username: {username} - ClientID: {client_id}" ) else: mqttserverlog.info( f"File Authentication Failed - Username: {username} - ClientID: {client_id}" ) else: mqttserverlog.info( f"File Authentication Failed - No Entry for Username: {username} - ClientID: {client_id}" ) except Exception as e: mqttserverlog.exception("Session: {} - {}".format( (kwargs.get("session", None)), e)) authenticated = False # Check for allow anonymous allow_anonymous = self.auth_config.get("allow-anonymous", True) if allow_anonymous and not authenticated: # If anonymous auth is allowed and it isn't already authenticated authenticated = True self.context.logger.debug( f"Anonymous Authentication Success: config allows anonymous - Username: {username}" ) mqttserverlog.info( f"Anonymous Authentication Success: config allows anonymous - Username: {username}" ) return authenticated
async def authenticate(self, *args, **kwargs): if not self.auth_config: # auth config section not found self.context.logger.warning( "'auth' section not found in context configuration" ) return False allow_anonymous = self.auth_config.get( "allow-anonymous", True ) # allow anonymous by default if allow_anonymous: authenticated = True self.context.logger.debug("Authentication success: config allows anonymous") else: try: session = kwargs.get("session", None) username = session.username password = session.password client_id = session.client_id didsplit = str(client_id).split("@") # If this isn't a fake user (fuid) then add as a bot if not ( str(didsplit[0]).startswith("fuid") or str(didsplit[0]).startswith("helper") ): tmpbotdetail = str(didsplit[1]).split("/") bumper.bot_add( username, didsplit[0], tmpbotdetail[0], tmpbotdetail[1], "eco-ng", ) mqttserverlog.debug( "new bot authenticated SN: {} DID: {}".format( username, didsplit[0] ) ) authenticated = True else: tmpclientdetail = str(didsplit[1]).split("/") userid = didsplit[0] realm = tmpclientdetail[0] resource = tmpclientdetail[1] if userid == "helper1": authenticated = True else: auth = False if bumper.check_authcode(didsplit[0], password): auth = True elif bumper.use_auth == False: auth = True if auth: bumper.client_add(userid, realm, resource) mqttserverlog.debug( "client authenticated {}".format(userid) ) authenticated = True else: authenticated = False except Exception as e: mqttserverlog.exception("Session: {} - {}".format((kwargs.get("session", None)),e)) authenticated = False return authenticated
async def test_mqttserver(): if os.path.exists("tests/tmp.db"): os.remove("tests/tmp.db") # Remove existing db bumper.db = "tests/tmp.db" # Set db location for testing mqtt_address = ("127.0.0.1", 8883) mqtt_server = bumper.MQTTServer(mqtt_address, password_file="tests/passwd", allow_anonymous=True) await mqtt_server.broker_coro() # Test helperbot connect mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address) await mqtt_helperbot.start_helper_bot() assert ( mqtt_helperbot.Client._connected_state._value == True ) # Check helperbot is connected await mqtt_helperbot.Client.disconnect() # Test client connect bumper.user_add("user_123") # Add user to db bumper.client_add("user_123", "ecouser.net", "resource_123") # Add client to db test_client = bumper.MQTTHelperBot(mqtt_address) test_client.client_id = "[email protected]/resource_123" # await test_client.start_helper_bot() test_client.Client = hbmqtt.client.MQTTClient( client_id=test_client.client_id, config={"check_hostname": False} ) await test_client.Client.connect( "mqtts://{}:{}/".format(test_client.address[0], test_client.address[1]), cafile=bumper.ca_cert, ) assert ( test_client.Client._connected_state._value == True ) # Check client is connected await test_client.Client.disconnect() assert ( test_client.Client._connected_state._value == False ) # Check client is disconnected # Test fake_bot connect fake_bot = bumper.MQTTHelperBot(mqtt_address) fake_bot.client_id = "bot_serial@ls1ok3/wC3g" await fake_bot.start_helper_bot() assert ( fake_bot.Client._connected_state._value == True ) # Check fake_bot is connected await fake_bot.Client.disconnect() # Test file auth client connect test_client = bumper.MQTTHelperBot(mqtt_address) test_client.client_id = "test-file-auth" # await test_client.start_helper_bot() test_client.Client = hbmqtt.client.MQTTClient( client_id=test_client.client_id, config={"check_hostname": False, "auto_reconnect": False, "reconnect_retries": 1} ) # good user/pass await test_client.Client.connect( f"mqtts://*****:*****@{test_client.address[0]}:{test_client.address[1]}/", cafile=bumper.ca_cert, cleansession=True ) assert ( test_client.Client._connected_state._value == True ) # Check client is connected await test_client.Client.disconnect() assert ( test_client.Client._connected_state._value == False ) # Check client is disconnected # bad password with LogCapture() as l: await test_client.Client.connect( f"mqtts://*****:*****@{test_client.address[0]}:{test_client.address[1]}/", cafile=bumper.ca_cert, cleansession=True ) l.check_present( ("mqttserver", "INFO", "File Authentication Failed - Username: test-client - ClientID: test-file-auth"), order_matters=False ) # no username in file await test_client.Client.connect( f"mqtts://*****:*****@{test_client.address[0]}:{test_client.address[1]}/", cafile=bumper.ca_cert, cleansession=True ) l.check_present( ("mqttserver", "INFO", 'File Authentication Failed - No Entry for Username: test-client-noexist - ClientID: test-file-auth'), order_matters=False ) await mqtt_server.broker.shutdown()
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( '<iq type="result" id="{}"><query xmlns="jabber:iq:auth"><username/><password/></query></iq>'.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 = "" 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 not self.uid.startswith("fuid"): # Need sample data to see details here bumper.bot_add("", self.uid, "", resource, "eco-legacy") xmppserverlog.info("bot authenticated {}".format(self.uid)) # Client authenticated, move to next state self._set_state("INIT") # Successful auth self.send('<iq type="result" id="{}"/>'.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('<iq type="result" id="{}"/>'.format(xml.get("id"))) else: # Failed auth self.send( '<iq type="error" id="{}"><error code="401" type="auth"><not-authorized xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error></iq>'.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))