Evaluate file access permissions

This commit is contained in:
2026-01-04 17:49:33 +01:00
parent 49c8d804d6
commit bfb0dc18cd
2 changed files with 38 additions and 2 deletions

View File

@@ -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/<path:filename>", 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

View File

@@ -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")