mirror of
https://gitlab.com/SIGBUS/nyaa.git
synced 2024-12-22 15:00:01 +00:00
Better bans (#341)
* better bans * put jinja2 template into correct file
This commit is contained in:
parent
6aab5557d6
commit
f8a314df4f
38
migrations/versions/500117641608_add_bans.py
Normal file
38
migrations/versions/500117641608_add_bans.py
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
"""Add bans table
|
||||||
|
|
||||||
|
Revision ID: 500117641608
|
||||||
|
Revises: b79d2fcafd88
|
||||||
|
Create Date: 2017-08-17 01:44:39.205126
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '500117641608'
|
||||||
|
down_revision = 'b79d2fcafd88'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.create_table('bans',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('created_time', sa.DateTime(), nullable=True),
|
||||||
|
sa.Column('admin_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('user_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('user_ip', sa.Binary(length=16), nullable=True),
|
||||||
|
sa.Column('reason', sa.String(length=2048), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['admin_id'], ['users.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
)
|
||||||
|
op.create_index('user_ip_16', 'bans', ['user_ip'], unique=True, mysql_length=16)
|
||||||
|
op.create_index('user_ip_4', 'bans', ['user_ip'], unique=True, mysql_length=4)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_index('user_ip_4', table_name='bans')
|
||||||
|
op.drop_index('user_ip_16', table_name='bans')
|
||||||
|
op.drop_table('bans')
|
|
@ -148,6 +148,33 @@ class CommentForm(FlaskForm):
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class InlineButtonWidget(object):
|
||||||
|
"""
|
||||||
|
Render a basic ``<button>`` field.
|
||||||
|
"""
|
||||||
|
input_type = 'submit'
|
||||||
|
html_params = staticmethod(html_params)
|
||||||
|
|
||||||
|
def __call__(self, field, label=None, **kwargs):
|
||||||
|
kwargs.setdefault('id', field.id)
|
||||||
|
kwargs.setdefault('type', self.input_type)
|
||||||
|
if not label:
|
||||||
|
label = field.label.text
|
||||||
|
return HTMLString('<button %s>' % self.html_params(name=field.name, **kwargs) + label)
|
||||||
|
|
||||||
|
|
||||||
|
class StringSubmitField(StringField):
|
||||||
|
"""
|
||||||
|
Represents an ``<button type="submit">``. This allows checking if a given
|
||||||
|
submit button has been pressed.
|
||||||
|
"""
|
||||||
|
widget = InlineButtonWidget()
|
||||||
|
|
||||||
|
|
||||||
|
class StringSubmitForm(FlaskForm):
|
||||||
|
submit = StringSubmitField('Submit')
|
||||||
|
|
||||||
|
|
||||||
class EditForm(FlaskForm):
|
class EditForm(FlaskForm):
|
||||||
display_name = StringField('Torrent display name', [
|
display_name = StringField('Torrent display name', [
|
||||||
Length(min=3, max=255, message='Torrent display name must be at least %(min)d characters '
|
Length(min=3, max=255, message='Torrent display name must be at least %(min)d characters '
|
||||||
|
@ -185,6 +212,8 @@ class EditForm(FlaskForm):
|
||||||
Length(max=10 * 1024, message='Description must be at most %(max)d characters long.')
|
Length(max=10 * 1024, message='Description must be at most %(max)d characters long.')
|
||||||
])
|
])
|
||||||
|
|
||||||
|
submit = SubmitField('Save Changes')
|
||||||
|
|
||||||
|
|
||||||
class DeleteForm(FlaskForm):
|
class DeleteForm(FlaskForm):
|
||||||
delete = SubmitField("Delete")
|
delete = SubmitField("Delete")
|
||||||
|
@ -193,6 +222,23 @@ class DeleteForm(FlaskForm):
|
||||||
unban = SubmitField("Unban")
|
unban = SubmitField("Unban")
|
||||||
|
|
||||||
|
|
||||||
|
class BanForm(FlaskForm):
|
||||||
|
ban_user = SubmitField("Delete & Ban and Ban User")
|
||||||
|
ban_userip = SubmitField("Delete & Ban and Ban User+IP")
|
||||||
|
unban = SubmitField("Unban")
|
||||||
|
|
||||||
|
_validator = DataRequired()
|
||||||
|
|
||||||
|
def _validate_reason(form, field):
|
||||||
|
if form.ban_user.data or form.ban_userip.data:
|
||||||
|
return BanForm._validator(form, field)
|
||||||
|
|
||||||
|
reason = TextAreaField('Ban Reason', [
|
||||||
|
_validate_reason,
|
||||||
|
Length(max=1024, message='Reason must be at most %(max)d characters long.')
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
class UploadForm(FlaskForm):
|
class UploadForm(FlaskForm):
|
||||||
torrent_file = FileField('Torrent file', [
|
torrent_file = FileField('Torrent file', [
|
||||||
FileRequired()
|
FileRequired()
|
||||||
|
|
|
@ -484,6 +484,8 @@ class User(db.Model):
|
||||||
sukebei_torrents = db.relationship('SukebeiTorrent', back_populates='user', lazy='dynamic')
|
sukebei_torrents = db.relationship('SukebeiTorrent', back_populates='user', lazy='dynamic')
|
||||||
sukebei_comments = db.relationship('SukebeiComment', back_populates='user', lazy='dynamic')
|
sukebei_comments = db.relationship('SukebeiComment', back_populates='user', lazy='dynamic')
|
||||||
|
|
||||||
|
bans = db.relationship('Ban', uselist=True, foreign_keys='Ban.user_id')
|
||||||
|
|
||||||
def __init__(self, username, email, password):
|
def __init__(self, username, email, password):
|
||||||
self.username = username
|
self.username = username
|
||||||
self.email = email
|
self.email = email
|
||||||
|
@ -521,14 +523,18 @@ class User(db.Model):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def userlevel_str(self):
|
def userlevel_str(self):
|
||||||
|
level = ''
|
||||||
if self.level == UserLevelType.REGULAR:
|
if self.level == UserLevelType.REGULAR:
|
||||||
return 'User'
|
level = 'User'
|
||||||
elif self.level == UserLevelType.TRUSTED:
|
elif self.level == UserLevelType.TRUSTED:
|
||||||
return 'Trusted'
|
level = 'Trusted'
|
||||||
elif self.level == UserLevelType.MODERATOR:
|
elif self.level == UserLevelType.MODERATOR:
|
||||||
return 'Moderator'
|
level = 'Moderator'
|
||||||
elif self.level >= UserLevelType.SUPERADMIN:
|
elif self.level >= UserLevelType.SUPERADMIN:
|
||||||
return 'Administrator'
|
level = 'Administrator'
|
||||||
|
if self.is_banned:
|
||||||
|
level = 'BANNED ' + level
|
||||||
|
return level
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def userstatus_str(self):
|
def userstatus_str(self):
|
||||||
|
@ -541,12 +547,16 @@ class User(db.Model):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def userlevel_color(self):
|
def userlevel_color(self):
|
||||||
|
color = ''
|
||||||
if self.level == UserLevelType.REGULAR:
|
if self.level == UserLevelType.REGULAR:
|
||||||
return 'default'
|
color = 'default'
|
||||||
elif self.level == UserLevelType.TRUSTED:
|
elif self.level == UserLevelType.TRUSTED:
|
||||||
return 'success'
|
color = 'success'
|
||||||
elif self.level >= UserLevelType.MODERATOR:
|
elif self.level >= UserLevelType.MODERATOR:
|
||||||
return 'purple'
|
color = 'purple'
|
||||||
|
if self.is_banned:
|
||||||
|
color += ' strike'
|
||||||
|
return color
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ip_string(self):
|
def ip_string(self):
|
||||||
|
@ -679,6 +689,51 @@ class ReportBase(DeclarativeHelperBase):
|
||||||
return cls.query.filter(cls.torrent_id == id, cls.status == 0).delete()
|
return cls.query.filter(cls.torrent_id == id, cls.status == 0).delete()
|
||||||
|
|
||||||
|
|
||||||
|
class Ban(db.Model):
|
||||||
|
__tablename__ = 'bans'
|
||||||
|
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
created_time = db.Column(db.DateTime(timezone=False), default=datetime.utcnow)
|
||||||
|
admin_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||||
|
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
||||||
|
user_ip = db.Column(db.Binary(length=16), nullable=True)
|
||||||
|
reason = db.Column(db.String(length=2048), nullable=False)
|
||||||
|
|
||||||
|
admin = db.relationship('User', uselist=False, lazy='joined', foreign_keys=[admin_id])
|
||||||
|
user = db.relationship('User', uselist=False, lazy='joined', foreign_keys=[user_id])
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
Index('user_ip_4', 'user_ip', mysql_length=4, unique=True),
|
||||||
|
Index('user_ip_16', 'user_ip', mysql_length=16, unique=True),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<Ban %r>' % self.id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ip_string(self):
|
||||||
|
if self.user_ip:
|
||||||
|
return str(ip_address(self.user_ip))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def all_bans(cls):
|
||||||
|
return cls.query
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def by_id(cls, id):
|
||||||
|
return cls.query.get(id)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def banned(cls, user_id, user_ip):
|
||||||
|
if user_id:
|
||||||
|
if user_ip:
|
||||||
|
return cls.query.filter((cls.user_id == user_id) | (cls.user_ip == user_ip))
|
||||||
|
return cls.query.filter(cls.user_id == user_id)
|
||||||
|
if user_ip:
|
||||||
|
return cls.query.filter(cls.user_ip == user_ip)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
# Actually declare our site-specific classes
|
# Actually declare our site-specific classes
|
||||||
|
|
||||||
# Torrent
|
# Torrent
|
||||||
|
|
|
@ -331,6 +331,10 @@ a.text-purple:hover, a.text-purple:active, a.text-purple:focus {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.strike {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
/* Torrent file list */
|
/* Torrent file list */
|
||||||
.torrent-file-list ul {
|
.torrent-file-list ul {
|
||||||
padding: 5px 20px 0px 20px;
|
padding: 5px 20px 0px 20px;
|
||||||
|
|
|
@ -180,10 +180,15 @@ document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
|
||||||
// Render markdown from elements with "markdown-text" attribute
|
// Render markdown from elements with "markdown-text" attribute
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
var markdownTargets = document.querySelectorAll('[markdown-text]');
|
var markdownTargets = document.querySelectorAll('[markdown-text],[markdown-text-inline]');
|
||||||
for (var i = 0; i < markdownTargets.length; i++) {
|
for (var i = 0; i < markdownTargets.length; i++) {
|
||||||
var target = markdownTargets[i];
|
var target = markdownTargets[i];
|
||||||
var rendered = markdown.render(target.innerHTML);
|
var rendered;
|
||||||
|
if (target.attributes["markdown-text-inline"]) {
|
||||||
|
rendered = markdown.renderInline(target.innerHTML);
|
||||||
|
} else {
|
||||||
|
rendered = markdown.render(target.innerHTML);
|
||||||
|
}
|
||||||
target.innerHTML = rendered;
|
target.innerHTML = rendered;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import os.path
|
import os.path
|
||||||
|
import re
|
||||||
from base64 import b32encode
|
from base64 import b32encode
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from email.utils import formatdate
|
from email.utils import formatdate
|
||||||
|
@ -135,3 +136,9 @@ def timesince(dt, default='just now'):
|
||||||
return '%d %s ago' % (period, singular if int(period) == 1 else plural)
|
return '%d %s ago' % (period, singular if int(period) == 1 else plural)
|
||||||
|
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
@bp.app_template_filter()
|
||||||
|
def regex_replace(s, find, replace):
|
||||||
|
"""A non-optimal implementation of a regex filter"""
|
||||||
|
return re.sub(find, replace, s)
|
||||||
|
|
55
nyaa/templates/admin_bans.html
Normal file
55
nyaa/templates/admin_bans.html
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
{% extends "layout.html" %}
|
||||||
|
{% block title %}Admin Log :: {{ config.SITE_NAME }}{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
{% from "_formhelpers.html" import render_field %}
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered table-hover table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 75px">#</th>
|
||||||
|
<th>Moderator/Admin</th>
|
||||||
|
<th>User</th>
|
||||||
|
<th>User IP</th>
|
||||||
|
<th style="width: 700px">Reason</th>
|
||||||
|
<th style="width: 175px">Date</th>
|
||||||
|
<th style="width: 75px">Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<form method="POST">
|
||||||
|
{{ form.csrf_token }}
|
||||||
|
{% for ban in bans.items %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ ban.id }}</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('users.view_user', user_name=ban.admin.username) }}">{{ ban.admin.username }}</a>
|
||||||
|
</td>
|
||||||
|
{% if ban.user %}
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('users.view_user', user_name=ban.user.username) }}">{{ ban.user.username }}</a>
|
||||||
|
</td>
|
||||||
|
{% else %}
|
||||||
|
<td>-</td>
|
||||||
|
{% endif %}
|
||||||
|
{% if g.user.is_superadmin %}
|
||||||
|
<td>{{ ban.ip_string }}</td>
|
||||||
|
{% else %}
|
||||||
|
<td>hidden</td>
|
||||||
|
{% endif %}
|
||||||
|
<td><div markdown-text-inline>{{ ban.reason }}</div></td>
|
||||||
|
<td class="center">{{ ban.created_time }}</td>
|
||||||
|
<td class="center">
|
||||||
|
{{ form.submit(label="Unban", value=ban.id, class="btn btn-danger btn-xs") }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</form>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=pagination>
|
||||||
|
{% from "bootstrap/pagination.html" import render_pagination %}
|
||||||
|
{{ render_pagination(bans) }}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -19,7 +19,11 @@
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ url_for('users.view_user', user_name=log.admin.username) }}">{{ log.admin.username }}</a>
|
<a href="{{ url_for('users.view_user', user_name=log.admin.username) }}">{{ log.admin.username }}</a>
|
||||||
</td>
|
</td>
|
||||||
<td><div markdown-text>{{ log.log }}</div></td>
|
{% if g.user.is_superadmin %}
|
||||||
|
<td><div markdown-text-inline>{{ log.log }}</div></td>
|
||||||
|
{% else %}
|
||||||
|
<td><div markdown-text-inline>{{ log.log | regex_replace("IP\(.*?\)", "IP(hidden)" )}}</div></td>
|
||||||
|
{% endif %}
|
||||||
<td>{{ log.created_time }}</td>
|
<td>{{ log.created_time }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -80,7 +80,7 @@
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
<input type="submit" value="Save Changes" class="btn btn-primary">
|
{{ form.submit(class="btn btn-primary") }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -93,10 +93,22 @@
|
||||||
<h3 class="panel-title">Danger Zone</h3>
|
<h3 class="panel-title">Danger Zone</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<form method="POST" action="{{ url_for('torrents.delete', torrent_id=torrent.id) }}">
|
<form method="POST">
|
||||||
{{ delete_form.csrf_token }}
|
{{ delete_form.csrf_token }}
|
||||||
|
|
||||||
{% if not torrent.deleted %}
|
<p>
|
||||||
|
{% if torrent.deleted %}
|
||||||
|
This torrent is <strong>deleted</strong>{% if torrent.banned %} and <strong>banned</strong>{% endif %}.<br>
|
||||||
|
{% endif %}
|
||||||
|
{% if torrent.user and torrent.user.is_banned %}
|
||||||
|
The uploader is <strong>banned</strong>.<br>
|
||||||
|
{% endif %}
|
||||||
|
{% if ipbanned %}
|
||||||
|
The uploader is <strong>IP banned</strong>.<br>
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{% if not torrent.deleted %}
|
||||||
<p class="lead">
|
<p class="lead">
|
||||||
Delete torrent.
|
Delete torrent.
|
||||||
{{ delete_form.delete(class="btn btn-danger pull-right") }}
|
{{ delete_form.delete(class="btn btn-danger pull-right") }}
|
||||||
|
@ -113,11 +125,11 @@
|
||||||
{{ delete_form.ban(class="btn btn-danger pull-right") }}
|
{{ delete_form.ban(class="btn btn-danger pull-right") }}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Soft deletes the torrent and disallows reuploading it.
|
Soft deletes the torrent.<br>
|
||||||
|
Bans it from the tracker and disallows reuploading it.
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %} {# if torrent.deleted #}
|
||||||
<p>This torrent is <strong>deleted</strong>{% if torrent.banned %} and <strong>banned</strong>{% endif %}.</p>
|
|
||||||
<p class="lead">
|
<p class="lead">
|
||||||
Undelete{% if torrent.banned %} and unban{% endif %} torrent.
|
Undelete{% if torrent.banned %} and unban{% endif %} torrent.
|
||||||
{% if torrent.banned %}
|
{% if torrent.banned %}
|
||||||
|
@ -140,19 +152,101 @@
|
||||||
Unbans torrent without undeleting it.<br>
|
Unbans torrent without undeleting it.<br>
|
||||||
Allows reuploading this torrent again.
|
Allows reuploading this torrent again.
|
||||||
</p>
|
</p>
|
||||||
{% else%}
|
{% else %}
|
||||||
<hr>
|
<hr>
|
||||||
<p class="lead">
|
<p class="lead">
|
||||||
Ban torrent.
|
Ban torrent.
|
||||||
{{ delete_form.ban(value="Ban", class="btn btn-danger pull-right") }}
|
{{ delete_form.ban(value="Ban", class="btn btn-danger pull-right") }}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Bans the torrent.<br>
|
Bans it from the tracker and disallows reuploading it.
|
||||||
Disallows reuploading this torrent.
|
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if ban_form %}
|
||||||
|
{% if (torrent.user and not torrent.user.is_banned) or not ipbanned %}
|
||||||
|
<hr>
|
||||||
|
<p class="lead">
|
||||||
|
{% if torrent.deleted %}
|
||||||
|
{% if torrent.banned %}
|
||||||
|
Ban uploader.
|
||||||
|
{% else %}
|
||||||
|
Ban torrent and ban uploader.
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
Delete and ban torrent and ban uploader.
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{% if torrent.deleted %}
|
||||||
|
{% if torrent.banned %}
|
||||||
|
Bans the uploader.
|
||||||
|
{% else %}
|
||||||
|
Bans it from the tracker and disallows reuploading it.<br>
|
||||||
|
Additionally bans the uploader.
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
Soft deletes the torrent.<br>
|
||||||
|
Bans it from the tracker and disallows reuploading it.<br>
|
||||||
|
Additionally bans the uploader.
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
{{ render_field(ban_form.reason, class_="form-control", placeholder="Specify a ban reason.") }}<br>
|
||||||
|
|
||||||
|
{% if torrent.user %}
|
||||||
|
<div class="pull-left">
|
||||||
|
{% if torrent.user.is_banned %}
|
||||||
|
<button type="button" class="btn btn-danger disabled">Already banned</button>
|
||||||
|
{% else %}
|
||||||
|
{% set text = "Ban User" %}
|
||||||
|
{% if not torrent.banned %}
|
||||||
|
{% set text = "Ban and Ban User" %}
|
||||||
|
{% if not torrent.deleted %}
|
||||||
|
{% set text = "Delete & Ban and Ban User" %}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{{ ban_form.ban_user(class="btn btn-danger") }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="pull-right">
|
||||||
|
{% if ipbanned %}
|
||||||
|
<button type="button" class="btn btn-danger disabled">Already IP banned</button>
|
||||||
|
{% else %}
|
||||||
|
{% set text = "Ban User+IP" %}
|
||||||
|
{% if not torrent.banned %}
|
||||||
|
{% set text = "Ban and Ban User+IP" %}
|
||||||
|
{% if not torrent.deleted %}
|
||||||
|
{% set text = "Delete & Ban and Ban User+IP" %}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{{ ban_form.ban_userip(value=text, class="btn btn-danger") }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="pull-left">
|
||||||
|
<button type="button" class="btn btn-danger disabled">No user</button>
|
||||||
|
</div>
|
||||||
|
<div class="pull-right">
|
||||||
|
{% if ipbanned %}
|
||||||
|
<button type="button" class="btn btn-danger disabled">Already IP banned</button>
|
||||||
|
{% else %}
|
||||||
|
{% set text = "Ban IP" %}
|
||||||
|
{% if not torrent.banned %}
|
||||||
|
{% set text = "Ban and Ban IP" %}
|
||||||
|
{% if not torrent.deleted %}
|
||||||
|
{% set text = "Delete & Ban and Ban IP" %}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{{ ban_form.ban_userip(value=text, class="btn btn-danger") }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -97,6 +97,7 @@
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li {% if request.path == url_for('admin.reports') %}class="active"{% endif %}><a href="{{ url_for('admin.reports') }}">Reports</a></li>
|
<li {% if request.path == url_for('admin.reports') %}class="active"{% endif %}><a href="{{ url_for('admin.reports') }}">Reports</a></li>
|
||||||
<li {% if request.path == url_for('admin.log') %}class="active"{% endif %}><a href="{{ url_for('admin.log') }}">Log</a></li>
|
<li {% if request.path == url_for('admin.log') %}class="active"{% endif %}><a href="{{ url_for('admin.log') }}">Log</a></li>
|
||||||
|
<li {% if request.path == url_for('admin.bans') %}class="active"{% endif %}><a href="{{ url_for('admin.bans') }}">Bans</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -11,14 +11,15 @@
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
{% from "_formhelpers.html" import render_menu_with_button %}
|
{% from "_formhelpers.html" import render_menu_with_button %}
|
||||||
|
{% from "_formhelpers.html" import render_field %}
|
||||||
|
|
||||||
{% if g.user and g.user.is_moderator %}
|
{% if g.user and g.user.is_moderator %}
|
||||||
<h2>User Information</h2><br>
|
<h2>User Information</h2><br>
|
||||||
<div class="row" style="margin-bottom: 20px;">
|
<div class="row" style="margin-bottom: 20px;">
|
||||||
<div class="col-sm-2" style="max-width: 150px;">
|
<div class="col-md-2" style="max-width: 150px;">
|
||||||
<img class="avatar" src="{{ user.gravatar_url() }}">
|
<img class="avatar" src="{{ user.gravatar_url() }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-10">
|
<div class="col-md-4">
|
||||||
<dl class="dl-horizontal">
|
<dl class="dl-horizontal">
|
||||||
<dt>User ID:</dt>
|
<dt>User ID:</dt>
|
||||||
<dd>{{ user.id }}</dd>
|
<dd>{{ user.id }}</dd>
|
||||||
|
@ -35,20 +36,74 @@
|
||||||
<dd>{{ user.ip_string }}</dd><br>
|
<dd>{{ user.ip_string }}</dd><br>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
{% if admin_form %}
|
||||||
</div>
|
<form method="POST">
|
||||||
{% if admin_form %}
|
{{ admin_form.csrf_token }}
|
||||||
<form method="POST">
|
|
||||||
{{ admin_form.csrf_token }}
|
|
||||||
|
|
||||||
<div class="form-group row">
|
<div class="form-group">
|
||||||
<div class="col-md-6">
|
{{ render_menu_with_button(admin_form.user_class) }}
|
||||||
{{ render_menu_with_button(admin_form.user_class) }}
|
</div>
|
||||||
|
</form>
|
||||||
|
<br>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% if ban_form %}
|
||||||
|
<div class="col-md-5">
|
||||||
|
<div class="panel panel-danger">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title">Danger Zone</h3>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<form method="POST">
|
||||||
|
{{ ban_form.csrf_token }}
|
||||||
|
{% if user.is_banned %}
|
||||||
|
This user is <strong>banned</strong>.<br>
|
||||||
|
{% endif %}
|
||||||
|
{% if ipbanned %}
|
||||||
|
This user is <strong>ip banned</strong>.<br>
|
||||||
|
{% endif %}
|
||||||
|
{% if not user.is_banned and not bans %}
|
||||||
|
This user is <strong>not banned</strong>.<br>
|
||||||
|
{% endif %}
|
||||||
|
<br>
|
||||||
|
{% if user.is_banned or bans %}
|
||||||
|
<p>
|
||||||
|
{% for ban in bans %}
|
||||||
|
#{{ ban.id }}
|
||||||
|
by <a href="{{ url_for('users.view_user', user_name=ban.admin.username) }}">{{ ban.admin.username }}</a>
|
||||||
|
for <span markdown-text-inline>{{ ban.reason }}</span>
|
||||||
|
<br>
|
||||||
|
{% endfor %}
|
||||||
|
</p>
|
||||||
|
{{ ban_form.unban(class="btn btn-info") }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if not user.is_banned or not ipbanned %}
|
||||||
|
{% if user.is_banned or bans %}
|
||||||
|
<hr>
|
||||||
|
{% endif %}
|
||||||
|
{{ render_field(ban_form.reason, class_="form-control", placeholder="Specify a ban reason.") }}<br>
|
||||||
|
<div class="pull-left">
|
||||||
|
{% if not user.is_banned %}
|
||||||
|
{{ ban_form.ban_user(value="Ban User", class="btn btn-danger") }}
|
||||||
|
{% else %}
|
||||||
|
<button type="button" class="btn btn-danger disabled">Already banned</button>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="pull-right">
|
||||||
|
{% if not ipbanned %}
|
||||||
|
{{ ban_form.ban_userip(value="Ban User+IP", class="btn btn-danger") }}
|
||||||
|
{% else %}
|
||||||
|
<button type="button" class="btn btn-danger disabled">Already IP banned</button>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
{% endif %}
|
||||||
<br>
|
</div>
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<h3>
|
<h3>
|
||||||
|
|
|
@ -28,12 +28,16 @@ def login():
|
||||||
if not user:
|
if not user:
|
||||||
user = models.User.by_email(username)
|
user = models.User.by_email(username)
|
||||||
|
|
||||||
if (not user or password != user.password_hash or
|
if not user or password != user.password_hash:
|
||||||
user.status == models.UserStatusType.INACTIVE):
|
|
||||||
flask.flash(flask.Markup(
|
flask.flash(flask.Markup(
|
||||||
'<strong>Login failed!</strong> Incorrect username or password.'), 'danger')
|
'<strong>Login failed!</strong> Incorrect username or password.'), 'danger')
|
||||||
return flask.redirect(flask.url_for('account.login'))
|
return flask.redirect(flask.url_for('account.login'))
|
||||||
|
|
||||||
|
if user.status != models.UserStatusType.ACTIVE:
|
||||||
|
flask.flash(flask.Markup(
|
||||||
|
'<strong>Login failed!</strong> Account is not activated or banned.'), 'danger')
|
||||||
|
return flask.redirect(flask.url_for('account.login'))
|
||||||
|
|
||||||
user.last_login_date = datetime.utcnow()
|
user.last_login_date = datetime.utcnow()
|
||||||
user.last_login_ip = ip_address(flask.request.remote_addr).packed
|
user.last_login_ip = ip_address(flask.request.remote_addr).packed
|
||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from ipaddress import ip_address
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
|
|
||||||
from nyaa import forms, models
|
from nyaa import forms, models
|
||||||
|
@ -20,6 +22,45 @@ def view_adminlog():
|
||||||
adminlog=logs)
|
adminlog=logs)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/bans', endpoint='bans', methods=['GET', 'POST'])
|
||||||
|
def view_adminbans():
|
||||||
|
if not flask.g.user or not flask.g.user.is_moderator:
|
||||||
|
flask.abort(403)
|
||||||
|
|
||||||
|
form = forms.StringSubmitForm()
|
||||||
|
if flask.request.method == 'POST' and form.validate():
|
||||||
|
ban = models.Ban.by_id(form.submit.data)
|
||||||
|
if not ban:
|
||||||
|
flask.abort(404)
|
||||||
|
|
||||||
|
log = 'Unbanned ban #{0}'.format(ban.id)
|
||||||
|
|
||||||
|
if ban.user:
|
||||||
|
log += ' ' + ban.user.username
|
||||||
|
ban.user.status = models.UserStatusType.ACTIVE
|
||||||
|
db.session.add(ban.user)
|
||||||
|
|
||||||
|
if ban.user_ip:
|
||||||
|
log += ' IP({0})'.format(ip_address(ban.user_ip))
|
||||||
|
|
||||||
|
adminlog = models.AdminLog(log=log, admin_id=flask.g.user.id)
|
||||||
|
db.session.add(adminlog)
|
||||||
|
|
||||||
|
db.session.delete(ban)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
flask.flash('Unbanned ban #{0}'.format(ban.id), 'success')
|
||||||
|
|
||||||
|
page = flask.request.args.get('p', flask.request.args.get('offset', 1, int), int)
|
||||||
|
bans = models.Ban.all_bans() \
|
||||||
|
.order_by(models.Ban.created_time.desc()) \
|
||||||
|
.paginate(page=page, per_page=20)
|
||||||
|
|
||||||
|
return flask.render_template('admin_bans.html',
|
||||||
|
bans=bans,
|
||||||
|
form=form)
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/reports', endpoint='reports', methods=['GET', 'POST'])
|
@bp.route('/reports', endpoint='reports', methods=['GET', 'POST'])
|
||||||
def view_reports():
|
def view_reports():
|
||||||
if not flask.g.user or not flask.g.user.is_moderator:
|
if not flask.g.user or not flask.g.user.is_moderator:
|
||||||
|
@ -40,7 +81,7 @@ def view_reports():
|
||||||
if not torrent or not report or report.status != 0:
|
if not torrent or not report or report.status != 0:
|
||||||
flask.abort(404)
|
flask.abort(404)
|
||||||
else:
|
else:
|
||||||
log = "Report #{}: {} [#{}]({}), reported by [{}]({})"
|
log = 'Report #{}: {} [#{}]({}), reported by [{}]({})'
|
||||||
if action == 'delete':
|
if action == 'delete':
|
||||||
torrent.deleted = True
|
torrent.deleted = True
|
||||||
report.status = 1
|
report.status = 1
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import math
|
import math
|
||||||
import re
|
import re
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
from ipaddress import ip_address
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
from flask_paginate import Pagination
|
from flask_paginate import Pagination
|
||||||
|
@ -28,6 +29,10 @@ def before_request():
|
||||||
if not user:
|
if not user:
|
||||||
return logout()
|
return logout()
|
||||||
|
|
||||||
|
# Logout inactive and banned users
|
||||||
|
if user.status != models.UserStatusType.ACTIVE:
|
||||||
|
return logout()
|
||||||
|
|
||||||
flask.g.user = user
|
flask.g.user = user
|
||||||
|
|
||||||
if 'timeout' not in flask.session or flask.session['timeout'] < datetime.now():
|
if 'timeout' not in flask.session or flask.session['timeout'] < datetime.now():
|
||||||
|
@ -35,7 +40,14 @@ def before_request():
|
||||||
flask.session.permanent = True
|
flask.session.permanent = True
|
||||||
flask.session.modified = True
|
flask.session.modified = True
|
||||||
|
|
||||||
if flask.g.user.status == models.UserStatusType.BANNED:
|
# Check if user is banned on POST
|
||||||
|
if flask.request.method == 'POST':
|
||||||
|
ip = ip_address(flask.request.remote_addr).packed
|
||||||
|
banned = models.Ban.banned(None, ip).first()
|
||||||
|
if banned:
|
||||||
|
if flask.g.user:
|
||||||
|
return logout()
|
||||||
|
|
||||||
return 'You are banned.', 403
|
return 'You are banned.', 403
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import json
|
import json
|
||||||
import os.path
|
import os.path
|
||||||
|
from ipaddress import ip_address
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
|
@ -80,8 +81,9 @@ def view_torrent(torrent_id):
|
||||||
def edit_torrent(torrent_id):
|
def edit_torrent(torrent_id):
|
||||||
torrent = models.Torrent.by_id(torrent_id)
|
torrent = models.Torrent.by_id(torrent_id)
|
||||||
form = forms.EditForm(flask.request.form)
|
form = forms.EditForm(flask.request.form)
|
||||||
delete_form = forms.DeleteForm()
|
|
||||||
form.category.choices = _create_upload_category_choices()
|
form.category.choices = _create_upload_category_choices()
|
||||||
|
delete_form = forms.DeleteForm()
|
||||||
|
ban_form = None
|
||||||
|
|
||||||
editor = flask.g.user
|
editor = flask.g.user
|
||||||
|
|
||||||
|
@ -96,7 +98,10 @@ def edit_torrent(torrent_id):
|
||||||
if not editor or not (editor is torrent.user or editor.is_moderator):
|
if not editor or not (editor is torrent.user or editor.is_moderator):
|
||||||
flask.abort(403)
|
flask.abort(403)
|
||||||
|
|
||||||
if flask.request.method == 'POST' and form.validate():
|
if editor and editor.is_moderator and editor.level > torrent.user.level:
|
||||||
|
ban_form = forms.BanForm()
|
||||||
|
|
||||||
|
if flask.request.method == 'POST' and form.submit.data and form.validate():
|
||||||
# Form has been sent, edit torrent with data.
|
# Form has been sent, edit torrent with data.
|
||||||
torrent.main_category_id, torrent.sub_category_id = \
|
torrent.main_category_id, torrent.sub_category_id = \
|
||||||
form.category.parsed_data.get_category_ids()
|
form.category.parsed_data.get_category_ids()
|
||||||
|
@ -127,9 +132,12 @@ def edit_torrent(torrent_id):
|
||||||
|
|
||||||
flask.flash(flask.Markup(
|
flask.flash(flask.Markup(
|
||||||
'Torrent has been successfully edited! Changes might take a few minutes to show up.'),
|
'Torrent has been successfully edited! Changes might take a few minutes to show up.'),
|
||||||
'info')
|
'success')
|
||||||
|
|
||||||
return flask.redirect(url)
|
return flask.redirect(url)
|
||||||
|
elif flask.request.method == 'POST' and delete_form.validate() and \
|
||||||
|
(not ban_form or ban_form.validate()):
|
||||||
|
return _delete_torrent(torrent, delete_form, ban_form)
|
||||||
else:
|
else:
|
||||||
if flask.request.method != 'POST':
|
if flask.request.method != 'POST':
|
||||||
# Fill form data only if the POST didn't fail
|
# Fill form data only if the POST didn't fail
|
||||||
|
@ -146,39 +154,43 @@ def edit_torrent(torrent_id):
|
||||||
form.is_trusted.data = torrent.trusted
|
form.is_trusted.data = torrent.trusted
|
||||||
form.is_deleted.data = torrent.deleted
|
form.is_deleted.data = torrent.deleted
|
||||||
|
|
||||||
|
ipbanned = None
|
||||||
|
if editor.is_moderator:
|
||||||
|
tbanned = models.Ban.banned(None, torrent.uploader_ip).first()
|
||||||
|
ubanned = True
|
||||||
|
if torrent.user:
|
||||||
|
ubanned = models.Ban.banned(None, torrent.user.last_login_ip).first()
|
||||||
|
ipbanned = (tbanned and ubanned)
|
||||||
|
|
||||||
return flask.render_template('edit.html',
|
return flask.render_template('edit.html',
|
||||||
form=form,
|
form=form,
|
||||||
delete_form=delete_form,
|
delete_form=delete_form,
|
||||||
torrent=torrent)
|
ban_form=ban_form,
|
||||||
|
torrent=torrent,
|
||||||
|
ipbanned=ipbanned)
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/view/<int:torrent_id>/delete', endpoint='delete', methods=['POST'])
|
def _delete_torrent(torrent, form, banform):
|
||||||
def delete_torrent(torrent_id):
|
|
||||||
torrent = models.Torrent.by_id(torrent_id)
|
|
||||||
form = forms.DeleteForm(flask.request.form)
|
|
||||||
|
|
||||||
editor = flask.g.user
|
editor = flask.g.user
|
||||||
|
uploader = torrent.user
|
||||||
if not torrent:
|
|
||||||
flask.abort(404)
|
|
||||||
|
|
||||||
# Only allow admins edit deleted torrents
|
# Only allow admins edit deleted torrents
|
||||||
if torrent.deleted and not (editor and editor.is_moderator):
|
if torrent.deleted and not (editor and editor.is_moderator):
|
||||||
flask.abort(404)
|
flask.abort(404)
|
||||||
|
|
||||||
# Only allow torrent owners or admins edit torrents
|
|
||||||
if not editor or not (editor is torrent.user or editor.is_moderator):
|
|
||||||
flask.abort(403)
|
|
||||||
|
|
||||||
action = None
|
action = None
|
||||||
url = flask.url_for('main.home')
|
url = flask.url_for('main.home')
|
||||||
|
|
||||||
|
ban_torrent = form.ban.data
|
||||||
|
if banform:
|
||||||
|
ban_torrent = ban_torrent or banform.ban_user.data or banform.ban_userip.data
|
||||||
|
|
||||||
if form.delete.data and not torrent.deleted:
|
if form.delete.data and not torrent.deleted:
|
||||||
action = 'deleted'
|
action = 'deleted'
|
||||||
torrent.deleted = True
|
torrent.deleted = True
|
||||||
db.session.add(torrent)
|
db.session.add(torrent)
|
||||||
|
|
||||||
elif form.ban.data and not torrent.banned and editor.is_moderator:
|
elif ban_torrent and not torrent.banned and editor.is_moderator:
|
||||||
action = 'banned'
|
action = 'banned'
|
||||||
torrent.banned = True
|
torrent.banned = True
|
||||||
if not torrent.deleted:
|
if not torrent.deleted:
|
||||||
|
@ -202,20 +214,80 @@ def delete_torrent(torrent_id):
|
||||||
backend.tracker_api([torrent.info_hash], 'unban')
|
backend.tracker_api([torrent.info_hash], 'unban')
|
||||||
db.session.add(torrent)
|
db.session.add(torrent)
|
||||||
|
|
||||||
if not action:
|
if not action and not ban_torrent:
|
||||||
flask.flash(flask.Markup('What the fuck are you doing?'), 'danger')
|
flask.flash(flask.Markup('What the fuck are you doing?'), 'danger')
|
||||||
return flask.redirect(flask.url_for('torrents.edit', torrent_id=torrent.id))
|
return flask.redirect(flask.url_for('torrents.edit', torrent_id=torrent.id))
|
||||||
|
|
||||||
if editor.is_moderator:
|
if action and editor.is_moderator:
|
||||||
url = flask.url_for('torrents.view', torrent_id=torrent.id)
|
url = flask.url_for('torrents.view', torrent_id=torrent.id)
|
||||||
if editor is not torrent.user:
|
if editor is not uploader:
|
||||||
log = "Torrent [#{0}]({1}) has been {2}".format(torrent.id, url, action)
|
log = "Torrent [#{0}]({1}) has been {2}".format(torrent.id, url, action)
|
||||||
adminlog = models.AdminLog(log=log, admin_id=editor.id)
|
adminlog = models.AdminLog(log=log, admin_id=editor.id)
|
||||||
db.session.add(adminlog)
|
db.session.add(adminlog)
|
||||||
|
|
||||||
|
if action:
|
||||||
|
db.session.commit()
|
||||||
|
flask.flash(flask.Markup('Torrent has been successfully {0}.'.format(action)), 'success')
|
||||||
|
|
||||||
|
if not banform or not (banform.ban_user.data or banform.ban_userip.data):
|
||||||
|
return flask.redirect(url)
|
||||||
|
|
||||||
|
if banform.ban_userip.data:
|
||||||
|
tbanned = models.Ban.banned(None, torrent.uploader_ip).first()
|
||||||
|
ubanned = True
|
||||||
|
if uploader:
|
||||||
|
ubanned = models.Ban.banned(None, uploader.last_login_ip).first()
|
||||||
|
ipbanned = (tbanned and ubanned)
|
||||||
|
|
||||||
|
if (banform.ban_user.data and (not uploader or uploader.is_banned)) or \
|
||||||
|
(banform.ban_userip.data and ipbanned):
|
||||||
|
flask.flash(flask.Markup('What the fuck are you doing?'), 'danger')
|
||||||
|
return flask.redirect(flask.url_for('torrents.edit', torrent_id=torrent.id))
|
||||||
|
|
||||||
|
flavor = "Nyaa" if app.config['SITE_FLAVOR'] == 'nyaa' else "Sukebei"
|
||||||
|
eurl = flask.url_for('torrents.view', torrent_id=torrent.id, _external=True)
|
||||||
|
reason = "[{0}#{1}]({2}) {3}".format(flavor, torrent.id, eurl, banform.reason.data)
|
||||||
|
ban1 = models.Ban(admin_id=editor.id, reason=reason)
|
||||||
|
ban2 = models.Ban(admin_id=editor.id, reason=reason)
|
||||||
|
db.session.add(ban1)
|
||||||
|
|
||||||
|
if uploader:
|
||||||
|
uploader.status = models.UserStatusType.BANNED
|
||||||
|
db.session.add(uploader)
|
||||||
|
ban1.user_id = uploader.id
|
||||||
|
ban2.user_id = uploader.id
|
||||||
|
|
||||||
|
if banform.ban_userip.data:
|
||||||
|
if not ubanned:
|
||||||
|
ban1.user_ip = ip_address(uploader.last_login_ip)
|
||||||
|
if not tbanned:
|
||||||
|
uploader_ip = ip_address(torrent.uploader_ip)
|
||||||
|
if ban1.user_ip != uploader_ip:
|
||||||
|
ban2.user_ip = uploader_ip
|
||||||
|
db.session.add(ban2)
|
||||||
|
else:
|
||||||
|
ban1.user_ip = ip_address(torrent.uploader_ip)
|
||||||
|
|
||||||
|
uploader_str = "Anonymous"
|
||||||
|
if uploader:
|
||||||
|
uploader_url = flask.url_for('users.view_user', user_name=uploader.username)
|
||||||
|
uploader_str = "[{0}]({1})".format(uploader.username, uploader_url)
|
||||||
|
if ban1.user_ip:
|
||||||
|
uploader_str += " IP({0})".format(ban1.user_ip)
|
||||||
|
ban1.user_ip = ban1.user_ip.packed
|
||||||
|
if ban2.user_ip:
|
||||||
|
uploader_str += " IP({0})".format(ban2.user_ip)
|
||||||
|
ban2.user_ip = ban2.user_ip.packed
|
||||||
|
|
||||||
|
log = "Uploader {0} of torrent [#{1}]({2}) has been banned.".format(
|
||||||
|
uploader_str, torrent.id, flask.url_for('torrents.view', torrent_id=torrent.id), action)
|
||||||
|
adminlog = models.AdminLog(log=log, admin_id=editor.id)
|
||||||
|
db.session.add(adminlog)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
flask.flash(flask.Markup('Torrent has been successfully {0}.'.format(action)), 'info')
|
flask.flash(flask.Markup('Uploader has been successfully banned.'), 'success')
|
||||||
|
|
||||||
return flask.redirect(url)
|
return flask.redirect(url)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import math
|
import math
|
||||||
|
from ipaddress import ip_address
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
from flask_paginate import Pagination
|
from flask_paginate import Pagination
|
||||||
|
@ -23,14 +24,23 @@ def view_user(user_name):
|
||||||
flask.abort(404)
|
flask.abort(404)
|
||||||
|
|
||||||
admin_form = None
|
admin_form = None
|
||||||
|
ban_form = None
|
||||||
|
bans = None
|
||||||
|
ipbanned = None
|
||||||
if flask.g.user and flask.g.user.is_moderator and flask.g.user.level > user.level:
|
if flask.g.user and flask.g.user.is_moderator and flask.g.user.level > user.level:
|
||||||
admin_form = forms.UserForm()
|
admin_form = forms.UserForm()
|
||||||
default, admin_form.user_class.choices = _create_user_class_choices(user)
|
default, admin_form.user_class.choices = _create_user_class_choices(user)
|
||||||
if flask.request.method == 'GET':
|
if flask.request.method == 'GET':
|
||||||
admin_form.user_class.data = default
|
admin_form.user_class.data = default
|
||||||
|
|
||||||
|
ban_form = forms.BanForm()
|
||||||
|
if flask.request.method == 'POST':
|
||||||
|
doban = (ban_form.ban_user.data or ban_form.unban.data or ban_form.ban_userip.data)
|
||||||
|
bans = models.Ban.banned(user.id, user.last_login_ip).all()
|
||||||
|
ipbanned = list(filter(lambda b: b.user_ip == user.last_login_ip, bans))
|
||||||
|
|
||||||
url = flask.url_for('users.view_user', user_name=user.username)
|
url = flask.url_for('users.view_user', user_name=user.username)
|
||||||
if flask.request.method == 'POST' and admin_form and admin_form.validate():
|
if flask.request.method == 'POST' and admin_form and not doban and admin_form.validate():
|
||||||
selection = admin_form.user_class.data
|
selection = admin_form.user_class.data
|
||||||
log = None
|
log = None
|
||||||
if selection == 'regular':
|
if selection == 'regular':
|
||||||
|
@ -42,9 +52,6 @@ def view_user(user_name):
|
||||||
elif selection == 'moderator':
|
elif selection == 'moderator':
|
||||||
user.level = models.UserLevelType.MODERATOR
|
user.level = models.UserLevelType.MODERATOR
|
||||||
log = "[{}]({}) changed to moderator user".format(user_name, url)
|
log = "[{}]({}) changed to moderator user".format(user_name, url)
|
||||||
elif selection == 'banned':
|
|
||||||
user.status = models.UserStatusType.BANNED
|
|
||||||
log = "[{}]({}) changed to banned user".format(user_name, url)
|
|
||||||
|
|
||||||
adminlog = models.AdminLog(log=log, admin_id=flask.g.user.id)
|
adminlog = models.AdminLog(log=log, admin_id=flask.g.user.id)
|
||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
|
@ -53,6 +60,46 @@ def view_user(user_name):
|
||||||
|
|
||||||
return flask.redirect(url)
|
return flask.redirect(url)
|
||||||
|
|
||||||
|
if flask.request.method == 'POST' and ban_form and doban and ban_form.validate():
|
||||||
|
if (ban_form.ban_user.data and user.is_banned) or \
|
||||||
|
(ban_form.ban_userip.data and ipbanned) or \
|
||||||
|
(ban_form.unban.data and not user.is_banned and not bans):
|
||||||
|
flask.flash(flask.Markup('What the fuck are you doing?'), 'danger')
|
||||||
|
return flask.redirect(url)
|
||||||
|
|
||||||
|
user_str = "[{0}]({1})".format(user.username, url)
|
||||||
|
|
||||||
|
if ban_form.unban.data:
|
||||||
|
action = "unbanned"
|
||||||
|
user.status = models.UserStatusType.ACTIVE
|
||||||
|
db.session.add(user)
|
||||||
|
|
||||||
|
for ban in bans:
|
||||||
|
if ban.user_ip:
|
||||||
|
user_str += " IP({0})".format(ip_address(ban.user_ip))
|
||||||
|
db.session.delete(ban)
|
||||||
|
else:
|
||||||
|
action = "banned"
|
||||||
|
user.status = models.UserStatusType.BANNED
|
||||||
|
db.session.add(user)
|
||||||
|
|
||||||
|
ban = models.Ban(admin_id=flask.g.user.id, user_id=user.id, reason=ban_form.reason.data)
|
||||||
|
db.session.add(ban)
|
||||||
|
|
||||||
|
if ban_form.ban_userip.data:
|
||||||
|
ban.user_ip = ip_address(user.last_login_ip)
|
||||||
|
user_str += " IP({0})".format(ban.user_ip)
|
||||||
|
ban.user_ip = ban.user_ip.packed
|
||||||
|
|
||||||
|
log = "User {0} has been {1}.".format(user_str, action)
|
||||||
|
adminlog = models.AdminLog(log=log, admin_id=flask.g.user.id)
|
||||||
|
db.session.add(adminlog)
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
flask.flash(flask.Markup('User has been successfully {0}.'.format(action)), 'success')
|
||||||
|
return flask.redirect(url)
|
||||||
|
|
||||||
req_args = flask.request.args
|
req_args = flask.request.args
|
||||||
|
|
||||||
search_term = chain_get(req_args, 'q', 'term')
|
search_term = chain_get(req_args, 'q', 'term')
|
||||||
|
@ -117,7 +164,10 @@ def view_user(user_name):
|
||||||
user=user,
|
user=user,
|
||||||
user_page=True,
|
user_page=True,
|
||||||
rss_filter=rss_query_string,
|
rss_filter=rss_query_string,
|
||||||
admin_form=admin_form)
|
admin_form=admin_form,
|
||||||
|
ban_form=ban_form,
|
||||||
|
bans=bans,
|
||||||
|
ipbanned=ipbanned)
|
||||||
# Similar logic as home page
|
# Similar logic as home page
|
||||||
else:
|
else:
|
||||||
if use_elastic:
|
if use_elastic:
|
||||||
|
@ -132,7 +182,10 @@ def view_user(user_name):
|
||||||
user=user,
|
user=user,
|
||||||
user_page=True,
|
user_page=True,
|
||||||
rss_filter=rss_query_string,
|
rss_filter=rss_query_string,
|
||||||
admin_form=admin_form)
|
admin_form=admin_form,
|
||||||
|
ban_form=ban_form,
|
||||||
|
bans=bans,
|
||||||
|
ipbanned=ipbanned)
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/user/activate/<payload>')
|
@bp.route('/user/activate/<payload>')
|
||||||
|
@ -164,8 +217,6 @@ def _create_user_class_choices(user):
|
||||||
choices.append(('trusted', 'Trusted'))
|
choices.append(('trusted', 'Trusted'))
|
||||||
if flask.g.user.is_superadmin:
|
if flask.g.user.is_superadmin:
|
||||||
choices.append(('moderator', 'Moderator'))
|
choices.append(('moderator', 'Moderator'))
|
||||||
if flask.g.user.is_moderator:
|
|
||||||
choices.append(('banned', 'Banned'))
|
|
||||||
|
|
||||||
if user:
|
if user:
|
||||||
if user.is_moderator:
|
if user.is_moderator:
|
||||||
|
|
Loading…
Reference in a new issue