Only generate signed S3 link to user export when user clicks download

This commit is contained in:
Bart Schuurmans 2024-03-24 13:03:47 +01:00
parent ab7b0893e0
commit 5bd66cb3f7
2 changed files with 29 additions and 30 deletions

View file

@ -126,27 +126,15 @@
<span>{{ export.size|get_file_size }}</span> <span>{{ export.size|get_file_size }}</span>
</td> </td>
<td> <td>
{% if export.job.complete and not export.job.status == "stopped" and not export.job.status == "failed" %}
{% if export.url %} {% if export.url %}
<p>
<a href="{{ export.url }}"> <a href="{{ export.url }}">
<span class="icon icon-download" aria-hidden="true"></span> <span class="icon icon-download" aria-hidden="true"></span>
<span class="is-hidden-mobile"> <span class="is-hidden-mobile">
{% trans "Download your export" %} {% trans "Download your export" %}
</span> </span>
</a> </a>
</p> {% elif export.job.complete and not export.job.status == "stopped" and not export.job.status == "failed" %}
{% else %} {% trans "Archive is no longer available" %}
<p>
<a download="" href="/preferences/user-export/{{ export.job.task_id }}">
<span class="icon icon-download" aria-hidden="true"></span>
<span class="is-hidden-mobile">
{% trans "Download your export" %}
</span>
</a>
</p>
{% endif %}
{% endif %} {% endif %}
</td> </td>
</tr> </tr>

View file

@ -10,6 +10,7 @@ from django.http import HttpResponse, HttpResponseServerError, Http404
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.utils import timezone from django.utils import timezone
from django.views import View from django.views import View
from django.urls import reverse
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.shortcuts import redirect from django.shortcuts import redirect
@ -166,17 +167,8 @@ class ExportUser(View):
export = {"job": job} export = {"job": job}
if settings.USE_S3: if settings.USE_S3:
# make custom_domain None so we can sign the url
# see https://github.com/jschneier/django-storages/issues/944
storage = S3Boto3Storage(querystring_auth=True, custom_domain=None) storage = S3Boto3Storage(querystring_auth=True, custom_domain=None)
# for s3 we download directly from s3, so we need a signed url
export["url"] = S3Boto3Storage.url(
storage,
f"/exports/{job.task_id}.tar.gz",
expire=settings.S3_SIGNED_URL_EXPIRY,
)
# for s3 we create a new tar file in s3, # for s3 we create a new tar file in s3,
# so we need to check the size of _that_ file # so we need to check the size of _that_ file
try: try:
@ -194,6 +186,9 @@ class ExportUser(View):
# file no longer exists # file no longer exists
export["size"] = 0 export["size"] = 0
if export["size"] > 0:
export["url"] = reverse("prefs-export-file", args=[job.task_id])
exports.append(export) exports.append(export)
site = models.SiteSettings.objects.get() site = models.SiteSettings.objects.get()
@ -235,6 +230,21 @@ class ExportArchive(View):
def get(self, request, archive_id): def get(self, request, archive_id):
"""download user export file""" """download user export file"""
export = BookwyrmExportJob.objects.get(task_id=archive_id, user=request.user) export = BookwyrmExportJob.objects.get(task_id=archive_id, user=request.user)
if isinstance(export.export_data.storage, storage_backends.ExportsS3Storage):
# make custom_domain None so we can sign the url
# see https://github.com/jschneier/django-storages/issues/944
storage = S3Boto3Storage(querystring_auth=True, custom_domain=None)
try:
url = S3Boto3Storage.url(
storage,
f"/exports/{export.task_id}.tar.gz",
expire=settings.S3_SIGNED_URL_EXPIRY,
)
except Exception:
raise Http404()
return redirect(url)
if isinstance(export.export_data.storage, storage_backends.ExportsFileStorage): if isinstance(export.export_data.storage, storage_backends.ExportsFileStorage):
try: try:
return HttpResponse( return HttpResponse(
@ -246,4 +256,5 @@ class ExportArchive(View):
) )
except FileNotFoundError: except FileNotFoundError:
raise Http404() raise Http404()
return HttpResponseServerError() return HttpResponseServerError()