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",
16 if (options == undefined) {
17 println("Failed to parse options");
20 verbose = options["verbose"];
22 if (options.ARGV.length != 2) {
23 println("Usage: minschema.js <URL> <classfile>");
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);
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",
55 attrib_attrs = new Array("objectClass", "lDAPDisplayName",
56 "isSingleValued", "linkID", "systemFlags", "systemOnly",
57 "schemaIDGUID", "adminDisplayName", "attributeID",
58 "attributeSyntax", "oMSyntax", "oMObjectClass");
71 print only if verbose is set
74 if (verbose != undefined) {
75 print(vsprintf(arguments));
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") {
94 create an objectclass object
96 function obj_objectClass(ldb, name) {
99 o.cn = get_object_cn(ldb, name);
104 create an attribute object
106 function obj_attribute(ldb, name) {
107 var o = new Object();
109 o.cn = get_object_cn(ldb, name);
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) {
147 fix a string DN to use ${BASEDN}
149 function fix_dn(dn) {
150 var s = strstr(dn, rootDse.defaultNamingContext);
154 return substr(dn, 0, strlen(dn) - strlen(s)) + "${BASEDN}";
158 dump an object as ldif
160 function write_ldif_one(o, attrs) {
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++) {
167 if (o[a] == undefined) {
170 /* special case for oMObjectClass, which is a binary object */
171 if (a == "oMObjectClass") {
172 printf("%s:: %s\n", a, o[a]);
176 if (typeof(v) == "string") {
180 for (j=0;j<v.length;j++) {
181 printf("%s: %s\n", a, fix_dn(v[j]));
188 dump an array of objects as ldif
190 function write_ldif(o, attrs) {
193 write_ldif_one(o[i], attrs);
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);
209 find the properties of an objectclass
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);
225 find the properties of an attribute
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);
236 /* special case for oMObjectClass, which is a binary object */
237 if (a == "oMObjectClass") {
238 o[a] = ldb.encode(msg[a]);
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
249 function find_objectclass_auto(ldb, o) {
250 if (o["exampleDN"] == undefined) {
253 var testdn = create_testdn(o.exampleDN);
256 dprintf("testdn is '%s'\n", testdn);
258 var ldif = "dn: " + testdn;
259 ldif = ldif + "\nobjectClass: " + o.name;
262 dprintf("error adding %s: %s\n", o.name, ldb.errstring());
263 dprintf("%s\n", ldif);
267 var res = ldb.search("", testdn, ldb.SCOPE_BASE);
268 ok = ldb.del(testdn);
273 attributes[a].autocreate = true;
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",
286 var res = ldb.search(
287 sprintf("(&(objectClass=classSchema)(ldapDisplayName=%s))", o.name),
288 rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrs);
290 dprintf("Expanding class %s\n", o.name);
291 assert(res != undefined);
292 assert(res.length == 1);
294 for (a=0;a<attrs.length;a++) {
295 var aname = attrs[a];
296 if (msg[aname] == undefined) {
299 var list = msg[aname];
300 if (typeof(list) == "string") {
301 list = new Array(msg[aname]);
304 for (i=0;i<list.length;i++) {
306 if (objectclasses[name] == undefined) {
307 dprintf("Found new objectclass '%s'\n", name);
308 objectclasses[name] = obj_objectClass(ldb, name);
316 add the must and may attributes from an objectclass to the full list
319 function add_objectclass_attributes(ldb, class) {
320 var attrs = new Array("mustContain", "systemMustContain",
321 "mayContain", "systemMayContain");
323 for (i=0;i<attrs.length;i++) {
324 var aname = attrs[i];
325 if (class[aname] == undefined) {
328 var alist = class[aname];
329 if (typeof(alist) == "string") {
330 alist = new Array(alist);
333 var len = alist.length;
334 for (j=0;j<len;j++) {
336 if (attributes[a] == undefined) {
337 attributes[a] = obj_attribute(ldb, a);
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());
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());
366 if (attributes[a] == undefined) {
367 attributes[a] = obj_attribute(ldb, a);
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());
384 for (r=0;r<res.length;r++) {
385 var msg = res[r].objectClass;
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;
394 walk_dn(ldb, res[r].dn);
399 trim the may attributes for an objectClass
401 function trim_objectclass_attributes(ldb, class) {
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);
413 for (j = 0;j < possinf.length; j++) {
415 if (objectclasses[x] != undefined) {
420 class["possibleInferiors"] = newpossinf;
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);
431 for (j = 0;j < sysmay.length; j++) {
434 if (newsysmay[0] == undefined) {
437 for (n = 0; n < newsysmay.length; n++) {
438 if (newsysmay[n] == x) {
447 class["systemMayContain"] = newsysmay;
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);
458 for (j = 0;j < may.length; j++) {
461 if (newmay[0] == undefined) {
464 for (n = 0; n < newmay.length; n++) {
465 if (newmay[n] == x) {
474 class["mayContain"] = newmay;
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);
490 if (res.length == 0) {
491 dprintf("unknown class '%s'\n", name);
494 return obj_objectClass(ldb, name);
500 function list_append(a1, a2) {
502 if (a1 == undefined) {
505 if (a2 == undefined) {
508 for (i=0;i<a2.length;i++) {
509 a1[a1.length] = a2[i];
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") {
523 if (typeof(a2) == "string") {
526 return list_append(a1, a2);
530 write out a list in aggregate form
532 function aggregate_list(name, list) {
533 if (list == undefined) {
537 printf("%s ( ", name);
538 for (i=0;i<list.length;i++) {
539 printf("%s ", list[i]);
540 if (i < (list.length - 1)) {
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']);
555 if (class.objectClassCategory == 1) {
556 printf("STRUCTURAL ");
557 } else if (class.objectClassCategory == 2) {
559 } else if (class.objectClassCategory == 3) {
560 printf("AUXILIARY ");
565 list = attribute_list(class, "systemMustContain", "mustContain");
566 aggregate_list("MUST", list);
568 list = attribute_list(class, "systemMayContain", "mayContain");
569 aggregate_list("MAY", list);
576 write the aggregate record for an ditcontentrule
578 function write_aggregate_ditcontentrule(class) {
579 var list = attribute_list(class, "auxiliaryClass", "systemAuxiliaryClass");
581 if (list == undefined) {
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++) {
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);
603 aggregate_list("MUST", must_list);
604 aggregate_list("MAY", may_list);
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 ");
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
633 objectCategory: CN=SubSchema,CN=Schema,CN=Configuration,${BASEDN}
635 for (i in objectclasses) {
636 write_aggregate_objectclass(objectclasses[i]);
638 for (i in attributes) {
639 write_aggregate_attribute(attributes[i]);
641 for (i in objectclasses) {
642 write_aggregate_ditcontentrule(objectclasses[i]);
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);
656 /* get the rootDSE */
657 var res = ldb.search("", "", ldb.SCOPE_BASE);
660 /* load the list of classes we are interested in */
661 var classes = load_list(classfile);
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;
673 expand the objectclass list as needed
677 /* calculate the actual number of classes */
678 for (i in objectclasses) {
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
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";
696 /* recalculate the actual number of classes */
698 for (i in objectclasses) {
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);
738 if (verbose == undefined) {
743 dump list of objectclasses
745 printf("objectClasses:\n")
746 for (i in objectclasses) {
749 printf("attributes:\n")
750 for (i in attributes) {
754 printf("autocreated attributes:\n");
755 for (i in attributes) {
756 if (attributes[i].autocreate == true) {