django-reverse-proxy-send-file
Summary
This package help writing views which use the X-Accel-Redirect header to have nginx serving files but still allow a permission check at django's side
Intro
The storage.ReverseProxySendFileFileSystemStorage class is a drop-in replacement of django's FileSystemStorage which make FileField (or ImageField) url to use the REVERSE_PROXY_SENDFILE_MEDIA_URL settings base url instead of MEDIA_URL
The storage.ReverseProxySendFileStorageMixin allow you to apply the overrode base_url on any storage class
The get_sendfile_response return HTTP response to tell reverse proxy what to do.
Installation
Install the django-reverse-proxy-send-file pypi package.
ex:
poetry add django-reverse-proxy-send-filepip install django-reverse-proxy-send-file
Usage
See example section bellow
Settings
| Name | Default | Description |
|---|---|---|
| REVERSE_PROXY_SENDFILE_MEDIA_ROOT | MEDIA_ROOT | Base path in django's context where to store media files when uploaded (used by Storage class) |
| REVERSE_PROXY_SENDFILE_MEDIA_URL | "/smedia/" |
URL that handle the resources that should be served by the reverse proxy. |
| REVERSE_PROXY_SENDFILE_REVERSE_PROXY_ROOT | "smedia/" |
Base path in reverse-proxy's context which is sent back to reverse-proxy in header so it can find the file |
| REVERSE_PROXY_SENDFILE_MODE | "nginx" |
Possible values: "nginx" or "apache". nginx mode will use X-Accel-Redirect header.apache mode will use X-Sendfile |
| REVERSE_PROXY_SENDFILE_HEADER_NAME | None |
A custom header name. If set this header will be used regardless REVERSE_PROXY_SENDFILE_MODE setting. |
| REVERSE_PROXY_SENDFILE_DEBUG_SERVE_RESOURCE | True |
In django's DEBUG mode, the resource is directly served by the dev server. |
Exemple
settings.py
...
REVERSE_PROXY_SENDFILE_MEDIA_URL = "/smedia/"
REVERSE_PROXY_SENDFILE_MEDIA_ROOT = "/django_path/to/smedia/"
REVERSE_PROXY_SENDFILE_REVERSE_PROXY_ROOT = "/nginx_path/to/smedia/"
...
models.py
from django.contrib.auth.models import User
from reverse_proxy_send_file.storage import ReverseProxySendFileFileSystemStorage
class MyModel(models.Model):
...
user = models.ForeignKey(User, on_delete=models.CASCADE)
my_file = models.FileField(
"My file",
upload_to="my_files/",
storage=ReverseProxySendFileFileSystemStorage(),
)
...
views.py
Using function view
from django.http import Http404
from reverse_proxy_send_file.response import get_sendfile_response
def my_file_download_view(request, path: str):
obj_qs = MyModel.objects.filter(my_file=path)
if not obj_qs.exists():
return HttpResponseNotFound()
if not attachment_qs.filter(user=request.user).exists():
return HttpResponseForbidden()
return get_sendfile_response(request, path)
Using class based view
from django.http import Http404
from reverse_proxy_send_file.response import get_sendfile_response
class ReverseProxySendFileMyFileView(View):
def get(self, request, path):
obj_qs = MyModel.objects.filter(my_file=path)
if not obj_qs.exists():
return HttpResponseNotFound()
if not attachment_qs.filter(user=request.user).exists():
return HttpResponseForbidden()
return get_sendfile_response(request, path)
urls.py
from django.conf import settings
from views import ReverseProxySendFileMyFileView
from reverse_proxy_send_file.url import smedia_url
urlpatterns = [
...
re_path(
settings.REVERSE_PROXY_SENDFILE_MEDIA_URL.lstrip("/") + "(?P<path>my_files/.*)$",
views.ReverseProxySendFileMyFileView.as_view(),
name="reverse_proxy_send_file",
),
# or more concisely with smedia_url
smedia_url(
"my_files",
views.ReverseProxySendFileMyFileView.as_view(),
name="reverse_proxy_send_file",
# param_name = "something_else" to change the default "path"
),
...
]
- User upload file. The file is stored in
/django_path/to/smedia/my_files/blop.pdf - User access
/smedia/my_files/blop.pdf - A django request is performed and it check file access permission for current user.
-
- If the user is allowed return a HTTP response with header :
X-Accel-Redirect=/nginx_path/to/smedia/my_files/blop.pdf(Nginx will use it to send the file to the client) - If the file os not found return a
404 note found. - If the user id forbidden, return a
403 response forbidden
- If the user is allowed return a HTTP response with header :
smedia_url helper function
The smedia_url function is a helper to avoid verbose re_path.
There are several ways to use it:
- Pass just the base dir of you files :
urlpatterns = [
smedia_url(
"my_files",
views.ReverseProxySendFileMyFileView.as_view(),
),
]
- Pass the base dir with a custom param name to match view one
def my_file_download_view(request, resource_path: str):
...
urlpatterns = [
smedia_url(
"my_files",
my_file_download_view,
param_name="resource_path"
),
]
- Force specific re_path
def my_file_download_view(request, path: str, filename: str):
...
urlpatterns = [
smedia_url(
None,
my_file_download_view,
re_path="(?P<path>.*+*?)/(?P<filename>.*)"
),
]
- Override secure media URL
urlpatterns = [
smedia_url(
"my_files",
my_file_download_view,
smedia_url="/other-smedia/"
),
]
Setup dev environnement
# install dev dependencies
poetry install --no-root
# install git pre-commit
pre-commit install
Run tests
Use tox command to run all tests on all supported python versions
Examples:
tox
tox -e py38-django40
tox -f py39
Build package and publish on PyPI
Change version number in pyproject.toml
poetry build
poetry publish
Metadata
Release files for django-reverse-proxy-send-file 2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| django_reverse_proxy_send_file-2.0.0.tar.gz | 5.9 kB | Without provenance | Details for django_reverse_proxy_send_file-2.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| django_reverse_proxy_send_file-2.0.0-py3-none-any.whl | Python 3 | none | any | Details for django_reverse_proxy_send_file-2.0.0-py3-none-any.whl |
Total release size: 12.2 kB
Release files / django_reverse_proxy_send_file-2.0.0.tar.gz
| Download URL | django_reverse_proxy_send_file-2.0.0.tar.gz |
|---|---|
| Size | 5.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e3e74bdee16800af8ec544bb7a3fa4d44cce021602bc30a48271ea03e380bca9
|
|
BLAKE2b-256 checksum How to use checksums |
c6486f3ddf3f15f22346b392131c5f13f5066749bf23a874f9ffe43fc568a8b6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/1.8.4 CPython/3.12.7 Linux/6.12.4-arch1-1
|
Release files / django_reverse_proxy_send_file-2.0.0-py3-none-any.whl
| Download URL | django_reverse_proxy_send_file-2.0.0-py3-none-any.whl |
|---|---|
| Size | 6.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c01315d12c2bf5da60fa6bfcd46eea5a93a32dce898a8415d61febe10212dc72
|
|
BLAKE2b-256 checksum How to use checksums |
80eceaae225a4a60aeb98f87a7bb60052b0290a383b4cf6d9153afd0b75f107a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/1.8.4 CPython/3.12.7 Linux/6.12.4-arch1-1
|
Security for django-reverse-proxy-send-file 2.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.