Add ::/list/ endpoint, with a secret to find them.

This commit is contained in:
Julien Palard 2023-04-25 14:09:29 +02:00
parent e376fab285
commit 2b13d6cbb0
Signed by: mdk
GPG Key ID: 0EFC1AC1006886F8
5 changed files with 73 additions and 11 deletions

View File

@ -12,12 +12,14 @@ class PasteAdmin(admin.ModelAdmin):
"paste_time",
"access_time",
"viewcount",
"auth",
)
fields = (
(
"slug",
"size",
),
"auth",
(
"paste_time",
"access_time",

View File

@ -0,0 +1,17 @@
# Generated by Django 4.2 on 2023-04-25 09:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("paste", "0008_paste_access_time"),
]
operations = [
migrations.AddField(
model_name="paste",
name="auth",
field=models.CharField(default="", max_length=64),
),
]

View File

@ -1,4 +1,5 @@
from datetime import datetime, timedelta
from hashlib import sha256
import shortuuid
from django.core.validators import MaxLengthValidator
@ -10,9 +11,16 @@ from django.utils.translation import gettext_lazy as _
from webtools import settings
class PasteQuerySet(models.QuerySet):
def by_secret(self, secret):
auth = sha256(secret.encode("UTF-8")).hexdigest()
return self.filter(auth=auth)
class Paste(models.Model):
"""Paste object."""
objects = PasteQuerySet.as_manager()
filename = models.CharField(max_length=255, default="")
slug = models.SlugField(unique=True, editable=False)
content = models.TextField(
@ -23,6 +31,14 @@ class Paste(models.Model):
access_time = models.DateTimeField(auto_now=True)
viewcount = models.IntegerField(default=0, editable=False)
# auth stores a sha256 (hexdigest) of the Authentication header.
auth = models.CharField(max_length=64, default="")
def set_secret(self, secret=None):
if not secret:
return
self.auth = sha256(secret.encode("UTF-8")).hexdigest()
def compute_size(self):
"""Computes size."""
self.size = len(self.content)

View File

@ -7,5 +7,6 @@ from webtools import settings
urlpatterns = [
path("", views.index, name="index"),
path("::/static/<path>", serve, {"document_root": settings.STATIC_ROOT}),
path("::/list/", views.list_view),
path("<path:slug>", views.show, name="short_paste"),
]

View File

@ -1,3 +1,4 @@
from datetime import datetime
from functools import lru_cache
from mimetypes import common_types, types_map
@ -29,6 +30,21 @@ def get_files(request):
return {"": request}
def pastes_as_table(request, pastes, headers=("URL", "size", "filename")):
def paste_attr(paste, attr):
if attr == "URL":
return request.build_absolute_uri(paste.get_absolute_url())
value = getattr(paste, attr)
if isinstance(value, datetime):
return value.isoformat(timespec="seconds")
return value
values = []
for paste in pastes:
values.append([paste_attr(paste, attr) for attr in headers])
return tabulate(values, headers=headers, tablefmt="github") + "\n"
@csrf_exempt
def index(request):
"""Displays form."""
@ -46,22 +62,32 @@ def index(request):
filename=filename.split("/")[-1],
content=the_file.read().decode("UTF-8"),
)
paste.set_secret(request.headers.get("Authorization"))
paste.compute_size()
paste.save()
pastes.append(paste)
table = [
(
request.build_absolute_uri(paste.get_absolute_url()),
paste.size,
paste.filename,
)
for paste in pastes
]
return HttpResponse(
tabulate(table, headers=("URL", "size", "filename"), tablefmt="github") + "\n",
content_type="text/plain",
return HttpResponse(pastes_as_table(request, pastes), content_type="text/plain")
def list_view(request):
secret = request.headers.get("Authorization")
pastes = []
if secret:
pastes = Paste.objects.by_secret(secret).order_by("paste_time")
table = pastes_as_table(
request,
pastes,
headers=("filename", "size", "URL", "paste_time", "access_time"),
)
if "html" in request.headers["accept"]:
return HttpResponse(
loader.render_to_string(
"paste/show-markdown.html", {"highlighted": markdown_to_html(table)}
)
)
else:
return HttpResponse(table, content_type="text/plain")
def show(request, slug):