176 lines
6.4 KiB
Python
176 lines
6.4 KiB
Python
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
|
|
import falcon
|
|
|
|
|
|
def access_control(req):
|
|
"""
|
|
Check administrator privilege in request headers to protect resources from invalid access
|
|
:param req: HTTP request
|
|
:return: HTTPError if invalid else None
|
|
"""
|
|
if 'USER-UUID' not in req.headers or \
|
|
not isinstance(req.headers['USER-UUID'], str) or \
|
|
len(str.strip(req.headers['USER-UUID'])) == 0:
|
|
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
|
|
description='API.INVALID_USER_UUID')
|
|
admin_user_uuid = str.strip(req.headers['USER-UUID'])
|
|
|
|
if 'TOKEN' not in req.headers or \
|
|
not isinstance(req.headers['TOKEN'], str) or \
|
|
len(str.strip(req.headers['TOKEN'])) == 0:
|
|
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
|
|
description='API.INVALID_TOKEN')
|
|
admin_token = str.strip(req.headers['TOKEN'])
|
|
|
|
# Check administrator privilege
|
|
cnx = mysql.connector.connect(**config.myems_user_db)
|
|
cursor = cnx.cursor()
|
|
query = (" SELECT utc_expires "
|
|
" FROM tbl_sessions "
|
|
" WHERE user_uuid = %s AND token = %s")
|
|
cursor.execute(query, (admin_user_uuid, admin_token,))
|
|
row = cursor.fetchone()
|
|
|
|
if row is None:
|
|
cursor.close()
|
|
cnx.close()
|
|
raise falcon.HTTPError(falcon.HTTP_404, title='API.NOT_FOUND',
|
|
description='API.ADMINISTRATOR_SESSION_NOT_FOUND')
|
|
else:
|
|
utc_expires = row[0]
|
|
if datetime.utcnow() > utc_expires:
|
|
cursor.close()
|
|
cnx.close()
|
|
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
|
|
description='API.ADMINISTRATOR_SESSION_TIMEOUT')
|
|
query = (" SELECT name "
|
|
" FROM tbl_users "
|
|
" WHERE uuid = %s AND is_admin = 1 ")
|
|
cursor.execute(query, (admin_user_uuid,))
|
|
row = cursor.fetchone()
|
|
cursor.close()
|
|
cnx.close()
|
|
if row is None:
|
|
raise falcon.HTTPError(falcon.HTTP_400, 'API.BAD_REQUEST', 'API.INVALID_PRIVILEGE')
|
|
|
|
|
|
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('write_log:' + str(e))
|
|
finally:
|
|
if cursor:
|
|
cursor.close()
|
|
if cnx:
|
|
cnx.close()
|
|
|
|
|
|
def user_logger(func):
|
|
"""
|
|
Decorator for logging user activities
|
|
:param func: the decorated function
|
|
:return: the decorator
|
|
"""
|
|
@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
|
|
headers = req.headers
|
|
if headers is not None and 'USER-UUID' in headers.keys():
|
|
user_uuid = headers['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)
|
|
os.remove(file_name)
|
|
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)
|
|
except Exception as e:
|
|
if isinstance(e, falcon.HTTPError):
|
|
raise e
|
|
else:
|
|
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)
|
|
os.remove(file_name)
|
|
func(*args, **kwargs)
|
|
write_log(user_uuid=user_uuid, request_method='PUT', resource_type=class_name,
|
|
resource_id=kwargs.get('id_'), request_body=raw_json)
|
|
except Exception as e:
|
|
if isinstance(e, falcon.HTTPError):
|
|
raise e
|
|
else:
|
|
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:
|
|
if isinstance(e, falcon.HTTPError):
|
|
raise e
|
|
else:
|
|
print('user_logger:' + str(e))
|
|
return
|
|
|
|
return logger
|