51 lines
2.4 KiB
Python
51 lines
2.4 KiB
Python
"""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
|