updated userlogger in API

pull/67/head
13621160019@163.com 2021-08-27 17:21:01 +08:00 committed by Caozhenhui
parent d27386c57e
commit 91a0342c2f
5 changed files with 128 additions and 217 deletions

View File

@ -66,18 +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,
`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_id`, `request_datetime_utc`, `request_method`);
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` (

View File

@ -2,14 +2,14 @@ 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,
`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_id`, `request_datetime_utc`, `request_method`);
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;

View File

@ -3,23 +3,20 @@ import simplejson as json
import mysql.connector
import config
import uuid
from log import decorator_record_action_log
from core.userlogger import user_logger
class EquipmentCollection:
@staticmethod
@decorator_record_action_log
def __init__():
"""Initializes EquipmentCollection"""
pass
@staticmethod
@decorator_record_action_log
def on_options(req, resp):
resp.status = falcon.HTTP_200
@staticmethod
@decorator_record_action_log
def on_get(req, resp):
cnx = mysql.connector.connect(**config.myems_system_db)
cursor = cnx.cursor(dictionary=True)
@ -62,7 +59,7 @@ class EquipmentCollection:
resp.body = json.dumps(result)
@staticmethod
@decorator_record_action_log
@user_logger
def on_post(req, resp):
"""Handles POST requests"""
try:
@ -150,18 +147,15 @@ class EquipmentCollection:
class EquipmentItem:
@staticmethod
@decorator_record_action_log
def __init__():
"""Initializes EquipmentItem"""
pass
@staticmethod
@decorator_record_action_log
def on_options(req, resp, id_):
resp.status = falcon.HTTP_200
@staticmethod
@decorator_record_action_log
def on_get(req, resp, id_):
if not id_.isdigit() or int(id_) <= 0:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
@ -208,7 +202,7 @@ class EquipmentItem:
resp.body = json.dumps(meta_result)
@staticmethod
@decorator_record_action_log
@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',
@ -295,7 +289,7 @@ class EquipmentItem:
resp.status = falcon.HTTP_204
@staticmethod
@decorator_record_action_log
@user_logger
def on_put(req, resp, id_):
"""Handles PUT requests"""
if not id_.isdigit() or int(id_) <= 0:
@ -392,7 +386,7 @@ class EquipmentItem:
# Clone an Equipment
@staticmethod
@decorator_record_action_log
@user_logger
def on_post(req, resp, id_):
"""Handles PUT requests"""
if not id_.isdigit() or int(id_) <= 0:
@ -538,18 +532,15 @@ class EquipmentItem:
class EquipmentParameterCollection:
@staticmethod
@decorator_record_action_log
def __init__():
"""Initializes EquipmentParameterCollection"""
pass
@staticmethod
@decorator_record_action_log
def on_options(req, resp, id_):
resp.status = falcon.HTTP_200
@staticmethod
@decorator_record_action_log
def on_get(req, resp, id_):
if not id_.isdigit() or int(id_) <= 0:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
@ -672,7 +663,7 @@ class EquipmentParameterCollection:
resp.body = json.dumps(result)
@staticmethod
@decorator_record_action_log
@user_logger
def on_post(req, resp, id_):
"""Handles POST requests"""
if not id_.isdigit() or int(id_) <= 0:
@ -856,18 +847,16 @@ class EquipmentParameterCollection:
class EquipmentParameterItem:
@staticmethod
@decorator_record_action_log
@user_logger
def __init__():
"""Initializes EquipmentParameterItem"""
pass
@staticmethod
@decorator_record_action_log
def on_options(req, resp, id_, pid):
resp.status = falcon.HTTP_200
@staticmethod
@decorator_record_action_log
def on_get(req, resp, id_, pid):
if not id_.isdigit() or int(id_) <= 0:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
@ -984,7 +973,7 @@ class EquipmentParameterItem:
resp.body = json.dumps(meta_result)
@staticmethod
@decorator_record_action_log
@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',
@ -1031,7 +1020,7 @@ class EquipmentParameterItem:
resp.status = falcon.HTTP_204
@staticmethod
@decorator_record_action_log
@user_logger
def on_put(req, resp, id_, pid):
"""Handles POST requests"""
if not id_.isdigit() or int(id_) <= 0:
@ -1233,18 +1222,15 @@ class EquipmentParameterItem:
class EquipmentMeterCollection:
@staticmethod
@decorator_record_action_log
def __init__():
"""Initializes EquipmentMeterCollection"""
pass
@staticmethod
@decorator_record_action_log
def on_options(req, resp, id_):
resp.status = falcon.HTTP_200
@staticmethod
@decorator_record_action_log
def on_get(req, resp, id_):
if not id_.isdigit() or int(id_) <= 0:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
@ -1293,7 +1279,7 @@ class EquipmentMeterCollection:
resp.body = json.dumps(result)
@staticmethod
@decorator_record_action_log
@user_logger
def on_post(req, resp, id_):
"""Handles POST requests"""
try:
@ -1365,18 +1351,16 @@ class EquipmentMeterCollection:
class EquipmentMeterItem:
@staticmethod
@decorator_record_action_log
def __init__():
"""Initializes EquipmentMeterItem"""
pass
@staticmethod
@decorator_record_action_log
def on_options(req, resp, id_, mid):
resp.status = falcon.HTTP_200
@staticmethod
@decorator_record_action_log
@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',
@ -1427,18 +1411,15 @@ class EquipmentMeterItem:
class EquipmentOfflineMeterCollection:
@staticmethod
@decorator_record_action_log
def __init__():
"""Initializes EquipmentOfflineMeterCollection"""
pass
@staticmethod
@decorator_record_action_log
def on_options(req, resp, id_):
resp.status = falcon.HTTP_200
@staticmethod
@decorator_record_action_log
def on_get(req, resp, id_):
if not id_.isdigit() or int(id_) <= 0:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
@ -1487,7 +1468,7 @@ class EquipmentOfflineMeterCollection:
resp.body = json.dumps(result)
@staticmethod
@decorator_record_action_log
@user_logger
def on_post(req, resp, id_):
"""Handles POST requests"""
try:
@ -1559,18 +1540,16 @@ class EquipmentOfflineMeterCollection:
class EquipmentOfflineMeterItem:
@staticmethod
@decorator_record_action_log
def __init__():
"""Initializes EquipmentOfflineMeterItem"""
pass
@staticmethod
@decorator_record_action_log
def on_options(req, resp, id_, mid):
resp.status = falcon.HTTP_200
@staticmethod
@decorator_record_action_log
@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',
@ -1622,18 +1601,15 @@ class EquipmentOfflineMeterItem:
class EquipmentVirtualMeterCollection:
@staticmethod
@decorator_record_action_log
def __init__():
"""Initializes EquipmentVirtualMeterCollection"""
pass
@staticmethod
@decorator_record_action_log
def on_options(req, resp, id_):
resp.status = falcon.HTTP_200
@staticmethod
@decorator_record_action_log
def on_get(req, resp, id_):
if not id_.isdigit() or int(id_) <= 0:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
@ -1682,7 +1658,7 @@ class EquipmentVirtualMeterCollection:
resp.body = json.dumps(result)
@staticmethod
@decorator_record_action_log
@user_logger
def on_post(req, resp, id_):
"""Handles POST requests"""
try:
@ -1754,18 +1730,16 @@ class EquipmentVirtualMeterCollection:
class EquipmentVirtualMeterItem:
@staticmethod
@decorator_record_action_log
def __init__():
"""Initializes EquipmentVirtualMeterItem"""
pass
@staticmethod
@decorator_record_action_log
def on_options(req, resp, id_, mid):
resp.status = falcon.HTTP_200
@staticmethod
@decorator_record_action_log
@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',

View File

@ -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

View File

@ -1,170 +0,0 @@
import os
from functools import wraps
import config
import mysql.connector
from datetime import datetime, timezone
import uuid
from gunicorn.http.body import Body
def write_log(user_uuid, action, _class, record_id, record_text):
"""
:param user_uuid: user_uuid
:param action: create, update, delete and others: login, logout, reset password, change password
:param _class: class_name
:param record_id: int
:param record_text: str
"""
now = datetime.utcnow()
cnx = None
cursor = None
try:
cnx = mysql.connector.connect(**config.myems_user_db)
cursor = cnx.cursor()
cursor.execute(" SELECT display_name "
" FROM tbl_users "
" WHERE uuid = %s ",
(user_uuid,))
row = cursor.fetchone()
user = dict()
if row is not None and len(row) > 0:
user["name"] = row[0]
else:
user["name"] = "visitor"
cnx = mysql.connector.connect(**config.myems_user_db)
cursor = cnx.cursor()
add_row = (" INSERT INTO tbl_action_logs "
" (user_name, date_time_utc, action, class, record_id, record_text) "
" VALUES (%s, %s, %s, %s, %s , %s) ")
cursor.execute(add_row, (user['name'],
now,
action,
_class,
record_id if record_id else None,
record_text if record_text else None,
))
cnx.commit()
except Exception as e:
print(str(e))
finally:
if cnx:
cnx.disconnect()
if cursor:
cursor.close()
def judge_admin(user_uuid):
cnx = None
cursor = None
try:
cnx = mysql.connector.connect(**config.myems_user_db)
cursor = cnx.cursor()
cursor.execute(" SELECT is_admin "
" FROM tbl_users "
" WHERE uuid = %s ",
(user_uuid,))
row = cursor.fetchone()
user = dict()
if row is not None and len(row) > 0:
user["admin"] = True if row[0] == 1 else False
else:
user["admin"] = False
return user["admin"]
except Exception as e:
print(str(e))
return False
finally:
if cnx:
cnx.disconnect()
if cursor:
cursor.close()
def decorator_record_action_log(func):
@wraps(func)
def log_fun(*args, **kwargs):
type_dict = {
"on_post": "create",
"on_put": "update",
"on_delete": "delete",
}
func_names = func.__qualname__
class_name = func_names.split(".")[0]
fun_name = func_names.split(".")[1]
# Judge on_post, on_put, on_delete
if fun_name not in type_dict.keys():
return func(*args, **kwargs)
action = type_dict.get(fun_name)
# Judge is_admin or not
if len(args) > 1:
req, resp = args
cookies = req.cookies
if cookies is not None and 'user_uuid' in cookies.keys():
user_uuid = cookies['user_uuid']
is_admin = judge_admin(user_uuid)
else:
user_uuid = None
is_admin = False
else:
return func(*args, **kwargs)
if not is_admin:
return func(*args, **kwargs)
if class_name == "UserLogin":
action = "login"
elif class_name == "UserLogout":
action = "logout"
elif class_name == "ResetPassword":
action = "reset password"
elif class_name == "ChangePassword":
action = "change password"
else:
pass
if fun_name == "on_post":
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)
write_log(user_uuid=user_uuid, action=action, _class=class_name,
record_id=None, record_text=raw_json)
func(*args, **kwargs)
os.remove(file_name)
return
elif fun_name == "on_put":
id_ = kwargs.get('id_')
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)
write_log(user_uuid=user_uuid, action=action, _class=class_name,
record_id=id_, record_text=raw_json)
func(*args, **kwargs)
os.remove(file_name)
return
elif fun_name == "on_delete":
id_ = kwargs.get('id_')
write_log(user_uuid=user_uuid, action=action, _class=class_name,
record_id=id_, record_text=None)
func(*args, **kwargs)
return
else:
func(*args, **kwargs)
return
return log_fun