]> git.k1024.org Git - pylibacl.git/blob - tests/test_acls.py
Add test for refcount leak
[pylibacl.git] / tests / test_acls.py
1 #
2 #
3
4 """Unittests for the posix1e module"""
5
6 #  Copyright (C) 2002-2009, 2012 Iustin Pop <iusty@k1024.org>
7 #
8 #  This library is free software; you can redistribute it and/or
9 #  modify it under the terms of the GNU Lesser General Public
10 #  License as published by the Free Software Foundation; either
11 #  version 2.1 of the License, or (at your option) any later version.
12 #
13 #  This library is distributed in the hope that it will be useful,
14 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 #  Lesser General Public License for more details.
17 #
18 #  You should have received a copy of the GNU Lesser General Public
19 #  License along with this library; if not, write to the Free Software
20 #  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 #  02110-1301  USA
22
23
24 import unittest
25 import os
26 import tempfile
27 import sys
28
29 import posix1e
30 from posix1e import *
31
32 TEST_DIR = os.environ.get("TESTDIR", ".")
33
34 BASIC_ACL_TEXT = "u::rw,g::r,o::-"
35
36 # This is to workaround python 2/3 differences at syntactic level
37 # (which can't be worked around via if's)
38 M0500 = 320 # octal 0500
39 M0644 = 420 # octal 0644
40 M0755 = 493 # octal 755
41
42 def _skip_test(fn):
43     """Wrapper to skip a test"""
44     new_fn = lambda x: None
45     new_fn.__doc__ = "SKIPPED %s" % fn.__doc__
46     return new_fn
47
48
49 def has_ext(extension):
50     """Decorator to skip tests based on platform support"""
51     if not extension:
52         return _skip_test
53     else:
54         return lambda x: x
55
56
57 class aclTest:
58     """Support functions ACLs"""
59
60     def setUp(self):
61         """set up function"""
62         self.rmfiles = []
63         self.rmdirs = []
64
65     def tearDown(self):
66         """tear down function"""
67         for fname in self.rmfiles:
68             os.unlink(fname)
69         for dname in self.rmdirs:
70             os.rmdir(dname)
71
72     def _getfile(self):
73         """create a temp file"""
74         fh, fname = tempfile.mkstemp(".test", "xattr-", TEST_DIR)
75         self.rmfiles.append(fname)
76         return fh, fname
77
78     def _getdir(self):
79         """create a temp dir"""
80         dname = tempfile.mkdtemp(".test", "xattr-", TEST_DIR)
81         self.rmdirs.append(dname)
82         return dname
83
84     def _getsymlink(self):
85         """create a symlink"""
86         fh, fname = self._getfile()
87         os.close(fh)
88         os.unlink(fname)
89         os.symlink(fname + ".non-existent", fname)
90         return fname
91
92
93 class LoadTests(aclTest, unittest.TestCase):
94     """Load/create tests"""
95     def testFromFile(self):
96         """Test loading ACLs from a file"""
97         _, fname = self._getfile()
98         acl1 = posix1e.ACL(file=fname)
99         self.failUnless(acl1.valid(), "ACL read from file should be valid")
100
101     def testFromDir(self):
102         """Test loading ACLs from a directory"""
103         dname = self._getdir()
104         acl1 = posix1e.ACL(file=dname)
105         acl2 = posix1e.ACL(filedef=dname)
106         self.failUnless(acl1.valid(),
107                         "ACL read from directory should be valid")
108         # default ACLs might or might not be valid; missing ones are
109         # not valid, so we don't test acl2 for validity
110
111     def testFromFd(self):
112         """Test loading ACLs from a file descriptor"""
113         fd, _ = self._getfile()
114         acl1 = posix1e.ACL(fd=fd)
115         self.failUnless(acl1.valid(), "ACL read from fd should be valid")
116
117     def testFromEmpty(self):
118         """Test creating an empty ACL"""
119         acl1 = posix1e.ACL()
120         self.failIf(acl1.valid(), "Empty ACL should not be valid")
121
122     def testFromText(self):
123         """Test creating an ACL from text"""
124         acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
125         self.failUnless(acl1.valid(),
126                         "ACL based on standard description should be valid")
127
128 class AclExtensions(aclTest, unittest.TestCase):
129     """ACL extensions checks"""
130
131     @has_ext(HAS_ACL_FROM_MODE)
132     def testFromMode(self):
133         """Test loading ACLs from an octal mode"""
134         acl1 = posix1e.ACL(mode=M0644)
135         self.failUnless(acl1.valid(),
136                         "ACL created via octal mode shoule be valid")
137
138     @has_ext(HAS_ACL_CHECK)
139     def testAclCheck(self):
140         """Test the acl_check method"""
141         acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
142         self.failIf(acl1.check(), "ACL is not valid")
143         acl2 = posix1e.ACL()
144         self.failUnless(acl2.check(), "Empty ACL should not be valid")
145
146     @has_ext(HAS_EXTENDED_CHECK)
147     def testExtended(self):
148         """Test the acl_extended function"""
149         fd, fname = self._getfile()
150         basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
151         basic_acl.applyto(fd)
152         for item in fd, fname:
153             self.failIf(has_extended(item),
154                         "A simple ACL should not be reported as extended")
155         enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
156         self.failUnless(enhanced_acl.valid(),
157                         "Failure to build an extended ACL")
158         enhanced_acl.applyto(fd)
159         for item in fd, fname:
160             self.failUnless(has_extended(item),
161                         "An extended ACL should be reported as such")
162
163     @has_ext(HAS_EQUIV_MODE)
164     def testEquivMode(self):
165         """Test the equiv_mode function"""
166         if HAS_ACL_FROM_MODE:
167             for mode in M0644, M0755:
168                 acl = posix1e.ACL(mode=mode)
169                 self.failUnlessEqual(acl.equiv_mode(), mode)
170         acl = posix1e.ACL(text="u::rw,g::r,o::r")
171         self.failUnlessEqual(acl.equiv_mode(), M0644)
172         acl = posix1e.ACL(text="u::rx,g::-,o::-")
173         self.failUnlessEqual(acl.equiv_mode(), M0500)
174
175
176 class WriteTests(aclTest, unittest.TestCase):
177     """Write tests"""
178
179     def testDeleteDefault(self):
180         """Test removing the default ACL"""
181         dname = self._getdir()
182         posix1e.delete_default(dname)
183
184     def testReapply(self):
185         """Test re-applying an ACL"""
186         fd, fname = self._getfile()
187         acl1 = posix1e.ACL(fd=fd)
188         acl1.applyto(fd)
189         acl1.applyto(fname)
190         dname = self._getdir()
191         acl2 = posix1e.ACL(file=fname)
192         acl2.applyto(dname)
193
194
195 class ModificationTests(aclTest, unittest.TestCase):
196     """ACL modification tests"""
197
198     @has_ext(HAS_ACL_ENTRY)
199     def testAppend(self):
200         """Test append a new Entry to the ACL"""
201         acl = posix1e.ACL()
202         e = acl.append()
203         e.tag_type = posix1e.ACL_OTHER
204         acl.calc_mask()
205         str_format = str(e)
206         ref_cnt = sys.getrefcount(str_format)
207         # FIXME: hardcoded value for the max ref count... but I've
208         # seen it overflow on bad reference counting, so it's better
209         # to be safe
210         if ref_cnt < 2 or ref_cnt > 1024:
211            self.fail("Wrong reference count, expected 2-1024 and got %d" %
212                      ref_cnt)
213
214     @has_ext(HAS_ACL_ENTRY)
215     def testDelete(self):
216         """Test delete Entry from the ACL"""
217         acl = posix1e.ACL()
218         e = acl.append()
219         e.tag_type = posix1e.ACL_OTHER
220         acl.calc_mask()
221         acl.delete_entry(e)
222         acl.calc_mask()
223
224     @has_ext(HAS_ACL_ENTRY)
225     def testDoubleEntries(self):
226         """Test double entries"""
227         acl = posix1e.ACL(text=BASIC_ACL_TEXT)
228         self.failUnless(acl.valid(), "ACL is not valid")
229         for tag_type in (posix1e.ACL_USER_OBJ, posix1e.ACL_GROUP_OBJ,
230                          posix1e.ACL_OTHER):
231             e = acl.append()
232             e.tag_type = tag_type
233             e.permset.clear()
234             self.failIf(acl.valid(),
235                         "ACL containing duplicate entries should not be valid")
236             acl.delete_entry(e)
237
238     @has_ext(HAS_ACL_ENTRY)
239     def testMultipleGoodEntries(self):
240         """Test multiple valid entries"""
241         acl = posix1e.ACL(text=BASIC_ACL_TEXT)
242         self.failUnless(acl.valid(), "ACL is not valid")
243         for tag_type in (posix1e.ACL_USER,
244                          posix1e.ACL_GROUP):
245             for obj_id in range(5):
246                 e = acl.append()
247                 e.tag_type = tag_type
248                 e.qualifier = obj_id
249                 e.permset.clear()
250                 acl.calc_mask()
251                 self.failUnless(acl.valid(),
252                                "ACL should be able to hold multiple"
253                                 " user/group entries")
254
255     @has_ext(HAS_ACL_ENTRY)
256     def testMultipleBadEntries(self):
257         """Test multiple invalid entries"""
258         acl = posix1e.ACL(text=BASIC_ACL_TEXT)
259         self.failUnless(acl.valid(), "ACL built from standard description"
260                         " should be valid")
261         for tag_type in (posix1e.ACL_USER,
262                          posix1e.ACL_GROUP):
263             e1 = acl.append()
264             e1.tag_type = tag_type
265             e1.qualifier = 0
266             e1.permset.clear()
267             acl.calc_mask()
268             self.failUnless(acl.valid(), "ACL should be able to add a"
269                             " user/group entry")
270             e2 = acl.append()
271             e2.tag_type = tag_type
272             e2.qualifier = 0
273             e2.permset.clear()
274             acl.calc_mask()
275             self.failIf(acl.valid(), "ACL should not validate when"
276                         " containing two duplicate entries")
277             acl.delete_entry(e1)
278             acl.delete_entry(e2)
279
280     @has_ext(HAS_ACL_ENTRY)
281     def testPermset(self):
282         """Test permissions"""
283         acl = posix1e.ACL()
284         e = acl.append()
285         ps = e.permset
286         ps.clear()
287         pmap = {
288             posix1e.ACL_READ: "read",
289             posix1e.ACL_WRITE: "write",
290             posix1e.ACL_EXECUTE: "execute",
291             }
292         for perm in pmap:
293             self.failIf(ps.test(perm), "Empty permission set should not"
294                         " have permission '%s'" % pmap[perm])
295             ps.add(perm)
296             self.failUnless(ps.test(perm), "Permission '%s' should exist"
297                         " after addition" % pmap[perm])
298             ps.delete(perm)
299             self.failIf(ps.test(perm), "Permission '%s' should not exist"
300                         " after deletion" % pmap[perm])
301
302
303 if __name__ == "__main__":
304     unittest.main()