~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_permissions.py

Merge from integration.

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
 
 
42
 
 
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):
 
48
        for d in dirs:
 
49
            p = os.path.join(root, d)
 
50
            os.chmod(p, dir_mode)
 
51
        for f in files:
 
52
            p = os.path.join(root, f)
 
53
            os.chmod(p, file_mode)
 
54
 
 
55
 
 
56
def check_mode_r(test, base, file_mode, dir_mode, include_base=True):
 
57
    """Check that all permissions match
 
58
 
 
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
 
64
    """
 
65
    assert os.path.isdir(base)
 
66
    t = get_transport(".")
 
67
    if include_base:
 
68
        test.assertTransportMode(t, base, dir_mode)
 
69
    for root, dirs, files in os.walk(base):
 
70
        for d in dirs:
 
71
            p = os.path.join(root, d)
 
72
            test.assertTransportMode(t, p, dir_mode)
 
73
        for f in files:
 
74
            p = os.path.join(root, f)
 
75
            test.assertTransportMode(t, p, file_mode)
 
76
 
 
77
 
 
78
def assertEqualMode(test, mode, mode_test):
 
79
    test.assertEqual(mode, mode_test,
 
80
                     'mode mismatch %o != %o' % (mode, mode_test))
 
81
 
 
82
 
 
83
class TestPermissions(TestCaseInTempDir):
 
84
 
 
85
    def test_new_files(self):
 
86
        if sys.platform == 'win32':
 
87
            raise TestSkipped('chmod has no effect on win32')
 
88
 
 
89
        b = Branch.initialize(u'.')
 
90
        t = b.working_tree()
 
91
        open('a', 'wb').write('foo\n')
 
92
        t.add('a')
 
93
        t.commit('foo')
 
94
 
 
95
        # Delete them because we are modifying the filesystem underneath them
 
96
        del b, t 
 
97
        chmod_r('.bzr', 0644, 0755)
 
98
        check_mode_r(self, '.bzr', 0644, 0755)
 
99
 
 
100
        b = Branch.open('.')
 
101
        t = b.working_tree()
 
102
        assertEqualMode(self, 0755, b._dir_mode)
 
103
        assertEqualMode(self, 0644, b._file_mode)
 
104
 
 
105
        # Modifying a file shouldn't break the permissions
 
106
        open('a', 'wb').write('foo2\n')
 
107
        t.commit('foo2')
 
108
        # The mode should be maintained after commit
 
109
        check_mode_r(self, '.bzr', 0644, 0755)
 
110
 
 
111
        # Adding a new file should maintain the permissions
 
112
        open('b', 'wb').write('new b\n')
 
113
        t.add('b')
 
114
        t.commit('new b')
 
115
        check_mode_r(self, '.bzr', 0644, 0755)
 
116
 
 
117
        del b, t
 
118
        # Recursively update the modes of all files
 
119
        chmod_r('.bzr', 0664, 0775)
 
120
        check_mode_r(self, '.bzr', 0664, 0775)
 
121
        b = Branch.open('.')
 
122
        t = b.working_tree()
 
123
        assertEqualMode(self, 0775, b._dir_mode)
 
124
        assertEqualMode(self, 0664, b._file_mode)
 
125
 
 
126
        open('a', 'wb').write('foo3\n')
 
127
        t.commit('foo3')
 
128
        check_mode_r(self, '.bzr', 0664, 0775)
 
129
 
 
130
        open('c', 'wb').write('new c\n')
 
131
        t.add('c')
 
132
        t.commit('new c')
 
133
        check_mode_r(self, '.bzr', 0664, 0775)
 
134
 
 
135
        # Test the group sticky bit
 
136
        del b, t
 
137
        # Recursively update the modes of all files
 
138
        chmod_r('.bzr', 0664, 02775)
 
139
        check_mode_r(self, '.bzr', 0664, 02775)
 
140
        b = Branch.open('.')
 
141
        t = b.working_tree()
 
142
        assertEqualMode(self, 02775, b._dir_mode)
 
143
        assertEqualMode(self, 0664, b._file_mode)
 
144
 
 
145
        open('a', 'wb').write('foo4\n')
 
146
        t.commit('foo4')
 
147
        check_mode_r(self, '.bzr', 0664, 02775)
 
148
 
 
149
        open('d', 'wb').write('new d\n')
 
150
        t.add('d')
 
151
        t.commit('new d')
 
152
        check_mode_r(self, '.bzr', 0664, 02775)
 
153
 
 
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
 
157
        import bzrlib.branch
 
158
        try:
 
159
            b = Branch.initialize(u'.')
 
160
            self.assertNotEqual(None, b._dir_mode)
 
161
            self.assertNotEqual(None, b._file_mode)
 
162
 
 
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)
 
167
 
 
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)
 
172
 
 
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)
 
177
 
 
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)
 
182
        finally:
 
183
            bzrlib.branch.BzrBranch._set_dir_mode = True
 
184
            bzrlib.branch.BzrBranch._set_file_mode = True
 
185
 
 
186
    def test_new_branch(self):
 
187
        if sys.platform == 'win32':
 
188
            raise TestSkipped('chmod has no effect on win32')
 
189
 
 
190
        os.mkdir('a')
 
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)
 
195
 
 
196
        os.mkdir('b')
 
197
        os.chmod('b', 02777)
 
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)
 
202
 
 
203
        os.mkdir('c')
 
204
        os.chmod('c', 02750)
 
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)
 
209
 
 
210
        os.mkdir('d')
 
211
        os.chmod('d', 0700)
 
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)
 
216
 
 
217
 
 
218
class TestSftpPermissions(TestCaseWithSFTPServer):
 
219
 
 
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
 
225
 
 
226
        from bzrlib.transport.sftp import SFTPTransport
 
227
 
 
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)
 
232
 
 
233
        os.mkdir('local')
 
234
        b_local = Branch.initialize(u'local')
 
235
        t_local = b_local.working_tree()
 
236
        open('local/a', 'wb').write('foo\n')
 
237
        t_local.add('a')
 
238
        t_local.commit('foo')
 
239
 
 
240
        # Delete them because we are modifying the filesystem underneath them
 
241
        del b_local, t_local 
 
242
        chmod_r('local/.bzr', 0644, 0755)
 
243
        check_mode_r(self, 'local/.bzr', 0644, 0755)
 
244
 
 
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)
 
249
 
 
250
        os.mkdir('sftp')
 
251
        sftp_url = self.get_remote_url('sftp')
 
252
        b_sftp = Branch.initialize(sftp_url)
 
253
 
 
254
        b_sftp.pull(b_local)
 
255
        del b_sftp
 
256
        chmod_r('sftp/.bzr', 0644, 0755)
 
257
        check_mode_r(self, 'sftp/.bzr', 0644, 0755)
 
258
 
 
259
        b_sftp = Branch.open(sftp_url)
 
260
        assertEqualMode(self, 0755, b_sftp._dir_mode)
 
261
        assertEqualMode(self, 0644, b_sftp._file_mode)
 
262
 
 
263
        open('local/a', 'wb').write('foo2\n')
 
264
        t_local.commit('foo2')
 
265
        b_sftp.pull(b_local)
 
266
        # The mode should be maintained after commit
 
267
        check_mode_r(self, 'sftp/.bzr', 0644, 0755)
 
268
 
 
269
        open('local/b', 'wb').write('new b\n')
 
270
        t_local.add('b')
 
271
        t_local.commit('new b')
 
272
        b_sftp.pull(b_local)
 
273
        check_mode_r(self, 'sftp/.bzr', 0644, 0755)
 
274
 
 
275
        del b_sftp
 
276
        # Recursively update the modes of all files
 
277
        chmod_r('sftp/.bzr', 0664, 0775)
 
278
        check_mode_r(self, 'sftp/.bzr', 0664, 0775)
 
279
 
 
280
        b_sftp = Branch.open(sftp_url)
 
281
        assertEqualMode(self, 0775, b_sftp._dir_mode)
 
282
        assertEqualMode(self, 0664, b_sftp._file_mode)
 
283
 
 
284
        open('local/a', 'wb').write('foo3\n')
 
285
        t_local.commit('foo3')
 
286
        b_sftp.pull(b_local)
 
287
        check_mode_r(self, 'sftp/.bzr', 0664, 0775)
 
288
 
 
289
        open('local/c', 'wb').write('new c\n')
 
290
        t_local.add('c')
 
291
        t_local.commit('new c')
 
292
        b_sftp.pull(b_local)
 
293
        check_mode_r(self, 'sftp/.bzr', 0664, 0775)
 
294
 
 
295
    def test_sftp_server_modes(self):
 
296
        if sys.platform == 'win32':
 
297
            raise TestSkipped('chmod has no effect on win32')
 
298
 
 
299
        umask = 0022
 
300
        original_umask = os.umask(umask)
 
301
 
 
302
        try:
 
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)
 
308
 
 
309
            # but Transport overrides umask
 
310
            t.put('b', 'txt', mode=0666)
 
311
            self.assertTransportMode(t, 'b', 0666)
 
312
 
 
313
            t._sftp.mkdir('c', mode=0777)
 
314
            self.assertTransportMode(t, 'c', 0777 &~umask)
 
315
 
 
316
            t.mkdir('d', mode=0777)
 
317
            self.assertTransportMode(t, 'd', 0777)
 
318
        finally:
 
319
            os.umask(original_umask)
 
320
 
 
321