From c8d5b4a7f3b94aa3e886b6c8b2a639fd4b74dcb5 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Mon, 23 Aug 2021 13:59:38 +0200 Subject: [PATCH] - Delete obsolete users - Alter pairing credentials to allow panel pairing - Start working on panel workflow --- README.md | 56 ++++++++++++++++--- backend/dbHelper.py | 15 +++++ backend/mlmAccess.py | 17 +++++- backend/pairingHandler.py | 3 + {mock-actor => mock-clients}/mock-actor.py | 0 .../mock-client-pair.py | 12 ++-- runBackend.sh | 2 +- 7 files changed, 89 insertions(+), 16 deletions(-) mode change 100644 => 100755 backend/mlmAccess.py rename {mock-actor => mock-clients}/mock-actor.py (100%) rename mock-actor/mock-actor-pair.py => mock-clients/mock-client-pair.py (74%) diff --git a/README.md b/README.md index b2c9b70..d19fed5 100644 --- a/README.md +++ b/README.md @@ -4,17 +4,59 @@ The backend server of MlmAccess # Workflows ## Pairing +###### Actor A new actor can pair like this: -1. Connect to the broker using `pair-actor` as username and the pair secret as password +1. Connect to the broker using `pair` as username and the pair secret as password 2. Subscribe to `mlmAccess/pair/response/actor` 3. Publish its desired ID to `mlmAccess/pair/request/actor` 4. The backend will publish the password for the actor to `mlmAccess/pair/response/actor` in case of success 5. In case of an error, the backend will publish an empty string to `mlmAccess/pair/response/actor` 6. The actor may now connect using `actor-{id}` as username and the password it got in 4 -## Actor normal operation -During normal operation (after pairing) the actor has to follow this workflow: -1. The actor subscribes to `mlmAccess/actor/{id}/action` -2. The actor publishes its status to `mlmAccess/actor/{id}/status` -3. The backend will publish a requested state change to `mlmAccess/actor/{id}/action` -4. The actor will publish its state to `mlmAccess/actor/{id}/status` to let the backend know that the action was executed successfully \ No newline at end of file +###### Panel +A new panel can pair in the same way as a new actor. Every occurence of `actor` as to be replaced by `panel`, though. + +## Actor +The actor should subscribe to: +- `mlmAccess/actor/{id}/action` +The actor can publish to: +- `mlmAccess/actor/{id}/status` + +### Action execution +If the backend requests an action to be executed, the actor follows this workflow: +1. The backend will publish a requested action to `mlmAccess/actor/{id}/action` +2. The actor will publish its state to `mlmAccess/actor/{id}/status` to let the backend know that the action was executed successfully. This will also happen when the state of the actor is unchanged. + +### Status change +If the status of the actor changes it follows this workflow: +1. The actor publishes its status to `mlmAccess/actor/{id}/status` + +## Panel +### Authentication workflow +The authentication workflow looks like this: +1. The user presents their RFID Chip to the reader +2. The panel publishes the id to `mlmAccess/panel/{id}/authentication/request` +3. The backend pulishes a JSON object to `mlmAccess/panel/{id}/authentication/response` which looks like this: + `{"status": 200, "data": {"fullName": "Max Mustermann", ...}}` the status is an int which can be either 200(success) or 404(id not found) +4. If the status is 200, the panel continues with the normal operation workflow +5. If the status is 404, the panel continues with the initial user registration workflow + +### Initial user registration +The initial user registration could work like this: +1. The user inputs its member ID +2. The user inputs its birthdate +3. The panel publishes a JSON object to `mlmAccess/panel/{id}/registration/request` which looks like this: + `{"memberId": "1234", "rfidId": "abcde"}` +4. The backend pulishes a JSON object to `mlmAccess/panel/{id}/registration/response` which looks like this: + `{"status": 200, "data": {"fullName": "Max Mustermann", ...}}` the status is an int which can be either 200(success), 404(memberId not found) or 401(authentication failed / rfidId not found) +5. If the status is 200, the panel continues with the normal operation workflow +6. Otherwise, the panel shows an error and goes back to idle. + +### Normal operation +The normal operation after authentication looks like this: +1. The user enters the id of the actor they want to unlock +2. The panel publishes a JSON object to `mlmAccess/panel/{id}/actor/request` which looks like this: + `{"rfidId": "abcde", "actorId": "101"}` +4. The backend pulishes a JSON object to `mlmAccess/panel/{id}/registration/response` which looks like this: + `{"status": 200}` the status is an int which can be either 200(success), 404(actor id not found) or 401(authentication failed / rfidID not found), 403(forbidden) +5. The panel gives Feedback to the user and goes back to idle \ No newline at end of file diff --git a/backend/dbHelper.py b/backend/dbHelper.py index 648e832..d04e8e9 100644 --- a/backend/dbHelper.py +++ b/backend/dbHelper.py @@ -90,6 +90,21 @@ class MaDbHelper: return users + def deleteUser(self, username): + if not self.userExists(username): + return False + + query = "DELETE FROM `vmq_auth_acl` WHERE username=%s;" + + self.mysqlCur.execute( + query, + (username) + ) + + self.mysqlConn.commit() + + return True + def _updateUser(self, username, password, publishAclPatterns, subscribeAclPatterns): query = """ diff --git a/backend/mlmAccess.py b/backend/mlmAccess.py old mode 100644 new mode 100755 index 1557e0f..75e3d38 --- a/backend/mlmAccess.py +++ b/backend/mlmAccess.py @@ -1,3 +1,5 @@ +#!/usr/bin/python3 + import paho.mqtt.client as mqtt from dbHelper import MaDbHelper from pairingHandler import MaPairingHandler @@ -23,13 +25,17 @@ class MlmAccess: self._db = MaDbHelper(self._config) initUsers = [ ("backend", self._config["MQTT_BACKEND_PASSWORD"], ["mlmAccess/#"], ["mlmAccess/#"]), - ("pair-actor", self._config["PAIR_SECRET"], ["mlmAccess/pair/request/actor"], ["mlmAccess/pair/response/actor"]) + ("pair", self._config["PAIR_SECRET"], ["mlmAccess/pair/request/#"], ["mlmAccess/pair/response/#"]) ] for username, password, publishAclPatterns, subscribeAclPatterns in initUsers: if not self._db.addUser(username, password, publishAclPatterns, subscribeAclPatterns): self._db.updateUser(username, password, publishAclPatterns, subscribeAclPatterns) + obsoleteSystemUsers = ["pair-actor"] + for username in obsoleteSystemUsers: + self._db.deleteUser(username) + def _initMqtt(self): logging.info("Initializing MQTT") self._mqtt = client = mqtt.Client(client_id="backend") @@ -51,8 +57,13 @@ class MlmAccess: for user in self._db.getAllUsers(): if user.startswith("actor-"): actorSubject = f"mlmAccess/actor/{user.replace('actor-', '')}/status" - logging.info(f"* {actorSubject}") - client.subscribe(actorSubject) + elif user.startswith("panel-"): + actorSubject = "mlmAccess/panel" + else: + continue + + logging.info(f"* {actorSubject}") + client.subscribe(actorSubject) # call hooks of child objects self._pairingHanlder._mqttOnConnect(client, userdata, flags, rc) diff --git a/backend/pairingHandler.py b/backend/pairingHandler.py index 79b0970..c6a8e46 100644 --- a/backend/pairingHandler.py +++ b/backend/pairingHandler.py @@ -8,6 +8,9 @@ class MaPairingHandler: def handlePairingRequest(self, topic, message): if "actor" in topic: self._handleActorPairRequest(message) + elif "panel" in topic: + pass + #self. else: logging.warn(f"Invalid pairing request: {topic}, {message}, Device type not know.") diff --git a/mock-actor/mock-actor.py b/mock-clients/mock-actor.py similarity index 100% rename from mock-actor/mock-actor.py rename to mock-clients/mock-actor.py diff --git a/mock-actor/mock-actor-pair.py b/mock-clients/mock-client-pair.py similarity index 74% rename from mock-actor/mock-actor-pair.py rename to mock-clients/mock-client-pair.py index 117a2d6..433edc7 100644 --- a/mock-actor/mock-actor-pair.py +++ b/mock-clients/mock-client-pair.py @@ -3,7 +3,9 @@ import logging, coloredlogs coloredlogs.install(level='INFO', fmt='%(asctime)s - [%(levelname)s] %(message)s') -PAIR_ID = "101" +PAIR_ID = "1" +DEVICE_TYPE = "actor" # actor or panel +PAIR_SECRET = "pair-secret" def on_connect(client, userdata, flags, rc): global PAIR_ID @@ -14,10 +16,10 @@ def on_connect(client, userdata, flags, rc): logging.info("Successfully connected to MQTT broker") - client.subscribe("mlmAccess/pair/response/actor") + client.subscribe(f"mlmAccess/pair/response/{DEVICE_TYPE}") logging.info("Requesting pairing") - client.publish("mlmAccess/pair/request/actor", PAIR_ID) + client.publish(f"mlmAccess/pair/request/{DEVICE_TYPE}", PAIR_ID) def on_message(client, userdata, message): messageContent = str(message.payload.decode("utf-8")) @@ -29,8 +31,8 @@ def on_message(client, userdata, message): logging.error("Pairing was not successfull!") exit(1) -client = mqtt.Client(client_id="pair-actor") -client.username_pw_set("pair-actor", "pair-actor") +client = mqtt.Client(client_id=f"pair") +client.username_pw_set(f"pair", f"pair-secret") client.on_connect = on_connect client.on_message = on_message diff --git a/runBackend.sh b/runBackend.sh index 05fc4b1..16c5406 100755 --- a/runBackend.sh +++ b/runBackend.sh @@ -5,6 +5,6 @@ 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 +export MLMACCESS_PAIR_SECRET=pair-secret python3 backend/mlmAccess.py \ No newline at end of file