diff --git a/app/config_default.json b/app/config_default.json index 3e6c171..1e68910 100644 --- a/app/config_default.json +++ b/app/config_default.json @@ -19,7 +19,8 @@ ], "PLUGINS": { "slack_notifier": { - "SLACK_CHANNEL": "#prismo-debug", + "SLACK_TOOL_CHANNEL": "#prismo-debug", + "SLACK_DOOR_CHANNEL": "#prismo-door-channel", "SLACK_TOKEN": "xoxb-this-is-not-areal-slack-token" } }, diff --git a/app/models/user.py b/app/models/user.py index b3e3161..fa452f7 100644 --- a/app/models/user.py +++ b/app/models/user.py @@ -75,17 +75,38 @@ def get_permissions(cls, user_key=None): # Fetch user data and permissions user_data = [] # Filter by user_key if provided + # Here we also add "Latest Activity" column, for reporting latest any tool/door use by user if user_key: cursor.execute( - "SELECT users.name, users.key FROM users WHERE users.key = ?", + """ + SELECT users.name, users.key, + (SELECT operation_time + FROM event_logs + WHERE user_key = users.key + ORDER BY operation_time DESC + LIMIT 1) AS latest_activity + FROM users + WHERE users.key = ? + """, (user_key,), ) else: - cursor.execute("SELECT users.name, users.key FROM users") + cursor.execute( + """ + SELECT users.name, users.key, + (SELECT operation_time + FROM event_logs + WHERE user_key = users.key + ORDER BY operation_time DESC + LIMIT 1) AS latest_activity + FROM users + """ + ) for row in cursor.fetchall(): user_name = row[0] user_key = row[1] + latest_activity = row[2] # Get device permissions for the current user device_permissions = [] @@ -118,6 +139,7 @@ def get_permissions(cls, user_key=None): "user_name": user_name, "user_key": user_key, "permissions": device_permissions, + "latest_activity": latest_activity, } user_data.append(user_record) diff --git a/app/plugins/slack_notifier/slack_notifier.py b/app/plugins/slack_notifier/slack_notifier.py index ddcc63f..b9051d4 100644 --- a/app/plugins/slack_notifier/slack_notifier.py +++ b/app/plugins/slack_notifier/slack_notifier.py @@ -47,6 +47,48 @@ def unlock_message_block_constructor(tool, user): }] +def door_message_block_constructor(door, user): + return [ + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "emoji", + "name": "door", + "unicode": "1f6aa" + }, + { + "type": "text", + "text": " " + }, + { + "type": "text", + "text": "%s" % user, + "style": { + "bold": True + } + }, + { + "type": "text", + "text": " entered through the " + }, + { + "type": "text", + "text": "%s" % door, + "style": { + "bold": True + } + } + ] + } + ] + } + ] + + class SlackNotifierPlugin: def __init__(self, app_context): # Configure the Slack client with your token @@ -102,19 +144,52 @@ def get_device_name(self, device_id): return None + def get_device_type(self, device_id): + """ + Get device name based on its ID + """ + connection = sqlite3.connect(self.db_uri) + cursor = connection.cursor() + + cursor.execute("SELECT type from devices WHERE id = ?", + (device_id,), + ) + connection.commit() + result = cursor.fetchone() + + connection.close() + if result: + return result[0] + + return None + def access_log_entry_added(self, event): try: if event["operation"] == "unlock": user_name = self.get_user_name(event["user_key"]) device_name = self.get_device_name(event["device_id"]) + device_type = self.get_device_type(event["device_id"]) self.logger.info("Access log entry added") self.logger.info("User name: %s", user_name) - self.logger.info("Device name: %s", device_name) - text_message = "🔓 * %s Tool was unlocked* by %s" % (device_name, user_name) - blocks = unlock_message_block_constructor(device_name, user_name) - self.slack_app.client.chat_postMessage(channel=self.config["SLACK_CHANNEL"], - text=text_message, - blocks=blocks) + if device_type == "tool": + self.logger.info("Device name: %s", device_name) + text_message = "🔓 * %s Tool was unlocked* by %s" % (device_name, user_name) + blocks = unlock_message_block_constructor(device_name, user_name) + self.slack_app.client.chat_postMessage( + channel=self.config["SLACK_TOOL_CHANNEL"], + text=text_message, + blocks=blocks) + elif device_type == "door": + self.logger.info("Door opened: %s", device_name) + text_message = "🔓 * %s Door opened by * by %s" % (device_name, user_name) + blocks = door_message_block_constructor(device_name, user_name) + self.slack_app.client.chat_postMessage( + channel=self.config["SLACK_DOOR_CHANNEL"], + text=text_message, + blocks=blocks) + else: + self.logger.error("Unknown reader type! %s", device_type) + except Exception as e: self.logger.error("Error in SlackNotifierPlugin.access_log_entry_added: %s", e) raise e diff --git a/app/static/js/devices.js b/app/static/js/devices.js index 86cea70..1d9e7af 100644 --- a/app/static/js/devices.js +++ b/app/static/js/devices.js @@ -1,8 +1,8 @@ // Global: Device ID, which is pending for update let deviceIDForUpdate = null; - +let deviceTypeForUpdate = null; function flashFirmware() { - console.log("Flashing device: ", deviceIDForUpdate); + console.log("Flashing device: ", deviceIDForUpdate, deviceTypeForUpdate); const socket = new WebSocket("ws://" + location.host + "/reader_flasher"); const logContainer = document.getElementById("log-container"); @@ -12,7 +12,7 @@ function flashFirmware() { console.log( "WebSocket connection established, send device id for flashing", ); - socket.send(deviceIDForUpdate); + socket.send(JSON.stringify({"device_id":deviceIDForUpdate, "device_type": deviceTypeForUpdate})); }); function log(data) { const obj = JSON.parse(data); @@ -58,7 +58,7 @@ function generateAccordionItems(devices) {
- +
@@ -86,14 +86,15 @@ function generateUUID() { return uuid } -function addDevice(deviceName) { +function addDevice(deviceName, isDeviceTool) { // Generate random UUID for device ID const deviceId = generateUUID(); // Prepare device data const deviceData = { device_name: deviceName, device_id: deviceId, - device_type: "tool", + // Looks like there is a bug in bootstrap toogle 5, can not just get value of toggle:( + device_type: (isDeviceTool ? "tool" : "door"), }; // Make API call to add device fetch("/api/devices", { diff --git a/app/templates/layout.html b/app/templates/layout.html index 531a1be..cfb6035 100644 --- a/app/templates/layout.html +++ b/app/templates/layout.html @@ -5,8 +5,11 @@ Prismo + + + diff --git a/app/templates/prismo/devices.html b/app/templates/prismo/devices.html index 7975795..5c33b84 100644 --- a/app/templates/prismo/devices.html +++ b/app/templates/prismo/devices.html @@ -10,10 +10,12 @@ - + + diff --git a/app/templates/prismo/users.html b/app/templates/prismo/users.html index c3ffca2..c897055 100644 --- a/app/templates/prismo/users.html +++ b/app/templates/prismo/users.html @@ -106,6 +106,7 @@
Add last used RFID card as new user: Delete' } }) diff --git a/app/utils/fimware_updater.py b/app/utils/fimware_updater.py index d05fb27..853239b 100644 --- a/app/utils/fimware_updater.py +++ b/app/utils/fimware_updater.py @@ -13,7 +13,7 @@ from flask import current_app as app -def update_firmware_full(socket, device_id): +def update_firmware_full(socket, device_id, device_type): """Run full firmware update, by running firmware updater script This function runs firmware updater script, which erases memory, flashes @@ -26,6 +26,7 @@ def update_firmware_full(socket, device_id): socket -- communication websocket device_id -- device ID string, MUST be UUID string in format 550e8400-e29b-41d4-a716-446655440000 + device_type -- type of device, should be "tool" or "door" Returns: bool -- True if firmware update was successful, False otherwise @@ -43,7 +44,8 @@ def update_firmware_full(socket, device_id): run_environment["HOST_WIFI_SSID"] = app.config["PRISMO"]["WIFI_SSID"] run_environment["HOST_WIFI_PASSWORD"] = app.config["PRISMO"]["WIFI_PASSWORD"] script_cwd = script_path.parent - process = subprocess.Popen([script_path, device_id], cwd=script_cwd, + process = subprocess.Popen([script_path, device_id, device_type], cwd=script_cwd, + stdout=subprocess.PIPE, env=run_environment) except FileNotFoundError: data["text"] = "Cannot find firmware update script" @@ -87,19 +89,23 @@ def update_firmware_full(socket, device_id): def firmware_updater_route(websocket): device_id = None + device_type = None while device_id is None: message = websocket.receive() if message: try: + json_data = json.loads(message) # Check if we have received valid uuid string. - uuid.UUID(message) - device_id = message + uuid.UUID(json_data["device_id"]) + device_id = json_data["device_id"] + device_type = json_data["device_type"] + except ValueError: - print("No uuid string was received:", message) + app.logger.error("Wrong message received: %s", message) - if device_id is not None: - update_result = update_firmware_full(websocket, device_id) + if device_id is not None and device_type is not None: + update_result = update_firmware_full(websocket, device_id, device_type) app.logger.warning("Update result: %s", update_result) else: - app.logger.warning("No device id was received") + app.logger.warning("No device id or device type was received") app.logger.warning("Close websocket")