68 lines
2.4 KiB
Python
68 lines
2.4 KiB
Python
# common/private_files/views.py
|
||
import os
|
||
from django.apps import apps
|
||
from django.http import FileResponse, Http404, HttpResponseBadRequest
|
||
from django.contrib.auth.decorators import login_required
|
||
from django.core import signing
|
||
from django.core.signing import BadSignature, SignatureExpired
|
||
from django.utils.encoding import smart_str
|
||
from django.utils.translation import gettext as _
|
||
|
||
from django.conf import settings
|
||
from .registry import get_guard
|
||
|
||
_TOKEN_MAX_AGE = getattr(settings, "PRIVATE_FILES_TOKEN_MAX_AGE", 14400)
|
||
|
||
|
||
|
||
@login_required
|
||
def private_file_download(request):
|
||
"""
|
||
Accès générique: app=<app>&model=<Model>&pk=<id>&field=<field>&token=<signed>
|
||
"""
|
||
app_label = request.GET.get("app")
|
||
model_name = request.GET.get("model")
|
||
pk = request.GET.get("pk")
|
||
field_name = request.GET.get("field")
|
||
token = request.GET.get("token")
|
||
|
||
if not all([app_label, model_name, pk, field_name]):
|
||
return HttpResponseBadRequest("Missing params")
|
||
|
||
# Vérifie le token signé (facultatif si tu ne veux pas signer)
|
||
if token:
|
||
try:
|
||
signing.loads(token, salt="private-files", max_age=_TOKEN_MAX_AGE)
|
||
except (BadSignature, SignatureExpired):
|
||
raise Http404(_("Lien expiré ou invalide"))
|
||
|
||
# Récupère le modèle et l’objet
|
||
try:
|
||
model = apps.get_model(app_label, model_name)
|
||
except LookupError:
|
||
raise Http404(_("Modèle inconnu"))
|
||
|
||
obj = model._default_manager.select_related().filter(pk=pk).first()
|
||
if not obj:
|
||
raise Http404(_("Objet introuvable"))
|
||
|
||
# Récupère le guard
|
||
guard = get_guard(app_label, model_name, field_name)
|
||
if guard is None:
|
||
# Par défaut, refuse si pas de guard enregistré (sécurité par défaut)
|
||
raise Http404(_("Fichier non exposé"))
|
||
|
||
# Vérif d’accès
|
||
if not guard(obj, request.user):
|
||
from django.core.exceptions import PermissionDenied
|
||
raise PermissionDenied(_("Accès refusé"))
|
||
|
||
# Ouvre le FileField
|
||
f = getattr(obj, field_name, None)
|
||
if not f or not getattr(f, "storage", None) or not f.name or not f.storage.exists(f.name):
|
||
raise Http404(_("Fichier introuvable"))
|
||
|
||
resp = FileResponse(f.open("rb"))
|
||
resp["Content-Disposition"] = f'inline; filename="{smart_str(os.path.basename(f.name))}"'
|
||
resp['X-Frame-Options'] = 'ALLOWALL' # Permet l’affichage dans un iframe
|
||
return resp
|