"""Expiring download links and secure file streaming endpoints.""" from pathlib import Path from django.conf import settings from django.core.signing import TimestampSigner, BadSignature, SignatureExpired from django.http import FileResponse, Http404 from rest_framework.decorators import api_view, permission_classes from rest_framework.permissions import AllowAny from rest_framework.response import Response from .services import error, get_item, readable, chain @api_view(['POST']) def content_url(request,item_id): item=get_item(item_id) if item.is_folder or not readable(item,request.user):return error('Fichier inaccessible.',403) inline=bool(request.data.get('inline',False)) safe_types={'image/jpeg','image/png','image/gif','image/webp','application/pdf', 'audio/mpeg','audio/ogg','audio/wav','audio/mp4'} if inline and item.mime_type not in safe_types: return error('Aperçu non disponible pour ce type de fichier.') token=TimestampSigner(salt='drive-content').sign(f'{item.pk}:{1 if inline else 0}') return Response({'url':f'/api/drive/items/{item.pk}/content/?token={token}', 'expires_in':300}) @api_view(['GET']) @permission_classes([AllowAny]) def content(request,item_id): # A short-lived signed URL permits native streaming/inline display without storing a JWT in the URL. token=request.query_params.get('token','') try: value=TimestampSigner(salt='drive-content').unsign(token,max_age=300) expected,inline=value.rsplit(':',1) if expected!=str(item_id) or inline not in ('0','1'):raise BadSignature() except (BadSignature,SignatureExpired,ValueError): raise Http404 item=get_item(item_id) if item.is_folder or item.trashed_at:raise Http404 if any(n.trashed_at for n in chain(item)):raise Http404 # Authorization performed when URL was minted, 5-minute token is a bearer credential. key=item.storage_key if not key or len(key)!=36: raise Http404 path=Path(settings.DRIVE_STORAGE_ROOT)/key[:2]/key if not path.is_file():raise Http404 response=FileResponse(open(path,'rb'),as_attachment=inline=='0',filename=item.name, content_type=item.mime_type if inline=='1' else 'application/octet-stream') response['Cache-Control']='private, no-store' response['X-Content-Type-Options']='nosniff' return response