From f08bc6c679b115d3052b0d7cd6cc4d01e00828a6 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Fri, 18 Dec 2020 19:17:23 +0100 Subject: [PATCH] [SECURITY] Bind relevant cached file downloads to the current session --- .../migrations/0162b_auto_20201218_1810.py | 23 +++++++++++++++++++ src/pretix/base/models/base.py | 2 ++ src/pretix/base/services/shredder.py | 6 +++-- src/pretix/base/views/cachedfiles.py | 6 ++++- src/pretix/control/views/orders.py | 2 +- src/pretix/control/views/organizer.py | 2 +- src/pretix/control/views/pdf.py | 4 ++-- src/pretix/control/views/shredder.py | 2 +- src/pretix/plugins/badges/views.py | 2 +- 9 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 src/pretix/base/migrations/0162b_auto_20201218_1810.py diff --git a/src/pretix/base/migrations/0162b_auto_20201218_1810.py b/src/pretix/base/migrations/0162b_auto_20201218_1810.py new file mode 100644 index 000000000..ec7e82502 --- /dev/null +++ b/src/pretix/base/migrations/0162b_auto_20201218_1810.py @@ -0,0 +1,23 @@ +# Generated by Django 3.0.11 on 2020-12-18 18:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pretixbase', '0173_auto_20201211_1648'), + ] + + operations = [ + migrations.AddField( + model_name='cachedfile', + name='session_key', + field=models.TextField(null=True), + ), + migrations.AddField( + model_name='cachedfile', + name='web_download', + field=models.BooleanField(default=True), + ), + ] diff --git a/src/pretix/base/models/base.py b/src/pretix/base/models/base.py index f7205e273..bb1bf30ca 100644 --- a/src/pretix/base/models/base.py +++ b/src/pretix/base/models/base.py @@ -28,6 +28,8 @@ class CachedFile(models.Model): filename = models.CharField(max_length=255) type = models.CharField(max_length=255) file = models.FileField(null=True, blank=True, upload_to=cachedfile_name, max_length=255) + web_download = models.BooleanField(default=True) # allow web download, True for backwards compatibility in plugins + session_key = models.TextField(null=True, blank=True) # only allow download in this session @receiver(post_delete, sender=CachedFile) diff --git a/src/pretix/base/services/shredder.py b/src/pretix/base/services/shredder.py index e0e38572f..ced385179 100644 --- a/src/pretix/base/services/shredder.py +++ b/src/pretix/base/services/shredder.py @@ -17,7 +17,7 @@ from pretix.celery_app import app @app.task(base=ProfiledEventTask) -def export(event: Event, shredders: List[str]) -> None: +def export(event: Event, shredders: List[str], session_key=None) -> None: known_shredders = event.get_data_shredders() with NamedTemporaryFile() as rawfile: @@ -54,7 +54,9 @@ def export(event: Event, shredders: List[str]) -> None: cf = CachedFile() cf.date = now() cf.filename = event.slug + '.zip' - cf.type = 'application/pdf' + cf.type = 'application/zip' + cf.session_key = session_key + cf.web_download = True cf.expires = now() + timedelta(hours=1) cf.save() cf.file.save(cachedfile_name(cf, cf.filename), rawfile) diff --git a/src/pretix/base/views/cachedfiles.py b/src/pretix/base/views/cachedfiles.py index 61b586185..1f907f2ec 100644 --- a/src/pretix/base/views/cachedfiles.py +++ b/src/pretix/base/views/cachedfiles.py @@ -13,7 +13,11 @@ class DownloadView(TemplateView): @cached_property def object(self) -> CachedFile: try: - return get_object_or_404(CachedFile, id=self.kwargs['id']) + o = get_object_or_404(CachedFile, id=self.kwargs['id'], web_download=True) + if o.session_key: + if o.session_key != self.request.session.session_key: + raise Http404() + return o except ValueError: # Invalid URLs raise Http404() diff --git a/src/pretix/control/views/orders.py b/src/pretix/control/views/orders.py index a2c56c6c5..6e6af2560 100644 --- a/src/pretix/control/views/orders.py +++ b/src/pretix/control/views/orders.py @@ -1956,7 +1956,7 @@ class ExportDoView(EventPermissionRequiredMixin, ExportMixin, AsyncAction, View) messages.error(self.request, _('There was a problem processing your input. See below for error details.')) return self.get(request, *args, **kwargs) - cf = CachedFile() + cf = CachedFile(web_download=True, session_key=request.session.session_key) cf.date = now() cf.expires = now() + timedelta(days=3) cf.save() diff --git a/src/pretix/control/views/organizer.py b/src/pretix/control/views/organizer.py index 8d943824c..67054c894 100644 --- a/src/pretix/control/views/organizer.py +++ b/src/pretix/control/views/organizer.py @@ -1238,7 +1238,7 @@ class ExportDoView(OrganizerPermissionRequiredMixin, ExportMixin, AsyncAction, V messages.error(self.request, _('There was a problem processing your input. See below for error details.')) return self.get(request, *args, **kwargs) - cf = CachedFile() + cf = CachedFile(web_download=True, session_key=request.session.session_key) cf.date = now() cf.expires = now() + timedelta(days=3) cf.save() diff --git a/src/pretix/control/views/pdf.py b/src/pretix/control/views/pdf.py index c82bb4c58..cb717ea69 100644 --- a/src/pretix/control/views/pdf.py +++ b/src/pretix/control/views/pdf.py @@ -137,7 +137,7 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView): buffer = BytesIO() p.write(buffer) buffer.seek(0) - c = CachedFile() + c = CachedFile(web_download=True) c.expires = now() + timedelta(days=7) c.date = now() c.filename = 'background_preview.pdf' @@ -162,7 +162,7 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView): "status": "error", "error": error }) - c = CachedFile() + c = CachedFile(web_download=True) c.expires = now() + timedelta(days=7) c.date = now() c.filename = 'background_preview.pdf' diff --git a/src/pretix/control/views/shredder.py b/src/pretix/control/views/shredder.py index 6b511288b..ba09d518f 100644 --- a/src/pretix/control/views/shredder.py +++ b/src/pretix/control/views/shredder.py @@ -75,7 +75,7 @@ class ShredExportView(RecentAuthenticationRequiredMixin, EventPermissionRequired if constr: return self.error(ShredError(self.get_error_url())) - return self.do(self.request.event.id, request.POST.getlist("shredder")) + return self.do(self.request.event.id, request.POST.getlist("shredder"), self.request.session.session_key) class ShredDoView(RecentAuthenticationRequiredMixin, EventPermissionRequiredMixin, ShredderMixin, AsyncAction, View): diff --git a/src/pretix/plugins/badges/views.py b/src/pretix/plugins/badges/views.py index d6ceca906..301c1d04e 100644 --- a/src/pretix/plugins/badges/views.py +++ b/src/pretix/plugins/badges/views.py @@ -235,7 +235,7 @@ class OrderPrintDo(EventPermissionRequiredMixin, AsyncAction, View): def post(self, request, *args, **kwargs): order = get_object_or_404(self.request.event.orders, code=request.GET.get("code")) - cf = CachedFile() + cf = CachedFile(web_download=True, session_key=self.request.session.session_key) cf.date = now() cf.type = 'application/pdf' cf.expires = now() + timedelta(days=3)