diff --git a/database/myems_system_db.sql b/database/myems_system_db.sql index 9244d43e..3d54cd70 100644 --- a/database/myems_system_db.sql +++ b/database/myems_system_db.sql @@ -1243,6 +1243,6 @@ USE `myems_system_db`; INSERT INTO `myems_system_db`.`tbl_versions` (`id`, `version`, `release_date`) VALUES -(1, '1.2.1', '2021-08-19'); +(1, '1.2.2', '2021-08-28'); COMMIT; diff --git a/database/myems_user_db.sql b/database/myems_user_db.sql index 92a477cb..8fe493f8 100644 --- a/database/myems_user_db.sql +++ b/database/myems_user_db.sql @@ -66,15 +66,18 @@ DROP TABLE IF EXISTS `myems_user_db`.`tbl_logs` ; CREATE TABLE IF NOT EXISTS `myems_user_db`.`tbl_logs` ( `id` BIGINT NOT NULL AUTO_INCREMENT, - `user_id` BIGINT NOT NULL, - `utc_date_time` DATETIME NOT NULL, - `activity` VARCHAR(256) NOT NULL, + `user_uuid` CHAR(36) NOT NULL, + `request_datetime_utc` DATETIME NOT NULL, + `request_method` VARCHAR(256) NOT NULL, + `resource_type` VARCHAR(256) NOT NULL, + `resource_id` BIGINT NULL, + `request_body` JSON NULL, PRIMARY KEY (`id`)); +CREATE INDEX `tbl_logs_index_1` ON `myems_user_db`.`tbl_logs` (`user_uuid`, `request_datetime_utc`, `request_method`); - --- ---------------------------------------------------------------------------------- +-- --------------------------------------------------------------------------------------------------------------------- -- Table `myems_user_db`.`tbl_notifications` --- ---------------------------------------------------------------------------------- +-- --------------------------------------------------------------------------------------------------------------------- DROP TABLE IF EXISTS `myems_user_db`.`tbl_notifications` ; CREATE TABLE IF NOT EXISTS `myems_user_db`.`tbl_notifications` ( diff --git a/database/upgrade/upgrade1.2.2.sql b/database/upgrade/upgrade1.2.2.sql new file mode 100644 index 00000000..1e1240ca --- /dev/null +++ b/database/upgrade/upgrade1.2.2.sql @@ -0,0 +1,15 @@ +DROP TABLE IF EXISTS `myems_user_db`.`tbl_logs` ; + +CREATE TABLE IF NOT EXISTS `myems_user_db`.`tbl_logs` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `user_uuid` CHAR(36) NOT NULL, + `request_datetime_utc` DATETIME NOT NULL, + `request_method` VARCHAR(256) NOT NULL, + `resource_type` VARCHAR(256) NOT NULL, + `resource_id` BIGINT NULL, + `request_body` JSON NULL, + PRIMARY KEY (`id`)); +CREATE INDEX `tbl_logs_index_1` ON `myems_user_db`.`tbl_logs` (`user_uuid`, `request_datetime_utc`, `request_method`); + +-- UPDATE VERSION NUMBER +UPDATE myems_system_db.tbl_versions SET version='1.2.2', release_date='2021-08-28' WHERE id=1; diff --git a/myems-api/core/equipment.py b/myems-api/core/equipment.py index ee9f59aa..42f39db3 100644 --- a/myems-api/core/equipment.py +++ b/myems-api/core/equipment.py @@ -3,6 +3,7 @@ import simplejson as json import mysql.connector import config import uuid +from core.userlogger import user_logger class EquipmentCollection: @@ -58,6 +59,7 @@ class EquipmentCollection: resp.body = json.dumps(result) @staticmethod + @user_logger def on_post(req, resp): """Handles POST requests""" try: @@ -200,6 +202,7 @@ class EquipmentItem: resp.body = json.dumps(meta_result) @staticmethod + @user_logger def on_delete(req, resp, id_): if not id_.isdigit() or int(id_) <= 0: raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', @@ -286,6 +289,7 @@ class EquipmentItem: resp.status = falcon.HTTP_204 @staticmethod + @user_logger def on_put(req, resp, id_): """Handles PUT requests""" if not id_.isdigit() or int(id_) <= 0: @@ -382,6 +386,7 @@ class EquipmentItem: # Clone an Equipment @staticmethod + @user_logger def on_post(req, resp, id_): """Handles PUT requests""" if not id_.isdigit() or int(id_) <= 0: @@ -658,6 +663,7 @@ class EquipmentParameterCollection: resp.body = json.dumps(result) @staticmethod + @user_logger def on_post(req, resp, id_): """Handles POST requests""" if not id_.isdigit() or int(id_) <= 0: @@ -841,6 +847,7 @@ class EquipmentParameterCollection: class EquipmentParameterItem: @staticmethod + @user_logger def __init__(): """Initializes EquipmentParameterItem""" pass @@ -966,6 +973,7 @@ class EquipmentParameterItem: resp.body = json.dumps(meta_result) @staticmethod + @user_logger def on_delete(req, resp, id_, pid): if not id_.isdigit() or int(id_) <= 0: raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', @@ -1012,6 +1020,7 @@ class EquipmentParameterItem: resp.status = falcon.HTTP_204 @staticmethod + @user_logger def on_put(req, resp, id_, pid): """Handles POST requests""" if not id_.isdigit() or int(id_) <= 0: @@ -1270,6 +1279,7 @@ class EquipmentMeterCollection: resp.body = json.dumps(result) @staticmethod + @user_logger def on_post(req, resp, id_): """Handles POST requests""" try: @@ -1350,6 +1360,7 @@ class EquipmentMeterItem: resp.status = falcon.HTTP_200 @staticmethod + @user_logger def on_delete(req, resp, id_, mid): if not id_.isdigit() or int(id_) <= 0: raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', @@ -1457,6 +1468,7 @@ class EquipmentOfflineMeterCollection: resp.body = json.dumps(result) @staticmethod + @user_logger def on_post(req, resp, id_): """Handles POST requests""" try: @@ -1537,6 +1549,7 @@ class EquipmentOfflineMeterItem: resp.status = falcon.HTTP_200 @staticmethod + @user_logger def on_delete(req, resp, id_, mid): if not id_.isdigit() or int(id_) <= 0: raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', @@ -1645,6 +1658,7 @@ class EquipmentVirtualMeterCollection: resp.body = json.dumps(result) @staticmethod + @user_logger def on_post(req, resp, id_): """Handles POST requests""" try: @@ -1725,6 +1739,7 @@ class EquipmentVirtualMeterItem: resp.status = falcon.HTTP_200 @staticmethod + @user_logger def on_delete(req, resp, id_, mid): if not id_.isdigit() or int(id_) <= 0: raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', diff --git a/myems-api/core/userlogger.py b/myems-api/core/userlogger.py new file mode 100644 index 00000000..0e48a0c8 --- /dev/null +++ b/myems-api/core/userlogger.py @@ -0,0 +1,107 @@ +import os +from functools import wraps +import config +import mysql.connector +from datetime import datetime +import uuid +from gunicorn.http.body import Body +import simplejson as json + + +def write_log(user_uuid, request_method, resource_type, resource_id, request_body): + """ + :param user_uuid: user_uuid + :param request_method: 'POST', 'PUT', 'DELETE' + :param resource_type: class_name + :param resource_id: int + :param request_body: json in raw string + """ + cnx = None + cursor = None + try: + cnx = mysql.connector.connect(**config.myems_user_db) + cursor = cnx.cursor() + add_row = (" INSERT INTO tbl_logs " + " (user_uuid, request_datetime_utc, request_method, resource_type, resource_id, request_body) " + " VALUES (%s, %s, %s, %s, %s , %s) ") + cursor.execute(add_row, (user_uuid, + datetime.utcnow(), + request_method, + resource_type, + resource_id if resource_id else None, + request_body if request_body else None, + )) + cnx.commit() + except Exception as e: + print(str(e)) + finally: + if cnx: + cnx.disconnect() + if cursor: + cursor.close() + + +def user_logger(func): + @wraps(func) + def logger(*args, **kwargs): + qualified_name = func.__qualname__ + class_name = qualified_name.split(".")[0] + func_name = qualified_name.split(".")[1] + + if func_name not in ("on_post", "on_put", "on_delete"): + # do not log for other HTTP Methods + func(*args, **kwargs) + return + req, resp = args + cookies = req.cookies + if cookies is not None and 'user_uuid' in cookies.keys(): + user_uuid = cookies['user_uuid'] + else: + # todo: deal with requests with NULL user_uuid + print('user_logger: user_uuid is NULL') + # do not log for NULL user_uuid + func(*args, **kwargs) + return + + if func_name == "on_post": + try: + file_name = str(uuid.uuid4()) + with open(file_name, "wb") as fw: + reads = req.stream.read() + fw.write(reads) + raw_json = reads.decode('utf-8') + with open(file_name, "rb") as fr: + req.stream = Body(fr) + func(*args, **kwargs) + write_log(user_uuid=user_uuid, request_method='POST', resource_type=class_name, + resource_id=kwargs.get('id_'), request_body=raw_json) + os.remove(file_name) + except Exception as e: + print('user_logger:' + str(e)) + return + elif func_name == "on_put": + try: + file_name = str(uuid.uuid4()) + with open(file_name, "wb") as fw: + reads = req.stream.read() + fw.write(reads) + raw_json = reads.decode('utf-8') + with open(file_name, "rb") as fr: + req.stream = Body(fr) + func(*args, **kwargs) + write_log(user_uuid=user_uuid, request_method="POST", resource_type=class_name, + resource_id=kwargs.get('id_'), request_body=raw_json) + os.remove(file_name) + except Exception as e: + print('user_logger:' + str(e)) + return + elif func_name == "on_delete": + try: + func(*args, **kwargs) + write_log(user_uuid=user_uuid, request_method="DELETE", resource_type=class_name, + resource_id=kwargs.get('id_'), request_body=json.dumps(kwargs)) + except Exception as e: + print('user_logger:' + str(e)) + return + + return logger