1
# Copyright (C) 2005 by Canonical Ltd
2
# -*- coding: utf-8 -*-
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
"""Tests for bzr setting permissions.
21
Files which are created underneath .bzr/ should inherit its permissions.
22
So if the directory is group writable, the files and subdirs should be as well.
24
In the future, when we have Repository/Branch/Checkout information, the
25
permissions should be inherited individually, rather than all be the same.
27
TODO: jam 20051215 There are no tests for ftp yet, because we have no ftp server
28
TODO: jam 20051215 Currently the default behavior for 'bzr branch' is just
29
defined by the local umask. This isn't terrible, is it
30
the truly desired behavior?
37
from bzrlib.branch import Branch
38
from bzrlib.tests import TestCaseInTempDir, TestSkipped
39
from bzrlib.tests.test_sftp_transport import TestCaseWithSFTPServer
40
from bzrlib.transport import get_transport
43
def chmod_r(base, file_mode, dir_mode):
44
"""Recursively chmod from a base directory"""
45
assert os.path.isdir(base)
46
os.chmod(base, dir_mode)
47
for root, dirs, files in os.walk(base):
49
p = os.path.join(root, d)
52
p = os.path.join(root, f)
53
os.chmod(p, file_mode)
56
def check_mode_r(test, base, file_mode, dir_mode, include_base=True):
57
"""Check that all permissions match
59
:param test: The TestCase being run
60
:param base: The path to the root directory to check
61
:param file_mode: The mode for all files
62
:param dir_mode: The mode for all directories
63
:param include_base: If false, only check the subdirectories
65
assert os.path.isdir(base)
66
t = get_transport(".")
68
test.assertTransportMode(t, base, dir_mode)
69
for root, dirs, files in os.walk(base):
71
p = os.path.join(root, d)
72
test.assertTransportMode(t, p, dir_mode)
74
p = os.path.join(root, f)
75
test.assertTransportMode(t, p, file_mode)
78
def assertEqualMode(test, mode, mode_test):
79
test.assertEqual(mode, mode_test,
80
'mode mismatch %o != %o' % (mode, mode_test))
83
class TestPermissions(TestCaseInTempDir):
85
def test_new_files(self):
86
if sys.platform == 'win32':
87
raise TestSkipped('chmod has no effect on win32')
89
b = Branch.initialize(u'.')
91
open('a', 'wb').write('foo\n')
95
# Delete them because we are modifying the filesystem underneath them
97
chmod_r('.bzr', 0644, 0755)
98
check_mode_r(self, '.bzr', 0644, 0755)
102
assertEqualMode(self, 0755, b._dir_mode)
103
assertEqualMode(self, 0644, b._file_mode)
105
# Modifying a file shouldn't break the permissions
106
open('a', 'wb').write('foo2\n')
108
# The mode should be maintained after commit
109
check_mode_r(self, '.bzr', 0644, 0755)
111
# Adding a new file should maintain the permissions
112
open('b', 'wb').write('new b\n')
115
check_mode_r(self, '.bzr', 0644, 0755)
118
# Recursively update the modes of all files
119
chmod_r('.bzr', 0664, 0775)
120
check_mode_r(self, '.bzr', 0664, 0775)
123
assertEqualMode(self, 0775, b._dir_mode)
124
assertEqualMode(self, 0664, b._file_mode)
126
open('a', 'wb').write('foo3\n')
128
check_mode_r(self, '.bzr', 0664, 0775)
130
open('c', 'wb').write('new c\n')
133
check_mode_r(self, '.bzr', 0664, 0775)
135
# Test the group sticky bit
137
# Recursively update the modes of all files
138
chmod_r('.bzr', 0664, 02775)
139
check_mode_r(self, '.bzr', 0664, 02775)
142
assertEqualMode(self, 02775, b._dir_mode)
143
assertEqualMode(self, 0664, b._file_mode)
145
open('a', 'wb').write('foo4\n')
147
check_mode_r(self, '.bzr', 0664, 02775)
149
open('d', 'wb').write('new d\n')
152
check_mode_r(self, '.bzr', 0664, 02775)
154
def test_disable_set_mode(self):
155
# TODO: jam 20051215 Ultimately, this test should probably test that
156
# extra chmod calls aren't being made
159
b = Branch.initialize(u'.')
160
self.assertNotEqual(None, b._dir_mode)
161
self.assertNotEqual(None, b._file_mode)
163
bzrlib.branch.BzrBranch._set_dir_mode = False
164
b = Branch.open(u'.')
165
self.assertEqual(None, b._dir_mode)
166
self.assertNotEqual(None, b._file_mode)
168
bzrlib.branch.BzrBranch._set_file_mode = False
169
b = Branch.open(u'.')
170
self.assertEqual(None, b._dir_mode)
171
self.assertEqual(None, b._file_mode)
173
bzrlib.branch.BzrBranch._set_dir_mode = True
174
b = Branch.open(u'.')
175
self.assertNotEqual(None, b._dir_mode)
176
self.assertEqual(None, b._file_mode)
178
bzrlib.branch.BzrBranch._set_file_mode = True
179
b = Branch.open(u'.')
180
self.assertNotEqual(None, b._dir_mode)
181
self.assertNotEqual(None, b._file_mode)
183
bzrlib.branch.BzrBranch._set_dir_mode = True
184
bzrlib.branch.BzrBranch._set_file_mode = True
186
def test_new_branch(self):
187
if sys.platform == 'win32':
188
raise TestSkipped('chmod has no effect on win32')
191
mode = stat.S_IMODE(os.stat('a').st_mode)
192
b = Branch.initialize('a')
193
assertEqualMode(self, mode, b._dir_mode)
194
assertEqualMode(self, mode & ~07111, b._file_mode)
198
b = Branch.initialize('b')
199
assertEqualMode(self, 02777, b._dir_mode)
200
assertEqualMode(self, 00666, b._file_mode)
201
check_mode_r(self, 'b/.bzr', 00666, 02777)
205
b = Branch.initialize('c')
206
assertEqualMode(self, 02750, b._dir_mode)
207
assertEqualMode(self, 00640, b._file_mode)
208
check_mode_r(self, 'c/.bzr', 00640, 02750)
212
b = Branch.initialize('d')
213
assertEqualMode(self, 0700, b._dir_mode)
214
assertEqualMode(self, 0600, b._file_mode)
215
check_mode_r(self, 'd/.bzr', 00600, 0700)
218
class TestSftpPermissions(TestCaseWithSFTPServer):
220
def test_new_files(self):
221
if sys.platform == 'win32':
222
raise TestSkipped('chmod has no effect on win32')
223
# Though it would be nice to test that SFTP to a server
224
# which does support chmod has the right effect
226
from bzrlib.transport.sftp import SFTPTransport
228
# We don't actually use it directly, we just want to
229
# keep the connection open, since StubSFTPServer only
230
# allows 1 connection
231
_transport = SFTPTransport(self._sftp_url)
234
b_local = Branch.initialize(u'local')
235
t_local = b_local.working_tree()
236
open('local/a', 'wb').write('foo\n')
238
t_local.commit('foo')
240
# Delete them because we are modifying the filesystem underneath them
242
chmod_r('local/.bzr', 0644, 0755)
243
check_mode_r(self, 'local/.bzr', 0644, 0755)
245
b_local = Branch.open(u'local')
246
t_local = b_local.working_tree()
247
assertEqualMode(self, 0755, b_local._dir_mode)
248
assertEqualMode(self, 0644, b_local._file_mode)
251
sftp_url = self.get_remote_url('sftp')
252
b_sftp = Branch.initialize(sftp_url)
256
chmod_r('sftp/.bzr', 0644, 0755)
257
check_mode_r(self, 'sftp/.bzr', 0644, 0755)
259
b_sftp = Branch.open(sftp_url)
260
assertEqualMode(self, 0755, b_sftp._dir_mode)
261
assertEqualMode(self, 0644, b_sftp._file_mode)
263
open('local/a', 'wb').write('foo2\n')
264
t_local.commit('foo2')
266
# The mode should be maintained after commit
267
check_mode_r(self, 'sftp/.bzr', 0644, 0755)
269
open('local/b', 'wb').write('new b\n')
271
t_local.commit('new b')
273
check_mode_r(self, 'sftp/.bzr', 0644, 0755)
276
# Recursively update the modes of all files
277
chmod_r('sftp/.bzr', 0664, 0775)
278
check_mode_r(self, 'sftp/.bzr', 0664, 0775)
280
b_sftp = Branch.open(sftp_url)
281
assertEqualMode(self, 0775, b_sftp._dir_mode)
282
assertEqualMode(self, 0664, b_sftp._file_mode)
284
open('local/a', 'wb').write('foo3\n')
285
t_local.commit('foo3')
287
check_mode_r(self, 'sftp/.bzr', 0664, 0775)
289
open('local/c', 'wb').write('new c\n')
291
t_local.commit('new c')
293
check_mode_r(self, 'sftp/.bzr', 0664, 0775)
295
def test_sftp_server_modes(self):
296
if sys.platform == 'win32':
297
raise TestSkipped('chmod has no effect on win32')
300
original_umask = os.umask(umask)
303
from bzrlib.transport.sftp import SFTPTransport
304
t = SFTPTransport(self._sftp_url)
305
# Direct access should be masked by umask
306
t._sftp_open_exclusive('a', mode=0666).write('foo\n')
307
self.assertTransportMode(t, 'a', 0666 &~umask)
309
# but Transport overrides umask
310
t.put('b', 'txt', mode=0666)
311
self.assertTransportMode(t, 'b', 0666)
313
t._sftp.mkdir('c', mode=0777)
314
self.assertTransportMode(t, 'c', 0777 &~umask)
316
t.mkdir('d', mode=0777)
317
self.assertTransportMode(t, 'd', 0777)
319
os.umask(original_umask)