Skip to content

Commit 23d0136

Browse files
committed
Merge branch 'lotheac-fix_uploaded_file_exec'
2 parents 06f7935 + e8c6aff commit 23d0136

File tree

3 files changed

+22
-5
lines changed

3 files changed

+22
-5
lines changed

django/core/files/storage.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,10 @@ def _save(self, name, content):
192192
else:
193193
# This fun binary flag incantation makes os.open throw an
194194
# OSError if the file already exists before we open it.
195-
fd = os.open(full_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, 'O_BINARY', 0))
195+
flags = (os.O_WRONLY | os.O_CREAT | os.O_EXCL |
196+
getattr(os, 'O_BINARY', 0))
197+
# The current umask value is masked out by os.open!
198+
fd = os.open(full_path, flags, 0o666)
196199
try:
197200
locks.lock(fd, locks.LOCK_EX)
198201
_file = None

docs/releases/1.5.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,11 @@ Miscellaneous
333333
function at :func:`django.utils.text.slugify`. Similarly, ``remove_tags`` is
334334
available at :func:`django.utils.html.remove_tags`.
335335

336+
* Uploaded files are no longer created as executable by default. If you need
337+
them to be executeable change :setting:`FILE_UPLOAD_PERMISSIONS` to your
338+
needs. The new default value is `0666` (octal) and the current umask value
339+
is first masked out.
340+
336341
Features deprecated in 1.5
337342
==========================
338343

tests/regressiontests/file_storage/tests.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import errno
55
import os
66
import shutil
7+
import sys
78
import tempfile
89
import time
910
from datetime import datetime, timedelta
@@ -23,6 +24,7 @@
2324
from django.test import SimpleTestCase
2425
from django.utils import six
2526
from django.utils import unittest
27+
from django.test.utils import override_settings
2628
from ..servers.tests import LiveServerBase
2729

2830
# Try to import PIL in either of the two ways it can end up installed.
@@ -433,22 +435,29 @@ def test_race_condition(self):
433435
self.storage.delete('conflict')
434436
self.storage.delete('conflict_1')
435437

438+
@unittest.skipIf(sys.platform.startswith('win'), "Windows only partially supports umasks and chmod.")
436439
class FileStoragePermissions(unittest.TestCase):
437440
def setUp(self):
438-
self.old_perms = settings.FILE_UPLOAD_PERMISSIONS
439-
settings.FILE_UPLOAD_PERMISSIONS = 0o666
441+
self.umask = 0o027
442+
self.old_umask = os.umask(self.umask)
440443
self.storage_dir = tempfile.mkdtemp()
441444
self.storage = FileSystemStorage(self.storage_dir)
442445

443446
def tearDown(self):
444-
settings.FILE_UPLOAD_PERMISSIONS = self.old_perms
445447
shutil.rmtree(self.storage_dir)
448+
os.umask(self.old_umask)
446449

450+
@override_settings(FILE_UPLOAD_PERMISSIONS=0o654)
447451
def test_file_upload_permissions(self):
448452
name = self.storage.save("the_file", ContentFile("data"))
449453
actual_mode = os.stat(self.storage.path(name))[0] & 0o777
450-
self.assertEqual(actual_mode, 0o666)
454+
self.assertEqual(actual_mode, 0o654)
451455

456+
@override_settings(FILE_UPLOAD_PERMISSIONS=None)
457+
def test_file_upload_default_permissions(self):
458+
fname = self.storage.save("some_file", ContentFile("data"))
459+
mode = os.stat(self.storage.path(fname))[0] & 0o777
460+
self.assertEqual(mode, 0o666 & ~self.umask)
452461

453462
class FileStoragePathParsing(unittest.TestCase):
454463
def setUp(self):

0 commit comments

Comments
 (0)