Store user agent with files

Needed for moderation.
This commit is contained in:
Mia Herkt 2023-03-29 07:21:36 +02:00
parent e00866f5e4
commit 3d1facaec3
No known key found for this signature in database
3 changed files with 47 additions and 7 deletions

View file

@ -135,6 +135,7 @@ class File(db.Model):
ext = db.Column(db.UnicodeText) ext = db.Column(db.UnicodeText)
mime = db.Column(db.UnicodeText) mime = db.Column(db.UnicodeText)
addr = db.Column(db.UnicodeText) addr = db.Column(db.UnicodeText)
ua = db.Column(db.UnicodeText)
removed = db.Column(db.Boolean, default=False) removed = db.Column(db.Boolean, default=False)
nsfw_score = db.Column(db.Float) nsfw_score = db.Column(db.Float)
expiration = db.Column(db.BigInteger) expiration = db.Column(db.BigInteger)
@ -143,11 +144,12 @@ class File(db.Model):
last_vscan = db.Column(db.DateTime) last_vscan = db.Column(db.DateTime)
size = db.Column(db.BigInteger) size = db.Column(db.BigInteger)
def __init__(self, sha256, ext, mime, addr, expiration, mgmt_token): def __init__(self, sha256, ext, mime, addr, ua, expiration, mgmt_token):
self.sha256 = sha256 self.sha256 = sha256
self.ext = ext self.ext = ext
self.mime = mime self.mime = mime
self.addr = addr self.addr = addr
self.ua = ua
self.expiration = expiration self.expiration = expiration
self.mgmt_token = mgmt_token self.mgmt_token = mgmt_token
@ -212,7 +214,7 @@ class File(db.Model):
Any value greater that the longest allowed file lifespan will be rounded down to that Any value greater that the longest allowed file lifespan will be rounded down to that
value. value.
""" """
def store(file_, requested_expiration: typing.Optional[int], addr, secret: bool): def store(file_, requested_expiration: typing.Optional[int], addr, ua, secret: bool):
data = file_.read() data = file_.read()
digest = sha256(data).hexdigest() digest = sha256(data).hexdigest()
@ -278,9 +280,10 @@ class File(db.Model):
mime = get_mime() mime = get_mime()
ext = get_ext(mime) ext = get_ext(mime)
mgmt_token = secrets.token_urlsafe() mgmt_token = secrets.token_urlsafe()
f = File(digest, ext, mime, addr, expiration, mgmt_token) f = File(digest, ext, mime, addr, ua, expiration, mgmt_token)
f.addr = addr f.addr = addr
f.ua = ua
if isnew: if isnew:
f.secret = None f.secret = None
@ -368,11 +371,11 @@ requested_expiration can be:
Any value greater that the longest allowed file lifespan will be rounded down to that Any value greater that the longest allowed file lifespan will be rounded down to that
value. value.
""" """
def store_file(f, requested_expiration: typing.Optional[int], addr, secret: bool): def store_file(f, requested_expiration: typing.Optional[int], addr, ua, secret: bool):
if in_upload_bl(addr): if in_upload_bl(addr):
return "Your host is blocked from uploading files.\n", 451 return "Your host is blocked from uploading files.\n", 451
sf, isnew = File.store(f, requested_expiration, addr, secret) sf, isnew = File.store(f, requested_expiration, addr, ua, secret)
response = make_response(sf.geturl()) response = make_response(sf.geturl())
response.headers["X-Expires"] = sf.expiration response.headers["X-Expires"] = sf.expiration
@ -382,7 +385,7 @@ def store_file(f, requested_expiration: typing.Optional[int], addr, secret: boo
return response return response
def store_url(url, addr, secret: bool): def store_url(url, addr, ua, secret: bool):
if is_fhost_url(url): if is_fhost_url(url):
abort(400) abort(400)
@ -403,7 +406,7 @@ def store_url(url, addr, secret: bool):
f = urlfile(read=r.raw.read, content_type=r.headers["content-type"], filename="") f = urlfile(read=r.raw.read, content_type=r.headers["content-type"], filename="")
return store_file(f, None, addr, secret) return store_file(f, None, addr, ua, secret)
else: else:
abort(413) abort(413)
else: else:
@ -498,6 +501,7 @@ def fhost():
request.files["file"], request.files["file"],
int(request.form["expires"]), int(request.form["expires"]),
request.remote_addr, request.remote_addr,
request.user_agent.string,
secret secret
) )
except ValueError: except ValueError:
@ -509,12 +513,14 @@ def fhost():
request.files["file"], request.files["file"],
None, None,
request.remote_addr, request.remote_addr,
request.user_agent.string,
secret secret
) )
elif "url" in request.form: elif "url" in request.form:
return store_url( return store_url(
request.form["url"], request.form["url"],
request.remote_addr, request.remote_addr,
request.user_agent.string,
secret secret
) )
elif "shorten" in request.form: elif "shorten" in request.form:

View file

@ -0,0 +1,30 @@
"""Store user agent string with files
Revision ID: dd0766afb7d2
Revises: 30bfe33aa328
Create Date: 2023-03-29 07:18:49.113200
"""
# revision identifiers, used by Alembic.
revision = 'dd0766afb7d2'
down_revision = '30bfe33aa328'
from alembic import op
import sqlalchemy as sa
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('file', schema=None) as batch_op:
batch_op.add_column(sa.Column('ua', sa.UnicodeText(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('file', schema=None) as batch_op:
batch_op.drop_column('ua')
# ### end Alembic commands ###

4
mod.py
View file

@ -26,6 +26,7 @@ class NullptrMod(Screen):
("f4", "filter(4, 'Filter extension:')", "Filter Ext."), ("f4", "filter(4, 'Filter extension:')", "Filter Ext."),
("f5", "refresh", "Refresh"), ("f5", "refresh", "Refresh"),
("f6", "filter_clear", "Clear filter"), ("f6", "filter_clear", "Clear filter"),
("f7", "filter(5, 'Filter user agent:')", "Filter UA"),
("r", "remove_file(False)", "Remove file"), ("r", "remove_file(False)", "Remove file"),
("ctrl+r", "remove_file(True)", "Ban file"), ("ctrl+r", "remove_file(True)", "Ban file"),
("p", "ban_ip(False)", "Ban IP"), ("p", "ban_ip(False)", "Ban IP"),
@ -60,6 +61,7 @@ class NullptrMod(Screen):
case 2: finput.value = self.current_file.addr case 2: finput.value = self.current_file.addr
case 3: finput.value = self.current_file.mime case 3: finput.value = self.current_file.mime
case 4: finput.value = self.current_file.ext case 4: finput.value = self.current_file.ext
case 5: finput.value = self.current_file.ua
def on_input_submitted(self, message: Input.Submitted) -> None: def on_input_submitted(self, message: Input.Submitted) -> None:
self.query_one("#filter_container").display = False self.query_one("#filter_container").display = False
@ -74,6 +76,7 @@ class NullptrMod(Screen):
case 2: ftable.query = ftable.base_query.filter(File.addr == message.value) case 2: ftable.query = ftable.base_query.filter(File.addr == message.value)
case 3: ftable.query = ftable.base_query.filter(File.mime.like(message.value)) case 3: ftable.query = ftable.base_query.filter(File.mime.like(message.value))
case 4: ftable.query = ftable.base_query.filter(File.ext.like(message.value)) case 4: ftable.query = ftable.base_query.filter(File.ext.like(message.value))
case 5: ftable.query = ftable.base_query.filter(File.ua.like(message.value))
else: else:
ftable.query = ftable.base_query ftable.query = ftable.base_query
@ -249,6 +252,7 @@ class NullptrMod(Screen):
("MIME type:", f.mime), ("MIME type:", f.mime),
("SHA256 checksum:", f.sha256), ("SHA256 checksum:", f.sha256),
("Uploaded by:", Text(f.addr)), ("Uploaded by:", Text(f.addr)),
("User agent:", Text(f.ua or "")),
("Management token:", f.mgmt_token), ("Management token:", f.mgmt_token),
("Secret:", f.secret), ("Secret:", f.secret),
("Is NSFW:", ("Yes" if f.is_nsfw else "No") + (f" (Score: {f.nsfw_score:0.4f})" if f.nsfw_score else " (Not scanned)")), ("Is NSFW:", ("Yes" if f.is_nsfw else "No") + (f" (Score: {f.nsfw_score:0.4f})" if f.nsfw_score else " (Not scanned)")),