New Upstream Snapshot - django-titofisto

Ready changes

Summary

Merged new upstream version: 0.2.2 (was: 0.1.2.post1).

Resulting package

Built on 2022-03-28T02:28 (took 9m46s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-snapshots python3-django-titofisto

Lintian Result

Diff

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 303abc5..7986d3d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,17 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
-## [Unreleased]
+## [0.2.2] - 2022-02-03
+### Fixed
+- Non-existing files in the public namespace erroneously raised interal server errors.
+
+## [0.2.1] – 2021-12-07
+### Changed
+- Allow Django 4.0
+
+## [0.2.0] - 2021-10-24
+### Changed
+- Provide mechanism to make files in a public namespace accessible without a token.
 
 ## [0.1.2.post1] - 2021-05-17
 ### Changed
@@ -27,4 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 [0.1.0]: https://edugit.org/AlekSIS/libs/django-titofisto/-/tags/0.1.0
 [0.1.1]: https://edugit.org/AlekSIS/libs/django-titofisto/-/tags/0.1.1
 [0.1.2]: https://edugit.org/AlekSIS/libs/django-titofisto/-/tags/0.1.2
-[0.1.2.post0]: https://edugit.org/AlekSIS/libs/django-titofisto/-/tags/0.1.2.post0
+[0.1.2.post1]: https://edugit.org/AlekSIS/libs/django-titofisto/-/tags/0.1.2.post1
+[0.2.0]: https://edugit.org/AlekSIS/libs/django-titofisto/-/tags/0.2.0
+[0.2.1]: https://edugit.org/AlekSIS/libs/django-titofisto/-/tags/0.2.1
+[0.2.2]: https://edugit.org/AlekSIS/libs/django-titofisto/-/tags/0.2.2
diff --git a/PKG-INFO b/PKG-INFO
index 329ec14..4687f0f 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: django-titofisto
-Version: 0.1.2.post1
+Version: 0.2.2
 Summary: Django Time-Token File Storage
 Home-page: https://edugit.org/AlekSIS/libs/django-titofisto
 License: Apache-2.0
@@ -14,8 +14,9 @@ Classifier: Framework :: Django
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: Apache Software License
 Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.10
 Classifier: Programming Language :: Python :: 3.9
-Requires-Dist: Django (>2.2,<4.0)
+Requires-Dist: Django (>2.2,<5.0)
 Project-URL: Repository, https://edugit.org/AlekSIS/libs/django-titofisto
 Description-Content-Type: text/x-rst
 
@@ -82,12 +83,26 @@ Add the following to your URL config::
 
 Django will start serving media files under the configured `MEDIA_URL`.
 
+Provide public media files
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Sometimes, there might be media files, for example favicons,
+you want to be accessible without any authentication. Per default,
+`django-titofisto` will serve all files stored in the directory `public` without a token.
+You can disable or configure this behavior using these settings:
+
+  TITOFISTO_USE_PUBLIC_NAMESPACE = True # optional, this is the default
+  TITOFISTO_PUBLIC_NAMESPACE = "public/" # optional, this is the default
+
 Credits
 -------
 
 `django-titofisto` was developed for the `AlekSIS`_ school information system by
 its team.
 
+  Copyright © 2021 Dominik George <dominik.george@teckids.org>
+  Copyright © 2021 Jonathan Weth <dev@jonathanweth.de>
+
 .. _django-titofisto: https://edugit.org/AlekSIS/libs/django-titofisto
 .. _poetry: https://python-poetry.org/
 .. _Django's cache framework: https://docs.djangoproject.com/en/3.2/topics/cache/
diff --git a/README.rst b/README.rst
index c4d3860..4e1652e 100644
--- a/README.rst
+++ b/README.rst
@@ -61,12 +61,26 @@ Add the following to your URL config::
 
 Django will start serving media files under the configured `MEDIA_URL`.
 
+Provide public media files
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Sometimes, there might be media files, for example favicons,
+you want to be accessible without any authentication. Per default,
+`django-titofisto` will serve all files stored in the directory `public` without a token.
+You can disable or configure this behavior using these settings:
+
+  TITOFISTO_USE_PUBLIC_NAMESPACE = True # optional, this is the default
+  TITOFISTO_PUBLIC_NAMESPACE = "public/" # optional, this is the default
+
 Credits
 -------
 
 `django-titofisto` was developed for the `AlekSIS`_ school information system by
 its team.
 
+  Copyright © 2021 Dominik George <dominik.george@teckids.org>
+  Copyright © 2021 Jonathan Weth <dev@jonathanweth.de>
+
 .. _django-titofisto: https://edugit.org/AlekSIS/libs/django-titofisto
 .. _poetry: https://python-poetry.org/
 .. _Django's cache framework: https://docs.djangoproject.com/en/3.2/topics/cache/
diff --git a/debian/changelog b/debian/changelog
index 1c86cd6..7f8dfc8 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,4 +1,4 @@
-django-titofisto (0.1.2.post1-2) UNRELEASED; urgency=medium
+django-titofisto (0.2.2-1) UNRELEASED; urgency=medium
 
   * Remove constraints unnecessary since buster:
     + Build-Depends: Drop versioned constraint on python3-django.
@@ -6,8 +6,9 @@ django-titofisto (0.1.2.post1-2) UNRELEASED; urgency=medium
   * Use secure URI in debian/watch.
   * Set debhelper-compat version in Build-Depends.
   * Set upstream metadata fields: Repository.
+  * New upstream release.
 
- -- Debian Janitor <janitor@jelmer.uk>  Wed, 02 Feb 2022 21:05:42 -0000
+ -- Debian Janitor <janitor@jelmer.uk>  Mon, 28 Mar 2022 02:21:45 -0000
 
 django-titofisto (0.1.2.post1-1) unstable; urgency=low
 
diff --git a/pyproject.toml b/pyproject.toml
index 310e141..c019c58 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,8 +1,8 @@
 [tool.poetry]
 name = "django-titofisto"
-version = "0.1.2.post1"
+version = "0.2.2"
 description = "Django Time-Token File Storage"
-authors = ["Dominik George <dominik.george@teckids.org>"]
+authors = ["Dominik George <dominik.george@teckids.org>", "Jonathan Weth <dev@jonathanweth.de>"]
 license = "Apache-2.0"
 readme = "README.rst"
 repository = "https://edugit.org/AlekSIS/libs/django-titofisto"
@@ -20,7 +20,7 @@ include = ["LICENSE", "CHANGELOG.md"]
 
 [tool.poetry.dependencies]
 python = "^3.9"
-Django = ">2.2,<4.0"
+Django = ">2.2,<5.0"
 
 [tool.poetry.dev-dependencies]
 freezegun = "^1.1.0"
diff --git a/setup.py b/setup.py
index 49ea4b5..0345fbb 100644
--- a/setup.py
+++ b/setup.py
@@ -8,13 +8,13 @@ package_data = \
 {'': ['*']}
 
 install_requires = \
-['Django>2.2,<4.0']
+['Django>2.2,<5.0']
 
 setup_kwargs = {
     'name': 'django-titofisto',
-    'version': '0.1.2.post1',
+    'version': '0.2.2',
     'description': 'Django Time-Token File Storage',
-    'long_description': 'Django Time-Token File Storage\n==============================\n\nThis is a simple extension to Django\'s `FileSystemStorage` that adds a URL\nparameter carrying a shared token, which is only valid for a defined period\nof time.\n\nFunctionality\n-------------\n\nThis is a drop-in replacement for the Django `FileSystemStorage`, usable if\nmedia files are served by Django itself. It does currently not work if media\nfiles are served from an independent web server.\n\nThe storage and its accompanying view do the following:\n\n* When a URL to a storage file is generated, a HMAC-based token is generated\n* The token and the timestamp when it was generated are appended as request\n  parameters to the URL\n* Upon retrieval of the file through the accompanying view, the requested\n  file name and the passed timestamp are used to recalculate the HMAC-based\n  token\n* Only if the tokens match, and a configured timeout has not passed, is the\n  file served\n\nThe HMAC-based token ensures that the token is invalidated when:\n\n* The filename changes\n* The timestamp changes\n* The mtime of the file changes\n* The `SECRET_KEY` changes\n\nThe HMAC is salted with the `SECRET_KEY`.\n\nInstallation\n------------\n\nTo add `django-titofisto`_ to a project, first add it as dependency to your\nproject, e.g. using `poetry`_::\n\n  $ poetry add django-titofisto\n\n`django-titofisto` will use the base `FileSystemStorage` for almost everything,\nincluding determining the `MEDIA_ROOT`. It merely adds a token as URL parameter\nto whatever the base `FileSystemStorage.url()` method returns.\n\nAdd the following to your settings::\n\n  DEFAULT_FILE_STORAGE = "titofisto.TitofistoStorage"\n  TITOFISTO_TIMEOUT = 3600  # optional, this is the default\n  TITOFISTO_PARAM = "titofisto_token"  # optional, this is the default\n\nAdd the following to your URL config::\n\n  from django.conf import settings\n  from django.urls import include, path\n\n  urlpatterns += [\n      path(settings.MEDIA_URL.removeprefix("/"), include("titofisto.urls")),\n  ]\n\nDjango will start serving media files under the configured `MEDIA_URL`.\n\nCredits\n-------\n\n`django-titofisto` was developed for the `AlekSIS`_ school information system by\nits team.\n\n.. _django-titofisto: https://edugit.org/AlekSIS/libs/django-titofisto\n.. _poetry: https://python-poetry.org/\n.. _Django\'s cache framework: https://docs.djangoproject.com/en/3.2/topics/cache/\n.. _AlekSIS: https://aleksis.org/\n',
+    'long_description': 'Django Time-Token File Storage\n==============================\n\nThis is a simple extension to Django\'s `FileSystemStorage` that adds a URL\nparameter carrying a shared token, which is only valid for a defined period\nof time.\n\nFunctionality\n-------------\n\nThis is a drop-in replacement for the Django `FileSystemStorage`, usable if\nmedia files are served by Django itself. It does currently not work if media\nfiles are served from an independent web server.\n\nThe storage and its accompanying view do the following:\n\n* When a URL to a storage file is generated, a HMAC-based token is generated\n* The token and the timestamp when it was generated are appended as request\n  parameters to the URL\n* Upon retrieval of the file through the accompanying view, the requested\n  file name and the passed timestamp are used to recalculate the HMAC-based\n  token\n* Only if the tokens match, and a configured timeout has not passed, is the\n  file served\n\nThe HMAC-based token ensures that the token is invalidated when:\n\n* The filename changes\n* The timestamp changes\n* The mtime of the file changes\n* The `SECRET_KEY` changes\n\nThe HMAC is salted with the `SECRET_KEY`.\n\nInstallation\n------------\n\nTo add `django-titofisto`_ to a project, first add it as dependency to your\nproject, e.g. using `poetry`_::\n\n  $ poetry add django-titofisto\n\n`django-titofisto` will use the base `FileSystemStorage` for almost everything,\nincluding determining the `MEDIA_ROOT`. It merely adds a token as URL parameter\nto whatever the base `FileSystemStorage.url()` method returns.\n\nAdd the following to your settings::\n\n  DEFAULT_FILE_STORAGE = "titofisto.TitofistoStorage"\n  TITOFISTO_TIMEOUT = 3600  # optional, this is the default\n  TITOFISTO_PARAM = "titofisto_token"  # optional, this is the default\n\nAdd the following to your URL config::\n\n  from django.conf import settings\n  from django.urls import include, path\n\n  urlpatterns += [\n      path(settings.MEDIA_URL.removeprefix("/"), include("titofisto.urls")),\n  ]\n\nDjango will start serving media files under the configured `MEDIA_URL`.\n\nProvide public media files\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nSometimes, there might be media files, for example favicons,\nyou want to be accessible without any authentication. Per default,\n`django-titofisto` will serve all files stored in the directory `public` without a token.\nYou can disable or configure this behavior using these settings:\n\n  TITOFISTO_USE_PUBLIC_NAMESPACE = True # optional, this is the default\n  TITOFISTO_PUBLIC_NAMESPACE = "public/" # optional, this is the default\n\nCredits\n-------\n\n`django-titofisto` was developed for the `AlekSIS`_ school information system by\nits team.\n\n  Copyright © 2021 Dominik George <dominik.george@teckids.org>\n  Copyright © 2021 Jonathan Weth <dev@jonathanweth.de>\n\n.. _django-titofisto: https://edugit.org/AlekSIS/libs/django-titofisto\n.. _poetry: https://python-poetry.org/\n.. _Django\'s cache framework: https://docs.djangoproject.com/en/3.2/topics/cache/\n.. _AlekSIS: https://aleksis.org/\n',
     'author': 'Dominik George',
     'author_email': 'dominik.george@teckids.org',
     'maintainer': None,
diff --git a/titofisto/settings.py b/titofisto/settings.py
index 90e233b..0ecb186 100644
--- a/titofisto/settings.py
+++ b/titofisto/settings.py
@@ -2,3 +2,5 @@ from django.conf import settings
 
 PARAM = getattr(settings, "TITOFISTO_PARAM", "titofisto_token")
 TIMEOUT = getattr(settings, "TITOFISTO_TIMEOUT", 60 * 60)
+USE_PUBLIC_NAMESPACE = getattr(settings, "TITOFISTO_USE_PUBLIC_NAMESPACE", True)
+PUBLIC_NAMESPACE = getattr(settings, "TITOFISTO_PUBLIC_NAMESPACE", "public/")
diff --git a/titofisto/storage.py b/titofisto/storage.py
index a007304..cb932a7 100644
--- a/titofisto/storage.py
+++ b/titofisto/storage.py
@@ -5,7 +5,7 @@ from typing import Optional
 from django.conf import settings
 from django.core.files.storage import FileSystemStorage
 
-from .settings import PARAM
+from .settings import PARAM, USE_PUBLIC_NAMESPACE, PUBLIC_NAMESPACE
 
 
 class TitofistoStorage(FileSystemStorage):
@@ -16,6 +16,11 @@ class TitofistoStorage(FileSystemStorage):
         # Get regular URL from base FileSystemStorage
         raw_url = super().url(name)
 
+        if USE_PUBLIC_NAMESPACE:
+            # Public files are accessible without a token
+            if name.startswith(PUBLIC_NAMESPACE):
+                return raw_url
+
         # Get token and timestamp
         token = self.get_token(name)
 
diff --git a/titofisto/tests.py b/titofisto/tests.py
index 8d9e75b..490aa37 100644
--- a/titofisto/tests.py
+++ b/titofisto/tests.py
@@ -23,6 +23,10 @@ class TitofistoTestCase(TestCase):
         self.test_file_2_name = "franztaxi.dat"
         self.storage.save(self.test_file_2_name, BytesIO(self.test_file_2))
 
+        self.test_file_3 = b"Franz jagt im komplett verwahrlosten Taxi quer durch Bayern"
+        self.test_file_3_name = "public/franztaxi.dat"
+        self.storage.save(self.test_file_3_name, BytesIO(self.test_file_3))
+
         self.param = "titofisto_token"
         self.timeout = 60 * 60
         self.client = Client()
@@ -139,3 +143,30 @@ class TitofistoTestCase(TestCase):
         res = self.client.get(url)
 
         self.assertEqual(res.status_code, 404)
+
+    def test_url(self):
+        """The URL for a file should contain a token."""
+        url1 = self.storage.url(self.test_file_1_name)
+        url2 = self.storage.url(self.test_file_2_name)
+        self.assertIn(self.param, url1)
+        self.assertIn(self.param, url2)
+
+    def test_public_files_url(self):
+        """The URL for a file in the public namespace doesn't contain a token."""
+        url = self.storage.url(self.test_file_3_name)
+        self.assertNotIn(self.param, url)
+
+    def test_public_files_view(self):
+        """A file in the public namespace can be acessed without a token."""
+        self.storage.url(self.test_file_3_name)
+        url = (
+            f"{settings.MEDIA_URL}{self.test_file_3_name}"
+        )
+        res = self.client.get(url)
+        self.assertEqual(res.status_code, 200)
+
+    def test_404_not_found(self):
+        """A 404 is returned for non-existent files."""
+        url = (f"{settings.MEDIA_URL}public/some_not_existing_file.txt")
+        res = self.client.get(url)
+        self.assertEqual(res.status_code, 404)
diff --git a/titofisto/views.py b/titofisto/views.py
index 2f6d262..b3c314e 100644
--- a/titofisto/views.py
+++ b/titofisto/views.py
@@ -4,7 +4,7 @@ from django.conf import settings
 from django.http import FileResponse, Http404, HttpRequest
 from django.views import View
 
-from .settings import PARAM, TIMEOUT
+from .settings import PARAM, TIMEOUT, USE_PUBLIC_NAMESPACE, PUBLIC_NAMESPACE
 from .storage import TitofistoStorage
 
 
@@ -13,6 +13,14 @@ class TitofistoMediaView(View):
         # Get storage
         storage = TitofistoStorage()
 
+        if USE_PUBLIC_NAMESPACE:
+            # Public files are directly served without needing a token
+            if name.startswith(PUBLIC_NAMESPACE):
+                if storage.exists(name):
+                    return FileResponse(storage._open(name))
+                else:
+                    raise Http404()
+
         # Inspect URL parameter for completeness and extract timestamp
         token = request.GET.get(PARAM, None)
         if token is None:

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/lib/python3/dist-packages/django_titofisto-0.2.2.egg-info/PKG-INFO
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/django_titofisto-0.2.2.egg-info/dependency_links.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/django_titofisto-0.2.2.egg-info/requires.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/django_titofisto-0.2.2.egg-info/top_level.txt

Files in first set of .debs but not in second

-rw-r--r--  root/root   /usr/lib/python3/dist-packages/django_titofisto-0.1.2.post1.egg-info/PKG-INFO
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/django_titofisto-0.1.2.post1.egg-info/dependency_links.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/django_titofisto-0.1.2.post1.egg-info/requires.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/django_titofisto-0.1.2.post1.egg-info/top_level.txt

No differences were encountered in the control files

More details

Full run details