From 6bdb3b1135d1bd7b2dc24131b9d26ac19ebdba67 Mon Sep 17 00:00:00 2001 From: Mel Collins Date: Mon, 13 May 2013 13:38:53 +0200 Subject: [PATCH] Fixed #13518 -- Added FILE_UPLOAD_DIRECTORY_PERMISSIONS setting This setting does for new directories what FILE_UPLOAD_PERMISSIONS does for new files. Thanks jacob@ for the suggestion. --- django/conf/global_settings.py | 5 +++++ django/core/files/storage.py | 11 ++++++++++- docs/ref/settings.txt | 13 +++++++++++++ docs/releases/1.7.txt | 4 ++++ docs/topics/http/file-uploads.txt | 7 ++++++- tests/file_storage/tests.py | 12 ++++++++++++ 6 files changed, 50 insertions(+), 2 deletions(-) diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index 19258fbcd41..95deaa8d87a 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -313,6 +313,11 @@ FILE_UPLOAD_TEMP_DIR = None # you'd pass directly to os.chmod; see http://docs.python.org/lib/os-file-dir.html. FILE_UPLOAD_PERMISSIONS = None +# The numeric mode to assign to newly-created directories, when uploading files. +# The value should be a mode as you'd pass to os.chmod; +# see http://docs.python.org/lib/os-file-dir.html. +FILE_UPLOAD_DIRECTORY_PERMISSIONS = None + # Python module path where user will place custom format definition. # The directory where this setting is pointing should contain subdirectories # named as the locales, containing a formats.py file diff --git a/django/core/files/storage.py b/django/core/files/storage.py index 5d301a317c5..5e587da2dac 100644 --- a/django/core/files/storage.py +++ b/django/core/files/storage.py @@ -172,7 +172,16 @@ class FileSystemStorage(Storage): directory = os.path.dirname(full_path) if not os.path.exists(directory): try: - os.makedirs(directory) + if settings.FILE_UPLOAD_DIRECTORY_PERMISSIONS is not None: + # os.makedirs applies the global umask, so we reset it, + # for consistency with FILE_UPLOAD_PERMISSIONS behavior. + old_umask = os.umask(0) + try: + os.makedirs(directory, settings.FILE_UPLOAD_DIRECTORY_PERMISSIONS) + finally: + os.umask(old_umask) + else: + os.makedirs(directory) except OSError as e: if e.errno != errno.EEXIST: raise diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 38d7275aed7..90545d96c56 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -1108,6 +1108,19 @@ Default: ``2621440`` (i.e. 2.5 MB). The maximum size (in bytes) that an upload will be before it gets streamed to the file system. See :doc:`/topics/files` for details. +.. setting:: FILE_UPLOAD_DIRECTORY_PERMISSIONS + +FILE_UPLOAD_DIRECTORY_PERMISSIONS +--------------------------------- + +.. versionadded:: 1.7 + +Default: ``None`` + +The numeric mode to apply to directories created in the process of +uploading files. This value mirrors the functionality and caveats of +the :setting:`FILE_UPLOAD_PERMISSIONS` setting. + .. setting:: FILE_UPLOAD_PERMISSIONS FILE_UPLOAD_PERMISSIONS diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt index ec37b382d42..6fda83ebc70 100644 --- a/docs/releases/1.7.txt +++ b/docs/releases/1.7.txt @@ -138,6 +138,10 @@ Minor features * The :func:`~django.contrib.auth.decorators.permission_required` decorator can take a list of permissions as well as a single permission. +* The new :setting:`FILE_UPLOAD_DIRECTORY_PERMISSIONS` setting controls + the file system permissions of directories created during file upload, like + :setting:`FILE_UPLOAD_PERMISSIONS` does for the files themselves. + Backwards incompatible changes in 1.7 ===================================== diff --git a/docs/topics/http/file-uploads.txt b/docs/topics/http/file-uploads.txt index 2cdab9ea9b4..d88524ee20d 100644 --- a/docs/topics/http/file-uploads.txt +++ b/docs/topics/http/file-uploads.txt @@ -132,7 +132,7 @@ upload behavior. Changing upload handler behavior -------------------------------- -Three settings control Django's file upload behavior: +There are a few settings which control Django's file upload behavior: :setting:`FILE_UPLOAD_MAX_MEMORY_SIZE` The maximum size, in bytes, for files that will be uploaded into memory. @@ -167,6 +167,11 @@ Three settings control Django's file upload behavior: **Always prefix the mode with a 0.** +:setting:`FILE_UPLOAD_DIRECTORY_PERMISSIONS` + The numeric mode to apply to directories created in the process of + uploading files. This value mirrors the functionality and caveats of + the :setting:`FILE_UPLOAD_PERMISSIONS` setting. + :setting:`FILE_UPLOAD_HANDLERS` The actual handlers for uploaded files. Changing this setting allows complete customization -- even replacement -- of Django's upload diff --git a/tests/file_storage/tests.py b/tests/file_storage/tests.py index cdd97203743..2a6f602e3e5 100644 --- a/tests/file_storage/tests.py +++ b/tests/file_storage/tests.py @@ -462,6 +462,18 @@ class FileStoragePermissions(unittest.TestCase): mode = os.stat(self.storage.path(fname))[0] & 0o777 self.assertEqual(mode, 0o666 & ~self.umask) + @override_settings(FILE_UPLOAD_DIRECTORY_PERMISSIONS=0o765) + def test_file_upload_directory_permissions(self): + name = self.storage.save("the_directory/the_file", ContentFile("data")) + dir_mode = os.stat(os.path.dirname(self.storage.path(name)))[0] & 0o777 + self.assertEqual(dir_mode, 0o765) + + @override_settings(FILE_UPLOAD_DIRECTORY_PERMISSIONS=None) + def test_file_upload_directory_default_permissions(self): + name = self.storage.save("the_directory/the_file", ContentFile("data")) + dir_mode = os.stat(os.path.dirname(self.storage.path(name)))[0] & 0o777 + self.assertEqual(dir_mode, 0o777 & ~self.umask) + class FileStoragePathParsing(unittest.TestCase): def setUp(self): self.storage_dir = tempfile.mkdtemp()