Pārlūkot izejas kodu

Fixed #21380 -- Added a way to set different permission for static directories.

Previously when collecting static files, the directories would receive permissions
from the global umask. Now the default permission comes from FILE_UPLOAD_DIRECTORY_PERMISSIONS
and there's an option to specify the permissions by subclassing any of the
static files storage classes and setting the directory_permissions_mode parameter.
Vajrasky Kok 11 gadi atpakaļ
vecāks
revīzija
7e2d61a972

+ 0 - 6
django/contrib/staticfiles/management/commands/collectstatic.py

@@ -294,12 +294,6 @@ Type 'yes' to continue, or 'no' to cancel: """
             self.log("Pretending to copy '%s'" % source_path, level=1)
         else:
             self.log("Copying '%s'" % source_path, level=1)
-            if self.local:
-                full_path = self.storage.path(prefixed_path)
-                try:
-                    os.makedirs(os.path.dirname(full_path))
-                except OSError:
-                    pass
             with source_storage.open(path) as source_file:
                 self.storage.save(prefixed_path, source_file)
         if not prefixed_path in self.copied_files:

+ 9 - 4
django/core/files/storage.py

@@ -149,7 +149,8 @@ class FileSystemStorage(Storage):
     Standard filesystem storage
     """
 
-    def __init__(self, location=None, base_url=None, file_permissions_mode=None):
+    def __init__(self, location=None, base_url=None, file_permissions_mode=None,
+            directory_permissions_mode=None):
         if location is None:
             location = settings.MEDIA_ROOT
         self.base_location = location
@@ -161,6 +162,10 @@ class FileSystemStorage(Storage):
             file_permissions_mode if file_permissions_mode is not None
             else settings.FILE_UPLOAD_PERMISSIONS
         )
+        self.directory_permissions_mode = (
+            directory_permissions_mode if directory_permissions_mode is not None
+            else settings.FILE_UPLOAD_DIRECTORY_PERMISSIONS
+        )
 
     def _open(self, name, mode='rb'):
         return File(open(self.path(name), mode))
@@ -175,12 +180,12 @@ class FileSystemStorage(Storage):
         directory = os.path.dirname(full_path)
         if not os.path.exists(directory):
             try:
-                if settings.FILE_UPLOAD_DIRECTORY_PERMISSIONS is not None:
+                if self.directory_permissions_mode is not None:
                     # os.makedirs applies the global umask, so we reset it,
-                    # for consistency with FILE_UPLOAD_PERMISSIONS behavior.
+                    # for consistency with file_permissions_mode behavior.
                     old_umask = os.umask(0)
                     try:
-                        os.makedirs(directory, settings.FILE_UPLOAD_DIRECTORY_PERMISSIONS)
+                        os.makedirs(directory, self.directory_permissions_mode)
                     finally:
                         os.umask(old_umask)
                 else:

+ 11 - 7
docs/ref/contrib/staticfiles.txt

@@ -60,16 +60,19 @@ by the :class:`~django.contrib.staticfiles.storage.CachedStaticFilesStorage`
 by default.
 
 By default, collected files receive permissions from
-:setting:`FILE_UPLOAD_PERMISSIONS`. If you would like different permissions for
-these files, you can subclass either of the :ref:`static files storage
-classes <staticfiles-storages>` and specify the ``file_permissions_mode``
-parameter. For example::
+:setting:`FILE_UPLOAD_PERMISSIONS` and collected directories receive permissions
+from :setting:`FILE_UPLOAD_DIRECTORY_PERMISSIONS`. If you would like different
+permissions for these files and/or directories, you can subclass either of the
+:ref:`static files storage classes <staticfiles-storages>` and specify the
+``file_permissions_mode`` and/or ``directory_permissions_mode`` parameters,
+respectively. For example::
 
     from django.contrib.staticfiles import storage
 
     class MyStaticFilesStorage(storage.StaticFilesStorage):
         def __init__(self, *args, **kwargs):
             kwargs['file_permissions_mode'] = 0o640
+            kwargs['directory_permissions_mode'] = 0o760
             super(CustomStaticFilesStorage, self).__init__(*args, **kwargs)
 
 Then set the :setting:`STATICFILES_STORAGE` setting to
@@ -77,9 +80,10 @@ Then set the :setting:`STATICFILES_STORAGE` setting to
 
 .. versionadded:: 1.7
 
-    The ability to override ``file_permissions_mode`` is new in Django 1.7.
-    Previously the file permissions always used
-    :setting:`FILE_UPLOAD_PERMISSIONS`.
+    The ability to override ``file_permissions_mode`` and
+    ``directory_permissions_mode`` is new in Django 1.7.  Previously the file
+    permissions always used :setting:`FILE_UPLOAD_PERMISSIONS` and the directory
+    permissions always used :setting:`FILE_UPLOAD_DIRECTORY_PERMISSIONS`.
 
 .. highlight:: console
 

+ 12 - 1
docs/ref/files/storage.txt

@@ -29,7 +29,7 @@ Django provides two convenient ways to access the current storage class:
 The FileSystemStorage Class
 ---------------------------
 
-.. class:: FileSystemStorage([location=None, base_url=None, file_permissions_mode=None])
+.. class:: FileSystemStorage([location=None, base_url=None, file_permissions_mode=None, directory_permissions_mode=None])
 
     The :class:`~django.core.files.storage.FileSystemStorage` class implements
     basic file storage on a local filesystem. It inherits from
@@ -46,6 +46,17 @@ The FileSystemStorage Class
             The ``file_permissions_mode`` attribute was added. Previously files
             always received :setting:`FILE_UPLOAD_PERMISSIONS` permissions.
 
+    .. attribute:: directory_permissions_mode
+
+        The file system permissions that the directory will receive when it is
+        saved. Defaults to :setting:`FILE_UPLOAD_DIRECTORY_PERMISSIONS`.
+
+        .. versionadded:: 1.7
+
+            The ``directory_permissions_mode`` attribute was added. Previously
+            directories always received
+            :setting:`FILE_UPLOAD_DIRECTORY_PERMISSIONS` permissions.
+
     .. note::
 
         The ``FileSystemStorage.delete()`` method will not raise

+ 10 - 4
docs/ref/settings.txt

@@ -1135,9 +1135,15 @@ FILE_UPLOAD_DIRECTORY_PERMISSIONS
 
 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.
+The numeric mode to apply to directories created in the process of uploading
+files.
+
+This setting also determines the default permissions for collected static
+directories when using the :djadmin:`collectstatic` management command. See
+:djadmin:`collectstatic` for details on overriding it.
+
+This value mirrors the functionality and caveats of the
+:setting:`FILE_UPLOAD_PERMISSIONS` setting.
 
 .. setting:: FILE_UPLOAD_PERMISSIONS
 
@@ -1157,7 +1163,7 @@ system's standard umask.
 
 This setting also determines the default permissions for collected static files
 when using the :djadmin:`collectstatic` management command. See
-:djadmin:`collectstatic` for details on overridding it.
+:djadmin:`collectstatic` for details on overriding it.
 
 .. warning::
 

+ 4 - 3
docs/releases/1.7.txt

@@ -256,10 +256,11 @@ Minor features
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 * The :ref:`static files storage classes <staticfiles-storages>` may be
-  subclassed to override the permissions that collected static files receive by
-  setting the
+  subclassed to override the permissions that collected static files and
+  directories receive by setting the
   :attr:`~django.core.files.storage.FileSystemStorage.file_permissions_mode`
-  parameter. See :djadmin:`collectstatic` for example usage.
+  and :attr:`~django.core.files.storage.FileSystemStorage.directory_permissions_mode`
+  parameters. See :djadmin:`collectstatic` for example usage.
 
 :mod:`django.contrib.syndication`
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

+ 31 - 2
tests/staticfiles_tests/tests.py

@@ -823,6 +823,7 @@ class CustomStaticFilesStorage(storage.StaticFilesStorage):
     """
     def __init__(self, *args, **kwargs):
         kwargs['file_permissions_mode'] = 0o640
+        kwargs['directory_permissions_mode'] = 0o740
         super(CustomStaticFilesStorage, self).__init__(*args, **kwargs)
 
 
@@ -839,21 +840,49 @@ class TestStaticFilePermissions(BaseCollectionTestCase, StaticFilesTestCase):
                       'link': False,
                       'dry_run': False}
 
+    def setUp(self):
+        self.umask = 0o027
+        self.old_umask = os.umask(self.umask)
+        super(TestStaticFilePermissions, self).setUp()
+
+    def tearDown(self):
+        os.umask(self.old_umask)
+        super(TestStaticFilePermissions, self).tearDown()
+
     # Don't run collectstatic command in this test class.
     def run_collectstatic(self, **kwargs):
         pass
 
-    @override_settings(FILE_UPLOAD_PERMISSIONS=0o655)
-    def test_collect_static_files_default_permissions(self):
+    @override_settings(FILE_UPLOAD_PERMISSIONS=0o655,
+                       FILE_UPLOAD_DIRECTORY_PERMISSIONS=0o765)
+    def test_collect_static_files_permissions(self):
         collectstatic.Command().execute(**self.command_params)
         test_file = os.path.join(settings.STATIC_ROOT, "test.txt")
+        test_dir = os.path.join(settings.STATIC_ROOT, "subdir")
         file_mode = os.stat(test_file)[0] & 0o777
+        dir_mode = os.stat(test_dir)[0] & 0o777
         self.assertEqual(file_mode, 0o655)
+        self.assertEqual(dir_mode, 0o765)
+
+    @override_settings(FILE_UPLOAD_PERMISSIONS=None,
+                       FILE_UPLOAD_DIRECTORY_PERMISSIONS=None)
+    def test_collect_static_files_default_permissions(self):
+        collectstatic.Command().execute(**self.command_params)
+        test_file = os.path.join(settings.STATIC_ROOT, "test.txt")
+        test_dir = os.path.join(settings.STATIC_ROOT, "subdir")
+        file_mode = os.stat(test_file)[0] & 0o777
+        dir_mode = os.stat(test_dir)[0] & 0o777
+        self.assertEqual(file_mode, 0o666 & ~self.umask)
+        self.assertEqual(dir_mode, 0o777 & ~self.umask)
 
     @override_settings(FILE_UPLOAD_PERMISSIONS=0o655,
+                       FILE_UPLOAD_DIRECTORY_PERMISSIONS=0o765,
                        STATICFILES_STORAGE='staticfiles_tests.tests.CustomStaticFilesStorage')
     def test_collect_static_files_subclass_of_static_storage(self):
         collectstatic.Command().execute(**self.command_params)
         test_file = os.path.join(settings.STATIC_ROOT, "test.txt")
+        test_dir = os.path.join(settings.STATIC_ROOT, "subdir")
         file_mode = os.stat(test_file)[0] & 0o777
+        dir_mode = os.stat(test_dir)[0] & 0o777
         self.assertEqual(file_mode, 0o640)
+        self.assertEqual(dir_mode, 0o740)