Browse Source

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.
Mel Collins 12 years ago
parent
commit
6bdb3b1135

+ 5 - 0
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

+ 10 - 1
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

+ 13 - 0
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

+ 4 - 0
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
 =====================================
 

+ 6 - 1
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

+ 12 - 0
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()