From bfb0dc18cdb39848447b9ea7cbeeb0b6950f80b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniele=20Vigan=C3=B2?= Date: Sun, 4 Jan 2026 17:49:33 +0100 Subject: [PATCH] Evaluate file access permissions --- ram/ram/urls.py | 3 ++- ram/ram/views.py | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/ram/ram/urls.py b/ram/ram/urls.py index 8ff96d3..e8974ab 100644 --- a/ram/ram/urls.py +++ b/ram/ram/urls.py @@ -21,7 +21,7 @@ from django.conf.urls.static import static from django.contrib import admin from django.urls import include, path -from ram.views import UploadImage +from ram.views import UploadImage, DownloadFile from portal.views import Render404 handler404 = Render404.as_view() @@ -32,6 +32,7 @@ urlpatterns = [ path("tinymce/upload_image", UploadImage.as_view(), name="upload_image"), path("portal/", include("portal.urls")), path("admin/", admin.site.urls), + path("media/files/", DownloadFile.as_view(), name="download_file"), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) # Enable the "/dcc" routing only if the "driver" app is active diff --git a/ram/ram/views.py b/ram/ram/views.py index 74fa63b..7fb1cc6 100644 --- a/ram/ram/views.py +++ b/ram/ram/views.py @@ -5,19 +5,25 @@ import posixpath from pathlib import Path from PIL import Image, UnidentifiedImageError -from django.views import View +from django.apps import apps from django.conf import settings from django.http import ( + Http404, HttpResponseBadRequest, HttpResponseForbidden, + FileResponse, JsonResponse, ) +from django.views import View from django.utils.text import slugify as slugify +from django.utils.encoding import smart_str from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt from rest_framework.pagination import LimitOffsetPagination +from ram.models import PrivateDocument + class CustomLimitOffsetPagination(LimitOffsetPagination): default_limit = 10 @@ -67,3 +73,32 @@ class UploadImage(View): ), } ) + + +class DownloadFile(View): + def get(self, request, filename): + # Clean up the filename to prevent directory traversal attacks + filename = os.path.basename(filename) + + # Find a document where the stored file name matches + # Find all models inheriting from PublishableFile + for model in apps.get_models(): + if issubclass(model, PrivateDocument) and not model._meta.abstract: + try: + doc = model.objects.get(file__endswith=filename) + if doc.private and not request.user.is_staff: + raise Http404("File not found") + + file_path = doc.file.path + if not os.path.exists(file_path): + raise Http404("File not found") + + response = FileResponse(open(file_path, "rb"), as_attachment=True) + response["Content-Disposition"] = ( + f'attachment; filename="{smart_str(os.path.basename(file_path))}"' + ) + return response + except model.DoesNotExist: + continue + + raise Http404("File not found")