mirror of
https://gitlab.com/SIGBUS/nyaa.git
synced 2024-12-22 09:00:00 +00:00
Add (optional) validation for minimum anonymous torrent size (#342)
MINIMUM_ANONYMOUS_TORRENT_SIZE can be used to require a minimum total size of torrents uploaded by anonymous users (ie. without accounts). Sets up a "framework" for post-WTForm torrent validation as well; this can easily be extended into filename blacklists and such.
This commit is contained in:
parent
48d4217f02
commit
39fcfc0058
|
@ -46,6 +46,9 @@ ENFORCE_MAIN_ANNOUNCE_URL = False
|
||||||
MAIN_ANNOUNCE_URL = 'http://127.0.0.1:6881/announce'
|
MAIN_ANNOUNCE_URL = 'http://127.0.0.1:6881/announce'
|
||||||
TRACKER_API_URL = 'http://127.0.0.1:6881/api'
|
TRACKER_API_URL = 'http://127.0.0.1:6881/api'
|
||||||
TRACKER_API_AUTH = 'topsecret'
|
TRACKER_API_AUTH = 'topsecret'
|
||||||
|
# Torrents uploaded without an account must be at least this big in total (bytes)
|
||||||
|
# Set to 0 to disable
|
||||||
|
MINIMUM_ANONYMOUS_TORRENT_SIZE = 1 * 1024 * 1024
|
||||||
|
|
||||||
BACKUP_TORRENT_FOLDER = 'torrents'
|
BACKUP_TORRENT_FOLDER = 'torrents'
|
||||||
|
|
||||||
|
|
|
@ -99,22 +99,25 @@ def v2_api_upload():
|
||||||
upload_form.category.choices = _create_upload_category_choices()
|
upload_form.category.choices = _create_upload_category_choices()
|
||||||
|
|
||||||
if upload_form.validate():
|
if upload_form.validate():
|
||||||
torrent = backend.handle_torrent_upload(upload_form, flask.g.user)
|
try:
|
||||||
|
torrent = backend.handle_torrent_upload(upload_form, flask.g.user)
|
||||||
|
|
||||||
# Create a response dict with relevant data
|
# Create a response dict with relevant data
|
||||||
torrent_metadata = {
|
torrent_metadata = {
|
||||||
'url': flask.url_for('torrents.view', torrent_id=torrent.id, _external=True),
|
'url': flask.url_for('torrents.view', torrent_id=torrent.id, _external=True),
|
||||||
'id': torrent.id,
|
'id': torrent.id,
|
||||||
'name': torrent.display_name,
|
'name': torrent.display_name,
|
||||||
'hash': torrent.info_hash.hex(),
|
'hash': torrent.info_hash.hex(),
|
||||||
'magnet': torrent.magnet_uri
|
'magnet': torrent.magnet_uri
|
||||||
}
|
}
|
||||||
|
|
||||||
return flask.jsonify(torrent_metadata)
|
return flask.jsonify(torrent_metadata)
|
||||||
else:
|
except backend.TorrentExtraValidationException:
|
||||||
# Map errors back from form fields into the api keys
|
pass
|
||||||
mapped_errors = {UPLOAD_API_FORM_KEYMAP.get(k, k): v for k, v in upload_form.errors.items()}
|
|
||||||
return flask.jsonify({'errors': mapped_errors}), 400
|
# Map errors back from form fields into the api keys
|
||||||
|
mapped_errors = {UPLOAD_API_FORM_KEYMAP.get(k, k): v for k, v in upload_form.errors.items()}
|
||||||
|
return flask.jsonify({'errors': mapped_errors}), 400
|
||||||
|
|
||||||
|
|
||||||
# #################################### TEMPORARY ####################################
|
# #################################### TEMPORARY ####################################
|
||||||
|
|
|
@ -15,6 +15,11 @@ from nyaa.extensions import db
|
||||||
app = flask.current_app
|
app = flask.current_app
|
||||||
|
|
||||||
|
|
||||||
|
class TorrentExtraValidationException(Exception):
|
||||||
|
def __init__(self, errors):
|
||||||
|
self.errors = errors
|
||||||
|
|
||||||
|
|
||||||
@utils.cached_function
|
@utils.cached_function
|
||||||
def get_category_id_map():
|
def get_category_id_map():
|
||||||
''' Reads database for categories and turns them into a dict with
|
''' Reads database for categories and turns them into a dict with
|
||||||
|
@ -44,7 +49,37 @@ def _replace_utf8_values(dict_or_list):
|
||||||
return did_change
|
return did_change
|
||||||
|
|
||||||
|
|
||||||
|
def validate_torrent_post_upload(torrent, upload_form=None):
|
||||||
|
''' Validates a Torrent instance before it's saved to the database.
|
||||||
|
Enforcing user-and-such-based validations is more flexible here vs WTForm context '''
|
||||||
|
errors = {
|
||||||
|
'torrent_file': []
|
||||||
|
}
|
||||||
|
|
||||||
|
# Encorce minimum size for userless uploads
|
||||||
|
minimum_anonymous_torrent_size = app.config['MINIMUM_ANONYMOUS_TORRENT_SIZE']
|
||||||
|
if torrent.user is None and torrent.filesize < minimum_anonymous_torrent_size:
|
||||||
|
errors['torrent_file'].append('Torrent too small for an anonymous uploader')
|
||||||
|
|
||||||
|
print(errors)
|
||||||
|
# Remove keys with empty lists
|
||||||
|
errors = {k: v for k, v in errors.items() if v}
|
||||||
|
if errors:
|
||||||
|
if upload_form:
|
||||||
|
# Add error messages to the form fields
|
||||||
|
for field_name, field_errors in errors.items():
|
||||||
|
getattr(upload_form, field_name).errors.extend(field_errors)
|
||||||
|
# Clear out the wtforms dict to force a regeneration
|
||||||
|
upload_form._errors = None
|
||||||
|
|
||||||
|
raise TorrentExtraValidationException(errors)
|
||||||
|
|
||||||
|
|
||||||
def handle_torrent_upload(upload_form, uploading_user=None, fromAPI=False):
|
def handle_torrent_upload(upload_form, uploading_user=None, fromAPI=False):
|
||||||
|
''' Stores a torrent to the database.
|
||||||
|
May throw TorrentExtraValidationException if the form/torrent fails
|
||||||
|
post-WTForm validation! Exception messages will also be added to their
|
||||||
|
relevant fields on the given form. '''
|
||||||
torrent_data = upload_form.torrent_file.parsed_data
|
torrent_data = upload_form.torrent_file.parsed_data
|
||||||
|
|
||||||
# Delete exisiting torrent which is marked as deleted
|
# Delete exisiting torrent which is marked as deleted
|
||||||
|
@ -197,6 +232,9 @@ def handle_torrent_upload(upload_form, uploading_user=None, fromAPI=False):
|
||||||
tracker_id=tracker.id, order=order)
|
tracker_id=tracker.id, order=order)
|
||||||
db.session.add(torrent_tracker)
|
db.session.add(torrent_tracker)
|
||||||
|
|
||||||
|
# Before final commit, validate the torrent again
|
||||||
|
validate_torrent_post_upload(torrent, upload_form)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
# Store the actual torrent file as well
|
# Store the actual torrent file as well
|
||||||
|
|
|
@ -307,13 +307,16 @@ def upload():
|
||||||
upload_form.category.choices = _create_upload_category_choices()
|
upload_form.category.choices = _create_upload_category_choices()
|
||||||
|
|
||||||
if flask.request.method == 'POST' and upload_form.validate():
|
if flask.request.method == 'POST' and upload_form.validate():
|
||||||
torrent = backend.handle_torrent_upload(upload_form, flask.g.user)
|
try:
|
||||||
|
torrent = backend.handle_torrent_upload(upload_form, flask.g.user)
|
||||||
|
|
||||||
return flask.redirect(flask.url_for('torrents.view', torrent_id=torrent.id))
|
return flask.redirect(flask.url_for('torrents.view', torrent_id=torrent.id))
|
||||||
else:
|
except backend.TorrentExtraValidationException:
|
||||||
# If we get here with a POST, it means the form data was invalid: return a non-okay status
|
pass
|
||||||
status_code = 400 if flask.request.method == 'POST' else 200
|
|
||||||
return flask.render_template('upload.html', upload_form=upload_form), status_code
|
# If we get here with a POST, it means the form data was invalid: return a non-okay status
|
||||||
|
status_code = 400 if flask.request.method == 'POST' else 200
|
||||||
|
return flask.render_template('upload.html', upload_form=upload_form), status_code
|
||||||
|
|
||||||
|
|
||||||
@cached_function
|
@cached_function
|
||||||
|
|
Loading…
Reference in a new issue