~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_permissions.py

update TODO

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005 by Canonical Ltd
 
2
# -*- coding: utf-8 -*-
 
3
 
 
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.
 
8
 
 
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.
 
13
 
 
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
 
17
 
 
18
 
 
19
"""Tests for bzr setting permissions.
 
20
 
 
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.
 
23
 
 
24
In the future, when we have Repository/Branch/Checkout information, the
 
25
permissions should be inherited individually, rather than all be the same.
 
26
 
 
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?
 
31
"""
 
32
 
 
33
import os
 
34
import sys
 
35
import stat
 
36
 
 
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
 
41
from bzrlib.workingtree import WorkingTree
 
42
 
 
43
 
 
44
def chmod_r(base, file_mode, dir_mode):
 
45
    """Recursively chmod from a base directory"""
 
46
    assert os.path.isdir(base)
 
47
    os.chmod(base, dir_mode)
 
48
    for root, dirs, files in os.walk(base):
 
49
        for d in dirs:
 
50
            p = os.path.join(root, d)
 
51
            os.chmod(p, dir_mode)
 
52
        for f in files:
 
53
            p = os.path.join(root, f)
 
54
            os.chmod(p, file_mode)
 
55
 
 
56
 
 
57
def check_mode_r(test, base, file_mode, dir_mode, include_base=True):
 
58
    """Check that all permissions match
 
59
 
 
60
    :param test: The TestCase being run
 
61
    :param base: The path to the root directory to check
 
62
    :param file_mode: The mode for all files
 
63
    :param dir_mode: The mode for all directories
 
64
    :param include_base: If false, only check the subdirectories
 
65
    """
 
66
    assert os.path.isdir(base)
 
67
    t = get_transport(".")
 
68
    if include_base:
 
69
        test.assertTransportMode(t, base, dir_mode)
 
70
    for root, dirs, files in os.walk(base):
 
71
        for d in dirs:
 
72
            p = os.path.join(root, d)
 
73
            test.assertTransportMode(t, p, dir_mode)
 
74
        for f in files:
 
75
            p = os.path.join(root, f)
 
76
            test.assertTransportMode(t, p, file_mode)
 
77
 
 
78
 
 
79
def assertEqualMode(test, mode, mode_test):
 
80
    test.assertEqual(mode, mode_test,
 
81
                     'mode mismatch %o != %o' % (mode, mode_test))
 
82
 
 
83
 
 
84
class TestPermissions(TestCaseInTempDir):
 
85
 
 
86
    def test_new_files(self):
 
87
        if sys.platform == 'win32':
 
88
            raise TestSkipped('chmod has no effect on win32')
 
89
 
 
90
        t = WorkingTree.create_standalone('.')
 
91
        b = t.branch
 
92
        open('a', 'wb').write('foo\n')
 
93
        t.add('a')
 
94
        t.commit('foo')
 
95
 
 
96
        # Delete them because we are modifying the filesystem underneath them
 
97
        del b, t 
 
98
        chmod_r('.bzr', 0644, 0755)
 
99
        check_mode_r(self, '.bzr', 0644, 0755)
 
100
 
 
101
        b = Branch.open('.')
 
102
        t = b.working_tree()
 
103
        assertEqualMode(self, 0755, b._dir_mode)
 
104
        assertEqualMode(self, 0644, b._file_mode)
 
105
 
 
106
        # Modifying a file shouldn't break the permissions
 
107
        open('a', 'wb').write('foo2\n')
 
108
        t.commit('foo2')
 
109
        # The mode should be maintained after commit
 
110
        check_mode_r(self, '.bzr', 0644, 0755)
 
111
 
 
112
        # Adding a new file should maintain the permissions
 
113
        open('b', 'wb').write('new b\n')
 
114
        t.add('b')
 
115
        t.commit('new b')
 
116
        check_mode_r(self, '.bzr', 0644, 0755)
 
117
 
 
118
        del b, t
 
119
        # Recursively update the modes of all files
 
120
        chmod_r('.bzr', 0664, 0775)
 
121
        check_mode_r(self, '.bzr', 0664, 0775)
 
122
        b = Branch.open('.')
 
123
        t = b.working_tree()
 
124
        assertEqualMode(self, 0775, b._dir_mode)
 
125
        assertEqualMode(self, 0664, b._file_mode)
 
126
 
 
127
        open('a', 'wb').write('foo3\n')
 
128
        t.commit('foo3')
 
129
        check_mode_r(self, '.bzr', 0664, 0775)
 
130
 
 
131
        open('c', 'wb').write('new c\n')
 
132
        t.add('c')
 
133
        t.commit('new c')
 
134
        check_mode_r(self, '.bzr', 0664, 0775)
 
135
 
 
136
        # Test the group sticky bit
 
137
        del b, t
 
138
        # Recursively update the modes of all files
 
139
        chmod_r('.bzr', 0664, 02775)
 
140
        check_mode_r(self, '.bzr', 0664, 02775)
 
141
        b = Branch.open('.')
 
142
        t = b.working_tree()
 
143
        assertEqualMode(self, 02775, b._dir_mode)
 
144
        assertEqualMode(self, 0664, b._file_mode)
 
145
 
 
146
        open('a', 'wb').write('foo4\n')
 
147
        t.commit('foo4')
 
148
        check_mode_r(self, '.bzr', 0664, 02775)
 
149
 
 
150
        open('d', 'wb').write('new d\n')
 
151
        t.add('d')
 
152
        t.commit('new d')
 
153
        check_mode_r(self, '.bzr', 0664, 02775)
 
154
 
 
155
    def test_disable_set_mode(self):
 
156
        # TODO: jam 20051215 Ultimately, this test should probably test that
 
157
        #                    extra chmod calls aren't being made
 
158
        import bzrlib.branch
 
159
        try:
 
160
            t = WorkingTree.create_standalone('.')
 
161
            b = t.branch
 
162
            self.assertNotEqual(None, b._dir_mode)
 
163
            self.assertNotEqual(None, b._file_mode)
 
164
 
 
165
            bzrlib.branch.BzrBranch._set_dir_mode = False
 
166
            b = Branch.open(u'.')
 
167
            self.assertEqual(None, b._dir_mode)
 
168
            self.assertNotEqual(None, b._file_mode)
 
169
 
 
170
            bzrlib.branch.BzrBranch._set_file_mode = False
 
171
            b = Branch.open(u'.')
 
172
            self.assertEqual(None, b._dir_mode)
 
173
            self.assertEqual(None, b._file_mode)
 
174
 
 
175
            bzrlib.branch.BzrBranch._set_dir_mode = True
 
176
            b = Branch.open(u'.')
 
177
            self.assertNotEqual(None, b._dir_mode)
 
178
            self.assertEqual(None, b._file_mode)
 
179
 
 
180
            bzrlib.branch.BzrBranch._set_file_mode = True
 
181
            b = Branch.open(u'.')
 
182
            self.assertNotEqual(None, b._dir_mode)
 
183
            self.assertNotEqual(None, b._file_mode)
 
184
        finally:
 
185
            bzrlib.branch.BzrBranch._set_dir_mode = True
 
186
            bzrlib.branch.BzrBranch._set_file_mode = True
 
187
 
 
188
    def test_new_branch(self):
 
189
        if sys.platform == 'win32':
 
190
            raise TestSkipped('chmod has no effect on win32')
 
191
 
 
192
        os.mkdir('a')
 
193
        mode = stat.S_IMODE(os.stat('a').st_mode)
 
194
        t = WorkingTree.create_standalone('.')
 
195
        b = t.branch
 
196
        assertEqualMode(self, mode, b._dir_mode)
 
197
        assertEqualMode(self, mode & ~07111, b._file_mode)
 
198
 
 
199
        os.mkdir('b')
 
200
        os.chmod('b', 02777)
 
201
        b = Branch.create('b')
 
202
        assertEqualMode(self, 02777, b._dir_mode)
 
203
        assertEqualMode(self, 00666, b._file_mode)
 
204
        check_mode_r(self, 'b/.bzr', 00666, 02777)
 
205
 
 
206
        os.mkdir('c')
 
207
        os.chmod('c', 02750)
 
208
        b = Branch.create('c')
 
209
        assertEqualMode(self, 02750, b._dir_mode)
 
210
        assertEqualMode(self, 00640, b._file_mode)
 
211
        check_mode_r(self, 'c/.bzr', 00640, 02750)
 
212
 
 
213
        os.mkdir('d')
 
214
        os.chmod('d', 0700)
 
215
        b = Branch.create('d')
 
216
        assertEqualMode(self, 0700, b._dir_mode)
 
217
        assertEqualMode(self, 0600, b._file_mode)
 
218
        check_mode_r(self, 'd/.bzr', 00600, 0700)
 
219
 
 
220
 
 
221
class TestSftpPermissions(TestCaseWithSFTPServer):
 
222
 
 
223
    def test_new_files(self):
 
224
        if sys.platform == 'win32':
 
225
            raise TestSkipped('chmod has no effect on win32')
 
226
        # Though it would be nice to test that SFTP to a server
 
227
        # which does support chmod has the right effect
 
228
 
 
229
        from bzrlib.transport.sftp import SFTPTransport
 
230
 
 
231
        # We don't actually use it directly, we just want to
 
232
        # keep the connection open, since StubSFTPServer only
 
233
        # allows 1 connection
 
234
        _transport = SFTPTransport(self._sftp_url)
 
235
 
 
236
        os.mkdir('local')
 
237
        t_local = WorkingTree.create_standalone('local')
 
238
        b_local = t_local.branch
 
239
        open('local/a', 'wb').write('foo\n')
 
240
        t_local.add('a')
 
241
        t_local.commit('foo')
 
242
 
 
243
        # Delete them because we are modifying the filesystem underneath them
 
244
        del b_local, t_local 
 
245
        chmod_r('local/.bzr', 0644, 0755)
 
246
        check_mode_r(self, 'local/.bzr', 0644, 0755)
 
247
 
 
248
        b_local = Branch.open(u'local')
 
249
        t_local = b_local.working_tree()
 
250
        assertEqualMode(self, 0755, b_local._dir_mode)
 
251
        assertEqualMode(self, 0644, b_local._file_mode)
 
252
 
 
253
        os.mkdir('sftp')
 
254
        sftp_url = self.get_remote_url('sftp')
 
255
        b_sftp = Branch.create(sftp_url)
 
256
 
 
257
        b_sftp.pull(b_local)
 
258
        del b_sftp
 
259
        chmod_r('sftp/.bzr', 0644, 0755)
 
260
        check_mode_r(self, 'sftp/.bzr', 0644, 0755)
 
261
 
 
262
        b_sftp = Branch.open(sftp_url)
 
263
        assertEqualMode(self, 0755, b_sftp._dir_mode)
 
264
        assertEqualMode(self, 0644, b_sftp._file_mode)
 
265
 
 
266
        open('local/a', 'wb').write('foo2\n')
 
267
        t_local.commit('foo2')
 
268
        b_sftp.pull(b_local)
 
269
        # The mode should be maintained after commit
 
270
        check_mode_r(self, 'sftp/.bzr', 0644, 0755)
 
271
 
 
272
        open('local/b', 'wb').write('new b\n')
 
273
        t_local.add('b')
 
274
        t_local.commit('new b')
 
275
        b_sftp.pull(b_local)
 
276
        check_mode_r(self, 'sftp/.bzr', 0644, 0755)
 
277
 
 
278
        del b_sftp
 
279
        # Recursively update the modes of all files
 
280
        chmod_r('sftp/.bzr', 0664, 0775)
 
281
        check_mode_r(self, 'sftp/.bzr', 0664, 0775)
 
282
 
 
283
        b_sftp = Branch.open(sftp_url)
 
284
        assertEqualMode(self, 0775, b_sftp._dir_mode)
 
285
        assertEqualMode(self, 0664, b_sftp._file_mode)
 
286
 
 
287
        open('local/a', 'wb').write('foo3\n')
 
288
        t_local.commit('foo3')
 
289
        b_sftp.pull(b_local)
 
290
        check_mode_r(self, 'sftp/.bzr', 0664, 0775)
 
291
 
 
292
        open('local/c', 'wb').write('new c\n')
 
293
        t_local.add('c')
 
294
        t_local.commit('new c')
 
295
        b_sftp.pull(b_local)
 
296
        check_mode_r(self, 'sftp/.bzr', 0664, 0775)
 
297
 
 
298
    def test_sftp_server_modes(self):
 
299
        if sys.platform == 'win32':
 
300
            raise TestSkipped('chmod has no effect on win32')
 
301
 
 
302
        umask = 0022
 
303
        original_umask = os.umask(umask)
 
304
 
 
305
        try:
 
306
            from bzrlib.transport.sftp import SFTPTransport
 
307
            t = SFTPTransport(self._sftp_url)
 
308
            # Direct access should be masked by umask
 
309
            t._sftp_open_exclusive('a', mode=0666).write('foo\n')
 
310
            self.assertTransportMode(t, 'a', 0666 &~umask)
 
311
 
 
312
            # but Transport overrides umask
 
313
            t.put('b', 'txt', mode=0666)
 
314
            self.assertTransportMode(t, 'b', 0666)
 
315
 
 
316
            t._sftp.mkdir('c', mode=0777)
 
317
            self.assertTransportMode(t, 'c', 0777 &~umask)
 
318
 
 
319
            t.mkdir('d', mode=0777)
 
320
            self.assertTransportMode(t, 'd', 0777)
 
321
        finally:
 
322
            os.umask(original_umask)