Implement the acl_extended_* functions
[pylibacl.git] / test / test_acls.py
1 #
2 #
3
4 import unittest
5 import os
6 import tempfile
7
8 import posix1e
9 from posix1e import *
10
11 TEST_DIR=os.environ.get("TESTDIR", ".")
12
13 BASIC_ACL_TEXT="u::rw,g::r,o::-"
14
15 def _skip_test(fn):
16     """Wrapper to skip a test"""
17     new_fn = lambda x: None
18     new_fn.__doc__ = "SKIPPED %s" % fn.__doc__
19     return new_fn
20
21
22 def has_ext(extension):
23     """Decorator to skip tests based on platform support"""
24     if not extension:
25         return _skip_test
26     else:
27         return lambda x: x
28
29
30 class aclTest:
31     """Support functions ACLs"""
32
33     def setUp(self):
34         """set up function"""
35         self.rmfiles = []
36         self.rmdirs = []
37
38     def tearDown(self):
39         """tear down function"""
40         for fname in self.rmfiles:
41             os.unlink(fname)
42         for dname in self.rmdirs:
43             os.rmdir(dname)
44
45     def _getfile(self):
46         """create a temp file"""
47         fh, fname = tempfile.mkstemp(".test", "xattr-", TEST_DIR)
48         self.rmfiles.append(fname)
49         return fh, fname
50
51     def _getdir(self):
52         """create a temp dir"""
53         dname = tempfile.mkdtemp(".test", "xattr-", TEST_DIR)
54         self.rmdirs.append(dname)
55         return dname
56
57     def _getsymlink(self):
58         """create a symlink"""
59         fh, fname = self._getfile()
60         os.close(fh)
61         os.unlink(fname)
62         os.symlink(fname + ".non-existent", fname)
63         return fname
64
65
66 class LoadTests(aclTest, unittest.TestCase):
67     """Load/create tests"""
68     def testFromFile(self):
69         """Test loading ACLs from a file"""
70         _, fname = self._getfile()
71         acl1 = posix1e.ACL(file=fname)
72         self.failUnless(acl1.valid(), "ACL read from file should be valid")
73
74     def testFromDir(self):
75         """Test loading ACLs from a directory"""
76         dname = self._getdir()
77         acl1 = posix1e.ACL(file=dname)
78         acl2 = posix1e.ACL(filedef=dname)
79         self.failUnless(acl1.valid(),
80                         "ACL read from directory should be valid")
81         # default ACLs might or might not be valid; missing ones are
82         # not valid, so we don't test acl2 for validity
83
84     def testFromFd(self):
85         """Test loading ACLs from a file descriptor"""
86         fd, _ = self._getfile()
87         acl1 = posix1e.ACL(fd=fd)
88         self.failUnless(acl1.valid(), "ACL read from fd should be valid")
89
90     def testFromEmpty(self):
91         """Test creating an empty ACL"""
92         acl1 = posix1e.ACL()
93         self.failIf(acl1.valid(), "Empty ACL should not be valid")
94
95     def testFromText(self):
96         """Test creating an ACL from text"""
97         acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
98         self.failUnless(acl1.valid(),
99                         "ACL based on standard description should be valid")
100
101 class AclExtensions(aclTest, unittest.TestCase):
102     """ACL extensions checks"""
103
104     @has_ext(HAS_ACL_FROM_MODE)
105     def testFromMode(self):
106         """Test loading ACLs from an octal mode"""
107         acl1 = posix1e.ACL(mode=0644)
108         self.failUnless(acl1.valid(),
109                         "ACL created via octal mode shoule be valid")
110
111     @has_ext(HAS_ACL_CHECK)
112     def testAclCheck(self):
113         """Test the acl_check method"""
114         acl1 = posix1e.ACL(text=BASIC_ACL_TEXT)
115         self.failIf(acl1.check(), "ACL is not valid")
116         acl2 = posix1e.ACL()
117         self.failUnless(acl2.check(), "Empty ACL should not be valid")
118
119     @has_ext(HAS_EXTENDED_CHECK)
120     def testExtended(self):
121         """Test the acl_extended function"""
122         fd, fname = self._getfile()
123         basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
124         basic_acl.applyto(fd)
125         for item in fd, fname:
126             self.failIf(has_extended(item),
127                         "A simple ACL should not be reported as extended")
128         enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
129         self.failUnless(enhanced_acl.valid(),
130                         "Failure to build an extended ACL")
131         enhanced_acl.applyto(fd)
132         for item in fd, fname:
133             self.failUnless(has_extended(item),
134                         "An extended ACL should be reported as such")
135
136
137 class WriteTests(aclTest, unittest.TestCase):
138     """Write tests"""
139
140     def testDeleteDefault(self):
141         """Test removing the default ACL"""
142         dname = self._getdir()
143         posix1e.delete_default(dname)
144
145     def testReapply(self):
146         """Test re-applying an ACL"""
147         fd, fname = self._getfile()
148         acl1 = posix1e.ACL(fd=fd)
149         acl1.applyto(fd)
150         acl1.applyto(fname)
151         dname = self._getdir()
152         acl2 = posix1e.ACL(file=fname)
153         acl2.applyto(dname)
154
155
156 class ModificationTests(aclTest, unittest.TestCase):
157     """ACL modification tests"""
158
159     @has_ext(HAS_ACL_ENTRY)
160     def testAppend(self):
161         """Test append a new Entry to the ACL"""
162         acl = posix1e.ACL()
163         e = acl.append()
164         e.tag_type = posix1e.ACL_OTHER
165         acl.calc_mask()
166
167     @has_ext(HAS_ACL_ENTRY)
168     def testDelete(self):
169         """Test delete Entry from the ACL"""
170         acl = posix1e.ACL()
171         e = acl.append()
172         e.tag_type = posix1e.ACL_OTHER
173         acl.calc_mask()
174         acl.delete_entry(e)
175         acl.calc_mask()
176
177     @has_ext(HAS_ACL_ENTRY)
178     def testDoubleEntries(self):
179         """Test double entries"""
180         acl = posix1e.ACL(text=BASIC_ACL_TEXT)
181         self.failUnless(acl.valid(), "ACL is not valid")
182         for tag_type in (posix1e.ACL_USER_OBJ, posix1e.ACL_GROUP_OBJ,
183                          posix1e.ACL_OTHER):
184             e = acl.append()
185             e.tag_type = tag_type
186             e.permset.clear()
187             self.failIf(acl.valid(),
188                         "ACL containing duplicate entries should not be valid")
189             acl.delete_entry(e)
190
191     @has_ext(HAS_ACL_ENTRY)
192     def testMultipleGoodEntries(self):
193         """Test multiple valid entries"""
194         acl = posix1e.ACL(text=BASIC_ACL_TEXT)
195         self.failUnless(acl.valid(), "ACL is not valid")
196         for tag_type in (posix1e.ACL_USER,
197                          posix1e.ACL_GROUP):
198             for obj_id in range(5):
199                 e = acl.append()
200                 e.tag_type = tag_type
201                 e.qualifier = obj_id
202                 e.permset.clear()
203                 acl.calc_mask()
204                 self.failUnless(acl.valid(),
205                                "ACL should be able to hold multiple"
206                                 " user/group entries")
207
208     @has_ext(HAS_ACL_ENTRY)
209     def testMultipleBadEntries(self):
210         """Test multiple invalid entries"""
211         acl = posix1e.ACL(text=BASIC_ACL_TEXT)
212         self.failUnless(acl.valid(), "ACL built from standard description"
213                         " should be valid")
214         for tag_type in (posix1e.ACL_USER,
215                          posix1e.ACL_GROUP):
216             e1 = acl.append()
217             e1.tag_type = tag_type
218             e1.qualifier = 0
219             e1.permset.clear()
220             acl.calc_mask()
221             self.failUnless(acl.valid(), "ACL should be able to add a"
222                             " user/group entry")
223             e2 = acl.append()
224             e2.tag_type = tag_type
225             e2.qualifier = 0
226             e2.permset.clear()
227             acl.calc_mask()
228             self.failIf(acl.valid(), "ACL should not validate when"
229                         " containing two duplicate entries")
230             acl.delete_entry(e1)
231             acl.delete_entry(e2)
232
233     @has_ext(HAS_ACL_ENTRY)
234     def testPermset(self):
235         """Test permissions"""
236         acl = posix1e.ACL()
237         e = acl.append()
238         ps = e.permset
239         ps.clear()
240         pmap = {
241             posix1e.ACL_READ: "read",
242             posix1e.ACL_WRITE: "write",
243             posix1e.ACL_EXECUTE: "execute",
244             }
245         for perm in pmap:
246             self.failIf(ps.test(perm), "Empty permission set should not"
247                         " have permission '%s'" % pmap[perm])
248             ps.add(perm)
249             self.failUnless(ps.test(perm), "Permission '%s' should exist"
250                         " after addition" % pmap[perm])
251             ps.delete(perm)
252             self.failIf(ps.test(perm), "Permission '%s' should not exist"
253                         " after deletion" % pmap[perm])
254
255
256 if __name__ == "__main__":
257     unittest.main()