diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..ed8ebf5 --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1 @@ +__pycache__ \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..2de7f16 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,8 @@ +FROM python:3-alpine + +RUN apk --no-cache add build-base openldap-dev python2-dev python3-dev +RUN pip3 install python-ldap requests coloredlogs + +COPY ./* ./ + +ENTRYPOINT [ "python3", "mlmAccess.py" ] \ No newline at end of file diff --git a/backend/actor.py b/backend/actor.py new file mode 100644 index 0000000..99569db --- /dev/null +++ b/backend/actor.py @@ -0,0 +1,6 @@ +class MaActor: + def __init__(self, id): + self._id = id + + def _mqttOnConnect(self, client, userdata, flags, rc): + client.subscribe("mlmAccess/pair/request/actor") \ No newline at end of file diff --git a/backend/dbHelper.py b/backend/dbHelper.py index 6de26ac..648e832 100644 --- a/backend/dbHelper.py +++ b/backend/dbHelper.py @@ -2,22 +2,23 @@ import pymysql import json class MaDbHelper: - def __init__(self): - self.mysqlConn = pymysql.connect(host='localhost', - user='root', - passwd='MlmAccess', - db='MlmAccess', - port=4306) + def __init__(self, config): + self.mysqlConn = pymysql.connect(host=config["MYSQL_HOST"], + user=config["MYSQL_USER"], + passwd=config["MYSQL_PASSWORD"], + db=config["MYSQL_DATABASE"], + port=config["MYSQL_PORT"]) self.mysqlCur = self.mysqlConn.cursor() + self._initDb() def _initDb(self): """ - Generates required tables and users + Generates required tables """ - """ - CREATE TABLE vmq_auth_acl + query = """ + CREATE TABLE IF NOT EXISTS vmq_auth_acl ( mountpoint VARCHAR(10) NOT NULL, client_id VARCHAR(128) NOT NULL, @@ -29,6 +30,12 @@ class MaDbHelper: ) """ + self.mysqlCur.execute( + query + ) + + self.mysqlConn.commit() + def addUser(self, username, password, publishAclPatterns, subscribeAclPatterns): if self.userExists(username): diff --git a/backend/mlmAccess.py b/backend/mlmAccess.py index 6ce8efa..1557e0f 100644 --- a/backend/mlmAccess.py +++ b/backend/mlmAccess.py @@ -1,7 +1,7 @@ import paho.mqtt.client as mqtt from dbHelper import MaDbHelper from pairingHandler import MaPairingHandler -import logging, coloredlogs +import logging, coloredlogs, os, sys, random, string coloredlogs.install(level='INFO', fmt='%(asctime)s - [%(levelname)s] %(message)s') @@ -9,6 +9,8 @@ class MlmAccess: def __init__(self): logging.info("=== MlmAccess ===") + self._config = self._readConfig() + self._initDb() self._initMqtt() @@ -18,10 +20,10 @@ class MlmAccess: def _initDb(self): logging.info("Initializing Database") - self._db = MaDbHelper() + self._db = MaDbHelper(self._config) initUsers = [ - ("backend", "backend", ["mlmAccess/#"], ["mlmAccess/#"]), - ("pair-actor", "pair-actor", ["mlmAccess/pair/request/actor"], ["mlmAccess/pair/response/actor"]) + ("backend", self._config["MQTT_BACKEND_PASSWORD"], ["mlmAccess/#"], ["mlmAccess/#"]), + ("pair-actor", self._config["PAIR_SECRET"], ["mlmAccess/pair/request/actor"], ["mlmAccess/pair/response/actor"]) ] for username, password, publishAclPatterns, subscribeAclPatterns in initUsers: @@ -31,12 +33,12 @@ class MlmAccess: def _initMqtt(self): logging.info("Initializing MQTT") self._mqtt = client = mqtt.Client(client_id="backend") - self._mqtt.username_pw_set("backend", "backend") + self._mqtt.username_pw_set("backend", self._config["MQTT_BACKEND_PASSWORD"]) self._mqtt.on_connect = self._mqttOnConnect self._mqtt.on_message = self._mqttOnMessage - self._mqtt.connect("localhost", 1883, 60) + self._mqtt.connect(self._config["MQTT_HOST"], self._config["MQTT_PORT"], 60) def _mqttOnConnect(self, client, userdata, flags, rc): if rc != 0: @@ -65,5 +67,48 @@ class MlmAccess: if topic.startswith("pair"): self._pairingHanlder.handlePairingRequest(topic, messageContent) + def _readConfig(self): + + configKeyPrefix = "MLMACCESS_" + + requiredConfigKeys = [ + 'MYSQL_USER', + 'MYSQL_PASSWORD', + 'MYSQL_DATABASE', + 'PAIR_SECRET' + ] + + allowedConfigKeys = [ + 'MYSQL_HOST', + 'MYSQL_PORT', + 'MQTT_HOST', + 'MQTT_PORT', + ] + + backendPassword = ''.join(random.choices(string.ascii_uppercase + string.ascii_lowercase + string.digits, k=20)) + config = { + "MYSQL_HOST": "mysql", + "MYSQL_PORT": 4306, + "MQTT_HOST": "mqtt", + "MQTT_PORT": 1883, + "MQTT_BACKEND_PASSWORD": backendPassword + } + + for configKey in requiredConfigKeys: + if configKeyPrefix + configKey not in os.environ: + logging.error(f"Required environment value {configKeyPrefix + configKey} is not set") + sys.exit(1) + config[configKey] = os.environ[configKeyPrefix + configKey] + + for configKey in allowedConfigKeys: + if configKeyPrefix + configKey in os.environ: + config[configKey] = os.environ[configKeyPrefix + configKey] + + logging.info("CONFIG:") + for key, value in config.items(): + logging.info(" * {:25}: {}".format(key, value)) + + return config + if __name__ == "__main__": MlmAccess() \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index b09ee00..51c151c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,5 @@ services: - broker: + mqtt: image: vernemq/vernemq:1.11.0 environment: - DOCKER_VERNEMQ_ACCEPT_EULA=yes @@ -7,7 +7,7 @@ services: - DOCKER_VERNEMQ_PLUGINS__VMQ_ACL=off - DOCKER_VERNEMQ_PLUGINS__VMQ_DIVERSITY=on - DOCKER_VERNEMQ_VMQ_DIVERSITY__AUTH_MYSQL__ENABLED=on - - DOCKER_VERNEMQ_VMQ_DIVERSITY__MYSQL__HOST=mysql-server + - DOCKER_VERNEMQ_VMQ_DIVERSITY__MYSQL__HOST=mysql - DOCKER_VERNEMQ_VMQ_DIVERSITY__MYSQL__PORT=3306 - DOCKER_VERNEMQ_VMQ_DIVERSITY__MYSQL__USER=root - DOCKER_VERNEMQ_VMQ_DIVERSITY__MYSQL__PASSWORD=MlmAccess @@ -21,7 +21,7 @@ services: - ./data/vernemq/data:/vernemq/data - ./data/vernemq/log:/vernemq/log - mysql-server: + mysql: image: mariadb restart: always command: --default-authentication-plugin=mysql_native_password @@ -37,7 +37,7 @@ services: image: phpmyadmin/phpmyadmin restart: always environment: - - PMA_HOST=mysql-server + - PMA_HOST=mysql - PMA_USER=root - PMA_PASSWORD=MlmAccess - UPLOAD_LIMIT=512M diff --git a/mock-actor/mock-actor-pair.py b/mock-actor/mock-actor-pair.py index 1a0d556..117a2d6 100644 --- a/mock-actor/mock-actor-pair.py +++ b/mock-actor/mock-actor-pair.py @@ -3,7 +3,7 @@ import logging, coloredlogs coloredlogs.install(level='INFO', fmt='%(asctime)s - [%(levelname)s] %(message)s') -PAIR_ID = "106" +PAIR_ID = "101" def on_connect(client, userdata, flags, rc): global PAIR_ID diff --git a/mock-actor/mock-actor.py b/mock-actor/mock-actor.py index 53836c2..bfebff2 100644 --- a/mock-actor/mock-actor.py +++ b/mock-actor/mock-actor.py @@ -1,28 +1,36 @@ import paho.mqtt.client as mqtt +import logging, coloredlogs + +coloredlogs.install(level='INFO', fmt='%(asctime)s - [%(levelname)s] %(message)s') + +ACTOR_ID = "101" +ACTOR_PASSWORD = "MgUiSW1dloFt9TVKJM5E" -# The callback for when the client receives a CONNACK response from the server. def on_connect(client, userdata, flags, rc): - print("Connected with result code "+str(rc)) - # Subscribing in on_connect() means that if we lose the connection and - # reconnect then subscriptions will be renewed. - print("Subscribing to topic","mlmAccess/pair/response/actor") - print(client.subscribe("mlmAccess/actor/102/action")) - print("Publishing message to topic","mlmAccess/pair/request/actor") - print(client.publish("mlmAccess/actor/102/status", "0", retain=True)) + global ACTOR_ID + + if rc != 0: + logging.error(f"Error connecting to MQTT broker: {rc}") + return + + logging.info("Successfully connected to MQTT broker") + + client.subscribe(f"mlmAccess/actor/{ACTOR_ID}/action") + client.publish(f"mlmAccess/actor/{ACTOR_ID}/status", "0", retain=True) def on_message(client, userdata, message): - print("message received " ,str(message.payload.decode("utf-8"))) - print("message topic=",message.topic) - print("message qos=",message.qos) - print("message retain flag=",message.retain) + messageContent = str(message.payload.decode("utf-8")) + logging.info(f"Got request to perform action: {messageContent}") + # report back action to let the backend know that it was executed successfully + client.publish(f"mlmAccess/actor/{ACTOR_ID}/status", messageContent, retain=True) -client = mqtt.Client(client_id="actor-102") -client.username_pw_set("actor-102", "YWT2JPOEZH") +client = mqtt.Client(client_id=f"actor-{ACTOR_ID}") +client.username_pw_set(f"actor-{ACTOR_ID}", ACTOR_PASSWORD) client.on_connect = on_connect client.on_message = on_message -print("connecting to broker") -print(client.connect("localhost", 1883, 60)) +logging.info("Initializing MQTT") +client.connect("localhost", 1883, 60) client.loop_forever() \ No newline at end of file diff --git a/runBackend.sh b/runBackend.sh new file mode 100755 index 0000000..05fc4b1 --- /dev/null +++ b/runBackend.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +export MLMACCESS_MYSQL_USER=root +export MLMACCESS_MYSQL_PASSWORD=MlmAccess +export MLMACCESS_MYSQL_DATABASE=MlmAccess +export MLMACCESS_MYSQL_HOST=localhost +export MLMACCESS_MQTT_HOST=localhost +export MLMACCESS_PAIR_SECRET=pair-actor + +python3 backend/mlmAccess.py \ No newline at end of file