r19299: Fix possible memleaks
[Samba/aatanasov.git] / testprogs / ejs / minschema.js
blobc51caaca84e3f060151601e8070b3bddf92c80cf
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 if (options == undefined) {
17    println("Failed to parse options");
18    return -1;
20 verbose = options["verbose"];
22 if (options.ARGV.length != 2) {
23    println("Usage: minschema.js <URL> <classfile>");
24    return -1;
27 var url = options.ARGV[0];
28 var classfile = options.ARGV[1];
30 /* use command line creds if available */
31 ldb.credentials = options.get_credentials();
33 var ok = ldb.connect(url);
34 assert(ok);
36 objectclasses = new Object();
37 attributes = new Object();
38 rootDse = new Object();
40 objectclasses_expanded = new Object();
42 /* the attributes we need for objectclasses */
43 class_attrs = new Array("objectClass", 
44                         "auxiliaryClass", "systemAuxiliaryClass",
45                         "possSuperiors", "systemPossSuperiors",
46                         "lDAPDisplayName", "governsID",
47                         "rDNAttID", "mustContain", "systemMustContain",
48                         "mayContain", "systemMayContain",
49                         "objectClassCategory", "subClassOf", 
50                         "defaultObjectCategory", "defaultHidingValue", 
51                         "systemFlags", "systemOnly", "defaultSecurityDescriptor",
52                         "objectCategory", "possibleInferiors", "displaySpecification",
53                         "schemaIDGUID");
55 attrib_attrs = new Array("objectClass", "lDAPDisplayName", 
56                          "isSingleValued", "linkID", "systemFlags", "systemOnly",
57                          "schemaIDGUID", "adminDisplayName", "attributeID",
58                          "attributeSyntax", "oMSyntax", "oMObjectClass");
61   notes:
63   objectClassCategory 
64       1: structural
65       2: abstract
66       3: auxiliary
71   print only if verbose is set
73 function dprintf() {
74         if (verbose != undefined) {
75                 print(vsprintf(arguments));
76         }
79 function get_object_cn(ldb, name) {
80         var attrs = new Array("cn");
82         var res = ldb.search(sprintf("(ldapDisplayName=%s)", name), rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrs);
83         assert(res != undefined);
84         assert(res.length == 1);
86         var cn = res[0]["cn"];
87         assert(cn != undefined);
88         if (typeof(cn) == "string") {
89                 return cn;
90         }
91         return cn[0];
94   create an objectclass object
96 function obj_objectClass(ldb, name) {
97         var o = new Object();
98         o.name = name;
99         o.cn = get_object_cn(ldb, name);
100         return o;
104   create an attribute object
106 function obj_attribute(ldb, name) {
107         var o = new Object();
108         o.name = name;
109         o.cn = get_object_cn(ldb, name);
110         return o;
114 syntaxmap = new Object();
116 syntaxmap['2.5.5.1']  = '1.3.6.1.4.1.1466.115.121.1.12';
117 syntaxmap['2.5.5.2']  = '1.3.6.1.4.1.1466.115.121.1.38';
118 syntaxmap['2.5.5.3']  = '1.2.840.113556.1.4.1362';
119 syntaxmap['2.5.5.4']  = '1.2.840.113556.1.4.905';
120 syntaxmap['2.5.5.5']  = '1.3.6.1.4.1.1466.115.121.1.26';
121 syntaxmap['2.5.5.6']  = '1.3.6.1.4.1.1466.115.121.1.36';
122 syntaxmap['2.5.5.7']  = '1.2.840.113556.1.4.903';
123 syntaxmap['2.5.5.8']  = '1.3.6.1.4.1.1466.115.121.1.7';
124 syntaxmap['2.5.5.9']  = '1.3.6.1.4.1.1466.115.121.1.27';
125 syntaxmap['2.5.5.10'] = '1.3.6.1.4.1.1466.115.121.1.40';
126 syntaxmap['2.5.5.11'] = '1.3.6.1.4.1.1466.115.121.1.24';
127 syntaxmap['2.5.5.12'] = '1.3.6.1.4.1.1466.115.121.1.15';
128 syntaxmap['2.5.5.13'] = '1.3.6.1.4.1.1466.115.121.1.43';
129 syntaxmap['2.5.5.14'] = '1.2.840.113556.1.4.904';
130 syntaxmap['2.5.5.15'] = '1.2.840.113556.1.4.907';
131 syntaxmap['2.5.5.16'] = '1.2.840.113556.1.4.906';
132 syntaxmap['2.5.5.17'] = '1.3.6.1.4.1.1466.115.121.1.40';
135   map some attribute syntaxes from some apparently MS specific
136   syntaxes to the standard syntaxes
138 function map_attribute_syntax(s) {
139         if (syntaxmap[s] != undefined) {
140                 return syntaxmap[s];
141         }
142         return s;
147   fix a string DN to use ${BASEDN}
149 function fix_dn(dn) {
150         var s = strstr(dn, rootDse.defaultNamingContext);
151         if (s == NULL) {
152                 return dn;
153         }
154         return substr(dn, 0, strlen(dn) - strlen(s)) + "${BASEDN}";
158   dump an object as ldif
160 function write_ldif_one(o, attrs) {
161         var i;
162         printf("dn: CN=%s,CN=Schema,CN=Configuration,${BASEDN}\n", o.cn);
163         printf("cn: %s\n", o.cn);
164         printf("name: %s\n", o.cn);
165         for (i=0;i<attrs.length;i++) {
166                 var a = attrs[i];
167                 if (o[a] == undefined) {
168                         continue;
169                 }
170                 /* special case for oMObjectClass, which is a binary object */
171                 if (a == "oMObjectClass") {
172                         printf("%s:: %s\n", a, o[a]);
173                         continue;
174                 }
175                 var v = o[a];
176                 if (typeof(v) == "string") {
177                         v = new Array(v);
178                 }
179                 var j;
180                 for (j=0;j<v.length;j++) {
181                         printf("%s: %s\n", a, fix_dn(v[j]));
182                 }
183         }
184         printf("\n");
188   dump an array of objects as ldif
190 function write_ldif(o, attrs) {
191         var i;
192         for (i in o) {
193                 write_ldif_one(o[i], attrs);
194         }
199   create a testDN based an an example DN
200   the idea is to ensure we obey any structural rules
202 function create_testdn(exampleDN) {
203         var a = split(",", exampleDN);
204         a[0] = "CN=TestDN";
205         return join(",", a);
209   find the properties of an objectclass
210  */
211 function find_objectclass_properties(ldb, o) {
212         var res = ldb.search(
213                 sprintf("(ldapDisplayName=%s)", o.name),
214                 rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, class_attrs);
215         assert(res != undefined);
216         assert(res.length == 1);
217         var msg = res[0];
218         var a;
219         for (a in msg) {
220                 o[a] = msg[a];
221         }
225   find the properties of an attribute
226  */
227 function find_attribute_properties(ldb, o) {
228         var res = ldb.search(
229                 sprintf("(ldapDisplayName=%s)", o.name),
230                 rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrib_attrs);
231         assert(res != undefined);
232         assert(res.length == 1);
233         var msg = res[0];
234         var a;
235         for (a in msg) {
236                 /* special case for oMObjectClass, which is a binary object */
237                 if (a == "oMObjectClass") {
238                         o[a] = ldb.encode(msg[a]);
239                         continue;
240                 }
241                 o[a] = msg[a];
242         }
246   find the auto-created properties of an objectclass. Only works for classes
247   that can be created using just a DN and the objectclass
248  */
249 function find_objectclass_auto(ldb, o) {
250         if (o["exampleDN"] == undefined) {
251                 return;
252         }
253         var testdn = create_testdn(o.exampleDN);
254         var ok;
256         dprintf("testdn is '%s'\n", testdn);
258         var ldif = "dn: " + testdn;
259         ldif = ldif + "\nobjectClass: " + o.name;
260         ok = ldb.add(ldif);
261         if (!ok) {
262                 dprintf("error adding %s: %s\n", o.name, ldb.errstring());
263                 dprintf("%s\n", ldif);
264                 return;
265         }
267         var res = ldb.search("", testdn, ldb.SCOPE_BASE);
268         ok = ldb.del(testdn);
269         assert(ok);
271         var a;
272         for (a in res[0]) {
273                 attributes[a].autocreate = true;
274         }
279   look at auxiliary information from a class to intuit the existance of more
280   classes needed for a minimal schema
282 function expand_objectclass(ldb, o) {
283         var attrs = new Array("auxiliaryClass", "systemAuxiliaryClass",
284                               "possSuperiors", "systemPossSuperiors",
285                               "subClassOf");
286         var res = ldb.search(
287                 sprintf("(&(objectClass=classSchema)(ldapDisplayName=%s))", o.name),
288                 rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrs);
289         var a;
290         dprintf("Expanding class %s\n", o.name);
291         assert(res != undefined);
292         assert(res.length == 1);
293         var msg = res[0];
294         for (a=0;a<attrs.length;a++) {
295                 var aname = attrs[a];
296                 if (msg[aname] == undefined) {
297                         continue;
298                 }
299                 var list = msg[aname];
300                 if (typeof(list) == "string") {
301                         list = new Array(msg[aname]);
302                 }
303                 var i;
304                 for (i=0;i<list.length;i++) {
305                         var name = list[i];
306                         if (objectclasses[name] == undefined) {
307                                 dprintf("Found new objectclass '%s'\n", name);
308                                 objectclasses[name] = obj_objectClass(ldb, name);
309                         }
310                 }
311         }
316   add the must and may attributes from an objectclass to the full list
317   of attributes
319 function add_objectclass_attributes(ldb, class) {
320         var attrs = new Array("mustContain", "systemMustContain", 
321                               "mayContain", "systemMayContain");
322         var i;
323         for (i=0;i<attrs.length;i++) {
324                 var aname = attrs[i];
325                 if (class[aname] == undefined) {
326                         continue;
327                 }
328                 var alist = class[aname];
329                 if (typeof(alist) == "string") {
330                         alist = new Array(alist);
331                 }
332                 var j;
333                 var len = alist.length;
334                 for (j=0;j<len;j++) {
335                         var a = alist[j];
336                         if (attributes[a] == undefined) {
337                                 attributes[a] = obj_attribute(ldb, a);
338                         }
339                 }
340         }
345   process an individual record, working out what attributes it has
347 function walk_dn(ldb, dn) {
348         /* get a list of all possible attributes for this object */
349         var attrs = new Array("allowedAttributes");
350         var res = ldb.search("objectClass=*", dn, ldb.SCOPE_BASE, attrs);
351         if (res == undefined) {
352                 dprintf("Unable to fetch allowedAttributes for '%s' - %s\n", 
353                        dn, ldb.errstring());
354                 return;
355         }
356         var allattrs = res[0].allowedAttributes;
357         res = ldb.search("objectClass=*", dn, ldb.SCOPE_BASE, allattrs);
358         if (res == undefined) {
359                 dprintf("Unable to fetch all attributes for '%s' - %s\n", 
360                        dn, ldb.errstring());
361                 return;
362         }
363         var a;
364         var msg = res[0];
365         for (a in msg) {
366                 if (attributes[a] == undefined) {
367                         attributes[a] = obj_attribute(ldb, a);
368                 }
369         }
373   walk a naming context, looking for all records
375 function walk_naming_context(ldb, namingContext) {
376         var attrs = new Array("objectClass");
377         var res = ldb.search("objectClass=*", namingContext, ldb.SCOPE_DEFAULT, attrs);
378         if (res == undefined) {
379                 dprintf("Unable to fetch objectClasses for '%s' - %s\n", 
380                        namingContext, ldb.errstring());
381                 return;
382         }
383         var r;
384         for (r=0;r<res.length;r++) {
385                 var msg = res[r].objectClass;
386                 var c;
387                 for (c=0;c<msg.length;c++) {
388                         var objectClass = msg[c];
389                         if (objectclasses[objectClass] == undefined) {
390                                 objectclasses[objectClass] = obj_objectClass(ldb, objectClass);
391                                 objectclasses[objectClass].exampleDN = res[r].dn;
392                         }
393                 }
394                 walk_dn(ldb, res[r].dn);
395         }
399   trim the may attributes for an objectClass
401 function trim_objectclass_attributes(ldb, class) {
402         var i,j,n;
404         /* trim possibleInferiors,
405          * include only the classes we extracted */
406         var possinf = class["possibleInferiors"];
407         if (possinf != undefined) {
408                 var newpossinf = new Array();
409                 if (typeof(possinf) == "string") {
410                         possinf = new Array(possinf);
411                 }
412                 n = 0;
413                 for (j = 0;j < possinf.length; j++) {
414                         var x = possinf[j];
415                         if (objectclasses[x] != undefined) {
416                                 newpossinf[n] = x;
417                                 n++;
418                         }
419                 }
420                 class["possibleInferiors"] = newpossinf;
421         }
423         /* trim systemMayContain,
424          * remove duplicates */
425         var sysmay = class["systemMayContain"];
426         if (sysmay != undefined) {
427                 var newsysmay = new Array();
428                 if (typeof(sysmay) == "string") {
429                         sysmay = new Array(sysmay);
430                 }
431                 for (j = 0;j < sysmay.length; j++) {
432                         var x = sysmay[j];
433                         var dup = false;
434                         if (newsysmay[0] == undefined) {
435                                 newsysmay[0] = x;
436                         } else {
437                                 for (n = 0; n < newsysmay.length; n++) {
438                                         if (newsysmay[n] == x) {
439                                                 dup = true;
440                                         }
441                                 }
442                                 if (dup == false) {
443                                         newsysmay[n] = x;
444                                 }
445                         }
446                 }
447                 class["systemMayContain"] = newsysmay;
448         }
450         /* trim mayContain,
451          * remove duplicates */
452         var may = class["mayContain"];
453         if (may != undefined) {
454                 var newmay = new Array();
455                 if (typeof(may) == "string") {
456                         may = new Array(may);
457                 }
458                 for (j = 0;j < may.length; j++) {
459                         var x = may[j];
460                         var dup = false;
461                         if (newmay[0] == undefined) {
462                                 newmay[0] = x;
463                         } else {
464                                 for (n = 0; n < newmay.length; n++) {
465                                         if (newmay[n] == x) {
466                                                 dup = true;
467                                         }
468                                 }
469                                 if (dup == false) {
470                                         newmay[n] = x;
471                                 }
472                         }
473                 }
474                 class["mayContain"] = newmay;
475         }
479   load the basic attributes of an objectClass
481 function build_objectclass(ldb, name) {
482         var attrs = new Array("name");
483         var res = ldb.search(
484                 sprintf("(&(objectClass=classSchema)(ldapDisplayName=%s))", name),
485                 rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrs);
486         if (res == undefined) {
487                 dprintf("unknown class '%s'\n", name);
488                 return undefined;
489         }
490         if (res.length == 0) {
491                 dprintf("unknown class '%s'\n", name);
492                 return undefined;
493         }
494         return obj_objectClass(ldb, name);
498   append 2 lists
500 function list_append(a1, a2) {
501         var i;
502         if (a1 == undefined) {
503                 return a2;
504         }
505         if (a2 == undefined) {
506                 return a1;
507         }
508         for (i=0;i<a2.length;i++) {
509                 a1[a1.length] = a2[i];
510         }
511         return a1;
515   form a coalesced attribute list
517 function attribute_list(class, attr1, attr2) {
518         var a1 = class[attr1];
519         var a2 = class[attr2];
520         if (typeof(a1) == "string") {
521                 a1 = new Array(a1);
522         }
523         if (typeof(a2) == "string") {
524                 a2 = new Array(a2);
525         }
526         return list_append(a1, a2);
530   write out a list in aggregate form
532 function aggregate_list(name, list) {
533         if (list == undefined) {
534                 return;
535         }
536         var i;
537         printf("%s ( ", name);
538         for (i=0;i<list.length;i++) {
539                 printf("%s ", list[i]);
540                 if (i < (list.length - 1)) {
541                         printf("$ ");
542                 }
543         }
544         printf(") ");
548   write the aggregate record for an objectclass
550 function write_aggregate_objectclass(class) {
551         printf("objectClasses: ( %s NAME '%s' ", class.governsID, class.name);
552         if (class['subClassOf'] != undefined) {
553                 printf("SUP %s ", class['subClassOf']);
554         }
555         if (class.objectClassCategory == 1) {
556                 printf("STRUCTURAL ");
557         } else if (class.objectClassCategory == 2) {
558                 printf("ABSTRACT ");
559         } else if (class.objectClassCategory == 3) {
560                 printf("AUXILIARY ");
561         }
563         var list;
565         list = attribute_list(class, "systemMustContain", "mustContain");
566         aggregate_list("MUST", list);
568         list = attribute_list(class, "systemMayContain", "mayContain");
569         aggregate_list("MAY", list);
571         printf(")\n");
576   write the aggregate record for an ditcontentrule
578 function write_aggregate_ditcontentrule(class) {
579         var list = attribute_list(class, "auxiliaryClass", "systemAuxiliaryClass");
580         var i;
581         if (list == undefined) {
582                 return;
583         }
585         printf("dITContentRules: ( %s NAME '%s' ", class.governsID, class.name);
587         aggregate_list("AUX", list);
589         var may_list = undefined;
590         var must_list = undefined;
592         for (i=0;i<list.length;i++) {
593                 var c = list[i];
594                 var list2;
595                 list2 = attribute_list(objectclasses[c], 
596                                        "mayContain", "systemMayContain");
597                 may_list = list_append(may_list, list2);
598                 list2 = attribute_list(objectclasses[c], 
599                                        "mustContain", "systemMustContain");
600                 must_list = list_append(must_list, list2);
601         }
603         aggregate_list("MUST", must_list);
604         aggregate_list("MAY", may_list);
606         printf(")\n");
610   write the aggregate record for an attribute
612 function write_aggregate_attribute(attrib) {
613         printf("attributeTypes: ( %s NAME '%s' SYNTAX '%s' ", 
614                attrib.attributeID, attrib.name, 
615                map_attribute_syntax(attrib.attributeSyntax));
616         if (attrib['isSingleValued'] == "TRUE") {
617                 printf("SINGLE-VALUE ");
618         }
619         printf(")\n");
624   write the aggregate record
626 function write_aggregate() {
627         printf("dn: CN=Aggregate,CN=Schema,CN=Configuration,${BASEDN}\n");
628         print("objectClass: top
629 objectClass: subSchema
630 cn: Aggregate
631 instanceType: 4
632 name: Aggregate
633 objectCategory: CN=SubSchema,CN=Schema,CN=Configuration,${BASEDN}
635         for (i in objectclasses) {
636                 write_aggregate_objectclass(objectclasses[i]);
637         }
638         for (i in attributes) {
639                 write_aggregate_attribute(attributes[i]);
640         }
641         for (i in objectclasses) {
642                 write_aggregate_ditcontentrule(objectclasses[i]);
643         }
647   load a list from a file
649 function load_list(file) {
650         var sys = sys_init();
651         var s = sys.file_load(file);
652         var a = split("\n", s);
653         return a;
656 /* get the rootDSE */
657 var res = ldb.search("", "", ldb.SCOPE_BASE);
658 rootDse = res[0];
660 /* load the list of classes we are interested in */
661 var classes = load_list(classfile);
662 var i;
663 for (i=0;i<classes.length;i++) {
664         var classname = classes[i];
665         var class = build_objectclass(ldb, classname);
666         if (class != undefined) {
667                 objectclasses[classname] = class;
668         }
673   expand the objectclass list as needed
675 var num_classes = 0;
676 var expanded = 0;
677 /* calculate the actual number of classes */
678 for (i in objectclasses) {
679         num_classes++;
681 /* so EJS do not have while nor the break statement
682    can't find any other way than doing more loops
683    than necessary to recursively expand all classes
684  */
685 var inf;
686 for (inf = 0;inf < 500; inf++) {
687         if (expanded < num_classes) {
688                 for (i in objectclasses) {
689                         var n = objectclasses[i];
690                         if (objectclasses_expanded[i] != "DONE") {
691                                 expand_objectclass(ldb, objectclasses[i]);
692                                 objectclasses_expanded[i] = "DONE";
693                                 expanded++;
694                         }
695                 }
696                 /* recalculate the actual number of classes */
697                 num_classes = 0;
698                 for (i in objectclasses) {
699                         num_classes++;
700                 }
701         }
705   find objectclass properties
707 for (i in objectclasses) {
708         find_objectclass_properties(ldb, objectclasses[i]);
712   form the full list of attributes
714 for (i in objectclasses) {
715         add_objectclass_attributes(ldb, objectclasses[i]);
718 /* and attribute properties */
719 for (i in attributes) {
720         find_attribute_properties(ldb, attributes[i]);
724   trim the 'may' attribute lists to those really needed
726 for (i in objectclasses) {
727         trim_objectclass_attributes(ldb, objectclasses[i]);
731   dump an ldif form of the attributes and objectclasses
733 write_ldif(attributes, attrib_attrs);
734 write_ldif(objectclasses, class_attrs);
736 write_aggregate();
738 if (verbose == undefined) {
739         exit(0);
743   dump list of objectclasses
745 printf("objectClasses:\n")
746 for (i in objectclasses) {
747         printf("\t%s\n", i);
749 printf("attributes:\n")
750 for (i in attributes) {
751         printf("\t%s\n", i);
754 printf("autocreated attributes:\n");
755 for (i in attributes) {
756         if (attributes[i].autocreate == true) {
757                 printf("\t%s\n", i);
758         }
761 return 0;