r22176: Make the LOCAL-MESSAGING test pass again. Messaging sockets are in
[Samba.git] / testprogs / ejs / minschema.js
blobffa6db666998530762270e5b97e7ae317f9b112d
1 #!/bin/sh
2 exec smbscript "$0" ${1+"$@"}
3 /*
4   work out the minimal schema for a set of objectclasses 
5 */
7 libinclude("base.js");
9 var ldb = ldb_init();
11 var options = GetOptions(ARGV, 
12                          "POPT_AUTOHELP",
13                          "POPT_COMMON_SAMBA",
14                          "POPT_COMMON_CREDENTIALS",
15                          "verbose",
16                          "classes",
17                          "attributes",
18                          "subschema",
19                          "subschema-auto");
20 if (options == undefined) {
21    println("Failed to parse options");
22    return -1;
24 verbose = options["verbose"];
25 dump_all = "yes";
26 dump_classes = options["classes"];
27 dump_attributes = options["attributes"];
28 dump_subschema = options["subschema"];
29 dump_subschema_auto = options["subschema-auto"];
31 if (dump_classes != undefined) {
32         dump_all = undefined;
34 if (dump_attributes != undefined) {
35         dump_all = undefined;
37 if (dump_subschema != undefined) {
38         dump_all = undefined;
40 if (dump_subschema_auto != undefined) {
41         dump_all = undefined;
42         dump_subschema = "yes";
44 if (dump_all != undefined) {
45         dump_classes = "yes";
46         dump_attributes = "yes";
47         dump_subschema = "yes";
48         dump_subschema_auto = "yes";
51 if (options.ARGV.length != 2) {
52    println("Usage: minschema.js <URL> <classfile>");
53    return -1;
56 var url = options.ARGV[0];
57 var classfile = options.ARGV[1];
59 /* use command line creds if available */
60 ldb.credentials = options.get_credentials();
62 var ok = ldb.connect(url);
63 assert(ok);
65 objectclasses = new Object();
66 attributes = new Object();
67 rootDse = new Object();
69 objectclasses_expanded = new Object();
71 /* the attributes we need for objectclasses */
72 class_attrs = new Array("objectClass",
73                         "subClassOf",
74                         "governsID",
75                         "possSuperiors",
76                         "mayContain",
77                         "mustContain",
78                         "auxiliaryClass",
79                         "rDNAttID",
80                         "showInAdvancedViewOnly",
81                         "adminDisplayName",
82                         "adminDescription",
83                         "objectClassCategory",
84                         "lDAPDisplayName",
85                         "schemaIDGUID",
86                         "systemOnly",
87                         "systemPossSuperiors",
88                         "systemMayContain",
89                         "systemMustContain",
90                         "systemAuxiliaryClass",
91                         "defaultSecurityDescriptor",
92                         "systemFlags",
93                         "defaultHidingValue",
94                         "objectCategory",
95                         "defaultObjectCategory",
97                         /* this attributes are not used by w2k3 */
98                         "schemaFlagsEx",
99                         "msDs-IntId",
100                         "msDs-Schema-Extensions",
101                         "classDisplayName",
102                         "isDefunct");
105 attrib_attrs = new Array("objectClass",
106                          "attributeID",
107                          "attributeSyntax",
108                          "isSingleValued",
109                          "rangeLower",
110                          "rangeUpper",
111                          "mAPIID",
112                          "linkID",
113                          "showInAdvancedViewOnly",
114                          "adminDisplayName",
115                          "oMObjectClass",
116                          "adminDescription",
117                          "oMSyntax",
118                          "searchFlags",
119                          "extendedCharsAllowed",
120                          "lDAPDisplayName",
121                          "schemaIDGUID",
122                          "attributeSecurityGUID",
123                          "systemOnly",
124                          "systemFlags",
125                          "isMemberOfPartialAttributeSet",
126                          "objectCategory",
128                          /* this attributes are not used by w2k3 */
129                          "schemaFlagsEx",
130                          "msDs-IntId",
131                          "msDs-Schema-Extensions",
132                          "classDisplayName",
133                          "isEphemeral",
134                          "isDefunct");
137   notes:
139   objectClassCategory 
140       1: structural
141       2: abstract
142       3: auxiliary
147   print only if verbose is set
149 function dprintf() {
150         if (verbose != undefined) {
151                 print(vsprintf(arguments));
152         }
155 function get_object_cn(ldb, name) {
156         var attrs = new Array("cn");
158         var res = ldb.search(sprintf("(ldapDisplayName=%s)", name), rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrs);
159         assert(res != undefined);
160         assert(res.msgs.length == 1);
162         var cn = res.msgs[0]["cn"];
163         assert(cn != undefined);
164         if (typeof(cn) == "string") {
165                 return cn;
166         }
167         return cn[0];
170   create an objectclass object
172 function obj_objectClass(ldb, name) {
173         var o = new Object();
174         o.name = name;
175         o.cn = get_object_cn(ldb, name);
176         return o;
180   create an attribute object
182 function obj_attribute(ldb, name) {
183         var o = new Object();
184         o.name = name;
185         o.cn = get_object_cn(ldb, name);
186         return o;
190 syntaxmap = new Object();
192 syntaxmap['2.5.5.1']  = '1.3.6.1.4.1.1466.115.121.1.12';
193 syntaxmap['2.5.5.2']  = '1.3.6.1.4.1.1466.115.121.1.38';
194 syntaxmap['2.5.5.3']  = '1.2.840.113556.1.4.1362';
195 syntaxmap['2.5.5.4']  = '1.2.840.113556.1.4.905';
196 syntaxmap['2.5.5.5']  = '1.3.6.1.4.1.1466.115.121.1.26';
197 syntaxmap['2.5.5.6']  = '1.3.6.1.4.1.1466.115.121.1.36';
198 syntaxmap['2.5.5.7']  = '1.2.840.113556.1.4.903';
199 syntaxmap['2.5.5.8']  = '1.3.6.1.4.1.1466.115.121.1.7';
200 syntaxmap['2.5.5.9']  = '1.3.6.1.4.1.1466.115.121.1.27';
201 syntaxmap['2.5.5.10'] = '1.3.6.1.4.1.1466.115.121.1.40';
202 syntaxmap['2.5.5.11'] = '1.3.6.1.4.1.1466.115.121.1.24';
203 syntaxmap['2.5.5.12'] = '1.3.6.1.4.1.1466.115.121.1.15';
204 syntaxmap['2.5.5.13'] = '1.3.6.1.4.1.1466.115.121.1.43';
205 syntaxmap['2.5.5.14'] = '1.2.840.113556.1.4.904';
206 syntaxmap['2.5.5.15'] = '1.2.840.113556.1.4.907';
207 syntaxmap['2.5.5.16'] = '1.2.840.113556.1.4.906';
208 syntaxmap['2.5.5.17'] = '1.3.6.1.4.1.1466.115.121.1.40';
211   map some attribute syntaxes from some apparently MS specific
212   syntaxes to the standard syntaxes
214 function map_attribute_syntax(s) {
215         if (syntaxmap[s] != undefined) {
216                 return syntaxmap[s];
217         }
218         return s;
223   fix a string DN to use ${SCHEMADN}
225 function fix_dn(dn) {
226         var s = strstr(dn, rootDse.schemaNamingContext);
227         if (s == NULL) {
228                 return dn;
229         }
230         return substr(dn, 0, strlen(dn) - strlen(s)) + "${SCHEMADN}";
234   dump an object as ldif
236 function write_ldif_one(o, attrs) {
237         var i;
238         printf("dn: CN=%s,${SCHEMADN}\n", o.cn);
239         for (i=0;i<attrs.length;i++) {
240                 var a = attrs[i];
241                 if (o[a] == undefined) {
242                         continue;
243                 }
244                 /* special case for oMObjectClass, which is a binary object */
245                 if (a == "oMObjectClass") {
246                         printf("%s:: %s\n", a, o[a]);
247                         continue;
248                 }
249                 var v = o[a];
250                 if (typeof(v) == "string") {
251                         v = new Array(v);
252                 }
253                 var j;
254                 for (j=0;j<v.length;j++) {
255                         printf("%s: %s\n", a, fix_dn(v[j]));
256                 }
257         }
258         printf("\n");
262   dump an array of objects as ldif
264 function write_ldif(o, attrs) {
265         var i;
266         for (i in o) {
267                 write_ldif_one(o[i], attrs);
268         }
273   create a testDN based an an example DN
274   the idea is to ensure we obey any structural rules
276 function create_testdn(exampleDN) {
277         var a = split(",", exampleDN);
278         a[0] = "CN=TestDN";
279         return join(",", a);
283   find the properties of an objectclass
284  */
285 function find_objectclass_properties(ldb, o) {
286         var res = ldb.search(
287                 sprintf("(ldapDisplayName=%s)", o.name),
288                 rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, class_attrs);
289         assert(res != undefined);
290         assert(res.msgs.length == 1);
291         var msg = res.msgs[0];
292         var a;
293         for (a in msg) {
294                 o[a] = msg[a];
295         }
299   find the properties of an attribute
300  */
301 function find_attribute_properties(ldb, o) {
302         var res = ldb.search(
303                 sprintf("(ldapDisplayName=%s)", o.name),
304                 rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrib_attrs);
305         assert(res != undefined);
306         assert(res.msgs.length == 1);
307         var msg = res.msgs[0];
308         var a;
309         for (a in msg) {
310                 /* special case for oMObjectClass, which is a binary object */
311                 if (a == "oMObjectClass") {
312                         o[a] = ldb.encode(msg[a]);
313                         continue;
314                 }
315                 o[a] = msg[a];
316         }
320   find the auto-created properties of an objectclass. Only works for classes
321   that can be created using just a DN and the objectclass
322  */
323 function find_objectclass_auto(ldb, o) {
324         if (o["exampleDN"] == undefined) {
325                 return;
326         }
327         var testdn = create_testdn(o.exampleDN);
328         var ok;
330         dprintf("testdn is '%s'\n", testdn);
332         var ldif = "dn: " + testdn;
333         ldif = ldif + "\nobjectClass: " + o.name;
334         ok = ldb.add(ldif);
335         if (ok.error != 0) {
336                 dprintf("error adding %s: %s\n", o.name, ok.errstr);
337                 dprintf("%s\n", ldif);
338                 return;
339         }
341         var res = ldb.search("", testdn, ldb.SCOPE_BASE);
342         ok = ldb.del(testdn);
343         assert(ok.error == 0);
345         var a;
346         for (a in res.msgs[0]) {
347                 attributes[a].autocreate = true;
348         }
353   look at auxiliary information from a class to intuit the existance of more
354   classes needed for a minimal schema
356 function expand_objectclass(ldb, o) {
357         var attrs = new Array("auxiliaryClass", "systemAuxiliaryClass",
358                               "possSuperiors", "systemPossSuperiors",
359                               "subClassOf");
360         var res = ldb.search(
361                 sprintf("(&(objectClass=classSchema)(ldapDisplayName=%s))", o.name),
362                 rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrs);
363         var a;
364         dprintf("Expanding class %s\n", o.name);
365         assert(res != undefined);
366         assert(res.msgs.length == 1);
367         var msg = res.msgs[0];
368         for (a=0;a<attrs.length;a++) {
369                 var aname = attrs[a];
370                 if (msg[aname] == undefined) {
371                         continue;
372                 }
373                 var list = msg[aname];
374                 if (typeof(list) == "string") {
375                         list = new Array(msg[aname]);
376                 }
377                 var i;
378                 for (i=0;i<list.length;i++) {
379                         var name = list[i];
380                         if (objectclasses[name] == undefined) {
381                                 dprintf("Found new objectclass '%s'\n", name);
382                                 objectclasses[name] = obj_objectClass(ldb, name);
383                         }
384                 }
385         }
390   add the must and may attributes from an objectclass to the full list
391   of attributes
393 function add_objectclass_attributes(ldb, class) {
394         var attrs = new Array("mustContain", "systemMustContain", 
395                               "mayContain", "systemMayContain");
396         var i;
397         for (i=0;i<attrs.length;i++) {
398                 var aname = attrs[i];
399                 if (class[aname] == undefined) {
400                         continue;
401                 }
402                 var alist = class[aname];
403                 if (typeof(alist) == "string") {
404                         alist = new Array(alist);
405                 }
406                 var j;
407                 var len = alist.length;
408                 for (j=0;j<len;j++) {
409                         var a = alist[j];
410                         if (attributes[a] == undefined) {
411                                 attributes[a] = obj_attribute(ldb, a);
412                         }
413                 }
414         }
419   process an individual record, working out what attributes it has
421 function walk_dn(ldb, dn) {
422         /* get a list of all possible attributes for this object */
423         var attrs = new Array("allowedAttributes");
424         var res = ldb.search("objectClass=*", dn, ldb.SCOPE_BASE, attrs);
425         if (res.error != 0) {
426                 dprintf("Unable to fetch allowedAttributes for '%s' - %s\n", 
427                        dn, res.errstr);
428                 return;
429         }
430         var allattrs = res.msgs[0].allowedAttributes;
431         res = ldb.search("objectClass=*", dn, ldb.SCOPE_BASE, allattrs);
432         if (res.error != 0) {
433                 dprintf("Unable to fetch all attributes for '%s' - %s\n", 
434                        dn, res.errstr);
435                 return;
436         }
437         var a;
438         var msg = res.msgs[0];
439         for (a in msg) {
440                 if (attributes[a] == undefined) {
441                         attributes[a] = obj_attribute(ldb, a);
442                 }
443         }
447   walk a naming context, looking for all records
449 function walk_naming_context(ldb, namingContext) {
450         var attrs = new Array("objectClass");
451         var res = ldb.search("objectClass=*", namingContext, ldb.SCOPE_DEFAULT, attrs);
452         if (res.error != 0) {
453                 dprintf("Unable to fetch objectClasses for '%s' - %s\n", 
454                        namingContext, res.errstr);
455                 return;
456         }
457         var r;
458         for (r=0;r<res.msgs.length;r++) {
459                 var msg = res.msgs[r].objectClass;
460                 var c;
461                 for (c=0;c<msg.length;c++) {
462                         var objectClass = msg[c];
463                         if (objectclasses[objectClass] == undefined) {
464                                 objectclasses[objectClass] = obj_objectClass(ldb, objectClass);
465                                 objectclasses[objectClass].exampleDN = res.msgs[r].dn;
466                         }
467                 }
468                 walk_dn(ldb, res.msgs[r].dn);
469         }
473   trim the may attributes for an objectClass
475 function trim_objectclass_attributes(ldb, class) {
476         var i,j,n;
478         /* trim possibleInferiors,
479          * include only the classes we extracted */
480         var possinf = class["possibleInferiors"];
481         if (possinf != undefined) {
482                 var newpossinf = new Array();
483                 if (typeof(possinf) == "string") {
484                         possinf = new Array(possinf);
485                 }
486                 n = 0;
487                 for (j = 0;j < possinf.length; j++) {
488                         var x = possinf[j];
489                         if (objectclasses[x] != undefined) {
490                                 newpossinf[n] = x;
491                                 n++;
492                         }
493                 }
494                 class["possibleInferiors"] = newpossinf;
495         }
497         /* trim systemMayContain,
498          * remove duplicates */
499         var sysmay = class["systemMayContain"];
500         if (sysmay != undefined) {
501                 var newsysmay = new Array();
502                 if (typeof(sysmay) == "string") {
503                         sysmay = new Array(sysmay);
504                 }
505                 for (j = 0;j < sysmay.length; j++) {
506                         var x = sysmay[j];
507                         var dup = false;
508                         if (newsysmay[0] == undefined) {
509                                 newsysmay[0] = x;
510                         } else {
511                                 for (n = 0; n < newsysmay.length; n++) {
512                                         if (newsysmay[n] == x) {
513                                                 dup = true;
514                                         }
515                                 }
516                                 if (dup == false) {
517                                         newsysmay[n] = x;
518                                 }
519                         }
520                 }
521                 class["systemMayContain"] = newsysmay;
522         }
524         /* trim mayContain,
525          * remove duplicates */
526         var may = class["mayContain"];
527         if (may != undefined) {
528                 var newmay = new Array();
529                 if (typeof(may) == "string") {
530                         may = new Array(may);
531                 }
532                 for (j = 0;j < may.length; j++) {
533                         var x = may[j];
534                         var dup = false;
535                         if (newmay[0] == undefined) {
536                                 newmay[0] = x;
537                         } else {
538                                 for (n = 0; n < newmay.length; n++) {
539                                         if (newmay[n] == x) {
540                                                 dup = true;
541                                         }
542                                 }
543                                 if (dup == false) {
544                                         newmay[n] = x;
545                                 }
546                         }
547                 }
548                 class["mayContain"] = newmay;
549         }
553   load the basic attributes of an objectClass
555 function build_objectclass(ldb, name) {
556         var attrs = new Array("name");
557         var res = ldb.search(
558                 sprintf("(&(objectClass=classSchema)(ldapDisplayName=%s))", name),
559                 rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrs);
560         if (res.error != 0) {
561                 dprintf("unknown class '%s'\n", name);
562                 return undefined;
563         }
564         if (res.msgs.length == 0) {
565                 dprintf("unknown class '%s'\n", name);
566                 return undefined;
567         }
568         return obj_objectClass(ldb, name);
572   append 2 lists
574 function list_append(a1, a2) {
575         var i;
576         if (a1 == undefined) {
577                 return a2;
578         }
579         if (a2 == undefined) {
580                 return a1;
581         }
582         for (i=0;i<a2.length;i++) {
583                 a1[a1.length] = a2[i];
584         }
585         return a1;
589   form a coalesced attribute list
591 function attribute_list(class, attr1, attr2) {
592         var a1 = class[attr1];
593         var a2 = class[attr2];
594         if (typeof(a1) == "string") {
595                 a1 = new Array(a1);
596         }
597         if (typeof(a2) == "string") {
598                 a2 = new Array(a2);
599         }
600         return list_append(a1, a2);
604   write out a list in aggregate form
606 function aggregate_list(name, list) {
607         if (list == undefined) {
608                 return;
609         }
610         var i;
611         printf("%s ( ", name);
612         for (i=0;i<list.length;i++) {
613                 printf("%s ", list[i]);
614                 if (i < (list.length - 1)) {
615                         printf("$ ");
616                 }
617         }
618         printf(") ");
622   write the aggregate record for an objectclass
624 function write_aggregate_objectclass(class) {
625         printf("objectClasses: ( %s NAME '%s' ", class.governsID, class.name);
626         if (class['subClassOf'] != undefined) {
627                 printf("SUP %s ", class['subClassOf']);
628         }
629         if (class.objectClassCategory == 1) {
630                 printf("STRUCTURAL ");
631         } else if (class.objectClassCategory == 2) {
632                 printf("ABSTRACT ");
633         } else if (class.objectClassCategory == 3) {
634                 printf("AUXILIARY ");
635         }
637         var list;
639         list = attribute_list(class, "systemMustContain", "mustContain");
640         aggregate_list("MUST", list);
642         list = attribute_list(class, "systemMayContain", "mayContain");
643         aggregate_list("MAY", list);
645         printf(")\n");
650   write the aggregate record for an ditcontentrule
652 function write_aggregate_ditcontentrule(class) {
653         var list = attribute_list(class, "auxiliaryClass", "systemAuxiliaryClass");
654         var i;
655         if (list == undefined) {
656                 return;
657         }
659         printf("dITContentRules: ( %s NAME '%s' ", class.governsID, class.name);
661         aggregate_list("AUX", list);
663         var may_list = undefined;
664         var must_list = undefined;
666         for (i=0;i<list.length;i++) {
667                 var c = list[i];
668                 var list2;
669                 list2 = attribute_list(objectclasses[c], 
670                                        "mayContain", "systemMayContain");
671                 may_list = list_append(may_list, list2);
672                 list2 = attribute_list(objectclasses[c], 
673                                        "mustContain", "systemMustContain");
674                 must_list = list_append(must_list, list2);
675         }
677         aggregate_list("MUST", must_list);
678         aggregate_list("MAY", may_list);
680         printf(")\n");
684   write the aggregate record for an attribute
686 function write_aggregate_attribute(attrib) {
687         printf("attributeTypes: ( %s NAME '%s' SYNTAX '%s' ", 
688                attrib.attributeID, attrib.name, 
689                map_attribute_syntax(attrib.attributeSyntax));
690         if (attrib['isSingleValued'] == "TRUE") {
691                 printf("SINGLE-VALUE ");
692         }
693         if (attrib['systemOnly'] == "TRUE") {
694                 printf("NO-USER-MODIFICATION ");
695         }
697         printf(")\n");
702   write the aggregate record
704 function write_aggregate() {
705         printf("dn: CN=Aggregate,${SCHEMADN}\n");
706         print("objectClass: top
707 objectClass: subSchema
708 objectCategory: CN=SubSchema,${SCHEMADN}
710         if (dump_subschema_auto == undefined) {
711                 return; 
712         }
714         for (i in objectclasses) {
715                 write_aggregate_objectclass(objectclasses[i]);
716         }
717         for (i in attributes) {
718                 write_aggregate_attribute(attributes[i]);
719         }
720         for (i in objectclasses) {
721                 write_aggregate_ditcontentrule(objectclasses[i]);
722         }
726   load a list from a file
728 function load_list(file) {
729         var sys = sys_init();
730         var s = sys.file_load(file);
731         var a = split("\n", s);
732         return a;
735 /* get the rootDSE */
736 var res = ldb.search("", "", ldb.SCOPE_BASE);
737 rootDse = res.msgs[0];
739 /* load the list of classes we are interested in */
740 var classes = load_list(classfile);
741 var i;
742 for (i=0;i<classes.length;i++) {
743         var classname = classes[i];
744         var class = build_objectclass(ldb, classname);
745         if (class != undefined) {
746                 objectclasses[classname] = class;
747         }
752   expand the objectclass list as needed
754 var num_classes = 0;
755 var expanded = 0;
756 /* calculate the actual number of classes */
757 for (i in objectclasses) {
758         num_classes++;
760 /* so EJS do not have while nor the break statement
761    cannot find any other way than doing more loops
762    than necessary to recursively expand all classes
763  */
764 var inf;
765 for (inf = 0;inf < 500; inf++) {
766         if (expanded < num_classes) {
767                 for (i in objectclasses) {
768                         var n = objectclasses[i];
769                         if (objectclasses_expanded[i] != "DONE") {
770                                 expand_objectclass(ldb, objectclasses[i]);
771                                 objectclasses_expanded[i] = "DONE";
772                                 expanded++;
773                         }
774                 }
775                 /* recalculate the actual number of classes */
776                 num_classes = 0;
777                 for (i in objectclasses) {
778                         num_classes++;
779                 }
780         }
784   find objectclass properties
786 for (i in objectclasses) {
787         find_objectclass_properties(ldb, objectclasses[i]);
791   form the full list of attributes
793 for (i in objectclasses) {
794         add_objectclass_attributes(ldb, objectclasses[i]);
797 /* and attribute properties */
798 for (i in attributes) {
799         find_attribute_properties(ldb, attributes[i]);
803   trim the 'may' attribute lists to those really needed
805 for (i in objectclasses) {
806         trim_objectclass_attributes(ldb, objectclasses[i]);
810   dump an ldif form of the attributes and objectclasses
812 if (dump_attributes != undefined) {
813         write_ldif(attributes, attrib_attrs);
815 if (dump_classes != undefined) {
816         write_ldif(objectclasses, class_attrs);
818 if (dump_subschema != undefined) {
819         write_aggregate();
822 if (verbose == undefined) {
823         exit(0);
827   dump list of objectclasses
829 printf("objectClasses:\n")
830 for (i in objectclasses) {
831         printf("\t%s\n", i);
833 printf("attributes:\n")
834 for (i in attributes) {
835         printf("\t%s\n", i);
838 printf("autocreated attributes:\n");
839 for (i in attributes) {
840         if (attributes[i].autocreate == true) {
841                 printf("\t%s\n", i);
842         }
845 return 0;