1
0
mirror of https://github.com/django/django.git synced 2025-08-12 12:59:14 +00:00

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.
This commit is contained in:
Vajrasky Kok 2013-11-05 18:02:54 +08:00 committed by Tim Graham
parent 42ac138009
commit 7e2d61a972
7 changed files with 77 additions and 27 deletions

View File

@ -294,12 +294,6 @@ Type 'yes' to continue, or 'no' to cancel: """
self.log("Pretending to copy '%s'" % source_path, level=1) self.log("Pretending to copy '%s'" % source_path, level=1)
else: else:
self.log("Copying '%s'" % source_path, level=1) 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: with source_storage.open(path) as source_file:
self.storage.save(prefixed_path, source_file) self.storage.save(prefixed_path, source_file)
if not prefixed_path in self.copied_files: if not prefixed_path in self.copied_files:

View File

@ -149,7 +149,8 @@ class FileSystemStorage(Storage):
Standard filesystem 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: if location is None:
location = settings.MEDIA_ROOT location = settings.MEDIA_ROOT
self.base_location = location self.base_location = location
@ -161,6 +162,10 @@ class FileSystemStorage(Storage):
file_permissions_mode if file_permissions_mode is not None file_permissions_mode if file_permissions_mode is not None
else settings.FILE_UPLOAD_PERMISSIONS 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'): def _open(self, name, mode='rb'):
return File(open(self.path(name), mode)) return File(open(self.path(name), mode))
@ -175,12 +180,12 @@ class FileSystemStorage(Storage):
directory = os.path.dirname(full_path) directory = os.path.dirname(full_path)
if not os.path.exists(directory): if not os.path.exists(directory):
try: 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, # 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) old_umask = os.umask(0)
try: try:
os.makedirs(directory, settings.FILE_UPLOAD_DIRECTORY_PERMISSIONS) os.makedirs(directory, self.directory_permissions_mode)
finally: finally:
os.umask(old_umask) os.umask(old_umask)
else: else:

View File

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

View File

@ -29,7 +29,7 @@ Django provides two convenient ways to access the current storage class:
The FileSystemStorage 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 The :class:`~django.core.files.storage.FileSystemStorage` class implements
basic file storage on a local filesystem. It inherits from 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 The ``file_permissions_mode`` attribute was added. Previously files
always received :setting:`FILE_UPLOAD_PERMISSIONS` permissions. 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:: .. note::
The ``FileSystemStorage.delete()`` method will not raise The ``FileSystemStorage.delete()`` method will not raise

View File

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

View File

@ -256,10 +256,11 @@ Minor features
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* The :ref:`static files storage classes <staticfiles-storages>` may be * The :ref:`static files storage classes <staticfiles-storages>` may be
subclassed to override the permissions that collected static files receive by subclassed to override the permissions that collected static files and
setting the directories receive by setting the
:attr:`~django.core.files.storage.FileSystemStorage.file_permissions_mode` :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` :mod:`django.contrib.syndication`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

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