add audit logs

This commit is contained in:
lza_menace 2022-08-31 23:28:48 -07:00
parent 143d08f2f7
commit 3094205362
7 changed files with 71 additions and 17 deletions

View file

@ -3,7 +3,7 @@ from os import makedirs
import click import click
from flask import Blueprint, url_for, current_app from flask import Blueprint, url_for, current_app
from suchwow.models import Post, Profile, Comment, Notification, db, Moderator, Ban from suchwow.models import Post, Profile, Comment, Notification, db, Moderator, Ban, AuditEvent
from suchwow.utils.helpers import get_latest_tipped_posts from suchwow.utils.helpers import get_latest_tipped_posts
from suchwow.utils.helpers import get_top_posters, get_top_posts from suchwow.utils.helpers import get_top_posters, get_top_posts
from suchwow.reddit import make_post from suchwow.reddit import make_post
@ -20,7 +20,7 @@ def init():
makedirs(f"{config.DATA_FOLDER}/{i}", exist_ok=True) makedirs(f"{config.DATA_FOLDER}/{i}", exist_ok=True)
# init db # init db
db.create_tables([Post, Profile, Comment, Notification, Moderator, Ban]) db.create_tables([Post, Profile, Comment, Notification, Moderator, Ban, AuditEvent])
@bp.cli.command("post_reddit") @bp.cli.command("post_reddit")

View file

@ -143,3 +143,12 @@ class Ban(Model):
class Meta: class Meta:
database = db database = db
class AuditEvent(Model):
id = AutoField()
user = ForeignKeyField(Profile)
timestamp = DateTimeField(default=datetime.now)
action = CharField()
class Meta:
database = db

View file

@ -1,8 +1,8 @@
from flask import Blueprint, render_template, redirect, url_for, flash, request from flask import Blueprint, render_template, redirect, url_for, flash, request
from suchwow.models import Post, Profile, Moderator, Ban, get_ban_reason from suchwow.models import AuditEvent, Post, Profile, Moderator, Ban, get_ban_reason
from suchwow.utils.decorators import moderator_required from suchwow.utils.decorators import moderator_required
from suchwow.utils.helpers import get_session_user from suchwow.utils.helpers import get_session_user, audit_event
from suchwow import config from suchwow import config
@ -48,6 +48,7 @@ def manage_mods():
flash('Cannot delete super admin you son-of-a-bitch.', 'is-danger') flash('Cannot delete super admin you son-of-a-bitch.', 'is-danger')
else: else:
m.delete_instance() m.delete_instance()
audit_event(f'Deleted {to_delete} from mods')
flash(f'Removed {to_delete} from mods!', 'is-success') flash(f'Removed {to_delete} from mods!', 'is-success')
return redirect(url_for('mod.manage_mods')) return redirect(url_for('mod.manage_mods'))
if request.method == 'POST': if request.method == 'POST':
@ -61,6 +62,7 @@ def manage_mods():
else: else:
m = Moderator(username=to_add) m = Moderator(username=to_add)
m.save() m.save()
audit_event(f'Added {to_add} to mods')
flash(f'Added {to_add} to mods!', 'is-success') flash(f'Added {to_add} to mods!', 'is-success')
mods = Profile.select().join(Moderator, on=(Profile.username == Moderator.username)) mods = Profile.select().join(Moderator, on=(Profile.username == Moderator.username))
return render_template('mod/manage.html', mods=mods) return render_template('mod/manage.html', mods=mods)
@ -80,6 +82,7 @@ def manage_bans():
flash('Cannot ban super admin you son-of-a-bitch.', 'is-danger') flash('Cannot ban super admin you son-of-a-bitch.', 'is-danger')
else: else:
ban.delete_instance() ban.delete_instance()
audit_event(f'Removed ban on {to_delete}')
flash(f'Unbanned {to_delete}!', 'is-success') flash(f'Unbanned {to_delete}!', 'is-success')
return redirect(url_for('mod.manage_bans')) return redirect(url_for('mod.manage_bans'))
if request.method == 'POST': if request.method == 'POST':
@ -98,9 +101,14 @@ def manage_bans():
reason = get_ban_reason() reason = get_ban_reason()
ban = Ban(user=u, reason=reason) ban = Ban(user=u, reason=reason)
ban.save() ban.save()
audit_event(f'Banned {to_add} ({reason})')
flash(f'Banned {to_add}!', 'is-success') flash(f'Banned {to_add}!', 'is-success')
bans = Ban.select() bans = Ban.select()
return render_template('mod/bans.html', bans=bans) return render_template('mod/bans.html', bans=bans)
# audit trail of activity
# view wallet rpc logs @bp.route('/mods/logs')
@moderator_required
def view_logs():
events = AuditEvent.select().order_by(AuditEvent.timestamp.desc()).limit(50)
return render_template('mod/logs.html', logs=events)

View file

@ -2,6 +2,7 @@ from datetime import datetime, timedelta
from os import path, remove from os import path, remove
from io import BytesIO from io import BytesIO
from base64 import b64encode from base64 import b64encode
from sys import audit
from qrcode import make as qrcode_make from qrcode import make as qrcode_make
from flask import render_template, Blueprint, request, session, flash from flask import render_template, Blueprint, request, session, flash
from flask import send_from_directory, redirect, url_for, current_app from flask import send_from_directory, redirect, url_for, current_app
@ -12,8 +13,7 @@ from suchwow import config
from suchwow.models import Post, Profile, Comment, Ban from suchwow.models import Post, Profile, Comment, Ban
from suchwow.utils.decorators import login_required, profile_required, moderator_required from suchwow.utils.decorators import login_required, profile_required, moderator_required
from suchwow.utils.helpers import allowed_file, is_moderator, get_session_user from suchwow.utils.helpers import allowed_file, is_moderator, get_session_user
from suchwow.utils.helpers import rw_cache, post_webhook from suchwow.utils.helpers import post_webhook, audit_event
from suchwow.reddit import make_post
from suchwow.discord import post_discord_webhook from suchwow.discord import post_discord_webhook
@ -57,7 +57,6 @@ def create():
submitter = get_session_user() submitter = get_session_user()
u = Profile.filter(username=submitter) u = Profile.filter(username=submitter)
banned = Ban.filter(user=u).first() banned = Ban.filter(user=u).first()
print(banned)
if banned: if banned:
flash(f"You can't post: {banned.reason}", "is-danger") flash(f"You can't post: {banned.reason}", "is-danger")
return redirect("/") return redirect("/")
@ -105,7 +104,7 @@ def create():
post.save() post.save()
post.save_thumbnail() post.save_thumbnail()
url = url_for('post.read', id=post.id, _external=True) url = url_for('post.read', id=post.id, _external=True)
post_webhook(f"New post :doge2: [{post.id}]({url}) by `{submitter}` :neckbeard:") audit_event(f'Created new post {post.id}')
flash("New post created and pending approval!", "is-success") flash("New post created and pending approval!", "is-success")
return redirect(url_for("main.index")) return redirect(url_for("main.index"))
return render_template("post/create.html") return render_template("post/create.html")
@ -119,11 +118,10 @@ def approve(id):
if not post.approved: if not post.approved:
post.approved = True post.approved = True
post.save() post.save()
post_webhook(f"Post [{post.id}]({url}) approved :white_check_mark: by `{get_session_user()}` :fieri_parrot:")
flash("Approved", "is-success") flash("Approved", "is-success")
audit_event(f'Approved post {post.id}')
if current_app.config["DEBUG"] is False: if current_app.config["DEBUG"] is False:
post_discord_webhook(post) post_discord_webhook(post)
post_webhook(f"Post [{post.id}]({url}) submitted :dab_parrot: to Discord.")
return redirect(url_for("mod.pending_posts")) return redirect(url_for("mod.pending_posts"))
else: else:
flash("You can't approve this", "is-success") flash("You can't approve this", "is-success")
@ -141,8 +139,8 @@ def delete(id):
save_path_base = path.join(current_app.config["DATA_FOLDER"], "uploads") save_path_base = path.join(current_app.config["DATA_FOLDER"], "uploads")
save_path = path.join(save_path_base, post.image_name) save_path = path.join(save_path_base, post.image_name)
remove(save_path) remove(save_path)
audit_event(f'Deleted post {post.id}')
post.delete_instance() post.delete_instance()
post_webhook(f"Post {post.id} deleted :dumpsterfire: by `{user}` :godmode:")
flash("Deleted that shit, brah!", "is-success") flash("Deleted that shit, brah!", "is-success")
if is_mod: if is_mod:
return redirect(url_for("mod.pending_posts")) return redirect(url_for("mod.pending_posts"))

View file

@ -0,0 +1,29 @@
{% extends 'base.html' %}
{% block content %}
<div class="container" style="text-align:center;">
<h1 class="title">Manage Mods</h1>
<nav class="breadcrumb is-centered" aria-label="breadcrumbs">
<ul>
<li><a href="/">Home</a></li>
<li><a href="{{ url_for('mod.main') }}">Mods</a></li>
</ul>
</nav>
<section class="section">
{% for log in logs %}
<article class="message" style="width: 40%; margin: 1em auto;">
<div class="message-header">
<p>{{ log.user.username }} - {{ log.timestamp | humanize }} </p>
</div>
<div class="message-body">
{{ log.action }}
</div>
</article>
{% endfor %}
</section>
</div>
{% endblock %}
{% block footer %}{% endblock %}

View file

@ -52,7 +52,7 @@
</a> </a>
</li> </li>
<li> <li>
<a> <a href="{{ url_for('mod.view_logs') }}">
<span class="icon is-small"></span> <span class="icon is-small"></span>
<span>View Logs</span> <span>View Logs</span>
</a> </a>

View file

@ -1,10 +1,12 @@
import pickle import pickle
from os import path, remove from os import path
from datetime import datetime, timedelta from datetime import datetime, timedelta
from requests import post as r_post
from json import dumps from json import dumps
from requests import post as r_post
from flask import session, current_app from flask import session, current_app
from suchwow.models import Moderator, Post
from suchwow.models import Moderator, Post, AuditEvent, Profile
from suchwow.wownero import Wallet, from_atomic from suchwow.wownero import Wallet, from_atomic
from suchwow import config from suchwow import config
@ -20,6 +22,14 @@ def is_moderator(username):
else: else:
return False return False
def get_profile():
p = Profile.filter(username=get_session_user()).first()
return p
def audit_event(event):
e = AuditEvent(user=get_profile(), action=event)
e.save()
def get_session_user(): def get_session_user():
if "auth" not in session or not session["auth"]: if "auth" not in session or not session["auth"]:
return None return None