meringue 1.2.0.dev5__tar.gz → 1.2.0.dev7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/.gitignore +1 -0
  2. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/PKG-INFO +1 -1
  3. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/__init__.py +1 -1
  4. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/conf/__init__.py +1 -0
  5. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/conf/default_settings.py +5 -0
  6. meringue-1.2.0.dev7/meringue/protected/fields.py +126 -0
  7. meringue-1.2.0.dev7/meringue/protected/utils.py +6 -0
  8. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/protected/views.py +14 -14
  9. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/pyproject.toml +2 -0
  10. meringue-1.2.0.dev5/meringue/protected/fields.py +0 -75
  11. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/AUTHORS +0 -0
  12. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/LICENSE +0 -0
  13. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/README.md +0 -0
  14. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/api/__init__.py +0 -0
  15. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/api/apps.py +0 -0
  16. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/api/docs/__init__.py +0 -0
  17. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/api/docs/patchers.py +0 -0
  18. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/api/docs/views.py +0 -0
  19. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/api/handlers.py +0 -0
  20. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/api/routers.py +0 -0
  21. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/api/utils.py +0 -0
  22. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/core/__init__.py +0 -0
  23. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/core/apps.py +0 -0
  24. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/core/locale/en/LC_MESSAGES/django.po +0 -0
  25. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/core/locale/ru/LC_MESSAGES/django.po +0 -0
  26. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/core/models.py +0 -0
  27. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/core/options.py +0 -0
  28. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/core/query.py +0 -0
  29. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/core/templatetags/__init__.py +0 -0
  30. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/core/templatetags/meringue_base.py +0 -0
  31. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/core/translation.py +0 -0
  32. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/core/upload_handlers.py +0 -0
  33. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/core/utils/__init__.py +0 -0
  34. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/core/utils/crypt.py +0 -0
  35. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/core/utils/datetime.py +0 -0
  36. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/core/utils/frontend.py +0 -0
  37. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/core/views.py +0 -0
  38. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/protected/__init__.py +0 -0
  39. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/protected/apps.py +0 -0
  40. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/thumbnail/__init__.py +0 -0
  41. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/thumbnail/actions.py +0 -0
  42. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/thumbnail/apps.py +0 -0
  43. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/thumbnail/constants.py +0 -0
  44. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/thumbnail/drf_fields.py +0 -0
  45. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/thumbnail/exceptions.py +0 -0
  46. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/thumbnail/generators.py +0 -0
  47. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/thumbnail/images.py +0 -0
  48. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/thumbnail/properties.py +0 -0
  49. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/thumbnail/shortcuts.py +0 -0
  50. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/thumbnail/storage.py +0 -0
  51. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/thumbnail/templatetags/__init__.py +0 -0
  52. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/thumbnail/templatetags/m_thumbnails.py +0 -0
  53. {meringue-1.2.0.dev5 → meringue-1.2.0.dev7}/meringue/thumbnail/types.py +0 -0
@@ -11,4 +11,5 @@ dist
11
11
  /build
12
12
  /stuff
13
13
  /venv
14
+ /media
14
15
  /_readthedocs
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: meringue
3
- Version: 1.2.0.dev5
3
+ Version: 1.2.0.dev7
4
4
  Summary: A set of various functionality for a Django based web application.
5
5
  Project-URL: Documentation, https://dd.github.io/Meringue
6
6
  Project-URL: Repository, https://github.com/dd/Meringue
@@ -1,4 +1,4 @@
1
- __version__ = "1.2.0.dev5"
1
+ __version__ = "1.2.0.dev7"
2
2
  """
3
3
  To update the version, use [hatch version](https://hatch.pypa.io/latest/version/#updating)
4
4
 
@@ -43,6 +43,7 @@ PARAMS_TO_IMPORT: Final[list[str]] = [
43
43
  "THUMBNAIL_IMAGE_CLASS",
44
44
  "THUMBNAIL_PROPERTIES",
45
45
  "THUMBNAIL_ACTIONS",
46
+ "PROTECTED_NGINX_LOCATION_GETTER",
46
47
  ]
47
48
  """
48
49
  List of options that contain the path to the module and must be imported.
@@ -125,3 +125,8 @@ response.
125
125
 
126
126
  The view [protected_file_view][meringue.protected.views.protected_file_view] adds the X-Accel-Redirect header with a link to the file.
127
127
  """
128
+
129
+ PROTECTED_NGINX_LOCATION_GETTER: Final[str] = "meringue.protected.utils.nginx_location_getter"
130
+ """
131
+ Default getter for the link to the file where nginx should serve it after access verification.
132
+ """
@@ -0,0 +1,126 @@
1
+ from django.contrib.contenttypes.models import ContentType
2
+ from django.db.models.fields.files import FileField
3
+ from django.db.models.fields.files import FieldFile
4
+ from django.db.models.fields.files import ImageField
5
+ from django.db.models.fields.files import ImageFieldFile
6
+ from django.urls import reverse
7
+ try:
8
+ from django_hosts.resolvers import reverse as hosts_reverse
9
+ except ImportError:
10
+ hosts_reverse = None
11
+
12
+ from meringue.conf import m_settings
13
+
14
+
15
+ class ProtectedFileMixin:
16
+ @property
17
+ def url(self):
18
+ """
19
+ Link to the view with access verification to the file
20
+ """
21
+
22
+ self._require_file()
23
+
24
+ reverse_kwargs = {
25
+ "cid": ContentType.objects.get_for_model(self.instance.__class__).id,
26
+ "field": self.field.name,
27
+ "pk": self.instance.pk,
28
+ "disp": self.field.m_protected_disposition,
29
+ }
30
+ if self.field.m_protected_host_name:
31
+ result_url = hosts_reverse(
32
+ self.field.m_protected_view_name,
33
+ kwargs=reverse_kwargs,
34
+ host=self.field.m_protected_host_name,
35
+ )
36
+ else:
37
+ result_url = reverse(self.field.m_protected_view_name, kwargs=reverse_kwargs)
38
+ return result_url
39
+
40
+ @property
41
+ def redirect_url(self):
42
+ """
43
+ Link to the file where nginx should serve it after access verification.
44
+ """
45
+
46
+ return self.field.m_protected_nginx_location_getter(self)
47
+
48
+ @property
49
+ def original_url(self):
50
+ """
51
+ The original link to the file.
52
+ """
53
+
54
+ self._require_file()
55
+ return self.storage.url(self.name)
56
+
57
+
58
+ class ProtectedFieldFile(ProtectedFileMixin, FieldFile):
59
+ pass
60
+
61
+
62
+ class ProtectedFileField(FileField):
63
+ """
64
+ A field that adds the mechanism of a protected file.
65
+ """
66
+
67
+ attr_class = ProtectedFieldFile
68
+
69
+ def __init__(
70
+ self,
71
+ view_name,
72
+ verbose_name=None,
73
+ name=None,
74
+ upload_to="protected",
75
+ storage=None,
76
+ host_name=None,
77
+ disposition="attachment",
78
+ nginx_location_getter=m_settings.PROTECTED_NGINX_LOCATION_GETTER,
79
+ **kwargs,
80
+ ):
81
+ self.m_protected_view_name = view_name
82
+ self.m_protected_host_name = host_name
83
+ self.m_protected_disposition = disposition
84
+ self.m_protected_nginx_location_getter = nginx_location_getter
85
+ super().__init__(verbose_name, name, upload_to, storage, **kwargs)
86
+
87
+ def deconstruct(self):
88
+ name, path, args, kwargs = super().deconstruct()
89
+ kwargs['view_name'] = self.m_protected_view_name
90
+ return name, path, args, kwargs
91
+
92
+
93
+ class ProtectedImageFieldFile(ProtectedFileMixin, ImageFieldFile):
94
+ pass
95
+
96
+
97
+ class ProtectedImageField(ImageField):
98
+ """
99
+ A field that adds the mechanism of a protected image.
100
+ """
101
+
102
+ attr_class = ProtectedImageFieldFile
103
+
104
+ def __init__(
105
+ self,
106
+ view_name,
107
+ verbose_name=None,
108
+ name=None,
109
+ width_field=None,
110
+ height_field=None,
111
+ host_name=None,
112
+ disposition="attachment",
113
+ nginx_location_getter=m_settings.PROTECTED_NGINX_LOCATION_GETTER,
114
+ **kwargs,
115
+ ):
116
+ kwargs.setdefault("upload_to", "protected")
117
+ self.m_protected_view_name = view_name
118
+ self.m_protected_host_name = host_name
119
+ self.m_protected_disposition = disposition
120
+ self.m_protected_nginx_location_getter = nginx_location_getter
121
+ super().__init__(verbose_name, name, width_field, height_field, **kwargs)
122
+
123
+ def deconstruct(self):
124
+ name, path, args, kwargs = super().deconstruct()
125
+ kwargs['view_name'] = self.m_protected_view_name
126
+ return name, path, args, kwargs
@@ -0,0 +1,6 @@
1
+ def nginx_location_getter(field_file):
2
+ """
3
+ Getter for the link to the file where nginx should serve it after access verification
4
+ """
5
+
6
+ return field_file.original_url
@@ -15,21 +15,18 @@ from meringue.protected.fields import ProtectedFieldFile
15
15
  from meringue.protected.fields import ProtectedImageFieldFile
16
16
 
17
17
 
18
- def protected_file_view(request, contenttype_id, field, pk):
18
+ def x_accel_redirect_view(request, cid, field, pk, disp="inline"):
19
19
  """
20
- В продакшен моде вьха редиректит на урл файла, где нжинкс отдаёт этот файл
21
- Защита работает за счёт параметра internal
22
- https://nginx.org/en/docs/http/ngx_http_core_module.html#internal
23
-
24
- ```conf
25
- location /media/protected/ {
26
- internal;
27
- alias /home/rpl/rpl-2023-back/public/media/protected/;
28
- }
29
- ```
20
+ The view checks the user's access to view and serves the file.
21
+
22
+ If you enable the parameter
23
+ [PROTECTED_SERVE_WITH_NGINX][meringue.conf.default_settings.PROTECTED_SERVE_WITH_NGINX], the
24
+ file will not be served; instead, the response will contain the X-Accel-Redirect header
25
+ pointing to the original file. It is expected that nginx will return the file using the
26
+ [internal](https://nginx.org/en/docs/http/ngx_http_core_module.html#internal) parameter.
30
27
  """
31
28
 
32
- contenttype = ContentType.objects.get(id=contenttype_id)
29
+ contenttype = ContentType.objects.get(id=cid)
33
30
 
34
31
  if not request.user.has_perm(f"{contenttype.app_label}.view_{contenttype.model}"):
35
32
  raise PermissionDenied()
@@ -44,7 +41,7 @@ def protected_file_view(request, contenttype_id, field, pk):
44
41
  file_name = Path(file.name).name
45
42
  response = HttpResponse()
46
43
  response["Content-Type"] = mimetypes.guess_type(file.path)[0]
47
- response["Content-Disposition"] = f"inline; filename={quote(file_name)}"
44
+ response["Content-Disposition"] = f"{disp}; filename={quote(file_name)}"
48
45
 
49
46
  if isinstance(file, ProtectedFieldFile | ProtectedImageFieldFile):
50
47
  redirect_url = file.original_url
@@ -54,4 +51,7 @@ def protected_file_view(request, contenttype_id, field, pk):
54
51
  response["X-Accel-Redirect"] = redirect_url
55
52
  return response
56
53
 
57
- return FileResponse(open(file.path, "rb"))
54
+ return FileResponse(
55
+ open(file.path, "rb"),
56
+ as_attachment = disp == "attachment",
57
+ )
@@ -157,6 +157,7 @@ dependencies = [
157
157
  "djangorestframework-simplejwt==5.3.1",
158
158
  "drf-spectacular==0.26.4",
159
159
  "pillow==10.3.0",
160
+ "django_hosts==5.2",
160
161
  ]
161
162
  [tool.hatch.envs.test.env-vars]
162
163
  DJANGO_SETTINGS_MODULE = "test_project.settings"
@@ -177,6 +178,7 @@ dependencies = [
177
178
  "pycryptodome==3.20.0",
178
179
  "drf-spectacular==0.26.4",
179
180
  "pillow==10.3.0",
181
+ "django_hosts==5.2",
180
182
  ]
181
183
  [tool.hatch.envs.mtest.overrides]
182
184
  matrix.django.dependencies = [
@@ -1,75 +0,0 @@
1
- from django.contrib.contenttypes.models import ContentType
2
- from django.db.models.fields.files import FileField
3
- from django.db.models.fields.files import FieldFile
4
- from django.db.models.fields.files import ImageField
5
- from django.db.models.fields.files import ImageFieldFile
6
- from django.urls import reverse
7
-
8
-
9
- class ProtectedFileMixin:
10
- @property
11
- def url(self):
12
- self._require_file()
13
- result_url = reverse(
14
- self.field.m_protected_view_name,
15
- kwargs={
16
- "contenttype_id": ContentType.objects.get_for_model(self.instance.__class__).id,
17
- "field": self.field.name,
18
- "pk": self.instance.pk,
19
- },
20
- )
21
- return result_url
22
-
23
- @property
24
- def original_url(self):
25
- self._require_file()
26
- return self.storage.url(self.name)
27
-
28
-
29
- class ProtectedFieldFile(ProtectedFileMixin, FieldFile):
30
- pass
31
-
32
-
33
- class ProtectedFileField(FileField):
34
- """
35
- A field that adds the mechanism of a protected file.
36
- """
37
-
38
- attr_class = ProtectedFieldFile
39
-
40
- def __init__(
41
- self,
42
- verbose_name=None,
43
- name=None,
44
- upload_to="protected",
45
- storage=None,
46
- protected_view_name="meringue-protected-file",
47
- **kwargs,
48
- ):
49
- self.m_protected_view_name = protected_view_name
50
- super().__init__(verbose_name, name, upload_to, storage, **kwargs)
51
-
52
-
53
- class ProtectedImageFieldFile(ProtectedFileMixin, ImageFieldFile):
54
- pass
55
-
56
-
57
- class ProtectedImageField(ImageField):
58
- """
59
- A field that adds the mechanism of a protected image.
60
- """
61
-
62
- attr_class = ProtectedImageFieldFile
63
-
64
- def __init__(
65
- self,
66
- verbose_name=None,
67
- name=None,
68
- width_field=None,
69
- height_field=None,
70
- protected_view_name="meringue-protected-file",
71
- **kwargs,
72
- ):
73
- kwargs.setdefault("upload_to", "protected")
74
- self.m_protected_view_name = protected_view_name
75
- super().__init__(verbose_name, name, width_field, height_field, **kwargs)
File without changes
File without changes
File without changes