Skip to main content

Bitbucket Pipelines license status version Python version django version black

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-file
  • pip 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"
    ),
    ...
]
  1. User upload file. The file is stored in /django_path/to/smedia/my_files/blop.pdf
  2. User access /smedia/my_files/blop.pdf
  3. 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

smedia_url helper function

The smedia_url function is a helper to avoid verbose re_path.

There are several ways to use it:

  1. Pass just the base dir of you files :
urlpatterns = [
    smedia_url(
        "my_files",
        views.ReverseProxySendFileMyFileView.as_view(),
    ),
]
  1. 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"
    ),
]
  1. 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>.*)"
    ),
]
  1. 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

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)

Source distribution for django-reverse-proxy-send-file 2.0.0
File Size Uploaded
django_reverse_proxy_send_file-2.0.0.tar.gz 5.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-reverse-proxy-send-file 2.0.0
File Interpreter ABI Platform
django_reverse_proxy_send_file-2.0.0-py3-none-any.whl Python 3 none any Details

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

Release history Release notifications | RSS feed

This release

2.0.0 This release

2 release files

1.2.0

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page