modified: web/app.py
modified: web/blueprints/auth.py modified: web/blueprints/group_admin.py modified: web/blueprints/site_admin.py new file: web/limiter.py modified: web/panel_db.py modified: web/requirements.txt new file: web/templates/429.html new file: web/templates/admin/audit_log.html modified: web/templates/admin/base.html
This commit is contained in:
@@ -10,6 +10,7 @@ from config import Config
|
||||
from mailer import send_mail, build_invite_email, force_https_url
|
||||
import panel_db as db
|
||||
from roles import GROUP_MANAGEMENT_ROLES, GROUP_ROLE_OPTIONS, GROUP_ROLE_SET, OWNER_ONLY_ROLES, role_label
|
||||
from limiter import limiter
|
||||
|
||||
group_admin = Blueprint("group_admin", __name__, url_prefix="/group-admin")
|
||||
|
||||
@@ -95,12 +96,20 @@ def member_add():
|
||||
return redirect(url_for("group_admin.members"))
|
||||
if user_id:
|
||||
db.add_group_member(user_id, group_id, role)
|
||||
target_user = db.get_user_by_id(user_id)
|
||||
db.log_audit_event(
|
||||
session["user_id"], session["username"], "member.added",
|
||||
entity_type="user", entity_id=user_id,
|
||||
details={"role": role, "target": target_user["username"] if target_user else str(user_id)},
|
||||
group_id=group_id, ip_address=request.remote_addr,
|
||||
)
|
||||
flash("Member added.", "success")
|
||||
return redirect(url_for("group_admin.members"))
|
||||
|
||||
|
||||
@group_admin.route("/members/invite", methods=["POST"])
|
||||
@group_admin_required
|
||||
@limiter.limit("30 per hour", methods=["POST"])
|
||||
def member_invite():
|
||||
group_id = session["group_id"]
|
||||
username = request.form.get("username", "").strip()
|
||||
@@ -146,6 +155,12 @@ def member_invite():
|
||||
token = db.create_group_invite(group_id, username, email, role, session["user_id"])
|
||||
invite = db.get_invite_by_token(token)
|
||||
invite_url = force_https_url(url_for("auth.accept_invite", token=token, _external=True))
|
||||
db.log_audit_event(
|
||||
session["user_id"], session["username"], "invite.created",
|
||||
entity_type="invite", entity_id=invite["id"] if invite else None,
|
||||
details={"username": username, "email": email, "role": role},
|
||||
group_id=group_id, ip_address=request.remote_addr,
|
||||
)
|
||||
mail_settings = db.get_site_mail_settings()
|
||||
|
||||
if mail_settings:
|
||||
@@ -171,6 +186,7 @@ def member_invite():
|
||||
|
||||
@group_admin.route("/invites/<int:invite_id>/resend", methods=["POST"])
|
||||
@group_admin_required
|
||||
@limiter.limit("20 per hour", methods=["POST"])
|
||||
def resend_invite(invite_id):
|
||||
group_id = session["group_id"]
|
||||
invite = db.get_group_invite_by_id(invite_id, group_id)
|
||||
@@ -204,6 +220,12 @@ def resend_invite(invite_id):
|
||||
try:
|
||||
send_mail(mail_settings, invite["invited_email"], subject, text_body, html_body=html_body)
|
||||
db.mark_group_invite_sent(invite_id, group_id)
|
||||
db.log_audit_event(
|
||||
session["user_id"], session["username"], "invite.resent",
|
||||
entity_type="invite", entity_id=invite_id,
|
||||
details={"to": invite["invited_email"], "username": invite["invited_username"]},
|
||||
group_id=group_id, ip_address=request.remote_addr,
|
||||
)
|
||||
flash("Invitation email resent.", "success")
|
||||
except Exception:
|
||||
flash("Resend failed. Please verify SMTP settings and try again.", "danger")
|
||||
@@ -213,7 +235,14 @@ def resend_invite(invite_id):
|
||||
@group_admin.route("/invites/<int:invite_id>/revoke", methods=["POST"])
|
||||
@group_admin_required
|
||||
def revoke_invite(invite_id):
|
||||
invite = db.get_group_invite_by_id(invite_id, session["group_id"])
|
||||
db.revoke_group_invite(invite_id, session["group_id"])
|
||||
db.log_audit_event(
|
||||
session["user_id"], session["username"], "invite.revoked",
|
||||
entity_type="invite", entity_id=invite_id,
|
||||
details={"username": invite["invited_username"] if invite else None},
|
||||
group_id=session["group_id"], ip_address=request.remote_addr,
|
||||
)
|
||||
flash("Invitation revoked.", "success")
|
||||
return redirect(url_for("group_admin.members"))
|
||||
|
||||
@@ -241,7 +270,14 @@ def member_edit(user_id):
|
||||
flash("Invalid role selected.", "danger")
|
||||
return redirect(url_for("group_admin.members"))
|
||||
new_perms = {key: bool(request.form.get(f"perm_{key}")) for key, _ in ALL_PERMISSIONS}
|
||||
old_role = member.get("role")
|
||||
db.update_member(user_id, group_id, role, new_perms)
|
||||
db.log_audit_event(
|
||||
session["user_id"], session["username"], "member.updated",
|
||||
entity_type="user", entity_id=user_id,
|
||||
details={"target": user["username"], "old_role": old_role, "new_role": role},
|
||||
group_id=group_id, ip_address=request.remote_addr,
|
||||
)
|
||||
flash("Permissions updated.", "success")
|
||||
return redirect(url_for("group_admin.members"))
|
||||
|
||||
@@ -258,7 +294,14 @@ def member_remove(user_id):
|
||||
if user_id == session["user_id"]:
|
||||
flash("You cannot remove yourself.", "danger")
|
||||
else:
|
||||
target_user = db.get_user_by_id(user_id)
|
||||
db.remove_group_member(user_id, session["group_id"])
|
||||
db.log_audit_event(
|
||||
session["user_id"], session["username"], "member.removed",
|
||||
entity_type="user", entity_id=user_id,
|
||||
details={"target": target_user["username"] if target_user else str(user_id)},
|
||||
group_id=session["group_id"], ip_address=request.remote_addr,
|
||||
)
|
||||
flash("Member removed.", "success")
|
||||
return redirect(url_for("group_admin.members"))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user