217ac6912a58f4f081cd6491f2782ba212cebc6b
[samba.git] / testprogs / ejs / samba3sam.js
1 #!/usr/bin/env smbscript
2 /*
3   (C) Jelmer Vernooij <jelmer@samba.org> 2005
4   (C) Martin Kuehl <mkhl@samba.org> 2006
5   Published under the GNU GPL
6   Sponsored by Google Summer of Code
7  */
8
9 var sys;
10 var ldb = ldb_init();
11 var options = GetOptions(ARGV, "POPT_AUTOHELP", "POPT_COMMON_SAMBA");
12 if (options == undefined) {
13         println("Failed to parse options");
14         return -1;
15 }
16
17 libinclude("base.js");
18
19 if (options.ARGV.length != 2) {
20         println("Usage: samba3sam.js <TESTDIR> <DATADIR>");
21         return -1;
22 }
23
24 var prefix = options.ARGV[0];
25 var datadir = options.ARGV[1];
26
27 function setup_data(obj, ldif)
28 {
29         assert(ldif != undefined);
30         ldif = substitute_var(ldif, obj);
31         assert(ldif != undefined);
32         var ok = obj.db.add(ldif);
33         assert(ok);
34 }
35
36 function setup_modules(ldb, s3, s4, ldif)
37 {
38         assert(ldif != undefined);
39         ldif = substitute_var(ldif, s4);
40         assert(ldif != undefined);
41         var ok = ldb.add(ldif);
42         assert(ok);
43
44         var ldif = "
45 dn: @MAP=samba3sam
46 @FROM: " + s4.BASEDN + "
47 @TO: " + s3.BASEDN + "
48
49 dn: @MODULES
50 @LIST: rootdse,paged_results,server_sort,extended_dn,asq,samldb,objectclass,password_hash,operational,objectguid,rdn_name,samba3sam,partition
51
52 dn: @PARTITION
53 partition: " + s4.BASEDN + ":" + s4.url + "
54 partition: " + s3.BASEDN + ":" + s3.url + "
55 replicateEntries: @SUBCLASSES
56 replicateEntries: @ATTRIBUTES
57 replicateEntries: @INDEXLIST
58 ";
59         var ok = ldb.add(ldif);
60         assert(ok);
61 }
62
63 function test_s3sam_search(ldb)
64 {
65         println("Looking up by non-mapped attribute");
66         var msg = ldb.search("(cn=Administrator)");
67         assert(msg.length == 1);
68         assert(msg[0].cn == "Administrator");
69
70         println("Looking up by mapped attribute");
71         var msg = ldb.search("(name=Backup Operators)");
72         assert(msg.length == 1);
73         assert(msg[0].name == "Backup Operators");
74
75         println("Looking up by old name of renamed attribute");
76         var msg = ldb.search("(displayName=Backup Operators)");
77         assert(msg.length == 0);
78
79         println("Looking up mapped entry containing SID");
80         var msg = ldb.search("(cn=Replicator)");
81         assert(msg.length == 1);
82         println(msg[0].dn);
83         assert(msg[0].dn == "cn=Replicator,ou=Groups,sambaDomainName=TESTS,dc=vernstok,dc=nl");
84         assert(msg[0].objectSid == "S-1-5-21-4231626423-2410014848-2360679739-552");
85
86         println("Checking mapping of objectClass");
87         var oc = msg[0].objectClass;
88         assert(oc != undefined);
89         for (var i in oc) {
90                 assert(oc[i] == "posixGroup" || oc[i] == "group");
91         }
92
93         println("Looking up by objectClass");
94         var msg = ldb.search("(|(objectClass=user)(cn=Administrator))");
95         assert(msg != undefined);
96         assert(msg.length == 2);
97         for (var i = 0; i < msg.length; i++) {
98                 assert((msg[i].dn == "unixName=Administrator,ou=Users,sambaDomainName=TESTS,dc=vernstok,dc=nl") ||
99                        (msg[i].dn == "unixName=nobody,ou=Users,sambaDomainName=TESTS,dc=vernstok,dc=nl"));
100         }
101 }
102
103 function test_s3sam_modify(ldb, s3)
104 {
105         println("Adding a record that will be fallbacked");
106         ok = ldb.add("
107 dn: cn=Foo,dc=idealx,dc=org
108 foo: bar
109 blah: Blie
110 cn: Foo
111 showInAdvancedViewOnly: TRUE
112 ");
113         assert(ok);
114
115         println("Checking for existence of record (local)");
116         /* TODO: This record must be searched in the local database, which is currently only supported for base searches
117          * msg = ldb.search("(cn=Foo)", new Array('foo','blah','cn','showInAdvancedViewOnly'));
118          * TODO: Actually, this version should work as well but doesn't...
119          * msg = ldb.search("(cn=Foo)", "dc=idealx,dc=org", ldb.LDB_SCOPE_SUBTREE new Array('foo','blah','cn','showInAdvancedViewOnly'));
120          */
121         msg = ldb.search("", "cn=Foo,dc=idealx,dc=org", ldb.LDB_SCOPE_BASE new Array('foo','blah','cn','showInAdvancedViewOnly'));
122         assert(msg.length == 1);
123         assert(msg[0].showInAdvancedViewOnly == "TRUE");
124         assert(msg[0].foo == "bar");
125         assert(msg[0].blah == "Blie");
126
127         println("Adding record that will be mapped");
128         ok = ldb.add("
129 dn: cn=Niemand,sambaDomainName=TESTS,dc=vernstok,dc=nl
130 objectClass: user
131 unixName: bin
132 unicodePwd: geheim
133 cn: Niemand
134 ");
135         assert(ok);
136
137         println("Checking for existence of record (remote)");
138         msg = ldb.search("(unixName=bin)", new Array('unixName','cn','dn', 'unicodePwd'));
139         assert(msg.length == 1);
140         assert(msg[0].cn == "Niemand"); 
141         assert(msg[0].unicodePwd == "geheim");
142
143         println("Checking for existence of record (local && remote)");
144         msg = ldb.search("(&(unixName=bin)(unicodePwd=geheim))", new Array('unixName','cn','dn', 'unicodePwd'));
145         assert(msg.length == 1);                // TODO: should check with more records
146         assert(msg[0].cn == "Niemand");
147         assert(msg[0].unixName == "bin");
148         assert(msg[0].unicodePwd == "geheim");
149
150         println("Checking for existence of record (local || remote)");
151         msg = ldb.search("(|(unixName=bin)(unicodePwd=geheim))", new Array('unixName','cn','dn', 'unicodePwd'));
152         assert(msg.length == 1);                // TODO: should check with more records
153         assert(msg[0].cn == "Niemand");
154         assert(msg[0].unixName == "bin" || msg[0].unicodePwd == "geheim");
155
156         println("Checking for data in destination database");
157         msg = s3.db.search("(cn=Niemand)");
158         assert(msg.length >= 1);
159         assert(msg[0].sambaSID == "S-1-5-21-4231626423-2410014848-2360679739-2001");
160         assert(msg[0].displayName == "Niemand");
161
162         println("Adding attribute...");
163         ok = ldb.modify("
164 dn: cn=Niemand,sambaDomainName=TESTS,dc=vernstok,dc=nl
165 changetype: modify
166 add: description
167 description: Blah
168 ");
169         assert(ok);
170
171         println("Checking whether changes are still there...");
172         msg = ldb.search("(cn=Niemand)");
173         assert(msg.length >= 1);
174         assert(msg[0].cn == "Niemand");
175         assert(msg[0].description == "Blah");
176
177         println("Modifying attribute...");
178         ok = ldb.modify("
179 dn: cn=Niemand,sambaDomainName=TESTS,dc=vernstok,dc=nl
180 changetype: modify
181 replace: description
182 description: Blie
183 ");
184         assert(ok);
185
186         println("Checking whether changes are still there...");
187         msg = ldb.search("(cn=Niemand)");
188         assert(msg.length >= 1);
189         assert(msg[0].description == "Blie");
190
191         println("Deleting attribute...");
192         ok = ldb.modify("
193 dn: cn=Niemand,sambaDomainName=TESTS,dc=vernstok,dc=nl
194 changetype: modify
195 delete: description
196 ");
197         assert(ok);
198
199         println("Checking whether changes are no longer there...");
200         msg = ldb.search("(cn=Niemand)");
201         assert(msg.length >= 1);
202         assert(msg[0].description == undefined);
203
204         println("Renaming record...");
205         ok = ldb.rename("cn=Niemand,sambaDomainName=TESTS,dc=vernstok,dc=nl", "cn=Niemand,dc=vernstok,dc=nl");
206
207         println("Checking whether DN has changed...");
208         msg = ldb.search("(cn=Niemand)");
209         assert(msg.length == 1);
210         assert(msg[0].dn == "cn=Niemand,dc=vernstok,dc=nl");
211
212         println("Deleting record...");
213         ok = ldb.del("cn=Niemand,dc=vernstok,dc=nl");
214         assert(ok);
215
216         println("Checking whether record is gone...");
217         msg = ldb.search("(cn=Niemand)");
218         assert(msg.length == 0);
219 }
220
221 function test_map_search(ldb, s3, s4)
222 {
223         println("Running search tests on mapped data");
224         var res;
225         var dn;
226         var attrs;
227
228         /* Add a set of split records */
229         var ldif = "
230 dn: " + s4.dn("cn=X") + "
231 objectClass: user
232 cn: X
233 codePage: x
234 revision: x
235 objectCategory: x
236 nextRid: y
237 lastLogon: x
238 description: x
239 objectSid: S-1-5-21-4231626423-2410014848-2360679739-552
240 primaryGroupID: 1-5-21-4231626423-2410014848-2360679739-512
241
242 dn: " + s4.dn("cn=Y") + "
243 objectClass: top
244 cn: Y
245 codePage: x
246 revision: x
247 objectCategory: y
248 nextRid: y
249 lastLogon: y
250 description: x
251
252 dn: " + s4.dn("cn=Z") + "
253 objectClass: top
254 cn: Z
255 codePage: x
256 revision: y
257 objectCategory: z
258 nextRid: y
259 lastLogon: z
260 description: y
261 ";
262         ldif = substitute_var(ldif, s4);
263         assert(ldif != undefined);
264         var ok = ldb.add(ldif);
265         assert(ok);
266
267         /* Add a set of remote records */
268         var ldif = "
269 dn: " + s3.dn("cn=A") + "
270 objectClass: posixAccount
271 cn: A
272 sambaNextRid: x
273 sambaBadPasswordCount: x
274 sambaLogonTime: x
275 description: x
276 sambaSID: S-1-5-21-4231626423-2410014848-2360679739-552
277 sambaPrimaryGroupSID: S-1-5-21-4231626423-2410014848-2360679739-512
278
279 dn: " + s3.dn("cn=B") + "
280 objectClass: top
281 cn:B
282 sambaNextRid: x
283 sambaBadPasswordCount: x
284 sambaLogonTime: y
285 description: x
286
287 dn: " + s3.dn("cn=C") + "
288 objectClass: top
289 cn: C
290 sambaNextRid: x
291 sambaBadPasswordCount: y
292 sambaLogonTime: z
293 description: y
294 ";
295         ldif = substitute_var(ldif, s3);
296         assert(ldif != undefined);
297         var ok = s3.db.add(ldif);
298         assert(ok);
299
300         println("Testing search by DN");
301
302         /* Search remote record by local DN */
303         dn = s4.dn("cn=A");
304         attrs = new Array("objectCategory", "lastLogon");
305         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
306         assert(res != undefined);
307         assert(res.length == 1);
308         assert(res[0].dn == dn);
309         assert(res[0].objectCategory == undefined);
310         assert(res[0].lastLogon == "x");
311
312         /* Search remote record by remote DN */
313         dn = s3.dn("cn=A");
314         attrs = new Array("objectCategory", "lastLogon", "sambaLogonTime");
315         res = s3.db.search("", dn, ldb.SCOPE_BASE, attrs);
316         assert(res != undefined);
317         assert(res.length == 1);
318         assert(res[0].dn == dn);
319         assert(res[0].objectCategory == undefined);
320         assert(res[0].lastLogon == undefined);
321         assert(res[0].sambaLogonTime == "x");
322
323         /* Search split record by local DN */
324         dn = s4.dn("cn=X");
325         attrs = new Array("objectCategory", "lastLogon");
326         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
327         assert(res != undefined);
328         assert(res.length == 1);
329         assert(res[0].dn == dn);
330         assert(res[0].objectCategory == "x");
331         assert(res[0].lastLogon == "x");
332
333         /* Search split record by remote DN */
334         dn = s3.dn("cn=X");
335         attrs = new Array("objectCategory", "lastLogon", "sambaLogonTime");
336         res = s3.db.search("", dn, ldb.SCOPE_BASE, attrs);
337         assert(res != undefined);
338         assert(res.length == 1);
339         assert(res[0].dn == dn);
340         assert(res[0].objectCategory == undefined);
341         assert(res[0].lastLogon == undefined);
342         assert(res[0].sambaLogonTime == "x");
343
344         println("Testing search by attribute");
345
346         /* Search by ignored attribute */
347         attrs = new Array("objectCategory", "lastLogon");
348         res = ldb.search("(revision=x)", NULL, ldb. SCOPE_DEFAULT, attrs);
349         assert(res != undefined);
350         assert(res.length == 2);
351         assert(res[0].dn == s4.dn("cn=Y"));
352         assert(res[0].objectCategory == "y");
353         assert(res[0].lastLogon == "y");
354         assert(res[1].dn == s4.dn("cn=X"));
355         assert(res[1].objectCategory == "x");
356         assert(res[1].lastLogon == "x");
357
358         /* Search by kept attribute */
359         attrs = new Array("objectCategory", "lastLogon");
360         res = ldb.search("(description=y)", NULL, ldb. SCOPE_DEFAULT, attrs);
361         assert(res != undefined);
362         assert(res.length == 2);
363         assert(res[0].dn == s4.dn("cn=Z"));
364         assert(res[0].objectCategory == "z");
365         assert(res[0].lastLogon == "z");
366         assert(res[1].dn == s4.dn("cn=C"));
367         assert(res[1].objectCategory == undefined);
368         assert(res[1].lastLogon == "z");
369
370         /* Search by renamed attribute */
371         attrs = new Array("objectCategory", "lastLogon");
372         res = ldb.search("(badPwdCount=x)", NULL, ldb. SCOPE_DEFAULT, attrs);
373         assert(res != undefined);
374         assert(res.length == 2);
375         assert(res[0].dn == s4.dn("cn=B"));
376         assert(res[0].objectCategory == undefined);
377         assert(res[0].lastLogon == "y");
378         assert(res[1].dn == s4.dn("cn=A"));
379         assert(res[1].objectCategory == undefined);
380         assert(res[1].lastLogon == "x");
381
382         /* Search by converted attribute */
383         attrs = new Array("objectCategory", "lastLogon", "objectSid");
384         /* TODO:
385            Using the SID directly in the parse tree leads to conversion
386            errors, letting the search fail with no results.
387         res = ldb.search("(objectSid=S-1-5-21-4231626423-2410014848-2360679739-552)", NULL, ldb. SCOPE_DEFAULT, attrs);
388         */
389         res = ldb.search("(objectSid=*)", NULL, ldb. SCOPE_DEFAULT, attrs);
390         assert(res != undefined);
391         assert(res.length == 2);
392         assert(res[0].dn == s4.dn("cn=X"));
393         assert(res[0].objectCategory == "x");
394         assert(res[0].lastLogon == "x");
395         assert(res[0].objectSid == "S-1-5-21-4231626423-2410014848-2360679739-552");
396         assert(res[1].dn == s4.dn("cn=A"));
397         assert(res[1].objectCategory == undefined);
398         assert(res[1].lastLogon == "x");
399         assert(res[1].objectSid == "S-1-5-21-4231626423-2410014848-2360679739-552");
400
401         /* Search by generated attribute */
402         /* In most cases, this even works when the mapping is missing
403          * a `convert_operator' by enumerating the remote db. */
404         attrs = new Array("objectCategory", "lastLogon", "primaryGroupID");
405         res = ldb.search("(primaryGroupID=1-5-21-4231626423-2410014848-2360679739-512)", NULL, ldb. SCOPE_DEFAULT, attrs);
406         assert(res != undefined);
407         assert(res.length == 1);
408         assert(res[0].dn == s4.dn("cn=A"));
409         assert(res[0].objectCategory == undefined);
410         assert(res[0].lastLogon == "x");
411         assert(res[0].primaryGroupID == "1-5-21-4231626423-2410014848-2360679739-512");
412
413         /* TODO: There should actually be two results, A and X.  The
414          * primaryGroupID of X seems to get corrupted somewhere, and the
415          * objectSid isn't available during the generation of remote (!) data,
416          * which can be observed with the following search.  Also note that Xs
417          * objectSid seems to be fine in the previous search for objectSid... */
418         /*
419         res = ldb.search("(primaryGroupID=*)", NULL, ldb. SCOPE_DEFAULT, attrs);
420         println(res.length + " results found");
421         for (i=0;i<res.length;i++) {
422                 for (obj in res[i]) {
423                         println(obj + ": " + res[i][obj]);
424                 }
425                 println("---");
426         }
427         */
428
429         /* Search by remote name of renamed attribute */
430         attrs = new Array("objectCategory", "lastLogon");
431         res = ldb.search("(sambaBadPasswordCount=*)", "", ldb. SCOPE_DEFAULT, attrs);
432         assert(res != undefined);
433         assert(res.length == 0);
434
435         /* Search by objectClass */
436         attrs = new Array("objectCategory", "lastLogon", "objectClass");
437         res = ldb.search("(objectClass=user)", NULL, ldb. SCOPE_DEFAULT, attrs);
438         assert(res != undefined);
439         assert(res.length == 2);
440         assert(res[0].dn == s4.dn("cn=X"));
441         assert(res[0].objectCategory == "x");
442         assert(res[0].lastLogon == "x");
443         assert(res[0].objectClass != undefined);
444         assert(res[0].objectClass[3] == "user");
445         assert(res[1].dn == s4.dn("cn=A"));
446         assert(res[1].objectCategory == undefined);
447         assert(res[1].lastLogon == "x");
448         assert(res[1].objectClass != undefined);
449         assert(res[1].objectClass[0] == "user");
450
451         /* Prove that the objectClass is actually used for the search */
452         res = ldb.search("(|(objectClass=user)(badPwdCount=x))", NULL, ldb. SCOPE_DEFAULT, attrs);
453         assert(res != undefined);
454         assert(res.length == 3);
455         assert(res[0].dn == s4.dn("cn=B"));
456         assert(res[0].objectCategory == undefined);
457         assert(res[0].lastLogon == "y");
458         assert(res[0].objectClass != undefined);
459         for (i=0;i<res[0].objectClass.length;i++) {
460                 assert(res[0].objectClass[i] != "user");
461         }
462         assert(res[1].dn == s4.dn("cn=X"));
463         assert(res[1].objectCategory == "x");
464         assert(res[1].lastLogon == "x");
465         assert(res[1].objectClass != undefined);
466         assert(res[1].objectClass[3] == "user");
467         assert(res[2].dn == s4.dn("cn=A"));
468         assert(res[2].objectCategory == undefined);
469         assert(res[2].lastLogon == "x");
470         assert(res[2].objectClass != undefined);
471         assert(res[2].objectClass[0] == "user");
472
473         println("Testing search by parse tree");
474
475         /* Search by conjunction of local attributes */
476         attrs = new Array("objectCategory", "lastLogon");
477         res = ldb.search("(&(codePage=x)(revision=x))", NULL, ldb.SCOPE_DEFAULT, attrs);
478         assert(res != undefined);
479         assert(res.length == 2);
480         assert(res[0].dn == s4.dn("cn=Y"));
481         assert(res[0].objectCategory == "y");
482         assert(res[0].lastLogon == "y");
483         assert(res[1].dn == s4.dn("cn=X"));
484         assert(res[1].objectCategory == "x");
485         assert(res[1].lastLogon == "x");
486
487         /* Search by conjunction of remote attributes */
488         attrs = new Array("objectCategory", "lastLogon");
489         res = ldb.search("(&(lastLogon=x)(description=x))", NULL, ldb.SCOPE_DEFAULT, attrs);
490         assert(res != undefined);
491         assert(res.length == 2);
492         assert(res[0].dn == s4.dn("cn=X"));
493         assert(res[0].objectCategory == "x");
494         assert(res[0].lastLogon == "x");
495         assert(res[1].dn == s4.dn("cn=A"));
496         assert(res[1].objectCategory == undefined);
497         assert(res[1].lastLogon == "x");
498         
499         /* Search by conjunction of local and remote attribute */
500         attrs = new Array("objectCategory", "lastLogon");
501         res = ldb.search("(&(codePage=x)(description=x))", NULL, ldb.SCOPE_DEFAULT, attrs);
502         assert(res != undefined);
503         assert(res.length == 2);
504         assert(res[0].dn == s4.dn("cn=Y"));
505         assert(res[0].objectCategory == "y");
506         assert(res[0].lastLogon == "y");
507         assert(res[1].dn == s4.dn("cn=X"));
508         assert(res[1].objectCategory == "x");
509         assert(res[1].lastLogon == "x");
510
511         /* Search by conjunction of local and remote attribute w/o match */
512         attrs = new Array("objectCategory", "lastLogon");
513         res = ldb.search("(&(codePage=x)(nextRid=x))", NULL, ldb.SCOPE_DEFAULT, attrs);
514         assert(res != undefined);
515         assert(res.length == 0);
516         res = ldb.search("(&(revision=x)(lastLogon=z))", NULL, ldb.SCOPE_DEFAULT, attrs);
517         assert(res != undefined);
518         assert(res.length == 0);
519
520         /* Search by disjunction of local attributes */
521         attrs = new Array("objectCategory", "lastLogon");
522         res = ldb.search("(|(revision=x)(objectCategory=x))", NULL, ldb.SCOPE_DEFAULT, attrs);
523         assert(res != undefined);
524         assert(res.length == 2);
525         assert(res[0].dn == s4.dn("cn=Y"));
526         assert(res[0].objectCategory == "y");
527         assert(res[0].lastLogon == "y");
528         assert(res[1].dn == s4.dn("cn=X"));
529         assert(res[1].objectCategory == "x");
530         assert(res[1].lastLogon == "x");
531
532         /* Search by disjunction of remote attributes */
533         attrs = new Array("objectCategory", "lastLogon");
534         res = ldb.search("(|(badPwdCount=x)(lastLogon=x))", NULL, ldb.SCOPE_DEFAULT, attrs);
535         assert(res != undefined);
536         assert(res.length == 3);
537         assert(res[0].dn == s4.dn("cn=B"));
538         assert(res[0].objectCategory == undefined);
539         assert(res[0].lastLogon == "y");
540         assert(res[1].dn == s4.dn("cn=X"));
541         assert(res[1].objectCategory == "x");
542         assert(res[1].lastLogon == "x");
543         assert(res[2].dn == s4.dn("cn=A"));
544         assert(res[2].objectCategory == undefined);
545         assert(res[2].lastLogon == "x");
546
547         /* Search by disjunction of local and remote attribute */
548         attrs = new Array("objectCategory", "lastLogon");
549         res = ldb.search("(|(revision=x)(lastLogon=y))", NULL, ldb.SCOPE_DEFAULT, attrs);
550         assert(res != undefined);
551         assert(res.length == 3);
552         assert(res[0].dn == s4.dn("cn=Y"));
553         assert(res[0].objectCategory == "y");
554         assert(res[0].lastLogon == "y");
555         assert(res[1].dn == s4.dn("cn=B"));
556         assert(res[1].objectCategory == undefined);
557         assert(res[1].lastLogon == "y");
558         assert(res[2].dn == s4.dn("cn=X"));
559         assert(res[2].objectCategory == "x");
560         assert(res[2].lastLogon == "x");
561
562         /* Search by disjunction of local and remote attribute w/o match */
563         attrs = new Array("objectCategory", "lastLogon");
564         res = ldb.search("(|(codePage=y)(nextRid=z))", NULL, ldb.SCOPE_DEFAULT, attrs);
565         assert(res != undefined);
566         assert(res.length == 0);
567
568         /* Search by negated local attribute */
569         attrs = new Array("objectCategory", "lastLogon");
570         res = ldb.search("(!(revision=x))", NULL, ldb.SCOPE_DEFAULT, attrs);
571         assert(res != undefined);
572         assert(res.length == 4);
573         assert(res[0].dn == s4.dn("cn=B"));
574         assert(res[0].objectCategory == undefined);
575         assert(res[0].lastLogon == "y");
576         assert(res[1].dn == s4.dn("cn=A"));
577         assert(res[1].objectCategory == undefined);
578         assert(res[1].lastLogon == "x");
579         assert(res[2].dn == s4.dn("cn=Z"));
580         assert(res[2].objectCategory == "z");
581         assert(res[2].lastLogon == "z");
582         assert(res[3].dn == s4.dn("cn=C"));
583         assert(res[3].objectCategory == undefined);
584         assert(res[3].lastLogon == "z");
585
586         /* Search by negated remote attribute */
587         attrs = new Array("objectCategory", "lastLogon");
588         res = ldb.search("(!(description=x))", NULL, ldb.SCOPE_DEFAULT, attrs);
589         assert(res != undefined);
590         assert(res.length == 2);
591         assert(res[0].dn == s4.dn("cn=Z"));
592         assert(res[0].objectCategory == "z");
593         assert(res[0].lastLogon == "z");
594         assert(res[1].dn == s4.dn("cn=C"));
595         assert(res[1].objectCategory == undefined);
596         assert(res[1].lastLogon == "z");
597
598         /* Search by negated conjunction of local attributes */
599         attrs = new Array("objectCategory", "lastLogon");
600         res = ldb.search("(!(&(codePage=x)(revision=x)))", NULL, ldb.SCOPE_DEFAULT, attrs);
601         assert(res != undefined);
602         assert(res.length == 4);
603         assert(res[0].dn == s4.dn("cn=B"));
604         assert(res[0].objectCategory == undefined);
605         assert(res[0].lastLogon == "y");
606         assert(res[1].dn == s4.dn("cn=A"));
607         assert(res[1].objectCategory == undefined);
608         assert(res[1].lastLogon == "x");
609         assert(res[2].dn == s4.dn("cn=Z"));
610         assert(res[2].objectCategory == "z");
611         assert(res[2].lastLogon == "z");
612         assert(res[3].dn == s4.dn("cn=C"));
613         assert(res[3].objectCategory == undefined);
614         assert(res[3].lastLogon == "z");
615
616         /* Search by negated conjunction of remote attributes */
617         attrs = new Array("objectCategory", "lastLogon");
618         res = ldb.search("(!(&(lastLogon=x)(description=x)))", NULL, ldb.SCOPE_DEFAULT, attrs);
619         assert(res != undefined);
620         assert(res.length == 4);
621         assert(res[0].dn == s4.dn("cn=Y"));
622         assert(res[0].objectCategory == "y");
623         assert(res[0].lastLogon == "y");
624         assert(res[1].dn == s4.dn("cn=B"));
625         assert(res[1].objectCategory == undefined);
626         assert(res[1].lastLogon == "y");
627         assert(res[2].dn == s4.dn("cn=Z"));
628         assert(res[2].objectCategory == "z");
629         assert(res[2].lastLogon == "z");
630         assert(res[3].dn == s4.dn("cn=C"));
631         assert(res[3].objectCategory == undefined);
632         assert(res[3].lastLogon == "z");
633
634         /* Search by negated conjunction of local and remote attribute */
635         attrs = new Array("objectCategory", "lastLogon");
636         res = ldb.search("(!(&(codePage=x)(description=x)))", NULL, ldb.SCOPE_DEFAULT, attrs);
637         assert(res != undefined);
638         assert(res.length == 4);
639         assert(res[0].dn == s4.dn("cn=B"));
640         assert(res[0].objectCategory == undefined);
641         assert(res[0].lastLogon == "y");
642         assert(res[1].dn == s4.dn("cn=A"));
643         assert(res[1].objectCategory == undefined);
644         assert(res[1].lastLogon == "x");
645         assert(res[2].dn == s4.dn("cn=Z"));
646         assert(res[2].objectCategory == "z");
647         assert(res[2].lastLogon == "z");
648         assert(res[3].dn == s4.dn("cn=C"));
649         assert(res[3].objectCategory == undefined);
650         assert(res[3].lastLogon == "z");
651
652         /* Search by negated disjunction of local attributes */
653         attrs = new Array("objectCategory", "lastLogon");
654         res = ldb.search("(!(|(revision=x)(objectCategory=x)))", NULL, ldb.SCOPE_DEFAULT, attrs);
655         assert(res != undefined);
656         assert(res[0].dn == s4.dn("cn=B"));
657         assert(res[0].objectCategory == undefined);
658         assert(res[0].lastLogon == "y");
659         assert(res[1].dn == s4.dn("cn=A"));
660         assert(res[1].objectCategory == undefined);
661         assert(res[1].lastLogon == "x");
662         assert(res[2].dn == s4.dn("cn=Z"));
663         assert(res[2].objectCategory == "z");
664         assert(res[2].lastLogon == "z");
665         assert(res[3].dn == s4.dn("cn=C"));
666         assert(res[3].objectCategory == undefined);
667         assert(res[3].lastLogon == "z");
668
669         /* Search by negated disjunction of remote attributes */
670         attrs = new Array("objectCategory", "lastLogon");
671         res = ldb.search("(!(|(badPwdCount=x)(lastLogon=x)))", NULL, ldb.SCOPE_DEFAULT, attrs);
672         assert(res != undefined);
673         assert(res.length == 3);
674         assert(res[0].dn == s4.dn("cn=Y"));
675         assert(res[0].objectCategory == "y");
676         assert(res[0].lastLogon == "y");
677         assert(res[1].dn == s4.dn("cn=Z"));
678         assert(res[1].objectCategory == "z");
679         assert(res[1].lastLogon == "z");
680         assert(res[2].dn == s4.dn("cn=C"));
681         assert(res[2].objectCategory == undefined);
682         assert(res[2].lastLogon == "z");
683
684         /* Search by negated disjunction of local and remote attribute */
685         attrs = new Array("objectCategory", "lastLogon");
686         res = ldb.search("(!(|(revision=x)(lastLogon=y)))", NULL, ldb.SCOPE_DEFAULT, attrs);
687         assert(res != undefined);
688         assert(res.length == 3);
689         assert(res[0].dn == s4.dn("cn=A"));
690         assert(res[0].objectCategory == undefined);
691         assert(res[0].lastLogon == "x");
692         assert(res[1].dn == s4.dn("cn=Z"));
693         assert(res[1].objectCategory == "z");
694         assert(res[1].lastLogon == "z");
695         assert(res[2].dn == s4.dn("cn=C"));
696         assert(res[2].objectCategory == undefined);
697         assert(res[2].lastLogon == "z");
698
699         /* Search by complex parse tree */
700         attrs = new Array("objectCategory", "lastLogon");
701         res = ldb.search("(|(&(revision=x)(objectCategory=x))(!(&(description=x)(nextRid=y)))(badPwdCount=y))", NULL, ldb.SCOPE_DEFAULT, attrs);
702         assert(res != undefined);
703         assert(res.length == 5);
704         assert(res[0].dn == s4.dn("cn=B"));
705         assert(res[0].objectCategory == undefined);
706         assert(res[0].lastLogon == "y");
707         assert(res[1].dn == s4.dn("cn=X"));
708         assert(res[1].objectCategory == "x");
709         assert(res[1].lastLogon == "x");
710         assert(res[2].dn == s4.dn("cn=A"));
711         assert(res[2].objectCategory == undefined);
712         assert(res[2].lastLogon == "x");
713         assert(res[3].dn == s4.dn("cn=Z"));
714         assert(res[3].objectCategory == "z");
715         assert(res[3].lastLogon == "z");
716         assert(res[4].dn == s4.dn("cn=C"));
717         assert(res[4].objectCategory == undefined);
718         assert(res[4].lastLogon == "z");
719
720         /* Clean up */
721         var dns = new Array();
722         dns[0] = s4.dn("cn=A");
723         dns[1] = s4.dn("cn=B");
724         dns[2] = s4.dn("cn=C");
725         dns[3] = s4.dn("cn=X");
726         dns[4] = s4.dn("cn=Y");
727         dns[5] = s4.dn("cn=Z");
728         for (i=0;i<dns.length;i++) {
729                 var ok = ldb.del(dns[i]);
730                 assert(ok);
731         }
732 }
733
734 function test_map_modify(ldb, s3, s4)
735 {
736         println("Running modification tests on mapped data");
737
738         var ldif;
739         var attrs;
740         var dn, dn2;
741         var res;
742         var ok;
743
744         println("Testing modification of local records");
745
746         /* Add local record */
747         dn = "cn=test,dc=idealx,dc=org";
748         ldif = "
749 dn: " + dn + "
750 cn: test
751 foo: bar
752 revision: 1
753 description: test
754 ";
755         ok = ldb.add(ldif);
756         assert(ok);
757         /* Check it's there */
758         attrs = new Array("foo", "revision", "description");
759         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
760         assert(res != undefined);
761         assert(res.length == 1);
762         assert(res[0].dn == dn);
763         assert(res[0].foo == "bar");
764         assert(res[0].revision == "1");
765         assert(res[0].description == "test");
766         /* Check it's not in the local db */
767         res = s4.db.search("(cn=test)", NULL, ldb.SCOPE_DEFAULT, attrs);
768         assert(res != undefined);
769         assert(res.length == 0);
770         /* Check it's not in the remote db */
771         res = s3.db.search("(cn=test)", NULL, ldb.SCOPE_DEFAULT, attrs);
772         assert(res != undefined);
773         assert(res.length == 0);
774
775         /* Modify local record */
776         ldif = "
777 dn: " + dn + "
778 replace: foo
779 foo: baz
780 replace: description
781 description: foo
782 ";
783         ok = ldb.modify(ldif);
784         assert(ok);
785         /* Check in local db */
786         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
787         assert(res != undefined);
788         assert(res.length == 1);
789         assert(res[0].dn == dn);
790         assert(res[0].foo == "baz");
791         assert(res[0].revision == "1");
792         assert(res[0].description == "foo");
793
794         /* Rename local record */
795         dn2 = "cn=toast,dc=idealx,dc=org";
796         ok = ldb.rename(dn, dn2);
797         assert(ok);
798         /* Check in local db */
799         res = ldb.search("", dn2, ldb.SCOPE_BASE, attrs);
800         assert(res != undefined);
801         assert(res.length == 1);
802         assert(res[0].dn == dn2);
803         assert(res[0].foo == "baz");
804         assert(res[0].revision == "1");
805         assert(res[0].description == "foo");
806
807         /* Delete local record */
808         ok = ldb.del(dn2);
809         assert(ok);
810         /* Check it's gone */
811         res = ldb.search("", dn2, ldb.SCOPE_BASE);
812         assert(res != undefined);
813         assert(res.length == 0);
814
815         println("Testing modification of remote records");
816
817         /* Add remote record */
818         dn = s4.dn("cn=test");
819         dn2 = s3.dn("cn=test");
820         ldif = "
821 dn: " + dn2 + "
822 cn: test
823 description: foo
824 sambaBadPasswordCount: 3
825 sambaNextRid: 1001
826 ";
827         ok = s3.db.add(ldif);
828         assert(ok);
829         /* Check it's there */
830         attrs = new Array("description", "sambaBadPasswordCount", "sambaNextRid");
831         res = s3.db.search("", dn2, ldb.SCOPE_BASE, attrs);
832         assert(res != undefined);
833         assert(res.length == 1);
834         assert(res[0].dn == dn2);
835         assert(res[0].description == "foo");
836         assert(res[0].sambaBadPasswordCount == "3");
837         assert(res[0].sambaNextRid == "1001");
838         /* Check in mapped db */
839         attrs = new Array("description", "badPwdCount", "nextRid");
840         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
841         assert(res != undefined);
842         assert(res.length == 1);
843         assert(res[0].dn == dn);
844         assert(res[0].description == "foo");
845         assert(res[0].badPwdCount == "3");
846         assert(res[0].nextRid == "1001");
847         /* Check in local db */
848         res = s4.db.search("", dn, ldb.SCOPE_BASE, attrs);
849         assert(res != undefined);
850         assert(res.length == 0);
851
852         /* Modify remote data of remote record */
853         ldif = "
854 dn: " + dn + "
855 replace: description
856 description: test
857 replace: badPwdCount
858 badPwdCount: 4
859 ";
860         ok = ldb.modify(ldif);
861         /* Check in mapped db */
862         attrs = new Array("description", "badPwdCount", "nextRid");
863         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
864         assert(res != undefined);
865         assert(res.length == 1);
866         assert(res[0].dn == dn);
867         assert(res[0].description == "test");
868         assert(res[0].badPwdCount == "4");
869         assert(res[0].nextRid == "1001");
870         /* Check in remote db */
871         attrs = new Array("description", "sambaBadPasswordCount", "sambaNextRid");
872         res = s3.db.search("", dn2, ldb.SCOPE_BASE, attrs);
873         assert(res != undefined);
874         assert(res.length == 1);
875         assert(res[0].dn == dn2);
876         assert(res[0].description == "test");
877         assert(res[0].sambaBadPasswordCount == "4");
878         assert(res[0].sambaNextRid == "1001");
879
880         /* Rename remote record */
881         dn2 = s4.dn("cn=toast");
882         ok = ldb.rename(dn, dn2);
883         assert(ok);
884         /* Check in mapped db */
885         dn = dn2;
886         attrs = new Array("description", "badPwdCount", "nextRid");
887         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
888         assert(res != undefined);
889         assert(res.length == 1);
890         assert(res[0].dn == dn);
891         assert(res[0].description == "test");
892         assert(res[0].badPwdCount == "4");
893         assert(res[0].nextRid == "1001");
894         /* Check in remote db */
895         dn2 = s3.dn("cn=toast");
896         attrs = new Array("description", "sambaBadPasswordCount", "sambaNextRid");
897         res = s3.db.search("", dn2, ldb.SCOPE_BASE, attrs);
898         assert(res != undefined);
899         assert(res.length == 1);
900         assert(res[0].dn == dn2);
901         assert(res[0].description == "test");
902         assert(res[0].sambaBadPasswordCount == "4");
903         assert(res[0].sambaNextRid == "1001");
904
905         /* Delete remote record */
906         ok = ldb.del(dn);
907         assert(ok);
908         /* Check in mapped db */
909         res = ldb.search("", dn, ldb.SCOPE_BASE);
910         assert(res != undefined);
911         assert(res.length == 0);
912         /* Check in remote db */
913         res = s3.db.search("", dn2, ldb.SCOPE_BASE);
914         assert(res != undefined);
915         assert(res.length == 0);
916
917         /* Add remote record (same as before) */
918         dn = s4.dn("cn=test");
919         dn2 = s3.dn("cn=test");
920         ldif = "
921 dn: " + dn2 + "
922 cn: test
923 description: foo
924 sambaBadPasswordCount: 3
925 sambaNextRid: 1001
926 ";
927         ok = s3.db.add(ldif);
928         assert(ok);
929
930         /* Modify local data of remote record */
931         ldif = "
932 dn: " + dn + "
933 add: revision
934 revision: 1
935 replace: description
936 description: test
937 ";
938         ok = ldb.modify(ldif);
939         /* Check in mapped db */
940         attrs = new Array("revision", "description");
941         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
942         assert(res != undefined);
943         assert(res.length == 1);
944         assert(res[0].dn == dn);
945         assert(res[0].description == "test");
946         assert(res[0].revision == "1");
947         /* Check in remote db */
948         res = s3.db.search("", dn2, ldb.SCOPE_BASE, attrs);
949         assert(res != undefined);
950         assert(res.length == 1);
951         assert(res[0].dn == dn2);
952         assert(res[0].description == "test");
953         assert(res[0].revision == undefined);
954         /* Check in local db */
955         res = s4.db.search("", dn, ldb.SCOPE_BASE, attrs);
956         assert(res != undefined);
957         assert(res.length == 1);
958         assert(res[0].dn == dn);
959         assert(res[0].description == undefined);
960         assert(res[0].revision == "1");
961
962         /* Delete (newly) split record */
963         ok = ldb.del(dn);
964         assert(ok);
965
966         println("Testing modification of split records");
967
968         /* Add split record */
969         dn = s4.dn("cn=test");
970         dn2 = s3.dn("cn=test");
971         ldif = "
972 dn: " + dn + "
973 cn: test
974 description: foo
975 badPwdCount: 3
976 nextRid: 1001
977 revision: 1
978 ";
979         ok = ldb.add(ldif);
980         assert(ok);
981         /* Check it's there */
982         attrs = new Array("description", "badPwdCount", "nextRid", "revision");
983         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
984         assert(res != undefined);
985         assert(res.length == 1);
986         assert(res[0].dn == dn);
987         assert(res[0].description == "foo");
988         assert(res[0].badPwdCount == "3");
989         assert(res[0].nextRid == "1001");
990         assert(res[0].revision == "1");
991         /* Check in local db */
992         res = s4.db.search("", dn, ldb.SCOPE_BASE, attrs);
993         assert(res != undefined);
994         assert(res.length == 1);
995         assert(res[0].dn == dn);
996         assert(res[0].description == undefined);
997         assert(res[0].badPwdCount == undefined);
998         assert(res[0].nextRid == undefined);
999         assert(res[0].revision == "1");
1000         /* Check in remote db */
1001         attrs = new Array("description", "sambaBadPasswordCount", "sambaNextRid", "revision");
1002         res = s3.db.search("", dn2, ldb.SCOPE_BASE, attrs);
1003         assert(res != undefined);
1004         assert(res.length == 1);
1005         assert(res[0].dn == dn2);
1006         assert(res[0].description == "foo");
1007         assert(res[0].sambaBadPasswordCount == "3");
1008         assert(res[0].sambaNextRid == "1001");
1009         assert(res[0].revision == undefined);
1010
1011         /* Modify of split record */
1012         ldif = "
1013 dn: " + dn + "
1014 replace: description
1015 description: test
1016 replace: badPwdCount
1017 badPwdCount: 4
1018 replace: revision
1019 revision: 2
1020 ";
1021         ok = ldb.modify(ldif);
1022         assert(ok);
1023         /* Check in mapped db */
1024         attrs = new Array("description", "badPwdCount", "nextRid", "revision");
1025         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
1026         assert(res != undefined);
1027         assert(res.length == 1);
1028         assert(res[0].dn == dn);
1029         assert(res[0].description == "test");
1030         assert(res[0].badPwdCount == "4");
1031         assert(res[0].nextRid == "1001");
1032         assert(res[0].revision == "2");
1033         /* Check in local db */
1034         res = s4.db.search("", dn, ldb.SCOPE_BASE, attrs);
1035         assert(res != undefined);
1036         assert(res.length == 1);
1037         assert(res[0].dn == dn);
1038         assert(res[0].description == undefined);
1039         assert(res[0].badPwdCount == undefined);
1040         assert(res[0].nextRid == undefined);
1041         assert(res[0].revision == "2");
1042         /* Check in remote db */
1043         attrs = new Array("description", "sambaBadPasswordCount", "sambaNextRid", "revision");
1044         res = s3.db.search("", dn2, ldb.SCOPE_BASE, attrs);
1045         assert(res != undefined);
1046         assert(res.length == 1);
1047         assert(res[0].dn == dn2);
1048         assert(res[0].description == "test");
1049         assert(res[0].sambaBadPasswordCount == "4");
1050         assert(res[0].sambaNextRid == "1001");
1051         assert(res[0].revision == undefined);
1052
1053         /* Rename split record */
1054         dn2 = s4.dn("cn=toast");
1055         ok = ldb.rename(dn, dn2);
1056         assert(ok);
1057         /* Check in mapped db */
1058         dn = dn2;
1059         attrs = new Array("description", "badPwdCount", "nextRid", "revision");
1060         res = ldb.search("", dn, ldb.SCOPE_BASE, attrs);
1061         assert(res != undefined);
1062         assert(res.length == 1);
1063         assert(res[0].dn == dn);
1064         assert(res[0].description == "test");
1065         assert(res[0].badPwdCount == "4");
1066         assert(res[0].nextRid == "1001");
1067         assert(res[0].revision == "2");
1068         /* Check in local db */
1069         res = s4.db.search("", dn, ldb.SCOPE_BASE, attrs);
1070         assert(res != undefined);
1071         assert(res.length == 1);
1072         assert(res[0].dn == dn);
1073         assert(res[0].description == undefined);
1074         assert(res[0].badPwdCount == undefined);
1075         assert(res[0].nextRid == undefined);
1076         assert(res[0].revision == "2");
1077         /* Check in remote db */
1078         dn2 = s3.dn("cn=toast");
1079         attrs = new Array("description", "sambaBadPasswordCount", "sambaNextRid", "revision");
1080         res = s3.db.search("", dn2, ldb.SCOPE_BASE, attrs);
1081         assert(res != undefined);
1082         assert(res.length == 1);
1083         assert(res[0].dn == dn2);
1084         assert(res[0].description == "test");
1085         assert(res[0].sambaBadPasswordCount == "4");
1086         assert(res[0].sambaNextRid == "1001");
1087         assert(res[0].revision == undefined);
1088
1089         /* Delete split record */
1090         ok = ldb.del(dn);
1091         assert(ok);
1092         /* Check in mapped db */
1093         res = ldb.search("", dn, ldb.SCOPE_BASE);
1094         assert(res != undefined);
1095         assert(res.length == 0);
1096         /* Check in local db */
1097         res = s4.db.search("", dn, ldb.SCOPE_BASE);
1098         assert(res != undefined);
1099         assert(res.length == 0);
1100         /* Check in remote db */
1101         res = s3.db.search("", dn2, ldb.SCOPE_BASE);
1102         assert(res != undefined);
1103         assert(res.length == 0);
1104 }
1105
1106 function make_dn(rdn)
1107 {
1108         return rdn + ",sambaDomainName=TESTS," + this.BASEDN;
1109 }
1110
1111 sys = sys_init();
1112 var ldbfile = prefix + "/" + "test.ldb";
1113 var ldburl = "tdb://" + ldbfile;
1114
1115 var samba4 = new Object("samba4 partition info");
1116 samba4.file = prefix + "/" + "samba4.ldb";
1117 samba4.url = "tdb://" + samba4.file;
1118 samba4.BASEDN = "dc=vernstok,dc=nl";
1119 samba4.db = ldb_init();
1120 samba4.dn = make_dn;
1121
1122 var samba3 = new Object("samba3 partition info");
1123 samba3.file = prefix + "/" + "samba3.ldb";
1124 samba3.url = "tdb://" + samba3.file;
1125 samba3.BASEDN = "cn=Samba3Sam," + samba4.BASEDN;
1126 samba3.db = ldb_init();
1127 samba3.dn = make_dn;
1128
1129 sys.unlink(ldbfile);
1130 sys.unlink(samba3.file);
1131 sys.unlink(samba4.file);
1132
1133 var ok = ldb.connect(ldburl);
1134 assert(ok);
1135 var ok = samba3.db.connect(samba3.url);
1136 assert(ok);
1137 var ok = samba4.db.connect(samba4.url);
1138 assert(ok);
1139
1140 setup_data(samba3, sys.file_load(datadir + "/" + "samba3.ldif"));
1141 setup_modules(ldb, samba3, samba4, sys.file_load(datadir + "/" + "provision_samba3sam.ldif"));
1142
1143 ldb = ldb_init();
1144 var ok = ldb.connect(ldburl);
1145 assert(ok);
1146
1147 test_s3sam_search(ldb);
1148 test_s3sam_modify(ldb, samba3);
1149
1150 sys.unlink(ldbfile);
1151 sys.unlink(samba3.file);
1152 sys.unlink(samba4.file);
1153
1154 ldb = ldb_init();
1155 var ok = ldb.connect(ldburl);
1156 assert(ok);
1157 samba3.db = ldb_init();
1158 var ok = samba3.db.connect(samba3.url);
1159 assert(ok);
1160 samba4.db = ldb_init();
1161 var ok = samba4.db.connect(samba4.url);
1162 assert(ok);
1163
1164 setup_modules(ldb, samba3, samba4, sys.file_load(datadir + "provision_samba3sam.ldif"));
1165
1166 ldb = ldb_init();
1167 var ok = ldb.connect(ldburl);
1168 assert(ok);
1169
1170 test_map_search(ldb, samba3, samba4);
1171 test_map_modify(ldb, samba3, samba4);
1172
1173 sys.unlink(ldbfile);
1174 sys.unlink(samba3.file);
1175 sys.unlink(samba4.file);
1176
1177 return 0;