- Delete obsolete users
- Alter pairing credentials to allow panel pairing - Start working on panel workflow
This commit is contained in:
parent
23f4d4ced9
commit
c8d5b4a7f3
7 changed files with 89 additions and 16 deletions
56
README.md
56
README.md
|
@ -4,17 +4,59 @@ The backend server of MlmAccess
|
||||||
|
|
||||||
# Workflows
|
# Workflows
|
||||||
## Pairing
|
## Pairing
|
||||||
|
###### Actor
|
||||||
A new actor can pair like this:
|
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`
|
2. Subscribe to `mlmAccess/pair/response/actor`
|
||||||
3. Publish its desired ID to `mlmAccess/pair/request/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
|
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`
|
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
|
6. The actor may now connect using `actor-{id}` as username and the password it got in 4
|
||||||
|
|
||||||
## Actor normal operation
|
###### Panel
|
||||||
During normal operation (after pairing) the actor has to follow this workflow:
|
A new panel can pair in the same way as a new actor. Every occurence of `actor` as to be replaced by `panel`, though.
|
||||||
1. The actor subscribes to `mlmAccess/actor/{id}/action`
|
|
||||||
2. The actor publishes its status to `mlmAccess/actor/{id}/status`
|
## Actor
|
||||||
3. The backend will publish a requested state change to `mlmAccess/actor/{id}/action`
|
The actor should subscribe to:
|
||||||
4. The actor will publish its state to `mlmAccess/actor/{id}/status` to let the backend know that the action was executed successfully
|
- `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
|
|
@ -90,6 +90,21 @@ class MaDbHelper:
|
||||||
|
|
||||||
return users
|
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):
|
def _updateUser(self, username, password, publishAclPatterns, subscribeAclPatterns):
|
||||||
|
|
||||||
query = """
|
query = """
|
||||||
|
|
13
backend/mlmAccess.py
Normal file → Executable file
13
backend/mlmAccess.py
Normal file → Executable file
|
@ -1,3 +1,5 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
import paho.mqtt.client as mqtt
|
import paho.mqtt.client as mqtt
|
||||||
from dbHelper import MaDbHelper
|
from dbHelper import MaDbHelper
|
||||||
from pairingHandler import MaPairingHandler
|
from pairingHandler import MaPairingHandler
|
||||||
|
@ -23,13 +25,17 @@ class MlmAccess:
|
||||||
self._db = MaDbHelper(self._config)
|
self._db = MaDbHelper(self._config)
|
||||||
initUsers = [
|
initUsers = [
|
||||||
("backend", self._config["MQTT_BACKEND_PASSWORD"], ["mlmAccess/#"], ["mlmAccess/#"]),
|
("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:
|
for username, password, publishAclPatterns, subscribeAclPatterns in initUsers:
|
||||||
if not self._db.addUser(username, password, publishAclPatterns, subscribeAclPatterns):
|
if not self._db.addUser(username, password, publishAclPatterns, subscribeAclPatterns):
|
||||||
self._db.updateUser(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):
|
def _initMqtt(self):
|
||||||
logging.info("Initializing MQTT")
|
logging.info("Initializing MQTT")
|
||||||
self._mqtt = client = mqtt.Client(client_id="backend")
|
self._mqtt = client = mqtt.Client(client_id="backend")
|
||||||
|
@ -51,6 +57,11 @@ class MlmAccess:
|
||||||
for user in self._db.getAllUsers():
|
for user in self._db.getAllUsers():
|
||||||
if user.startswith("actor-"):
|
if user.startswith("actor-"):
|
||||||
actorSubject = f"mlmAccess/actor/{user.replace('actor-', '')}/status"
|
actorSubject = f"mlmAccess/actor/{user.replace('actor-', '')}/status"
|
||||||
|
elif user.startswith("panel-"):
|
||||||
|
actorSubject = "mlmAccess/panel"
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
logging.info(f"* {actorSubject}")
|
logging.info(f"* {actorSubject}")
|
||||||
client.subscribe(actorSubject)
|
client.subscribe(actorSubject)
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,9 @@ class MaPairingHandler:
|
||||||
def handlePairingRequest(self, topic, message):
|
def handlePairingRequest(self, topic, message):
|
||||||
if "actor" in topic:
|
if "actor" in topic:
|
||||||
self._handleActorPairRequest(message)
|
self._handleActorPairRequest(message)
|
||||||
|
elif "panel" in topic:
|
||||||
|
pass
|
||||||
|
#self.
|
||||||
else:
|
else:
|
||||||
logging.warn(f"Invalid pairing request: {topic}, {message}, Device type not know.")
|
logging.warn(f"Invalid pairing request: {topic}, {message}, Device type not know.")
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,9 @@ import logging, coloredlogs
|
||||||
|
|
||||||
coloredlogs.install(level='INFO', fmt='%(asctime)s - [%(levelname)s] %(message)s')
|
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):
|
def on_connect(client, userdata, flags, rc):
|
||||||
global PAIR_ID
|
global PAIR_ID
|
||||||
|
@ -14,10 +16,10 @@ def on_connect(client, userdata, flags, rc):
|
||||||
|
|
||||||
logging.info("Successfully connected to MQTT broker")
|
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")
|
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):
|
def on_message(client, userdata, message):
|
||||||
messageContent = str(message.payload.decode("utf-8"))
|
messageContent = str(message.payload.decode("utf-8"))
|
||||||
|
@ -29,8 +31,8 @@ def on_message(client, userdata, message):
|
||||||
logging.error("Pairing was not successfull!")
|
logging.error("Pairing was not successfull!")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
client = mqtt.Client(client_id="pair-actor")
|
client = mqtt.Client(client_id=f"pair")
|
||||||
client.username_pw_set("pair-actor", "pair-actor")
|
client.username_pw_set(f"pair", f"pair-secret")
|
||||||
|
|
||||||
client.on_connect = on_connect
|
client.on_connect = on_connect
|
||||||
client.on_message = on_message
|
client.on_message = on_message
|
|
@ -5,6 +5,6 @@ export MLMACCESS_MYSQL_PASSWORD=MlmAccess
|
||||||
export MLMACCESS_MYSQL_DATABASE=MlmAccess
|
export MLMACCESS_MYSQL_DATABASE=MlmAccess
|
||||||
export MLMACCESS_MYSQL_HOST=localhost
|
export MLMACCESS_MYSQL_HOST=localhost
|
||||||
export MLMACCESS_MQTT_HOST=localhost
|
export MLMACCESS_MQTT_HOST=localhost
|
||||||
export MLMACCESS_PAIR_SECRET=pair-actor
|
export MLMACCESS_PAIR_SECRET=pair-secret
|
||||||
|
|
||||||
python3 backend/mlmAccess.py
|
python3 backend/mlmAccess.py
|
Loading…
Reference in a new issue