1 """
2 Test cases for LDIF directory tree writing/reading.
3 """
4
5 from twisted.trial import unittest
6 import os, random, errno, shutil, sets
7 from ldaptor import ldiftree, entry, delta, testutil
8 from ldaptor.entry import BaseLDAPEntry
9 from ldaptor.protocols.ldap import ldaperrors, ldifprotocol
10
12 f = file(path, 'w')
13 f.write(content)
14 f.close()
15
18 r = self.__listdir(*args, **kwargs)
19 random.shuffle(r)
20 return r
21
23 self.__listdir = os.listdir
24 os.listdir = self.randomListdir
25
27 os.listdir = self.__listdir
28
29 -class Dir2LDIF(RandomizeListdirMixin, unittest.TestCase):
31 self.tree = self.mktemp()
32 os.mkdir(self.tree)
33 com = os.path.join(self.tree, 'dc=com.dir')
34 os.mkdir(com)
35 example = os.path.join(com, 'dc=example.dir')
36 os.mkdir(example)
37 writeFile(os.path.join(example, 'cn=foo.ldif'),
38 """\
39 dn: cn=foo,dc=example,dc=com
40 cn: foo
41 objectClass: top
42
43 """)
44 writeFile(os.path.join(example, 'cn=bad-two-entries.ldif'),
45 """\
46 dn: cn=bad-two-entries,dc=example,dc=com
47 cn: bad-two-entries
48 objectClass: top
49
50 dn: cn=more,dc=example,dc=com
51 cn: more
52 objectClass: top
53
54 """)
55 writeFile(os.path.join(example, 'cn=bad-missing-end.ldif'),
56 """\
57 dn: cn=bad-missing-end,dc=example,dc=com
58 cn: bad-missing-end
59 objectClass: top
60 """)
61 writeFile(os.path.join(example, 'cn=bad-empty.ldif'), '')
62 writeFile(os.path.join(example, 'cn=bad-only-newline.ldif'), '\n')
63 sales = os.path.join(example, 'ou=Sales.dir')
64 os.mkdir(sales)
65 writeFile(os.path.join(sales, 'cn=sales-thingie.ldif'),
66 """\
67 dn: cn=sales-thingie,ou=Sales,dc=example,dc=com
68 cn: sales-thingie
69 objectClass: top
70
71 """)
72
74 want = BaseLDAPEntry(dn='cn=foo,dc=example,dc=com',
75 attributes={
76 'objectClass': ['top'],
77 'cn': ['foo'],
78 })
79 d = ldiftree.get(self.tree, want.dn)
80 d.addCallback(self.failUnlessEqual, want)
81 return d
82
84 os.chmod(os.path.join(self.tree,
85 'dc=com.dir',
86 'dc=example.dir',
87 'cn=foo.ldif'),
88 0)
89 d = ldiftree.get(self.tree, 'cn=foo,dc=example,dc=com')
90 def eb(fail):
91 fail.trap(IOError)
92 self.assertEquals(fail.value.errno, errno.EACCES)
93 d.addCallbacks(testutil.mustRaise, eb)
94 return d
95
96 if os.getuid() == 0:
97 testNoAccess.skip = "Can't test as root"
98
100 d = ldiftree.get(self.tree, dn)
101 def eb(fail):
102 fail.trap(exceptionClass)
103 d.addCallbacks(testutil.mustRaise, eb)
104 return d
105
110
115
120
125
127 want = BaseLDAPEntry(dn='cn=sales-thingie,ou=Sales,dc=example,dc=com',
128 attributes={
129 'objectClass': ['top'],
130 'cn': ['sales-thingie'],
131 })
132 d = ldiftree.get(self.tree, want.dn)
133 d.addCallback(self.failUnlessEqual, want)
134 return d
135
136 -class LDIF2Dir(RandomizeListdirMixin, unittest.TestCase):
138 self.tree = self.mktemp()
139 os.mkdir(self.tree)
140 com = os.path.join(self.tree, 'dc=com.dir')
141 os.mkdir(com)
142 example = os.path.join(com, 'dc=example.dir')
143 os.mkdir(example)
144 writeFile(os.path.join(example, 'cn=pre-existing.ldif'),
145 """\
146 dn: cn=pre-existing,dc=example,dc=com
147 cn: pre-existing
148 objectClass: top
149
150 """)
151 writeFile(os.path.join(example, 'ou=OrgUnit.ldif'),
152 """\
153 dn: ou=OrgUnit,dc=example,dc=com
154 ou: OrgUnit
155 objectClass: organizationalUnit
156
157 """)
158
168
170 path = os.path.join(self.tree, 'dc=com.dir', 'dc=example.dir', 'cn=foo.ldif')
171 self.failUnless(os.path.isfile(path))
172 self.failUnlessEqual(file(path).read(),
173 """\
174 dn: cn=foo,dc=example,dc=com
175 objectClass: top
176 cn: foo
177
178 """)
179
189
191 path = os.path.join(self.tree, 'dc=com.dir', 'dc=example.dir',
192 'ou=OrgUnit.dir', 'cn=create-me.ldif')
193 self.failUnless(os.path.isfile(path))
194 self.failUnlessEqual(file(path).read(),
195 """\
196 dn: cn=create-me,ou=OrgUnit,dc=example,dc=com
197 objectClass: top
198 cn: create-me
199
200 """)
201
203 e = BaseLDAPEntry(dn='cn=create-me,ou=OrgUnit,dc=example,dc=com',
204 attributes={
205 'objectClass': ['top'],
206 'cn': ['create-me'],
207 })
208 dirpath = os.path.join(self.tree, 'dc=com.dir', 'dc=example.dir',
209 'ou=OrgUnit.dir')
210 os.mkdir(dirpath)
211 d = ldiftree.put(self.tree, e)
212 d.addCallback(self._cb_testDirExists, dirpath)
213 return d
214
216 path = os.path.join(dirpath, 'cn=create-me.ldif')
217 self.failUnless(os.path.isfile(path))
218 self.failUnlessEqual(file(path).read(),
219 """\
220 dn: cn=create-me,ou=OrgUnit,dc=example,dc=com
221 objectClass: top
222 cn: create-me
223
224 """)
225
236
238 raise unittest.FailTest('Should have raised an exception.')
241
251
253 path = os.path.join(self.tree, 'dc=org.ldif')
254 self.failUnless(os.path.isfile(path))
255 self.failUnlessEqual(file(path).read(),
256 """\
257 dn: dc=org
258 objectClass: dcObject
259 dc: org
260
261 """)
262
263
264 -class Tree(RandomizeListdirMixin, unittest.TestCase):
265
266
268 self.tree = self.mktemp()
269 os.mkdir(self.tree)
270 com = os.path.join(self.tree, 'dc=com.dir')
271 os.mkdir(com)
272 example = os.path.join(com, 'dc=example.dir')
273 os.mkdir(example)
274 meta = os.path.join(example, 'ou=metasyntactic.dir')
275 os.mkdir(meta)
276 writeFile(os.path.join(example, 'ou=metasyntactic.ldif'),
277 """\
278 dn: ou=metasyntactic,dc=example,dc=com
279 objectClass: a
280 objectClass: b
281 ou: metasyntactic
282
283 """)
284 foo = os.path.join(meta, 'cn=foo.dir')
285 writeFile(os.path.join(meta, 'cn=foo.ldif'),
286 """\
287 dn: cn=foo,ou=metasyntactic,dc=example,dc=com
288 objectClass: a
289 objectClass: b
290 cn: foo
291
292 """)
293 bar = os.path.join(meta, 'cn=bar.dir')
294 writeFile(os.path.join(meta, 'cn=bar.ldif'),
295 """\
296 dn: cn=bar,ou=metasyntactic,dc=example,dc=com
297 objectClass: a
298 objectClass: b
299 cn: bar
300
301 """)
302 empty = os.path.join(example, 'ou=empty.dir')
303 writeFile(os.path.join(example, 'ou=empty.ldif'),
304 """\
305 dn: ou=empty,dc=example,dc=com
306 objectClass: a
307 objectClass: b
308 ou: empty
309
310 """)
311 oneChild = os.path.join(example, 'ou=oneChild.dir')
312 os.mkdir(oneChild)
313 writeFile(os.path.join(example, 'ou=oneChild.ldif'),
314 """\
315 dn: ou=oneChild,dc=example,dc=com
316 objectClass: a
317 objectClass: b
318 ou: oneChild
319
320 """)
321 theChild = os.path.join(oneChild, 'cn=theChild.dir')
322 writeFile(os.path.join(oneChild, 'cn=theChild.ldif'),
323 """\
324 dn: cn=theChild,ou=oneChild,dc=example,dc=com
325 objectClass: a
326 objectClass: b
327 cn: theChild
328
329 """)
330 self.root = ldiftree.LDIFTreeEntry(self.tree)
331 self.example = ldiftree.LDIFTreeEntry(example, 'dc=example,dc=com')
332 self.empty = ldiftree.LDIFTreeEntry(empty, 'ou=empty,dc=example,dc=com')
333 self.meta = ldiftree.LDIFTreeEntry(meta, 'ou=metasyntactic,dc=example,dc=com')
334 self.foo = ldiftree.LDIFTreeEntry(foo, 'cn=foo,ou=metasyntactic,dc=example,dc=com')
335 self.bar = ldiftree.LDIFTreeEntry(bar, 'cn=bar,ou=metasyntactic,dc=example,dc=com')
336 self.oneChild = ldiftree.LDIFTreeEntry(oneChild, 'ou=oneChild,dc=example,dc=com')
337 self.theChild = ldiftree.LDIFTreeEntry(theChild, 'cn=theChild,ou=oneChild,dc=example,dc=com')
338
340 d = self.empty.children()
341 def cb(children):
342 self.assertEquals(children, [])
343 d.addCallback(cb)
344 return d
345
350
352 self.assertEquals(len(children), 1)
353 got = [e.dn for e in children]
354 want = ['cn=theChild,ou=oneChild,dc=example,dc=com']
355 got.sort()
356 want.sort()
357 self.assertEquals(got, want)
358
360 """Test that .children() returns a copy of the data so that modifying it does not affect behaviour."""
361 d = self.oneChild.children()
362 d.addCallback(self._cb_test_children_repeat_1)
363 return d
364
373
375 self.assertEquals(len(children2), 1)
376
381
383 self.assertEquals(len(children), 2)
384 want = [
385 'cn=foo,ou=metasyntactic,dc=example,dc=com',
386 'cn=bar,ou=metasyntactic,dc=example,dc=com',
387 ]
388 got = [e.dn for e in children]
389 got.sort()
390 want.sort()
391 self.assertEquals(got, want)
392
398
400 self.assertIdentical(r, None)
401 self.assertEquals(len(children), 2)
402 want = [
403 'cn=foo,ou=metasyntactic,dc=example,dc=com',
404 'cn=bar,ou=metasyntactic,dc=example,dc=com',
405 ]
406 got = [e.dn for e in children]
407 got.sort()
408 want.sort()
409 self.assertEquals(got, want)
410
412 os.chmod(self.meta.path, 0300)
413 d = self.meta.children()
414 def eb(fail):
415 fail.trap(OSError)
416 self.assertEquals(fail.value.errno, errno.EACCES)
417 os.chmod(self.meta.path, 0755)
418 d.addCallbacks(testutil.mustRaise, eb)
419 return d
420
421 if os.getuid() == 0:
422 test_children_noAccess_dir_noRead.skip = "Can't test as root"
423
425 os.chmod(self.meta.path, 0600)
426 d = self.meta.children()
427 def eb(fail):
428 fail.trap(IOError)
429 self.assertEquals(fail.value.errno, errno.EACCES)
430 os.chmod(self.meta.path, 0755)
431 d.addCallbacks(testutil.mustRaise, eb)
432 return d
433
434 if os.getuid() == 0:
435 test_children_noAccess_dir_noExec.skip = "Can't test as root"
436
438 os.chmod(os.path.join(self.meta.path, 'cn=foo.ldif'), 0)
439 d = self.meta.children()
440 def eb(fail):
441 fail.trap(IOError)
442 self.assertEquals(fail.value.errno, errno.EACCES)
443 d.addCallbacks(testutil.mustRaise, eb)
444 return d
445
446 if os.getuid() == 0:
447 test_children_noAccess_file.skip = "Can't test as root"
448
459
461 self.assertEquals(len(children), 1)
462 got = [e.dn for e in children]
463 want = [
464 'a=b,ou=empty,dc=example,dc=com',
465 ]
466 got.sort()
467 want.sort()
468 self.assertEquals(got, want)
469
478
480 self.assertEquals(self.foo.parent(), self.meta)
481 self.assertEquals(self.meta.parent(), self.example)
482 self.assertEquals(self.root.parent(), None)
483
484
489
491 self.assertEquals(len(entries), 1)
492
497
499 got = results
500 want = [
501 self.oneChild,
502 self.theChild,
503 ]
504 self.assertEquals(got, want)
505
511
513 self.assertEquals(r, None)
514
515 want = [
516 self.oneChild,
517 self.theChild,
518 ]
519 self.assertEquals(got, want)
520
525
527 got = results
528 want = [
529 self.example,
530 self.oneChild,
531 self.theChild,
532 self.empty,
533 self.meta,
534 self.bar,
535 self.foo,
536 ]
537 got.sort()
538 want.sort()
539 self.assertEquals(got, want)
540
546
548 self.assertEquals(r, None)
549
550 want = [
551 self.example,
552 self.oneChild,
553 self.theChild,
554 self.empty,
555 self.meta,
556 self.bar,
557 self.foo,
558 ]
559 got.sort()
560 want.sort()
561 self.assertEquals(got, want)
562
569 d.addCallbacks(testutil.mustRaise, eb)
570 return d
571
578 d.addCallbacks(testutil.mustRaise, eb)
579 return d
580
587 d.addCallbacks(testutil.mustRaise, eb)
588
590 writeFile(os.path.join(self.example.path,
591 'cn=bad-two-entries.ldif'),
592 """\
593 dn: cn=bad-two-entries,dc=example,dc=com
594 cn: bad-two-entries
595 objectClass: top
596
597 dn: cn=more,dc=example,dc=com
598 cn: more
599 objectClass: top
600
601 """)
602 self.assertRaises(
603 ldiftree.LDIFTreeEntryContainsMultipleEntries,
604 self.example.lookup,
605 'cn=bad-two-entries,dc=example,dc=com')
606
615
621
623 self.assertEquals(r, self.bar)
624
629 d.addCallbacks(testutil.mustRaise, eb)
630 return d
631
636 d.addCallbacks(testutil.mustRaise, eb)
637 return d
638
643
649
651 self.assertEquals(r, [self.bar])
652
657
663
665 self.assertEquals(r, [self.foo])
666
671 d.addCallbacks(testutil.mustRaise, eb)
672 return d
673
675 self.foo.setPassword('s3krit', salt='\xf2\x4a')
676 self.failUnless('userPassword' in self.foo)
677 self.assertEquals(self.foo['userPassword'],
678 ['{SSHA}0n/Iw1NhUOKyaI9gm9v5YsO3ZInySg=='])
679
681 self.foo.setPassword('s3krit')
682 self.failUnless('userPassword' in self.foo)
683 d = self.foo.bind('s3krit')
684 d.addCallback(self.assertIdentical, self.foo)
685 d.addCallback(lambda _: self.foo.bind('s4krit'))
686 def eb(fail):
687 fail.trap(ldaperrors.LDAPInvalidCredentials)
688 d.addCallbacks(testutil.mustRaise, eb)
689 return d
690
692 d = self.root.diffTree(self.root)
693 d.addCallback(self.assertEquals, [])
694 return d
695
703
713 d.addCallback(cb1)
714
715 def cb2(r):
716 d = self.root.diffTree(other)
717 d.addCallback(self.assertEquals, [delta.AddOp(r)])
718 return d
719 d.addCallback(cb2)
720 return d
721
722
724 otherDir = self.mktemp()
725 shutil.copytree(self.tree, otherDir)
726 other = ldiftree.LDIFTreeEntry(otherDir)
727
728 d = other.lookup('ou=empty,dc=example,dc=com')
729 def cb1(otherEmpty):
730 return otherEmpty.delete()
731 d.addCallback(cb1)
732 def cb2(dummy):
733 return self.root.diffTree(other)
734 d.addCallback(cb2)
735 def cb3(got):
736 self.assertEquals(got, [delta.DeleteOp(self.empty)])
737 d.addCallback(cb3)
738 return d
739
741 otherDir = self.mktemp()
742 shutil.copytree(self.tree, otherDir)
743 other = ldiftree.LDIFTreeEntry(otherDir)
744
745 d = other.lookup('ou=empty,dc=example,dc=com')
746 def cb1(otherEmpty):
747 otherEmpty['foo'] = ['bar']
748 return otherEmpty.commit()
749 d.addCallback(cb1)
750
751 def cb2(dummy):
752 return self.root.diffTree(other)
753 d.addCallback(cb2)
754
755 def cb3(got):
756 self.assertEquals(got, [
757 delta.ModifyOp(self.empty.dn,
758 [delta.Add('foo', ['bar'])],
759 ),
760 ])
761 d.addCallback(cb3)
762 return d
763
764
766 d = self.empty.move('ou=moved,dc=example,dc=com')
767 def getChildren(dummy):
768 return self.example.children()
769 d.addCallback(getChildren)
770 d.addCallback(sets.Set)
771 d.addCallback(self.assertEquals, sets.Set([
772 self.meta,
773 BaseLDAPEntry(
774 dn='ou=moved,dc=example,dc=com',
775 attributes={ 'objectClass': ['a', 'b'],
776 'ou': ['moved'],
777 }),
778 self.oneChild,
779 ]))
780 return d
781
783 d = self.meta.move('ou=moved,dc=example,dc=com')
784 def getChildren(dummy):
785 return self.example.children()
786 d.addCallback(getChildren)
787 d.addCallback(sets.Set)
788 d.addCallback(self.assertEquals, sets.Set([
789 BaseLDAPEntry(dn='ou=moved,dc=example,dc=com',
790 attributes={ 'objectClass': ['a', 'b'],
791 'ou': ['moved'],
792 }),
793 self.empty,
794 self.oneChild,
795 ]))
796 return d
797
798
800 d = self.empty.move('ou=moved,ou=oneChild,dc=example,dc=com')
801 def getChildren(dummy):
802 return self.example.children()
803 d.addCallback(getChildren)
804 d.addCallback(sets.Set)
805 d.addCallback(self.assertEquals, sets.Set([
806 self.meta,
807 self.oneChild,
808 ]))
809 def getChildren2(dummy):
810 return self.oneChild.children()
811 d.addCallback(getChildren2)
812 d.addCallback(sets.Set)
813 d.addCallback(self.assertEquals, sets.Set([
814 self.theChild,
815 BaseLDAPEntry(
816 dn='ou=moved,ou=oneChild,dc=example,dc=com',
817 attributes={ 'objectClass': ['a', 'b'],
818 'ou': ['moved'],
819 }),
820 ]))
821 return d
822
824 d = self.meta.move('ou=moved,ou=oneChild,dc=example,dc=com')
825 def getChildren(dummy):
826 return self.example.children()
827 d.addCallback(getChildren)
828 d.addCallback(sets.Set)
829 d.addCallback(self.assertEquals, sets.Set([
830 self.empty,
831 self.oneChild,
832 ]))
833 def getChildren2(dummy):
834 return self.oneChild.children()
835 d.addCallback(getChildren2)
836 d.addCallback(sets.Set)
837 d.addCallback(self.assertEquals, sets.Set([
838 self.theChild,
839 BaseLDAPEntry(dn='ou=moved,ou=oneChild,dc=example,dc=com',
840 attributes={ 'objectClass': ['a', 'b'],
841 'ou': ['moved'],
842 }),
843 ]))
844 return d
845