3 # Unix SMB/CIFS implementation.
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2005-2007
5 # Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
7 # This is a Python port of the original in testprogs/ejs/samba3sam.js
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 3 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
27 from samba import Ldb, substitute_var
28 from samba.tests import LdbTestCase, TestCaseInTempDir
30 datadir = os.path.join(os.path.dirname(__file__), "../../../../../testdata/samba3")
32 class Samba3SamTestCase(TestCaseInTempDir):
33 def setup_data(self, obj, ldif):
34 self.assertTrue(ldif is not None)
35 obj.db.add_ldif(substitute_var(ldif, obj.substvars))
37 def setup_modules(self, ldb, s3, s4):
41 @FROM: """ + s4.basedn + """
42 @TO: sambaDomainName=TESTS,""" + s3.basedn + """
45 @LIST: rootdse,paged_results,server_sort,extended_dn,asq,samldb,password_hash,operational,objectguid,rdn_name,samba3sam,partition
48 partition: """ + s4.basedn + ":" + s4.url + """
49 partition: """ + s3.basedn + ":" + s3.url + """
50 replicateEntries: @SUBCLASSES
51 replicateEntries: @ATTRIBUTES
52 replicateEntries: @INDEXLIST
56 def _test_s3sam_search(self, ldb):
57 print "Looking up by non-mapped attribute"
58 msg = ldb.search(expression="(cn=Administrator)")
59 self.assertEquals(len(msg), 1)
60 self.assertEquals(msg[0]["cn"], "Administrator")
62 print "Looking up by mapped attribute"
63 msg = ldb.search(expression="(name=Backup Operators)")
64 self.assertEquals(len(msg), 1)
65 self.assertEquals(msg[0]["name"], "Backup Operators")
67 print "Looking up by old name of renamed attribute"
68 msg = ldb.search(expression="(displayName=Backup Operators)")
69 self.assertEquals(len(msg), 0)
71 print "Looking up mapped entry containing SID"
72 msg = ldb.search(expression="(cn=Replicator)")
73 self.assertEquals(len(msg), 1)
75 self.assertEquals(str(msg[0].dn), "cn=Replicator,ou=Groups,dc=vernstok,dc=nl")
76 self.assertEquals(msg[0]["objectSid"], "S-1-5-21-4231626423-2410014848-2360679739-552")
78 print "Checking mapping of objectClass"
79 oc = set(msg[0]["objectClass"])
80 self.assertTrue(oc is not None)
82 self.assertEquals(oc[i] == "posixGroup" or oc[i], "group")
84 print "Looking up by objectClass"
85 msg = ldb.search(expression="(|(objectClass=user)(cn=Administrator))")
86 self.assertEquals(len(msg), 2)
87 for i in range(len(msg)):
88 self.assertEquals((str(msg[i].dn), "unixName=Administrator,ou=Users,dc=vernstok,dc=nl") or
89 (str(msg[i].dn) == "unixName=nobody,ou=Users,dc=vernstok,dc=nl"))
92 def _test_s3sam_modify(ldb, s3):
93 print "Adding a record that will be fallbacked"
99 showInAdvancedViewOnly: TRUE
102 print "Checking for existence of record (local)"
103 # TODO: This record must be searched in the local database, which is currently only supported for base searches
104 # msg = ldb.search(expression="(cn=Foo)", ['foo','blah','cn','showInAdvancedViewOnly')]
105 # TODO: Actually, this version should work as well but doesn't...
108 attrs = ['foo','blah','cn','showInAdvancedViewOnly']
109 msg = ldb.search(expression="(cn=Foo)", base="cn=Foo", scope=ldb.LDB_SCOPE_BASE, attrs=attrs)
110 self.assertEquals(len(msg), 1)
111 self.assertEquals(msg[0]["showInAdvancedViewOnly"], "TRUE")
112 self.assertEquals(msg[0]["foo"], "bar")
113 self.assertEquals(msg[0]["blah"], "Blie")
115 print "Adding record that will be mapped"
117 dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl
120 sambaUnicodePwd: geheim
124 print "Checking for existence of record (remote)"
125 msg = ldb.search(expression="(unixName=bin)", attrs=['unixName','cn','dn', 'sambaUnicodePwd'])
126 self.assertEquals(len(msg), 1)
127 self.assertEquals(msg[0]["cn"], "Niemand")
128 self.assertEquals(msg[0]["sambaUnicodePwd"], "geheim")
130 print "Checking for existence of record (local && remote)"
131 msg = ldb.search(expression="(&(unixName=bin)(sambaUnicodePwd=geheim))",
132 attrs=['unixName','cn','dn', 'sambaUnicodePwd'])
133 self.assertEquals(len(msg), 1) # TODO: should check with more records
134 self.assertEquals(msg[0]["cn"], "Niemand")
135 self.assertEquals(msg[0]["unixName"], "bin")
136 self.assertEquals(msg[0]["sambaUnicodePwd"], "geheim")
138 print "Checking for existence of record (local || remote)"
139 msg = ldb.search(expression="(|(unixName=bin)(sambaUnicodePwd=geheim))",
140 attrs=['unixName','cn','dn', 'sambaUnicodePwd'])
141 print "got " + len(msg) + " replies"
142 self.assertEquals(len(msg), 1) # TODO: should check with more records
143 self.assertEquals(msg[0]["cn"], "Niemand")
144 self.assertEquals(msg[0]["unixName"] == "bin" or msg[0]["sambaUnicodePwd"], "geheim")
146 print "Checking for data in destination database"
147 msg = s3.db.search("(cn=Niemand)")
148 self.assertTrue(len(msg) >= 1)
149 self.assertEquals(msg[0]["sambaSID"], "S-1-5-21-4231626423-2410014848-2360679739-2001")
150 self.assertEquals(msg[0]["displayName"], "Niemand")
152 print "Adding attribute..."
154 dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl
160 print "Checking whether changes are still there..."
161 msg = ldb.search(expression="(cn=Niemand)")
162 self.assertTrue(len(msg) >= 1)
163 self.assertEquals(msg[0]["cn"], "Niemand")
164 self.assertEquals(msg[0]["description"], "Blah")
166 print "Modifying attribute..."
168 dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl
174 print "Checking whether changes are still there..."
175 msg = ldb.search(expression="(cn=Niemand)")
176 self.assertTrue(len(msg) >= 1)
177 self.assertEquals(msg[0]["description"], "Blie")
179 print "Deleting attribute..."
181 dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl
186 print "Checking whether changes are no longer there..."
187 msg = ldb.search(expression="(cn=Niemand)")
188 self.assertTrue(len(msg) >= 1)
189 self.assertEquals(msg[0]["description"], undefined)
191 print "Renaming record..."
192 ldb.rename("cn=Niemand,cn=Users,dc=vernstok,dc=nl", "cn=Niemand2,cn=Users,dc=vernstok,dc=nl")
194 print "Checking whether DN has changed..."
195 msg = ldb.search(expression="(cn=Niemand2)")
196 self.assertEquals(len(msg), 1)
197 self.assertEquals(str(msg[0].dn), "cn=Niemand2,cn=Users,dc=vernstok,dc=nl")
199 print "Deleting record..."
200 ldb.delete("cn=Niemand2,cn=Users,dc=vernstok,dc=nl")
202 print "Checking whether record is gone..."
203 msg = ldb.search(expression="(cn=Niemand2)")
204 self.assertEquals(len(msg), 0)
206 def _test_map_search(self, ldb, s3, s4):
207 print "Running search tests on mapped data"
209 dn: """ + "sambaDomainName=TESTS,""" + s3.basedn + """
210 objectclass: sambaDomain
212 sambaSID: S-1-5-21-4231626423-2410014848-2360679739
214 sambaDomainName: TESTS"""
215 s3.db.add_ldif(substitute_var(ldif, s3.substvars))
217 print "Add a set of split records"
219 dn: """ + s4.dn("cn=X") + """
228 objectSid: S-1-5-21-4231626423-2410014848-2360679739-552
229 primaryGroupID: 1-5-21-4231626423-2410014848-2360679739-512
231 dn: """ + s4.dn("cn=Y") + """
241 dn: """ + s4.dn("cn=Z") + """
252 ldb.add_ldif(substitute_var(ldif, s4.substvars))
254 print "Add a set of remote records"
257 dn: """ + s3.dn("cn=A") + """
258 objectClass: posixAccount
261 sambaBadPasswordCount: x
264 sambaSID: S-1-5-21-4231626423-2410014848-2360679739-552
265 sambaPrimaryGroupSID: S-1-5-21-4231626423-2410014848-2360679739-512
267 dn: """ + s3.dn("cn=B") + """
271 sambaBadPasswordCount: x
275 dn: """ + s3.dn("cn=C") + """
279 sambaBadPasswordCount: y
283 s3.add_ldif(substitute_var(ldif, s3.substvars))
285 print "Testing search by DN"
287 # Search remote record by local DN
289 attrs = ["dnsHostName", "lastLogon"]
290 res = ldb.search(dn, scope=ldb.SCOPE_BASE, attrs=attrs)
291 self.assertEquals(len(res), 1)
292 self.assertEquals(str(str(res[0].dn)), dn)
293 self.assertEquals(res[0]["dnsHostName"], undefined)
294 self.assertEquals(res[0]["lastLogon"], "x")
296 # Search remote record by remote DN
298 attrs = ["dnsHostName", "lastLogon", "sambaLogonTime"]
299 res = s3.db.search(dn, scope=ldb.SCOPE_BASE, attrs=attrs)
300 self.assertEquals(len(res), 1)
301 self.assertEquals(str(str(res[0].dn)), dn)
302 self.assertEquals(res[0]["dnsHostName"], undefined)
303 self.assertEquals(res[0]["lastLogon"], undefined)
304 self.assertEquals(res[0]["sambaLogonTime"], "x")
306 # Search split record by local DN
308 attrs = ["dnsHostName", "lastLogon"]
309 res = ldb.search(dn, scope=ldb.SCOPE_BASE, attrs=attrs)
310 self.assertEquals(len(res), 1)
311 self.assertEquals(str(str(res[0].dn)), dn)
312 self.assertEquals(res[0]["dnsHostName"], "x")
313 self.assertEquals(res[0]["lastLogon"], "x")
315 # Search split record by remote DN
317 attrs = ["dnsHostName", "lastLogon", "sambaLogonTime"]
318 res = s3.db.search(dn, scope=ldb.SCOPE_BASE, attrs=attrs)
319 self.assertEquals(len(res), 1)
320 self.assertEquals(str(str(res[0].dn)), dn)
321 self.assertEquals(res[0]["dnsHostName"], undefined)
322 self.assertEquals(res[0]["lastLogon"], undefined)
323 self.assertEquals(res[0]["sambaLogonTime"], "x")
325 print "Testing search by attribute"
327 # Search by ignored attribute
328 attrs = ["dnsHostName", "lastLogon"]
329 res = ldb.search(expression="(revision=x)", scope=ldb.SCOPE_DEFAULT, attrs=attrs)
330 self.assertEquals(len(res), 2)
331 self.assertEquals(str(str(res[0].dn)), s4.dn("cn=Y"))
332 self.assertEquals(res[0]["dnsHostName"], "y")
333 self.assertEquals(res[0]["lastLogon"], "y")
334 self.assertEquals(str(str(res[1].dn)), s4.dn("cn=X"))
335 self.assertEquals(res[1]["dnsHostName"], "x")
336 self.assertEquals(res[1]["lastLogon"], "x")
338 # Search by kept attribute
339 attrs = ["dnsHostName", "lastLogon"]
340 res = ldb.search(expression="(description=y)", scope=ldb.SCOPE_DEFAULT, attrs=attrs)
341 self.assertEquals(len(res), 2)
342 self.assertEquals(str(str(res[0].dn)), s4.dn("cn=Z"))
343 self.assertEquals(res[0]["dnsHostName"], "z")
344 self.assertEquals(res[0]["lastLogon"], "z")
345 self.assertEquals(str(str(res[1].dn)), s4.dn("cn=C"))
346 self.assertEquals(res[1]["dnsHostName"], undefined)
347 self.assertEquals(res[1]["lastLogon"], "z")
349 # Search by renamed attribute
350 attrs = ["dnsHostName", "lastLogon"]
351 res = ldb.search(expression="(badPwdCount=x)", scope=ldb.SCOPE_DEFAULT, attrs=attrs)
352 self.assertEquals(len(res), 2)
353 self.assertEquals(str(res[0].dn), s4.dn("cn=B"))
354 self.assertEquals(res[0]["dnsHostName"], undefined)
355 self.assertEquals(res[0]["lastLogon"], "y")
356 self.assertEquals(str(res[1].dn), s4.dn("cn=A"))
357 self.assertEquals(res[1]["dnsHostName"], undefined)
358 self.assertEquals(res[1]["lastLogon"], "x")
360 # Search by converted attribute
361 attrs = ["dnsHostName", "lastLogon", "objectSid"]
363 # Using the SID directly in the parse tree leads to conversion
364 # errors, letting the search fail with no results.
365 #res = ldb.search("(objectSid=S-1-5-21-4231626423-2410014848-2360679739-552)", NULL, ldb. SCOPE_DEFAULT, attrs)
366 res = ldb.search(expression="(objectSid=*)", attrs=attrs)
367 self.assertEquals(len(res), 3)
368 self.assertEquals(str(res[0].dn), s4.dn("cn=X"))
369 self.assertEquals(res[0]["dnsHostName"], "x")
370 self.assertEquals(res[0]["lastLogon"], "x")
371 self.assertEquals(res[0]["objectSid"], "S-1-5-21-4231626423-2410014848-2360679739-552")
372 self.assertEquals(str(res[1].dn), s4.dn("cn=A"))
373 self.assertEquals(res[1]["dnsHostName"], undefined)
374 self.assertEquals(res[1]["lastLogon"], "x")
375 self.assertEquals(res[1]["objectSid"], "S-1-5-21-4231626423-2410014848-2360679739-552")
377 # Search by generated attribute
378 # In most cases, this even works when the mapping is missing
379 # a `convert_operator' by enumerating the remote db.
380 attrs = ["dnsHostName", "lastLogon", "primaryGroupID"]
381 res = ldb.search(expression="(primaryGroupID=512)", attrs=attrs)
382 self.assertEquals(len(res), 1)
383 self.assertEquals(str(res[0].dn), s4.dn("cn=A"))
384 self.assertEquals(res[0]["dnsHostName"], undefined)
385 self.assertEquals(res[0]["lastLogon"], "x")
386 self.assertEquals(res[0]["primaryGroupID"], "512")
388 # TODO: There should actually be two results, A and X. The
389 # primaryGroupID of X seems to get corrupted somewhere, and the
390 # objectSid isn't available during the generation of remote (!) data,
391 # which can be observed with the following search. Also note that Xs
392 # objectSid seems to be fine in the previous search for objectSid... */
393 #res = ldb.search(expression="(primaryGroupID=*)", NULL, ldb. SCOPE_DEFAULT, attrs)
394 #print len(res) + " results found"
395 #for i in range(len(res)):
396 # for (obj in res[i]) {
397 # print obj + ": " + res[i][obj]
402 # Search by remote name of renamed attribute */
403 attrs = ["dnsHostName", "lastLogon"]
404 res = ldb.search(expression="(sambaBadPasswordCount=*)", attrs=attrs)
405 self.assertEquals(len(res), 0)
407 # Search by objectClass
408 attrs = ["dnsHostName", "lastLogon", "objectClass"]
409 res = ldb.search(expression="(objectClass=user)", attrs=attrs)
410 self.assertEquals(len(res), 2)
411 self.assertEquals(str(res[0].dn), s4.dn("cn=X"))
412 self.assertEquals(res[0]["dnsHostName"], "x")
413 self.assertEquals(res[0]["lastLogon"], "x")
414 self.assertTrue(res[0]["objectClass"] is not None)
415 self.assertEquals(res[0]["objectClass"][0], "user")
416 self.assertEquals(str(res[1].dn), s4.dn("cn=A"))
417 self.assertEquals(res[1]["dnsHostName"], undefined)
418 self.assertEquals(res[1]["lastLogon"], "x")
419 self.assertTrue(res[1]["objectClass"] is not None)
420 self.assertEquals(res[1]["objectClass"][0], "user")
422 # Prove that the objectClass is actually used for the search
423 res = ldb.search(expression="(|(objectClass=user)(badPwdCount=x))", attrs=attrs)
424 self.assertEquals(len(res), 3)
425 self.assertEquals(str(res[0].dn), s4.dn("cn=B"))
426 self.assertEquals(res[0]["dnsHostName"], undefined)
427 self.assertEquals(res[0]["lastLogon"], "y")
428 self.assertTrue(res[0]["objectClass"] is not None)
429 for oc in set(res[0]["objectClass"]):
430 self.assertEquals(oc, "user")
431 self.assertEquals(str(res[1].dn), s4.dn("cn=X"))
432 self.assertEquals(res[1]["dnsHostName"], "x")
433 self.assertEquals(res[1]["lastLogon"], "x")
434 self.assertTrue(res[1]["objectClass"] is not None)
435 self.assertEquals(res[1]["objectClass"][0], "user")
436 self.assertEquals(str(res[2].dn), s4.dn("cn=A"))
437 self.assertEquals(res[2]["dnsHostName"], undefined)
438 self.assertEquals(res[2]["lastLogon"], "x")
439 self.assertTrue(res[2]["objectClass"] is not None)
440 self.assertEquals(res[2]["objectClass"][0], "user")
442 print "Testing search by parse tree"
444 # Search by conjunction of local attributes
445 attrs = ["dnsHostName", "lastLogon"]
446 res = ldb.search(expression="(&(codePage=x)(revision=x))", attrs=attrs)
447 self.assertEquals(len(res), 2)
448 self.assertEquals(str(res[0].dn), s4.dn("cn=Y"))
449 self.assertEquals(res[0]["dnsHostName"], "y")
450 self.assertEquals(res[0]["lastLogon"], "y")
451 self.assertEquals(str(res[1].dn), s4.dn("cn=X"))
452 self.assertEquals(res[1]["dnsHostName"], "x")
453 self.assertEquals(res[1]["lastLogon"], "x")
455 # Search by conjunction of remote attributes
456 attrs = ["dnsHostName", "lastLogon"]
457 res = ldb.search(expression="(&(lastLogon=x)(description=x))", attrs=attrs)
458 self.assertEquals(len(res), 2)
459 self.assertEquals(str(res[0].dn), s4.dn("cn=X"))
460 self.assertEquals(res[0]["dnsHostName"], "x")
461 self.assertEquals(res[0]["lastLogon"], "x")
462 self.assertEquals(str(res[1].dn), s4.dn("cn=A"))
463 self.assertEquals(res[1]["dnsHostName"], undefined)
464 self.assertEquals(res[1]["lastLogon"], "x")
466 # Search by conjunction of local and remote attribute
467 attrs = ["dnsHostName", "lastLogon"]
468 res = ldb.search(expression="(&(codePage=x)(description=x))", attrs=attrs)
469 self.assertEquals(len(res), 2)
470 self.assertEquals(str(res[0].dn), s4.dn("cn=Y"))
471 self.assertEquals(res[0]["dnsHostName"], "y")
472 self.assertEquals(res[0]["lastLogon"], "y")
473 self.assertEquals(str(res[1].dn), s4.dn("cn=X"))
474 self.assertEquals(res[1]["dnsHostName"], "x")
475 self.assertEquals(res[1]["lastLogon"], "x")
477 # Search by conjunction of local and remote attribute w/o match
478 attrs = ["dnsHostName", "lastLogon"]
479 res = ldb.search(expression="(&(codePage=x)(nextRid=x))", attrs=attrs)
480 self.assertEquals(len(res), 0)
481 res = ldb.search(expression="(&(revision=x)(lastLogon=z))", attrs=attrs)
482 self.assertEquals(len(res), 0)
484 # Search by disjunction of local attributes
485 attrs = ["dnsHostName", "lastLogon"]
486 res = ldb.search(expression="(|(revision=x)(dnsHostName=x))", attrs=attrs)
487 self.assertEquals(len(res), 2)
488 self.assertEquals(str(res[0].dn), s4.dn("cn=Y"))
489 self.assertEquals(res[0]["dnsHostName"], "y")
490 self.assertEquals(res[0]["lastLogon"], "y")
491 self.assertEquals(str(res[1].dn), s4.dn("cn=X"))
492 self.assertEquals(res[1]["dnsHostName"], "x")
493 self.assertEquals(res[1]["lastLogon"], "x")
495 # Search by disjunction of remote attributes
496 attrs = ["dnsHostName", "lastLogon"]
497 res = ldb.search(expression="(|(badPwdCount=x)(lastLogon=x))", attrs=attrs)
498 self.assertEquals(len(res), 3)
499 self.assertEquals(str(res[0].dn), s4.dn("cn=B"))
500 self.assertEquals(res[0]["dnsHostName"], undefined)
501 self.assertEquals(res[0]["lastLogon"], "y")
502 self.assertEquals(str(res[1].dn), s4.dn("cn=X"))
503 self.assertEquals(res[1]["dnsHostName"], "x")
504 self.assertEquals(res[1]["lastLogon"], "x")
505 self.assertEquals(str(res[2].dn), s4.dn("cn=A"))
506 self.assertEquals(res[2]["dnsHostName"], undefined)
507 self.assertEquals(res[2]["lastLogon"], "x")
509 # Search by disjunction of local and remote attribute
510 attrs = ["dnsHostName", "lastLogon"]
511 res = ldb.search(expression="(|(revision=x)(lastLogon=y))", attrs=attrs)
512 self.assertEquals(len(res), 3)
513 self.assertEquals(str(res[0].dn), s4.dn("cn=Y"))
514 self.assertEquals(res[0]["dnsHostName"], "y")
515 self.assertEquals(res[0]["lastLogon"], "y")
516 self.assertEquals(str(res[1].dn), s4.dn("cn=B"))
517 self.assertEquals(res[1]["dnsHostName"], undefined)
518 self.assertEquals(res[1]["lastLogon"], "y")
519 self.assertEquals(str(res[2].dn), s4.dn("cn=X"))
520 self.assertEquals(res[2]["dnsHostName"], "x")
521 self.assertEquals(res[2]["lastLogon"], "x")
523 # Search by disjunction of local and remote attribute w/o match
524 attrs = ["dnsHostName", "lastLogon"]
525 res = ldb.search(expression="(|(codePage=y)(nextRid=z))", attrs=attrs)
526 self.assertEquals(len(res), 0)
528 # Search by negated local attribute
529 attrs = ["dnsHostName", "lastLogon"]
530 res = ldb.search(expression="(!(revision=x))", attrs=attrs)
531 self.assertEquals(len(res), 5)
532 self.assertEquals(str(res[0].dn), s4.dn("cn=B"))
533 self.assertEquals(res[0]["dnsHostName"], undefined)
534 self.assertEquals(res[0]["lastLogon"], "y")
535 self.assertEquals(str(res[1].dn), s4.dn("cn=A"))
536 self.assertEquals(res[1]["dnsHostName"], undefined)
537 self.assertEquals(res[1]["lastLogon"], "x")
538 self.assertEquals(str(res[2].dn), s4.dn("cn=Z"))
539 self.assertEquals(res[2]["dnsHostName"], "z")
540 self.assertEquals(res[2]["lastLogon"], "z")
541 self.assertEquals(str(res[3].dn), s4.dn("cn=C"))
542 self.assertEquals(res[3]["dnsHostName"], undefined)
543 self.assertEquals(res[3]["lastLogon"], "z")
545 # Search by negated remote attribute
546 attrs = ["dnsHostName", "lastLogon"]
547 res = ldb.search(expression="(!(description=x))", attrs=attrs)
548 self.assertEquals(len(res), 3)
549 self.assertEquals(str(res[0].dn), s4.dn("cn=Z"))
550 self.assertEquals(res[0]["dnsHostName"], "z")
551 self.assertEquals(res[0]["lastLogon"], "z")
552 self.assertEquals(str(res[1].dn), s4.dn("cn=C"))
553 self.assertEquals(res[1]["dnsHostName"], undefined)
554 self.assertEquals(res[1]["lastLogon"], "z")
556 # Search by negated conjunction of local attributes
557 attrs = ["dnsHostName", "lastLogon"]
558 res = ldb.search(expression="(!(&(codePage=x)(revision=x)))", attrs=attrs)
559 self.assertEquals(len(res), 5)
560 self.assertEquals(str(res[0].dn), s4.dn("cn=B"))
561 self.assertEquals(res[0]["dnsHostName"], undefined)
562 self.assertEquals(res[0]["lastLogon"], "y")
563 self.assertEquals(str(res[1].dn), s4.dn("cn=A"))
564 self.assertEquals(res[1]["dnsHostName"], undefined)
565 self.assertEquals(res[1]["lastLogon"], "x")
566 self.assertEquals(str(res[2].dn), s4.dn("cn=Z"))
567 self.assertEquals(res[2]["dnsHostName"], "z")
568 self.assertEquals(res[2]["lastLogon"], "z")
569 self.assertEquals(str(res[3].dn), s4.dn("cn=C"))
570 self.assertEquals(res[3]["dnsHostName"], undefined)
571 self.assertEquals(res[3]["lastLogon"], "z")
573 # Search by negated conjunction of remote attributes
574 attrs = ["dnsHostName", "lastLogon"]
575 res = ldb.search(expression="(!(&(lastLogon=x)(description=x)))", attrs=attrs)
576 self.assertEquals(len(res), 5)
577 self.assertEquals(str(res[0].dn), s4.dn("cn=Y"))
578 self.assertEquals(res[0]["dnsHostName"], "y")
579 self.assertEquals(res[0]["lastLogon"], "y")
580 self.assertEquals(str(res[1].dn), s4.dn("cn=B"))
581 self.assertEquals(res[1]["dnsHostName"], undefined)
582 self.assertEquals(res[1]["lastLogon"], "y")
583 self.assertEquals(str(res[2].dn), s4.dn("cn=Z"))
584 self.assertEquals(res[2]["dnsHostName"], "z")
585 self.assertEquals(res[2]["lastLogon"], "z")
586 self.assertEquals(str(res[3].dn), s4.dn("cn=C"))
587 self.assertEquals(res[3]["dnsHostName"], undefined)
588 self.assertEquals(res[3]["lastLogon"], "z")
590 # Search by negated conjunction of local and remote attribute
591 attrs = ["dnsHostName", "lastLogon"]
592 res = ldb.search(expression="(!(&(codePage=x)(description=x)))", attrs=attrs)
593 self.assertEquals(len(res), 5)
594 self.assertEquals(str(res[0].dn), s4.dn("cn=B"))
595 self.assertEquals(res[0]["dnsHostName"], undefined)
596 self.assertEquals(res[0]["lastLogon"], "y")
597 self.assertEquals(str(res[1].dn), s4.dn("cn=A"))
598 self.assertEquals(res[1]["dnsHostName"], undefined)
599 self.assertEquals(res[1]["lastLogon"], "x")
600 self.assertEquals(str(res[2].dn), s4.dn("cn=Z"))
601 self.assertEquals(res[2]["dnsHostName"], "z")
602 self.assertEquals(res[2]["lastLogon"], "z")
603 self.assertEquals(str(res[3].dn), s4.dn("cn=C"))
604 self.assertEquals(res[3]["dnsHostName"], undefined)
605 self.assertEquals(res[3]["lastLogon"], "z")
607 # Search by negated disjunction of local attributes
608 attrs = ["dnsHostName", "lastLogon"]
609 res = ldb.search(expression="(!(|(revision=x)(dnsHostName=x)))", attrs=attrs)
610 self.assertEquals(str(res[0].dn), s4.dn("cn=B"))
611 self.assertEquals(res[0]["dnsHostName"], undefined)
612 self.assertEquals(res[0]["lastLogon"], "y")
613 self.assertEquals(str(res[1].dn), s4.dn("cn=A"))
614 self.assertEquals(res[1]["dnsHostName"], undefined)
615 self.assertEquals(res[1]["lastLogon"], "x")
616 self.assertEquals(str(res[2].dn), s4.dn("cn=Z"))
617 self.assertEquals(res[2]["dnsHostName"], "z")
618 self.assertEquals(res[2]["lastLogon"], "z")
619 self.assertEquals(str(res[3].dn), s4.dn("cn=C"))
620 self.assertEquals(res[3]["dnsHostName"], undefined)
621 self.assertEquals(res[3]["lastLogon"], "z")
623 # Search by negated disjunction of remote attributes
624 attrs = ["dnsHostName", "lastLogon"]
625 res = ldb.search(expression="(!(|(badPwdCount=x)(lastLogon=x)))", attrs=attrs)
626 self.assertEquals(len(res), 4)
627 self.assertEquals(str(res[0].dn), s4.dn("cn=Y"))
628 self.assertEquals(res[0]["dnsHostName"], "y")
629 self.assertEquals(res[0]["lastLogon"], "y")
630 self.assertEquals(str(res[1].dn), s4.dn("cn=Z"))
631 self.assertEquals(res[1]["dnsHostName"], "z")
632 self.assertEquals(res[1]["lastLogon"], "z")
633 self.assertEquals(str(res[2].dn), s4.dn("cn=C"))
634 self.assertEquals(res[2]["dnsHostName"], undefined)
635 self.assertEquals(res[2]["lastLogon"], "z")
637 # Search by negated disjunction of local and remote attribute
638 attrs = ["dnsHostName", "lastLogon"]
639 res = ldb.search(expression="(!(|(revision=x)(lastLogon=y)))", attrs=attrs)
640 self.assertEquals(len(res), 4)
641 self.assertEquals(str(res[0].dn), s4.dn("cn=A"))
642 self.assertEquals(res[0]["dnsHostName"], undefined)
643 self.assertEquals(res[0]["lastLogon"], "x")
644 self.assertEquals(str(res[1].dn), s4.dn("cn=Z"))
645 self.assertEquals(res[1]["dnsHostName"], "z")
646 self.assertEquals(res[1]["lastLogon"], "z")
647 self.assertEquals(str(res[2].dn), s4.dn("cn=C"))
648 self.assertEquals(res[2]["dnsHostName"], undefined)
649 self.assertEquals(res[2]["lastLogon"], "z")
651 print "Search by complex parse tree"
652 attrs = ["dnsHostName", "lastLogon"]
653 res = ldb.search(expression="(|(&(revision=x)(dnsHostName=x))(!(&(description=x)(nextRid=y)))(badPwdCount=y))", attrs=attrs)
654 self.assertEquals(len(res), 6)
655 self.assertEquals(str(res[0].dn), s4.dn("cn=B"))
656 self.assertEquals(res[0]["dnsHostName"], undefined)
657 self.assertEquals(res[0]["lastLogon"], "y")
658 self.assertEquals(str(res[1].dn), s4.dn("cn=X"))
659 self.assertEquals(res[1]["dnsHostName"], "x")
660 self.assertEquals(res[1]["lastLogon"], "x")
661 self.assertEquals(str(res[2].dn), s4.dn("cn=A"))
662 self.assertEquals(res[2]["dnsHostName"], undefined)
663 self.assertEquals(res[2]["lastLogon"], "x")
664 self.assertEquals(str(res[3].dn), s4.dn("cn=Z"))
665 self.assertEquals(res[3]["dnsHostName"], "z")
666 self.assertEquals(res[3]["lastLogon"], "z")
667 self.assertEquals(str(res[4].dn), s4.dn("cn=C"))
668 self.assertEquals(res[4]["dnsHostName"], undefined)
669 self.assertEquals(res[4]["lastLogon"], "z")
672 dns = [s4.dn("cn=%s" % n) for n in ["A","B","C","X","Y","Z"]]
676 def _test_map_modify(self, ldb, s3, s4):
677 print "Running modification tests on mapped data"
679 print "Testing modification of local records"
682 dn = "cn=test,dc=idealx,dc=org"
692 attrs = ["foo", "revision", "description"]
693 res = ldb.search(dn, scope=ldb.SCOPE_BASE, attrs=attrs)
694 self.assertEquals(len(res), 1)
695 self.assertEquals(str(res[0].dn), dn)
696 self.assertEquals(res[0]["foo"], "bar")
697 self.assertEquals(res[0]["revision"], "1")
698 self.assertEquals(res[0]["description"], "test")
699 # Check it's not in the local db
700 res = s4.db.search("(cn=test)", NULL, ldb.SCOPE_DEFAULT, attrs)
701 self.assertEquals(len(res), 0)
702 # Check it's not in the remote db
703 res = s3.db.search("(cn=test)", NULL, ldb.SCOPE_DEFAULT, attrs)
704 self.assertEquals(len(res), 0)
706 # Modify local record
714 ldb.modify_ldif(ldif)
716 res = ldb.search(dn, scope=ldb.SCOPE_BASE, attrs=attrs)
717 self.assertEquals(len(res), 1)
718 self.assertEquals(str(res[0].dn), dn)
719 self.assertEquals(res[0]["foo"], "baz")
720 self.assertEquals(res[0]["revision"], "1")
721 self.assertEquals(res[0]["description"], "foo")
723 # Rename local record
724 dn2 = "cn=toast,dc=idealx,dc=org"
727 res = ldb.search(dn2, scope=ldb.SCOPE_BASE, attrs=attrs)
728 self.assertEquals(len(res), 1)
729 self.assertEquals(str(res[0].dn), dn2)
730 self.assertEquals(res[0]["foo"], "baz")
731 self.assertEquals(res[0]["revision"], "1")
732 self.assertEquals(res[0]["description"], "foo")
734 # Delete local record
737 res = ldb.search(dn2, scope=ldb.SCOPE_BASE)
738 self.assertEquals(len(res), 0)
740 print "Testing modification of remote records"
743 dn = s4.dn("cn=test")
744 dn2 = s3.dn("cn=test")
749 sambaBadPasswordCount: 3
754 attrs = ["description", "sambaBadPasswordCount", "sambaNextRid"]
755 res = s3.db.search("", dn2, ldb.SCOPE_BASE, attrs)
756 self.assertEquals(len(res), 1)
757 self.assertEquals(str(res[0].dn), dn2)
758 self.assertEquals(res[0]["description"], "foo")
759 self.assertEquals(res[0]["sambaBadPasswordCount"], "3")
760 self.assertEquals(res[0]["sambaNextRid"], "1001")
762 attrs = ["description", "badPwdCount", "nextRid"]
763 res = ldb.search(dn, scope=ldb.SCOPE_BASE, attrs=attrs)
764 self.assertEquals(len(res), 1)
765 self.assertEquals(str(res[0].dn), dn)
766 self.assertEquals(res[0]["description"], "foo")
767 self.assertEquals(res[0]["badPwdCount"], "3")
768 self.assertEquals(res[0]["nextRid"], "1001")
770 res = s4.db.search("", dn, ldb.SCOPE_BASE, attrs)
771 self.assertEquals(len(res), 0)
773 # Modify remote data of remote record
781 ldb.modify_ldif(ldif)
783 attrs = ["description", "badPwdCount", "nextRid"]
784 res = ldb.search(dn, scope=ldb.SCOPE_BASE, attrs=attrs)
785 self.assertEquals(len(res), 1)
786 self.assertEquals(str(res[0].dn), dn)
787 self.assertEquals(res[0]["description"], "test")
788 self.assertEquals(res[0]["badPwdCount"], "4")
789 self.assertEquals(res[0]["nextRid"], "1001")
791 attrs = ["description", "sambaBadPasswordCount", "sambaNextRid"]
792 res = s3.db.search("", dn2, ldb.SCOPE_BASE, attrs)
793 self.assertEquals(len(res), 1)
794 self.assertEquals(str(res[0].dn), dn2)
795 self.assertEquals(res[0]["description"], "test")
796 self.assertEquals(res[0]["sambaBadPasswordCount"], "4")
797 self.assertEquals(res[0]["sambaNextRid"], "1001")
799 # Rename remote record
800 dn2 = s4.dn("cn=toast")
804 attrs = ["description", "badPwdCount", "nextRid"]
805 res = ldb.search(dn, scope=ldb.SCOPE_BASE, attrs=attrs)
806 self.assertEquals(len(res), 1)
807 self.assertEquals(str(res[0].dn), dn)
808 self.assertEquals(res[0]["description"], "test")
809 self.assertEquals(res[0]["badPwdCount"], "4")
810 self.assertEquals(res[0]["nextRid"], "1001")
812 dn2 = s3.dn("cn=toast")
813 attrs = ["description", "sambaBadPasswordCount", "sambaNextRid"]
814 res = s3.db.search("", dn2, ldb.SCOPE_BASE, attrs)
815 self.assertEquals(len(res), 1)
816 self.assertEquals(str(res[0].dn), dn2)
817 self.assertEquals(res[0]["description"], "test")
818 self.assertEquals(res[0]["sambaBadPasswordCount"], "4")
819 self.assertEquals(res[0]["sambaNextRid"], "1001")
821 # Delete remote record
824 res = ldb.search(dn, scope=ldb.SCOPE_BASE)
825 self.assertEquals(len(res), 0)
827 res = s3.db.search("", dn2, ldb.SCOPE_BASE)
828 self.assertEquals(len(res), 0)
830 # Add remote record (same as before)
831 dn = s4.dn("cn=test")
832 dn2 = s3.dn("cn=test")
837 sambaBadPasswordCount: 3
842 # Modify local data of remote record
850 ldb.modify_ldif(ldif)
852 attrs = ["revision", "description"]
853 res = ldb.search(dn, scope=ldb.SCOPE_BASE, attrs=attrs)
854 self.assertEquals(len(res), 1)
855 self.assertEquals(str(res[0].dn), dn)
856 self.assertEquals(res[0]["description"], "test")
857 self.assertEquals(res[0]["revision"], "1")
859 res = s3.db.search("", dn2, ldb.SCOPE_BASE, attrs)
860 self.assertEquals(len(res), 1)
861 self.assertEquals(str(res[0].dn), dn2)
862 self.assertEquals(res[0]["description"], "test")
863 self.assertEquals(res[0]["revision"], undefined)
865 res = s4.db.search("", dn, ldb.SCOPE_BASE, attrs)
866 self.assertEquals(len(res), 1)
867 self.assertEquals(str(res[0].dn), dn)
868 self.assertEquals(res[0]["description"], undefined)
869 self.assertEquals(res[0]["revision"], "1")
871 # Delete (newly) split record
874 print "Testing modification of split records"
877 dn = s4.dn("cn=test")
878 dn2 = s3.dn("cn=test")
889 attrs = ["description", "badPwdCount", "nextRid", "revision"]
890 res = ldb.search(dn, scope=ldb.SCOPE_BASE, attrs=attrs)
891 self.assertEquals(len(res), 1)
892 self.assertEquals(str(res[0].dn), dn)
893 self.assertEquals(res[0]["description"], "foo")
894 self.assertEquals(res[0]["badPwdCount"], "3")
895 self.assertEquals(res[0]["nextRid"], "1001")
896 self.assertEquals(res[0]["revision"], "1")
898 res = s4.db.search("", dn, ldb.SCOPE_BASE, attrs)
899 self.assertEquals(len(res), 1)
900 self.assertEquals(str(res[0].dn), dn)
901 self.assertEquals(res[0]["description"], undefined)
902 self.assertEquals(res[0]["badPwdCount"], undefined)
903 self.assertEquals(res[0]["nextRid"], undefined)
904 self.assertEquals(res[0]["revision"], "1")
906 attrs = ["description", "sambaBadPasswordCount", "sambaNextRid", "revision"]
907 res = s3.db.search("", dn2, ldb.SCOPE_BASE, attrs)
908 self.assertEquals(len(res), 1)
909 self.assertEquals(str(res[0].dn), dn2)
910 self.assertEquals(res[0]["description"], "foo")
911 self.assertEquals(res[0]["sambaBadPasswordCount"], "3")
912 self.assertEquals(res[0]["sambaNextRid"], "1001")
913 self.assertEquals(res[0]["revision"], undefined)
915 # Modify of split record
925 ldb.modify_ldif(ldif)
927 attrs = ["description", "badPwdCount", "nextRid", "revision"]
928 res = ldb.search(dn, scope=ldb.SCOPE_BASE, attrs=attrs)
929 self.assertEquals(len(res), 1)
930 self.assertEquals(str(res[0].dn), dn)
931 self.assertEquals(res[0]["description"], "test")
932 self.assertEquals(res[0]["badPwdCount"], "4")
933 self.assertEquals(res[0]["nextRid"], "1001")
934 self.assertEquals(res[0]["revision"], "2")
936 res = s4.db.search("", dn, ldb.SCOPE_BASE, attrs)
937 self.assertEquals(len(res), 1)
938 self.assertEquals(str(res[0].dn), dn)
939 self.assertEquals(res[0]["description"], undefined)
940 self.assertEquals(res[0]["badPwdCount"], undefined)
941 self.assertEquals(res[0]["nextRid"], undefined)
942 self.assertEquals(res[0]["revision"], "2")
944 attrs = ["description", "sambaBadPasswordCount", "sambaNextRid", "revision"]
945 res = s3.db.search("", dn2, ldb.SCOPE_BASE, attrs)
946 self.assertEquals(len(res), 1)
947 self.assertEquals(str(res[0].dn), dn2)
948 self.assertEquals(res[0]["description"], "test")
949 self.assertEquals(res[0]["sambaBadPasswordCount"], "4")
950 self.assertEquals(res[0]["sambaNextRid"], "1001")
951 self.assertEquals(res[0]["revision"], undefined)
953 # Rename split record
954 dn2 = s4.dn("cn=toast")
958 attrs = ["description", "badPwdCount", "nextRid", "revision"]
959 res = ldb.search(dn, scope=ldb.SCOPE_BASE, attrs=attrs)
960 self.assertEquals(len(res), 1)
961 self.assertEquals(str(res[0].dn), dn)
962 self.assertEquals(res[0]["description"], "test")
963 self.assertEquals(res[0]["badPwdCount"], "4")
964 self.assertEquals(res[0]["nextRid"], "1001")
965 self.assertEquals(res[0]["revision"], "2")
967 res = s4.db.search("", dn, ldb.SCOPE_BASE, attrs)
968 self.assertEquals(len(res), 1)
969 self.assertEquals(str(res[0].dn), dn)
970 self.assertEquals(res[0]["description"], undefined)
971 self.assertEquals(res[0]["badPwdCount"], undefined)
972 self.assertEquals(res[0]["nextRid"], undefined)
973 self.assertEquals(res[0]["revision"], "2")
975 dn2 = s3.dn("cn=toast")
976 attrs = ["description", "sambaBadPasswordCount", "sambaNextRid", "revision"]
977 res = s3.db.search("", dn2, ldb.SCOPE_BASE, attrs)
978 self.assertEquals(len(res), 1)
979 self.assertEquals(str(res[0].dn), dn2)
980 self.assertEquals(res[0]["description"], "test")
981 self.assertEquals(res[0]["sambaBadPasswordCount"], "4")
982 self.assertEquals(res[0]["sambaNextRid"], "1001")
983 self.assertEquals(res[0]["revision"], undefined)
985 # Delete split record
988 res = ldb.search(dn, scope=ldb.SCOPE_BASE)
989 self.assertEquals(len(res), 0)
991 res = s4.db.search("", dn, ldb.SCOPE_BASE)
992 self.assertEquals(len(res), 0)
994 res = s3.db.search("", dn2, ldb.SCOPE_BASE)
995 self.assertEquals(len(res), 0)
998 super(Samba3SamTestCase, self).setUp()
1000 def make_dn(basedn, rdn):
1001 return rdn + ",sambaDomainName=TESTS," + basedn
1003 def make_s4dn(basedn, rdn):
1004 return rdn + "," + basedn
1006 self.ldbfile = os.path.join(self.tempdir, "test.ldb")
1007 self.ldburl = "tdb://" + self.ldbfile
1009 tempdir = self.tempdir
1013 """Simple helper class that contains data for a specific SAM connection."""
1014 def __init__(self, file, basedn, dn):
1015 self.file = os.path.join(tempdir, file)
1016 self.url = "tdb://" + self.file
1017 self.basedn = basedn
1018 self.substvars = {"BASEDN": self.basedn}
1023 return self._dn(rdn, self.basedn)
1026 return self.db.connect(self.url)
1028 self.samba4 = Target("samba4.ldb", "dc=vernstok,dc=nl", make_s4dn)
1029 self.samba3 = Target("samba3.ldb", "cn=Samba3Sam", make_dn)
1030 self.templates = Target("templates.ldb", "cn=templates", None)
1032 self.samba3.connect()
1033 self.templates.connect()
1034 self.samba4.connect()
1037 os.unlink(self.ldbfile)
1038 os.unlink(self.samba3.file)
1039 os.unlink(self.templates.file)
1040 os.unlink(self.samba4.file)
1041 super(Samba3SamTestCase, self).tearDown()
1043 def test_s3sam(self):
1044 ldb = Ldb(self.ldburl)
1045 self.setup_data(self.samba3, open(os.path.join(datadir, "samba3.ldif"), 'r').read())
1046 self.setup_data(self.templates, open(os.path.join(datadir, "provision_samba3sam_templates.ldif"), 'r').read())
1047 ldif = open(os.path.join(datadir, "provision_samba3sam.ldif"), 'r').read()
1048 ldb.add_ldif(substitute_var(ldif, self.samba4.substvars))
1049 self.setup_modules(ldb, self.samba3, self.samba4)
1051 ldb = Ldb(self.ldburl)
1053 self._test_s3sam_search(ldb)
1054 self._test_s3sam_modify(ldb, self.samba3)
1057 ldb = Ldb(self.ldburl)
1058 self.setup_data(self.templates, open(os.path.join(datadir, "provision_samba3sam_templates.ldif"), 'r').read())
1059 ldif = open(os.path.join(datadir, "provision_samba3sam.ldif"), 'r').read()
1060 ldb.add_ldif(substitute_var(ldif, self.samba4.substvars))
1061 self.setup_modules(ldb, self.samba3, self.samba4)
1063 ldb = Ldb(self.ldburl)
1064 self._test_map_search(ldb, self.samba3, self.samba4)
1065 self._test_map_modify(ldb, self.samba3, self.samba4)