Merge pull request 'Enable stl download' (#3) from stl_download into master
Reviewed-on: #3
This commit was merged in pull request #3.
This commit is contained in:
@@ -18,9 +18,11 @@ import secrets
|
||||
|
||||
from django.contrib import messages
|
||||
from django.db import IntegrityError, transaction
|
||||
from django.http import FileResponse, Http404
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.utils.html import format_html
|
||||
from django.views.decorators.http import require_safe
|
||||
from django.views.generic import CreateView
|
||||
|
||||
from .emails import send_confirmation_email
|
||||
@@ -28,6 +30,48 @@ from .forms import SubmissionForm
|
||||
from .models import Submission, VerifiedEmail
|
||||
|
||||
|
||||
@require_safe
|
||||
def serve_stl(request, path: str):
|
||||
"""Auth-checked passthrough to a Submission's uploaded `.stl`. URL is
|
||||
`/media/<path>` so it matches `MEDIA_URL` -- which means
|
||||
`{{ submission.stl_file.url }}` in templates Just Works.
|
||||
|
||||
`path` is the relative name Django stored (e.g. `stl/foo.stl`); we
|
||||
look the row up by that string rather than reading the filesystem
|
||||
directly so an attacker can't escape `MEDIA_ROOT` with `..` segments.
|
||||
|
||||
Access tiers match `SubmissionDetailView` (plan.md §7.4 step 7):
|
||||
|
||||
- staff: yes (operators need the file to drive the printer).
|
||||
- authenticated owner (`submitted_by == request.user`): yes.
|
||||
- everyone else: 404 -- we deliberately don't 403 so the existence
|
||||
of a row isn't leaked to non-owners by status code.
|
||||
|
||||
Guests can't authenticate by design, so they can't pull their own STL
|
||||
via this path. That's consistent with the detail page's two-tier
|
||||
access; the submitter has the file on their own machine in any case.
|
||||
"""
|
||||
sub = Submission.objects.filter(stl_file=path).first()
|
||||
if sub is None:
|
||||
raise Http404
|
||||
user = request.user
|
||||
is_owner = (
|
||||
user.is_authenticated
|
||||
and sub.submitted_by_id is not None
|
||||
and sub.submitted_by_id == user.id
|
||||
)
|
||||
if not (user.is_staff or is_owner):
|
||||
raise Http404
|
||||
# `FieldFile.open("rb")` is rooted in MEDIA_ROOT; FileResponse handles
|
||||
# streaming, range requests, and closing the handle for us.
|
||||
filename = sub.stl_file.name.rsplit("/", 1)[-1]
|
||||
return FileResponse(
|
||||
sub.stl_file.open("rb"),
|
||||
as_attachment=True,
|
||||
filename=filename,
|
||||
)
|
||||
|
||||
|
||||
class SubmitView(CreateView):
|
||||
"""Public submit form. GET renders, POST creates a Submission."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user