2 exec smbscript "$0" ${1+"$@"}
4 work out the minimal schema for a set of objectclasses
11 var options = GetOptions(ARGV,
14 "POPT_COMMON_CREDENTIALS",
20 if (options == undefined) {
21 println("Failed to parse options");
24 verbose = options["verbose"];
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) {
34 if (dump_attributes != undefined) {
37 if (dump_subschema != undefined) {
40 if (dump_subschema_auto != undefined) {
42 dump_subschema = "yes";
44 if (dump_all != undefined) {
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>");
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);
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",
80 "showInAdvancedViewOnly",
83 "objectClassCategory",
87 "systemPossSuperiors",
90 "systemAuxiliaryClass",
91 "defaultSecurityDescriptor",
95 "defaultObjectCategory",
97 /* this attributes are not used by w2k3 */
100 "msDs-Schema-Extensions",
105 attrib_attrs = new Array("objectClass",
113 "showInAdvancedViewOnly",
119 "extendedCharsAllowed",
122 "attributeSecurityGUID",
125 "isMemberOfPartialAttributeSet",
128 /* this attributes are not used by w2k3 */
131 "msDs-Schema-Extensions",
147 print only if verbose is set
150 if (verbose != undefined) {
151 print(vsprintf(arguments));
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") {
170 create an objectclass object
172 function obj_objectClass(ldb, name) {
173 var o = new Object();
175 o.cn = get_object_cn(ldb, name);
180 create an attribute object
182 function obj_attribute(ldb, name) {
183 var o = new Object();
185 o.cn = get_object_cn(ldb, name);
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) {
223 fix a string DN to use ${SCHEMADN}
225 function fix_dn(dn) {
226 var s = strstr(dn, rootDse.schemaNamingContext);
230 return substr(dn, 0, strlen(dn) - strlen(s)) + "${SCHEMADN}";
234 dump an object as ldif
236 function write_ldif_one(o, attrs) {
238 printf("dn: CN=%s,${SCHEMADN}\n", o.cn);
239 for (i=0;i<attrs.length;i++) {
241 if (o[a] == undefined) {
244 /* special case for oMObjectClass, which is a binary object */
245 if (a == "oMObjectClass") {
246 printf("%s:: %s\n", a, o[a]);
250 if (typeof(v) == "string") {
254 for (j=0;j<v.length;j++) {
255 printf("%s: %s\n", a, fix_dn(v[j]));
262 dump an array of objects as ldif
264 function write_ldif(o, attrs) {
267 write_ldif_one(o[i], attrs);
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);
283 find the properties of an objectclass
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];
299 find the properties of an attribute
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];
310 /* special case for oMObjectClass, which is a binary object */
311 if (a == "oMObjectClass") {
312 o[a] = ldb.encode(msg[a]);
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
323 function find_objectclass_auto(ldb, o) {
324 if (o["exampleDN"] == undefined) {
327 var testdn = create_testdn(o.exampleDN);
330 dprintf("testdn is '%s'\n", testdn);
332 var ldif = "dn: " + testdn;
333 ldif = ldif + "\nobjectClass: " + o.name;
336 dprintf("error adding %s: %s\n", o.name, ok.errstr);
337 dprintf("%s\n", ldif);
341 var res = ldb.search("", testdn, ldb.SCOPE_BASE);
342 ok = ldb.del(testdn);
343 assert(ok.error == 0);
346 for (a in res.msgs[0]) {
347 attributes[a].autocreate = true;
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",
360 var res = ldb.search(
361 sprintf("(&(objectClass=classSchema)(ldapDisplayName=%s))", o.name),
362 rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrs);
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) {
373 var list = msg[aname];
374 if (typeof(list) == "string") {
375 list = new Array(msg[aname]);
378 for (i=0;i<list.length;i++) {
380 if (objectclasses[name] == undefined) {
381 dprintf("Found new objectclass '%s'\n", name);
382 objectclasses[name] = obj_objectClass(ldb, name);
390 add the must and may attributes from an objectclass to the full list
393 function add_objectclass_attributes(ldb, class) {
394 var attrs = new Array("mustContain", "systemMustContain",
395 "mayContain", "systemMayContain");
397 for (i=0;i<attrs.length;i++) {
398 var aname = attrs[i];
399 if (class[aname] == undefined) {
402 var alist = class[aname];
403 if (typeof(alist) == "string") {
404 alist = new Array(alist);
407 var len = alist.length;
408 for (j=0;j<len;j++) {
410 if (attributes[a] == undefined) {
411 attributes[a] = obj_attribute(ldb, a);
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",
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",
438 var msg = res.msgs[0];
440 if (attributes[a] == undefined) {
441 attributes[a] = obj_attribute(ldb, a);
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);
458 for (r=0;r<res.msgs.length;r++) {
459 var msg = res.msgs[r].objectClass;
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;
468 walk_dn(ldb, res.msgs[r].dn);
473 trim the may attributes for an objectClass
475 function trim_objectclass_attributes(ldb, class) {
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);
487 for (j = 0;j < possinf.length; j++) {
489 if (objectclasses[x] != undefined) {
494 class["possibleInferiors"] = newpossinf;
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);
505 for (j = 0;j < sysmay.length; j++) {
508 if (newsysmay[0] == undefined) {
511 for (n = 0; n < newsysmay.length; n++) {
512 if (newsysmay[n] == x) {
521 class["systemMayContain"] = newsysmay;
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);
532 for (j = 0;j < may.length; j++) {
535 if (newmay[0] == undefined) {
538 for (n = 0; n < newmay.length; n++) {
539 if (newmay[n] == x) {
548 class["mayContain"] = newmay;
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);
564 if (res.msgs.length == 0) {
565 dprintf("unknown class '%s'\n", name);
568 return obj_objectClass(ldb, name);
574 function list_append(a1, a2) {
576 if (a1 == undefined) {
579 if (a2 == undefined) {
582 for (i=0;i<a2.length;i++) {
583 a1[a1.length] = a2[i];
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") {
597 if (typeof(a2) == "string") {
600 return list_append(a1, a2);
604 write out a list in aggregate form
606 function aggregate_list(name, list) {
607 if (list == undefined) {
611 printf("%s ( ", name);
612 for (i=0;i<list.length;i++) {
613 printf("%s ", list[i]);
614 if (i < (list.length - 1)) {
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']);
629 if (class.objectClassCategory == 1) {
630 printf("STRUCTURAL ");
631 } else if (class.objectClassCategory == 2) {
633 } else if (class.objectClassCategory == 3) {
634 printf("AUXILIARY ");
639 list = attribute_list(class, "systemMustContain", "mustContain");
640 aggregate_list("MUST", list);
642 list = attribute_list(class, "systemMayContain", "mayContain");
643 aggregate_list("MAY", list);
650 write the aggregate record for an ditcontentrule
652 function write_aggregate_ditcontentrule(class) {
653 var list = attribute_list(class, "auxiliaryClass", "systemAuxiliaryClass");
655 if (list == undefined) {
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++) {
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);
677 aggregate_list("MUST", must_list);
678 aggregate_list("MAY", may_list);
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 ");
693 if (attrib['systemOnly'] == "TRUE") {
694 printf("NO-USER-MODIFICATION ");
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) {
714 for (i in objectclasses) {
715 write_aggregate_objectclass(objectclasses[i]);
717 for (i in attributes) {
718 write_aggregate_attribute(attributes[i]);
720 for (i in objectclasses) {
721 write_aggregate_ditcontentrule(objectclasses[i]);
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);
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);
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;
752 expand the objectclass list as needed
756 /* calculate the actual number of classes */
757 for (i in objectclasses) {
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
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";
775 /* recalculate the actual number of classes */
777 for (i in objectclasses) {
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) {
822 if (verbose == undefined) {
827 dump list of objectclasses
829 printf("objectClasses:\n")
830 for (i in objectclasses) {
833 printf("attributes:\n")
834 for (i in attributes) {
838 printf("autocreated attributes:\n");
839 for (i in attributes) {
840 if (attributes[i].autocreate == true) {