Coverage for crateweb/consent/storage.py: 79%

14 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-08-27 10:34 -0500

1""" 

2crate_anon/crateweb/consent/storage.py 

3 

4=============================================================================== 

5 

6 Copyright (C) 2015, University of Cambridge, Department of Psychiatry. 

7 Created by Rudolf Cardinal (rnc1001@cam.ac.uk). 

8 

9 This file is part of CRATE. 

10 

11 CRATE is free software: you can redistribute it and/or modify 

12 it under the terms of the GNU General Public License as published by 

13 the Free Software Foundation, either version 3 of the License, or 

14 (at your option) any later version. 

15 

16 CRATE is distributed in the hope that it will be useful, 

17 but WITHOUT ANY WARRANTY; without even the implied warranty of 

18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

19 GNU General Public License for more details. 

20 

21 You should have received a copy of the GNU General Public License 

22 along with CRATE. If not, see <https://www.gnu.org/licenses/>. 

23 

24=============================================================================== 

25 

26**File system storage class and instance for the consent-to-contact system.** 

27 

28""" 

29 

30import logging 

31from urllib.parse import urljoin 

32from django.core.files.storage import FileSystemStorage 

33from django.conf import settings 

34from django.urls import get_script_prefix 

35from django.utils.encoding import filepath_to_uri 

36 

37from crate_anon.crateweb.config.constants import ( 

38 DOWNLOAD_PRIVATESTORAGE_URL_STEM, 

39) 

40 

41log = logging.getLogger(__name__) 

42 

43 

44class CustomFileSystemStorage(FileSystemStorage): 

45 """ 

46 Subclasses :class:`django.core.files.storage.FileSystemStorage` to improve 

47 URL processing, as below. 

48 

49 *Notes:* 

50 

51 In order to use :func:`reverse`, ``config/urls.py`` can't import views that 

52 import models that use this... so use string, not actual Python object, 

53 references for ``urls.py``. 

54 

55 However, in ``urls.py``, we also need things like ``mgr_admin_site.urls``, 

56 and ``consent/admin.py`` also imports ``models`` (which imports 

57 ``storage``). Problem. Specifically, a problem for making the URLs 

58 non-absolute for non-root hosting. 

59 

60 So we use :func:`get_script_prefix` instead? 

61 

62 - https://docs.djangoproject.com/en/1.8/ref/urlresolvers/ 

63 

64 But that isn't set at the time this is loaded, so that's no good either. 

65 The only other way to handle this would be to override the view form, 

66 or offer the private storage via Apache (but the latter would prevent 

67 proper security, so that's a no-no). 

68 

69 Therefore, let's subclass 

70 :class:`django.core.files.storage.FileSystemStorage`, so we can make the 

71 change at runtime. 

72 

73 """ 

74 

75 def url(self, name: str) -> str: 

76 """ 

77 Returns the URL for a given filename. 

78 """ 

79 if self.base_url is None: 

80 raise ValueError("This file is not accessible via a URL.") 

81 # log.debug("get_script_prefix(): %s" % get_script_prefix()) 

82 return urljoin( 

83 get_script_prefix() + self.base_url, filepath_to_uri(name) 

84 ) 

85 

86 

87privatestorage = CustomFileSystemStorage( 

88 location=settings.PRIVATE_FILE_STORAGE_ROOT, 

89 base_url=DOWNLOAD_PRIVATESTORAGE_URL_STEM, # NB must match urls.py 

90) 

91 

92 

93# log.debug("privatestorage.base_url: %s" % privatestorage.base_url) 

94# log.debug("get_script_prefix(): %s" % get_script_prefix())