-
-
Notifications
You must be signed in to change notification settings - Fork 867
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
96 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
Filesystem | ||
========== | ||
|
||
django-storages contains an alternative filesystem storage backend. | ||
|
||
Unlike Django's builtin filesystem storage, this one always overwrites files of the same name, and never renames files. | ||
|
||
|
||
Usage | ||
***** | ||
|
||
:: | ||
|
||
from storages.backends.filesystem import FileSystemOverwriteStorage | ||
storage = FileSystemOverwriteStorage(location='/media/photos') | ||
storage.save("myfile.txt", ContentFile("content 1")) | ||
# This will overwrite the previous file, *not* create a new file. | ||
storage.save("myfile.txt", ContentFile("content 2")) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import os | ||
import pathlib | ||
|
||
from django.core.exceptions import SuspiciousFileOperation | ||
from django.core.files.storage import FileSystemStorage | ||
|
||
|
||
class FileSystemOverwriteStorage(FileSystemStorage): | ||
""" | ||
Filesystem storage that never renames files. | ||
Files uploaded via this storage class will automatically overwrite any files of the same name. | ||
""" | ||
|
||
# Don't throw errors if the file already exists when saving. | ||
# https://manpages.debian.org/bullseye/manpages-dev/open.2.en.html#O_EXCL | ||
OS_OPEN_FLAGS = FileSystemStorage.OS_OPEN_FLAGS & ~os.O_EXCL | ||
|
||
# Don't check what files already exist; just use the original name. | ||
def get_available_name(self, name, max_length=None): | ||
# Do validate it though (just like FileSystemStorage does) | ||
name = str(name).replace("\\", "/") | ||
dir_name, _ = os.path.split(name) | ||
if ".." in pathlib.PurePath(dir_name).parts: | ||
raise SuspiciousFileOperation( | ||
"Detected path traversal attempt in '%s'" % dir_name | ||
) | ||
return name |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import tempfile | ||
|
||
import pytest | ||
from django.core.exceptions import SuspiciousFileOperation | ||
from django.core.files.base import ContentFile | ||
|
||
from storages.backends import filesystem | ||
|
||
|
||
@pytest.fixture() | ||
def storage(): | ||
with tempfile.TemporaryDirectory() as tmpdirname: | ||
yield filesystem.FileSystemOverwriteStorage(location=tmpdirname) | ||
|
||
|
||
def test_save_overwrite(storage): | ||
content = ContentFile("content") | ||
name = "testfile.txt" | ||
storage.save(name, content) | ||
|
||
assert storage.exists(name) | ||
assert storage.size(name) == len(content) | ||
|
||
content2 = ContentFile("content2") | ||
storage.save(name, content2) | ||
# No rename was done; the same file was overwritten | ||
assert storage.exists(name) | ||
assert storage.size(name) == len(content2) | ||
|
||
|
||
def test_filename_validate(storage): | ||
content = ContentFile("content") | ||
with pytest.raises(SuspiciousFileOperation): | ||
storage.save("/badfile.txt", content) | ||
|
||
with pytest.raises(SuspiciousFileOperation): | ||
storage.save("foo/../../../badfile.txt", content) |