mirror of
https://github.com/minetest/contentdb.git
synced 2024-12-22 14:02:24 +01:00
Add ability to block domains
This commit is contained in:
parent
13dcd373f2
commit
72b4029ed3
@ -26,7 +26,8 @@ from wtforms import *
|
||||
from wtforms.validators import *
|
||||
from app.models import db, PackageReview, Thread, ThreadReply, NotificationType, PackageReviewVote, Package, UserRank, \
|
||||
Permission, AuditSeverity, PackageState
|
||||
from app.utils import is_package_page, addNotification, get_int_or_abort, isYes, is_safe_url, rank_required, addAuditLog
|
||||
from app.utils import is_package_page, addNotification, get_int_or_abort, isYes, is_safe_url, rank_required, \
|
||||
addAuditLog, has_blocked_domains
|
||||
from app.tasks.webhooktasks import post_discord_webhook
|
||||
|
||||
|
||||
@ -73,61 +74,64 @@ def review(package):
|
||||
|
||||
# Validate and submit
|
||||
elif can_review and form.validate_on_submit():
|
||||
was_new = False
|
||||
if not review:
|
||||
was_new = True
|
||||
review = PackageReview()
|
||||
review.package = package
|
||||
review.author = current_user
|
||||
db.session.add(review)
|
||||
|
||||
review.recommends = form.recommends.data == "yes"
|
||||
|
||||
thread = review.thread
|
||||
if not thread:
|
||||
thread = Thread()
|
||||
thread.author = current_user
|
||||
thread.private = False
|
||||
thread.package = package
|
||||
thread.review = review
|
||||
db.session.add(thread)
|
||||
|
||||
thread.watchers.append(current_user)
|
||||
|
||||
reply = ThreadReply()
|
||||
reply.thread = thread
|
||||
reply.author = current_user
|
||||
reply.comment = form.comment.data
|
||||
db.session.add(reply)
|
||||
|
||||
thread.replies.append(reply)
|
||||
if has_blocked_domains(form.comment.data, current_user.username, f"review of {package.getId()}"):
|
||||
flash(gettext("Linking to malicious sites is not allowed."), "danger")
|
||||
else:
|
||||
reply = thread.first_reply
|
||||
reply.comment = form.comment.data
|
||||
was_new = False
|
||||
if not review:
|
||||
was_new = True
|
||||
review = PackageReview()
|
||||
review.package = package
|
||||
review.author = current_user
|
||||
db.session.add(review)
|
||||
|
||||
thread.title = form.title.data
|
||||
review.recommends = form.recommends.data == "yes"
|
||||
|
||||
db.session.commit()
|
||||
thread = review.thread
|
||||
if not thread:
|
||||
thread = Thread()
|
||||
thread.author = current_user
|
||||
thread.private = False
|
||||
thread.package = package
|
||||
thread.review = review
|
||||
db.session.add(thread)
|
||||
|
||||
package.recalcScore()
|
||||
thread.watchers.append(current_user)
|
||||
|
||||
if was_new:
|
||||
notif_msg = "New review '{}'".format(form.title.data)
|
||||
type = NotificationType.NEW_REVIEW
|
||||
else:
|
||||
notif_msg = "Updated review '{}'".format(form.title.data)
|
||||
type = NotificationType.OTHER
|
||||
reply = ThreadReply()
|
||||
reply.thread = thread
|
||||
reply.author = current_user
|
||||
reply.comment = form.comment.data
|
||||
db.session.add(reply)
|
||||
|
||||
addNotification(package.maintainers, current_user, type, notif_msg,
|
||||
url_for("threads.view", id=thread.id), package)
|
||||
thread.replies.append(reply)
|
||||
else:
|
||||
reply = thread.first_reply
|
||||
reply.comment = form.comment.data
|
||||
|
||||
if was_new:
|
||||
post_discord_webhook.delay(thread.author.username,
|
||||
"Reviewed {}: {}".format(package.title, thread.getViewURL(absolute=True)), False)
|
||||
thread.title = form.title.data
|
||||
|
||||
db.session.commit()
|
||||
db.session.commit()
|
||||
|
||||
return redirect(package.getURL("packages.view"))
|
||||
package.recalcScore()
|
||||
|
||||
if was_new:
|
||||
notif_msg = "New review '{}'".format(form.title.data)
|
||||
type = NotificationType.NEW_REVIEW
|
||||
else:
|
||||
notif_msg = "Updated review '{}'".format(form.title.data)
|
||||
type = NotificationType.OTHER
|
||||
|
||||
addNotification(package.maintainers, current_user, type, notif_msg,
|
||||
url_for("threads.view", id=thread.id), package)
|
||||
|
||||
if was_new:
|
||||
post_discord_webhook.delay(thread.author.username,
|
||||
"Reviewed {}: {}".format(package.title, thread.getViewURL(absolute=True)), False)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
return redirect(package.getURL("packages.view"))
|
||||
|
||||
return render_template("packages/review_create_edit.html",
|
||||
form=form, package=package, review=review)
|
||||
@ -217,7 +221,6 @@ def review_vote(package, review_id):
|
||||
return redirect(review.thread.getViewURL())
|
||||
|
||||
|
||||
|
||||
@bp.route("/packages/<author>/<name>/review-votes/")
|
||||
@rank_required(UserRank.ADMIN)
|
||||
@is_package_page
|
||||
|
@ -23,7 +23,7 @@ bp = Blueprint("threads", __name__)
|
||||
|
||||
from flask_login import current_user, login_required
|
||||
from app.models import *
|
||||
from app.utils import addNotification, isYes, addAuditLog, get_system_user, rank_required
|
||||
from app.utils import addNotification, isYes, addAuditLog, get_system_user, rank_required, has_blocked_domains
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import *
|
||||
from wtforms.validators import *
|
||||
@ -189,7 +189,7 @@ def edit_reply(id):
|
||||
if reply_id is None:
|
||||
abort(404)
|
||||
|
||||
reply = ThreadReply.query.get(reply_id)
|
||||
reply: ThreadReply = ThreadReply.query.get(reply_id)
|
||||
if reply is None or reply.thread != thread:
|
||||
abort(404)
|
||||
|
||||
@ -199,17 +199,19 @@ def edit_reply(id):
|
||||
form = CommentForm(formdata=request.form, obj=reply)
|
||||
if form.validate_on_submit():
|
||||
comment = form.comment.data
|
||||
if has_blocked_domains(comment, current_user.username, f"edit to reply {reply.get_url(True)}"):
|
||||
flash(gettext("Linking to malicious sites is not allowed."), "danger")
|
||||
else:
|
||||
msg = "Edited reply by {}".format(reply.author.display_name)
|
||||
severity = AuditSeverity.NORMAL if current_user == reply.author else AuditSeverity.MODERATION
|
||||
addNotification(reply.author, current_user, NotificationType.OTHER, msg, thread.getViewURL(), thread.package)
|
||||
addAuditLog(severity, current_user, msg, thread.getViewURL(), thread.package, reply.comment)
|
||||
|
||||
msg = "Edited reply by {}".format(reply.author.display_name)
|
||||
severity = AuditSeverity.NORMAL if current_user == reply.author else AuditSeverity.MODERATION
|
||||
addNotification(reply.author, current_user, NotificationType.OTHER, msg, thread.getViewURL(), thread.package)
|
||||
addAuditLog(severity, current_user, msg, thread.getViewURL(), thread.package, reply.comment)
|
||||
reply.comment = comment
|
||||
|
||||
reply.comment = comment
|
||||
db.session.commit()
|
||||
|
||||
db.session.commit()
|
||||
|
||||
return redirect(thread.getViewURL())
|
||||
return redirect(thread.getViewURL())
|
||||
|
||||
return render_template("threads/edit_reply.html", thread=thread, reply=reply, form=form)
|
||||
|
||||
@ -230,6 +232,10 @@ def view(id):
|
||||
flash(gettext("Please wait before commenting again"), "danger")
|
||||
return redirect(thread.getViewURL())
|
||||
|
||||
if has_blocked_domains(comment, current_user.username, f"reply to {thread.getViewURL(True)}"):
|
||||
flash(gettext("Linking to malicious sites is not allowed."), "danger")
|
||||
return render_template("threads/view.html", thread=thread, form=form)
|
||||
|
||||
reply = ThreadReply()
|
||||
reply.author = current_user
|
||||
reply.comment = comment
|
||||
@ -318,55 +324,58 @@ def new():
|
||||
|
||||
# Validate and submit
|
||||
elif form.validate_on_submit():
|
||||
thread = Thread()
|
||||
thread.author = current_user
|
||||
thread.title = form.title.data
|
||||
thread.private = form.private.data if allow_private_change else def_is_private
|
||||
thread.package = package
|
||||
db.session.add(thread)
|
||||
if has_blocked_domains(form.comment.data, current_user.username, f"new thread"):
|
||||
flash(gettext("Linking to malicious sites is not allowed."), "danger")
|
||||
else:
|
||||
thread = Thread()
|
||||
thread.author = current_user
|
||||
thread.title = form.title.data
|
||||
thread.private = form.private.data if allow_private_change else def_is_private
|
||||
thread.package = package
|
||||
db.session.add(thread)
|
||||
|
||||
thread.watchers.append(current_user)
|
||||
if package and package.author != current_user:
|
||||
thread.watchers.append(package.author)
|
||||
thread.watchers.append(current_user)
|
||||
if package and package.author != current_user:
|
||||
thread.watchers.append(package.author)
|
||||
|
||||
reply = ThreadReply()
|
||||
reply.thread = thread
|
||||
reply.author = current_user
|
||||
reply.comment = form.comment.data
|
||||
db.session.add(reply)
|
||||
reply = ThreadReply()
|
||||
reply.thread = thread
|
||||
reply.author = current_user
|
||||
reply.comment = form.comment.data
|
||||
db.session.add(reply)
|
||||
|
||||
thread.replies.append(reply)
|
||||
thread.replies.append(reply)
|
||||
|
||||
db.session.commit()
|
||||
db.session.commit()
|
||||
|
||||
if is_review_thread:
|
||||
package.review_thread = thread
|
||||
if is_review_thread:
|
||||
package.review_thread = thread
|
||||
|
||||
for mentioned_username in get_user_mentions(render_markdown(form.comment.data)):
|
||||
mentioned = User.query.filter_by(username=mentioned_username).first()
|
||||
if mentioned is None:
|
||||
continue
|
||||
for mentioned_username in get_user_mentions(render_markdown(form.comment.data)):
|
||||
mentioned = User.query.filter_by(username=mentioned_username).first()
|
||||
if mentioned is None:
|
||||
continue
|
||||
|
||||
msg = "Mentioned by {} in new thread '{}'".format(current_user.display_name, thread.title)
|
||||
addNotification(mentioned, current_user, NotificationType.NEW_THREAD,
|
||||
msg, thread.getViewURL(), thread.package)
|
||||
msg = "Mentioned by {} in new thread '{}'".format(current_user.display_name, thread.title)
|
||||
addNotification(mentioned, current_user, NotificationType.NEW_THREAD,
|
||||
msg, thread.getViewURL(), thread.package)
|
||||
|
||||
thread.watchers.append(mentioned)
|
||||
thread.watchers.append(mentioned)
|
||||
|
||||
notif_msg = "New thread '{}'".format(thread.title)
|
||||
if package is not None:
|
||||
addNotification(package.maintainers, current_user, NotificationType.NEW_THREAD, notif_msg, thread.getViewURL(), package)
|
||||
notif_msg = "New thread '{}'".format(thread.title)
|
||||
if package is not None:
|
||||
addNotification(package.maintainers, current_user, NotificationType.NEW_THREAD, notif_msg, thread.getViewURL(), package)
|
||||
|
||||
approvers = User.query.filter(User.rank >= UserRank.APPROVER).all()
|
||||
addNotification(approvers, current_user, NotificationType.EDITOR_MISC, notif_msg, thread.getViewURL(), package)
|
||||
approvers = User.query.filter(User.rank >= UserRank.APPROVER).all()
|
||||
addNotification(approvers, current_user, NotificationType.EDITOR_MISC, notif_msg, thread.getViewURL(), package)
|
||||
|
||||
if is_review_thread:
|
||||
post_discord_webhook.delay(current_user.username,
|
||||
"Opened approval thread: {}".format(thread.getViewURL(absolute=True)), True)
|
||||
if is_review_thread:
|
||||
post_discord_webhook.delay(current_user.username,
|
||||
"Opened approval thread: {}".format(thread.getViewURL(absolute=True)), True)
|
||||
|
||||
db.session.commit()
|
||||
db.session.commit()
|
||||
|
||||
return redirect(thread.getViewURL())
|
||||
return redirect(thread.getViewURL())
|
||||
|
||||
|
||||
return render_template("threads/new.html", form=form, allow_private_change=allow_private_change, package=package)
|
||||
|
@ -7,7 +7,7 @@ from wtforms import *
|
||||
from wtforms.validators import *
|
||||
|
||||
from app.models import *
|
||||
from app.utils import nonEmptyOrNone, addAuditLog, randomString, rank_required
|
||||
from app.utils import nonEmptyOrNone, addAuditLog, randomString, rank_required, has_blocked_domains
|
||||
from app.tasks.emails import send_verify_email
|
||||
from . import bp
|
||||
|
||||
@ -53,7 +53,7 @@ class UserProfileForm(FlaskForm):
|
||||
submit = SubmitField(lazy_gettext("Save"))
|
||||
|
||||
|
||||
def handle_profile_edit(form, user, username):
|
||||
def handle_profile_edit(form: UserProfileForm, user: User, username: str):
|
||||
severity = AuditSeverity.NORMAL if current_user == user else AuditSeverity.MODERATION
|
||||
addAuditLog(severity, current_user, "Edited {}'s profile".format(user.display_name),
|
||||
url_for("users.profile", username=username))
|
||||
@ -80,8 +80,13 @@ def handle_profile_edit(form, user, username):
|
||||
url_for("users.profile", username=username))
|
||||
|
||||
if user.checkPerm(current_user, Permission.CHANGE_PROFILE_URLS):
|
||||
user.website_url = form["website_url"].data
|
||||
user.donate_url = form["donate_url"].data
|
||||
if has_blocked_domains(form.website_url.data, current_user.username, f"{user.username}'s website_url") or \
|
||||
has_blocked_domains(form.donate_url.data, current_user.username, f"{user.username}'s donate_url"):
|
||||
flash(gettext("Linking to malicious sites is not allowed."), "danger")
|
||||
return
|
||||
|
||||
user.website_url = form.website_url.data
|
||||
user.donate_url = form.donate_url.data
|
||||
|
||||
db.session.commit()
|
||||
|
||||
|
@ -22,7 +22,7 @@ from flask_babel import lazy_gettext
|
||||
from app.logic.LogicError import LogicError
|
||||
from app.models import User, Package, PackageType, MetaPackage, Tag, ContentWarning, db, Permission, AuditSeverity, \
|
||||
License, UserRank, PackageDevState
|
||||
from app.utils import addAuditLog
|
||||
from app.utils import addAuditLog, has_blocked_domains
|
||||
from app.utils.url import clean_youtube_url
|
||||
|
||||
|
||||
@ -118,6 +118,11 @@ def do_edit_package(user: User, package: Package, was_new: bool, was_web: bool,
|
||||
|
||||
validate(data)
|
||||
|
||||
for field in ["short_desc", "desc", "website", "issueTracker", "repo", "video_url"]:
|
||||
if field in data and has_blocked_domains(data[field], user.username,
|
||||
f"{field} of {package.getId()}"):
|
||||
raise LogicError(403, lazy_gettext("Linking to malicious sites is not allowed."))
|
||||
|
||||
if "type" in data:
|
||||
data["type"] = PackageType.coerce(data["type"])
|
||||
|
||||
|
@ -144,8 +144,8 @@ class ThreadReply(db.Model):
|
||||
|
||||
created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow)
|
||||
|
||||
def get_url(self):
|
||||
return url_for('threads.view', id=self.thread.id) + "#reply-" + str(self.id)
|
||||
def get_url(self, absolute=False):
|
||||
return self.thread.getViewURL(absolute) + "#reply-" + str(self.id)
|
||||
|
||||
def checkPerm(self, user, perm):
|
||||
if not user.is_authenticated:
|
||||
|
@ -20,6 +20,7 @@ import secrets
|
||||
from .flask import *
|
||||
from .models import *
|
||||
from .user import *
|
||||
from flask import current_app
|
||||
|
||||
YESES = ["yes", "true", "1", "on"]
|
||||
|
||||
@ -51,3 +52,19 @@ def shouldReturnJson():
|
||||
|
||||
def randomString(n):
|
||||
return secrets.token_hex(int(n / 2))
|
||||
|
||||
|
||||
def has_blocked_domains(text: str, username: str, location: str) -> bool:
|
||||
if text is None:
|
||||
return False
|
||||
|
||||
blocked_domains = current_app.config["BLOCKED_DOMAINS"]
|
||||
for domain in blocked_domains:
|
||||
if domain in text:
|
||||
from app.tasks.webhooktasks import post_discord_webhook
|
||||
post_discord_webhook.delay(username,
|
||||
f"Attempted to post link to blocked domain {domain} in {location}",
|
||||
True)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
@ -34,6 +34,4 @@ DISCORD_WEBHOOK_QUEUE = None
|
||||
TEMPLATES_AUTO_RELOAD = False
|
||||
LOG_SQL = False
|
||||
|
||||
LANGUAGES = {
|
||||
'en': 'English',
|
||||
}
|
||||
BLOCKED_DOMAINS = []
|
||||
|
Loading…
Reference in New Issue
Block a user