Add Audit Logging for several endpoints

- Use transactions for DB commits to avoid audit-less logs

Endpoints Supported:
- Manual Link
- Reset Row
- Update Row
pull/392/head
ZeldaZach 1 year ago
parent a34af372d0
commit c9878758ac
No known key found for this signature in database

@ -64,6 +64,15 @@ CREATE TYPE thumbnail_mode as ENUM (
'CUSTOM' 'CUSTOM'
); );
CREATE TABLE events_edits_audit_log (
timestamp TIMESTAMP NOT NULL DEFAULT NOW(),
modified_row_id UUID NOT NULL,
api_action TEXT NOT NULL,
editor TEXT NOT NULL,
old_data JSONB,
new_data JSONB
);
CREATE TABLE events ( CREATE TABLE events (
id UUID PRIMARY KEY, id UUID PRIMARY KEY,

@ -282,9 +282,9 @@ def update_row(ident, editor=None):
del new_row[extra] del new_row[extra]
# Check a row with id = ident is in the database # Check a row with id = ident is in the database
conn = app.db_manager.get_conn() with app.db_manager.get_conn() as conn:
built_query = sql.SQL(""" built_query = sql.SQL("""
SELECT id, state, {} SELECT FOR UPDATE id, state, {}
FROM events FROM events
WHERE id = %s WHERE id = %s
""").format(sql.SQL(', ').join( """).format(sql.SQL(', ').join(
@ -446,6 +446,7 @@ def update_row(ident, editor=None):
if result.rowcount != 1: if result.rowcount != 1:
return 'Video likely already published', 403 return 'Video likely already published', 403
_write_audit_log(conn, ident, "update-row", editor, old_row, new_row)
logging.info('Row {} updated to state {}'.format(ident, new_row['state'])) logging.info('Row {} updated to state {}'.format(ident, new_row['state']))
return '' return ''
@ -470,7 +471,7 @@ def manual_link(ident, editor=None):
else: else:
return 'Upload location must be "manual" or "youtube-manual"', 400 return 'Upload location must be "manual" or "youtube-manual"', 400
conn = app.db_manager.get_conn() with app.db_manager.get_conn() as conn:
results = database.query(conn, """ results = database.query(conn, """
SELECT id, state SELECT id, state
FROM events FROM events
@ -489,6 +490,15 @@ def manual_link(ident, editor=None):
editor = %s, edit_time = %s, upload_time = %s, thumbnail_mode = 'NONE' editor = %s, edit_time = %s, upload_time = %s, thumbnail_mode = 'NONE'
WHERE id = %s AND state = 'UNEDITED' WHERE id = %s AND state = 'UNEDITED'
""", upload_location, link, video_id, editor, now, now, ident) """, upload_location, link, video_id, editor, now, now, ident)
if results.rowcount > 0:
_write_audit_log(conn, ident, "manual-link", editor, new_row={
"state": "DONE",
"upload_location": upload_location,
"video_link": link,
"video_id": video_id,
"thumbnail_mode": None,
})
logging.info("Row {} video_link set to {}".format(ident, link)) logging.info("Row {} video_link set to {}".format(ident, link))
return '' return ''
@ -503,7 +513,7 @@ def reset_row(ident, editor=None):
(state is UNEDITED, EDITED or CLAIMED) (state is UNEDITED, EDITED or CLAIMED)
""" """
force = (flask.request.args.get('force', '').lower() == "true") force = (flask.request.args.get('force', '').lower() == "true")
conn = app.db_manager.get_conn() with app.db_manager.get_conn() as conn:
query = """ query = """
UPDATE events UPDATE events
SET state='UNEDITED', error = NULL, video_id = NULL, video_link = NULL, SET state='UNEDITED', error = NULL, video_id = NULL, video_link = NULL,
@ -516,6 +526,8 @@ def reset_row(ident, editor=None):
results = database.query(conn, query, ident) results = database.query(conn, query, ident)
if results.rowcount != 1: if results.rowcount != 1:
return 'Row id = {} not found or not in cancellable state'.format(ident), 404 return 'Row id = {} not found or not in cancellable state'.format(ident), 404
_write_audit_log(conn, ident, "reset-row", editor)
logging.info("Row {} reset to 'UNEDITED'".format(ident)) logging.info("Row {} reset to 'UNEDITED'".format(ident))
return '' return ''
@ -645,6 +657,13 @@ def time_is_pm(conn, timestamp, clock, timeofday):
return since_night.total_seconds() > 4*60*60 return since_night.total_seconds() > 4*60*60
def _write_audit_log(conn, ident, api_action, editor, old_row=None, new_row=None):
database.query(conn, """
INSERT INTO events_edits_audit_log (modified_row_id, api_action, editor, old_data, new_data)
VALUES (%(id)s, %(api_action)%s, %(editor)s, %(old_row)s, %(new_row)s)
""", id=ident, api_action=api_action, editor=editor, old_row=old_row, new_row=new_row)
@argh.arg('--host', help='Address or socket server will listen to. Default is 0.0.0.0 (everything on the local machine).') @argh.arg('--host', help='Address or socket server will listen to. Default is 0.0.0.0 (everything on the local machine).')
@argh.arg('--port', help='Port server will listen on. Default is 8004.') @argh.arg('--port', help='Port server will listen on. Default is 8004.')
@argh.arg('connection-string', help='Postgres connection string, which is either a space-separated list of key=value pairs, or a URI like: postgresql://USER:PASSWORD@HOST/DBNAME?KEY=VALUE') @argh.arg('connection-string', help='Postgres connection string, which is either a space-separated list of key=value pairs, or a URI like: postgresql://USER:PASSWORD@HOST/DBNAME?KEY=VALUE')

Loading…
Cancel
Save