Fixed #4282 -- Made startapp/startproject management commands honor umask.

Co-authored-by: Christian Schmitt <c.schmitt@briefdomain.de>
This commit is contained in:
Ad Timmering 2021-11-19 23:15:24 +09:00 committed by Mariusz Felisiak
parent 1555e5850d
commit 59f4796918
2 changed files with 37 additions and 4 deletions

View File

@ -181,7 +181,7 @@ class TemplateCommand(BaseCommand):
if self.verbosity >= 2:
self.stdout.write('Creating %s' % new_path)
try:
shutil.copymode(old_path, new_path)
self.apply_umask(old_path, new_path)
self.make_writeable(new_path)
except OSError:
self.stderr.write(
@ -345,6 +345,12 @@ class TemplateCommand(BaseCommand):
scheme = template.split(':', 1)[0].lower()
return scheme in self.url_schemes
def apply_umask(self, old_path, new_path):
current_umask = os.umask(0)
os.umask(current_umask)
current_mode = stat.S_IMODE(os.stat(old_path).st_mode)
os.chmod(new_path, current_mode & ~current_umask)
def make_writeable(self, filename):
"""
Make sure that the file is writeable.

View File

@ -7,6 +7,7 @@ import os
import re
import shutil
import socket
import stat
import subprocess
import sys
import tempfile
@ -32,6 +33,7 @@ from django.test import (
LiveServerTestCase, SimpleTestCase, TestCase, override_settings,
)
from django.test.utils import captured_stderr, captured_stdout
from django.utils.version import PY39
custom_templates_dir = os.path.join(os.path.dirname(__file__), 'custom_templates')
@ -95,7 +97,7 @@ class AdminScriptTestCase(SimpleTestCase):
paths.append(os.path.dirname(backend_dir))
return paths
def run_test(self, args, settings_file=None, apps=None):
def run_test(self, args, settings_file=None, apps=None, umask=None):
base_dir = os.path.dirname(self.test_dir)
# The base dir for Django's tests is one level up.
tests_dir = os.path.dirname(os.path.dirname(__file__))
@ -124,11 +126,13 @@ class AdminScriptTestCase(SimpleTestCase):
cwd=self.test_dir,
env=test_environ,
text=True,
# subprocess.run()'s umask was added in Python 3.9.
**({'umask': umask} if umask and PY39 else {}),
)
return p.stdout, p.stderr
def run_django_admin(self, args, settings_file=None):
return self.run_test(['-m', 'django', *args], settings_file)
def run_django_admin(self, args, settings_file=None, umask=None):
return self.run_test(['-m', 'django', *args], settings_file, umask=umask)
def run_manage(self, args, settings_file=None, manage_py=None):
template_manage_py = (
@ -2297,6 +2301,29 @@ class StartProject(LiveServerTestCase, AdminScriptTestCase):
not_excluded = os.path.join(testproject_dir, project_name)
self.assertIs(os.path.exists(not_excluded), True)
@unittest.skipIf(
sys.platform == 'win32',
'Windows only partially supports umasks and chmod.',
)
@unittest.skipUnless(PY39, "subprocess.run()'s umask was added in Python 3.9.")
def test_honor_umask(self):
_, err = self.run_django_admin(['startproject', 'testproject'], umask=0o077)
self.assertNoOutput(err)
testproject_dir = os.path.join(self.test_dir, 'testproject')
self.assertIs(os.path.isdir(testproject_dir), True)
tests = [
(['manage.py'], 0o700),
(['testproject'], 0o700),
(['testproject', 'settings.py'], 0o600),
]
for paths, expected_mode in tests:
file_path = os.path.join(testproject_dir, *paths)
with self.subTest(paths[-1]):
self.assertEqual(
stat.S_IMODE(os.stat(file_path).st_mode),
expected_mode,
)
class StartApp(AdminScriptTestCase):