Coverage for src/mesh/views/views_file.py: 23%

46 statements  

« prev     ^ index     » next       coverage.py v7.7.0, created at 2025-04-28 07:45 +0000

1import os 

2import re 

3 

4from django.http import HttpRequest, HttpResponse, HttpResponseNotFound 

5from django.views.generic import View 

6from django_sendfile import sendfile 

7from ptf.url_utils import decode_from_url 

8 

9from mesh.views.mixins import RoleMixin 

10 

11from ..models.editorial_models import EditorialDecisionFile 

12from ..models.file_models import BaseFileWrapperModel 

13from ..models.review_models import ReviewAdditionalFile 

14from ..models.submission_models import SubmissionAdditionalFile, SubmissionMainFile 

15 

16# Django Models inheriting from our custom BaseFileWrapperModel. 

17# cf. FileServingView. 

18FILE_MODELS = [ 

19 SubmissionMainFile, 

20 SubmissionAdditionalFile, 

21 ReviewAdditionalFile, 

22 EditorialDecisionFile, 

23] 

24 

25 

26class FileServingView(RoleMixin, View): 

27 """ 

28 This view is used to check the user rights before serving a file. 

29 

30 File models protected by this process must be registered in the `FILE_MODELS` 

31 variable. The requested file is identified by iterating over the registered models 

32 and asking if the URL match their unique pattern. 

33 """ 

34 

35 def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: 

36 bad_response = HttpResponseNotFound("The requested file was not found on this server.") 

37 

38 # Decode the base64 encoded file_identifier 

39 file_identifier = kwargs["file_identifier"] 

40 try: 

41 file_identifier = decode_from_url(file_identifier) 

42 except Exception: 

43 return bad_response 

44 

45 # Loop over the registered models to find the requested file 

46 for model in FILE_MODELS: 

47 lookup_func = getattr(model, "reverse_file_path", None) 

48 if not lookup_func: 

49 continue 

50 

51 instance: BaseFileWrapperModel | None = lookup_func(file_identifier) 

52 if instance is None: 

53 continue 

54 

55 # Make sure the file exists 

56 if not os.path.exists(instance.file.path): 

57 break 

58 

59 # Check if the user can access the requested file 

60 # We need to check the right over the file with all roles 

61 right_check_func = getattr(instance, "check_access_right", None) 

62 if not right_check_func or not right_check_func(self.role_handler, "read"): 

63 break 

64 

65 response = sendfile(request, instance.file.path) 

66 

67 # Set the original name of the served file for display to the 

68 # download client 

69 if not hasattr(response, "headers"): 

70 return response 

71 

72 resp_headers = response.headers 

73 content_disposition = resp_headers.get("Content-Disposition") 

74 if not content_disposition: 

75 return response 

76 

77 filename_match = re.search(r"(?P<filename>filename=\".+?\")", content_disposition) 

78 if not filename_match: 

79 return response 

80 

81 filename = filename_match.group("filename") 

82 resp_headers["Content-Disposition"] = content_disposition.replace( 

83 filename, f'filename="{instance.name}"' 

84 ) 

85 return response 

86 

87 return bad_response