🔒️(backend) prevent accessing files if they are not ready

With the addition of the ANALYSING state
files could be accessed in the short time they
were in that state.
We now require files to be in ready.

Also adds missing frontend types (no impact).
This commit is contained in:
Florent Chehab
2026-06-03 15:15:24 +02:00
parent 22b2e6bd1e
commit c79984a883
7 changed files with 16 additions and 11 deletions
+1
View File
@@ -20,6 +20,7 @@ and this project adheres to
### Fixed ### Fixed
- 🔇(summary) make ffmpeg quiet #1404 - 🔇(summary) make ffmpeg quiet #1404
- 🔒️(backend) prevent accessing files if they are not ready #1395
## [1.18.0] - 2026-06-03 ## [1.18.0] - 2026-06-03
+3 -3
View File
@@ -177,7 +177,7 @@ class FileAdmin(admin.ModelAdmin):
"hard_deleted_at", "hard_deleted_at",
"description", "description",
"malware_detection_info", "malware_detection_info",
"is_pending_upload", "is_ready",
"preview_url", "preview_url",
"extension", "extension",
"key_base", "key_base",
@@ -226,7 +226,7 @@ class FileAdmin(admin.ModelAdmin):
_("Derived info"), _("Derived info"),
{ {
"fields": ( "fields": (
"is_pending_upload", "is_ready",
"extension", "extension",
"key_base", "key_base",
"file_key", "file_key",
@@ -240,7 +240,7 @@ class FileAdmin(admin.ModelAdmin):
@admin.display(description=_("File preview")) @admin.display(description=_("File preview"))
def preview_url(self, obj): def preview_url(self, obj):
"""Return a clickable preview URL for the file.""" """Return a clickable preview URL for the file."""
if obj.is_pending_upload: if not obj.is_ready:
return "-" return "-"
url = generate_download_file_url(obj, expires_in=60 * 60) url = generate_download_file_url(obj, expires_in=60 * 60)
+1 -1
View File
@@ -456,7 +456,7 @@ class ListFileSerializer(serializers.ModelSerializer):
def get_url(self, obj): def get_url(self, obj):
"""Return the URL of the file.""" """Return the URL of the file."""
if obj.is_pending_upload: if not obj.is_ready:
return None return None
return f"{settings.MEDIA_BASE_URL}{settings.MEDIA_URL}{quote(obj.file_key)}" return f"{settings.MEDIA_BASE_URL}{settings.MEDIA_URL}{quote(obj.file_key)}"
+1 -1
View File
@@ -1417,7 +1417,7 @@ class FileViewSet(
request, MEDIA_STORAGE_URL_PATTERN request, MEDIA_STORAGE_URL_PATTERN
) )
if file.is_pending_upload: if not file.is_ready:
logger.warning("File '%s' is not ready", file.id) logger.warning("File '%s' is not ready", file.id)
raise drf_exceptions.PermissionDenied() raise drf_exceptions.PermissionDenied()
+3 -3
View File
@@ -925,9 +925,9 @@ class File(BaseModel):
return super().delete(using, keep_parents) return super().delete(using, keep_parents)
@property @property
def is_pending_upload(self): def is_ready(self):
"""Return whether the file is in a pending upload state""" """Return whether the file is in a ready upload state"""
return self.upload_state == FileUploadStateChoices.PENDING return self.upload_state == FileUploadStateChoices.READY
@property @property
def extension(self): def extension(self):
@@ -86,7 +86,11 @@ def test_api_files_media_get_own():
assert response.content.decode("utf-8") == "my prose" assert response.content.decode("utf-8") == "my prose"
def test_api_files_media_auth_file_pending(): @pytest.mark.parametrize(
"rejecting_status",
[models.FileUploadStateChoices.PENDING, models.FileUploadStateChoices.ANALYZING],
)
def test_api_files_media_auth_rejects(rejecting_status):
""" """
Users who have a specific access to an file, whatever the role, should not be able to Users who have a specific access to an file, whatever the role, should not be able to
retrieve related attachments if the file is not ready. retrieve related attachments if the file is not ready.
@@ -97,7 +101,7 @@ def test_api_files_media_auth_file_pending():
file = factories.FileFactory( file = factories.FileFactory(
type=models.FileTypeChoices.BACKGROUND_IMAGE, type=models.FileTypeChoices.BACKGROUND_IMAGE,
upload_state=models.FileUploadStateChoices.PENDING, upload_state=rejecting_status,
creator=user, creator=user,
) )
+1 -1
View File
@@ -5,7 +5,7 @@ export type ApiFileCreator = {
} }
export type ApiFileType = 'background_image' export type ApiFileType = 'background_image'
export type ApiFileUploadState = 'pending' | 'ready' export type ApiFileUploadState = 'pending' | 'analyzing' | 'ready'
export type ApiFileItem = { export type ApiFileItem = {
id: string // UUID id: string // UUID