Coverage for api\views\media_views.py: 90.0%

32 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-08-30 20:58 +0200

1from django.http import Http404, HttpResponse 

2from django.conf import settings 

3from rest_framework.permissions import IsAuthenticated 

4from rest_framework.decorators import api_view, permission_classes 

5import os 

6import mimetypes 

7 

8 

9def _safe_path(filename): 

10 """ 

11 Devuelve la ruta absoluta del fichero pedido, o `None` si se sale del sitio. 

12 """ 

13 # Se rechaza lo que nunca puede ser un nombre de fichero 

14 if not filename or filename in ('.', '..') or '/' in filename or '\\' in filename: 

15 return None 

16 

17 base = os.path.realpath(os.path.join(settings.MEDIA_ROOT, 'profile_pics')) 

18 full_path = os.path.realpath(os.path.join(base, filename)) 

19 

20 if full_path != base and not full_path.startswith(base + os.sep): 20 ↛ 21line 20 didn't jump to line 21 because the condition on line 20 was never true

21 return None 

22 

23 return full_path 

24 

25 

26@api_view(['GET']) 

27@permission_classes([IsAuthenticated]) 

28def serve_protected_profile_picture(request, filename): 

29 """ 

30 Endpoint para servir las imagenes de perfil, es necesario la autenticacion previa 

31 Uso: GET /api/v1/media/profile_pics/{filename} 

32 """ 

33 file_path = _safe_path(filename) 

34 

35 if file_path is None or not os.path.exists(file_path): 

36 raise Http404 

37 

38 # Coge el tipo de contenido del archivo 

39 content_type, _ = mimetypes.guess_type(file_path) 

40 if content_type is None: 

41 content_type = 'application/octet-stream' 

42 

43 # Se lee y se devuelve el archivo 

44 try: 

45 with open(file_path, 'rb') as f: 

46 response = HttpResponse(f.read(), content_type=content_type) 

47 response['Content-Length'] = os.path.getsize(file_path) 

48 # Se añade un encabezado para controlar el chacheo del archivo 

49 response['Cache-Control'] = 'max-age=3600' # Se pone por una hora 

50 response['X-Content-Type-Options'] = 'nosniff' 

51 return response 

52 except IOError: 

53 return HttpResponse("File could not be read", status=500)