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();
41 /* the attributes we need for objectclasses */
42 class_attrs = new Array("objectClass",
43 "auxiliaryClass", "systemAuxiliaryClass",
44 "possSuperiors", "systemPossSuperiors",
45 "lDAPDisplayName", "governsID",
46 "rDNAttID", "mustContain", "systemMustContain",
47 "mayContain", "systemMayContain",
48 "objectClassCategory", "subClassOf",
49 "defaultObjectCategory", "defaultHidingValue",
50 "systemFlags", "systemOnly", "defaultSecurityDescriptor",
51 "objectCategory", "possibleInferiors", "displaySpecification",
54 attrib_attrs = new Array("objectClass", "lDAPDisplayName",
55 "isSingleValued", "linkID", "systemFlags", "systemOnly",
56 "schemaIDGUID", "adminDisplayName", "attributeID",
57 "attributeSyntax", "oMSyntax", "oMObjectClass");
70 print only if verbose is set
73 if (verbose != undefined) {
74 print(vsprintf(arguments));
78 function get_object_cn(ldb, name) {
79 var attrs = new Array("cn");
81 var res = ldb.search(sprintf("(ldapDisplayName=%s)", name), rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrs);
82 assert(res != undefined);
83 assert(res.length == 1);
85 var cn = res[0]["cn"];
86 assert(cn != undefined);
87 if (typeof(cn) == "string") {
93 create an objectclass object
95 function obj_objectClass(ldb, name) {
98 o.cn = get_object_cn(ldb, name);
103 create an attribute object
105 function obj_attribute(ldb, name) {
106 var o = new Object();
108 o.cn = get_object_cn(ldb, name);
113 syntaxmap = new Object();
115 syntaxmap['2.5.5.1'] = '1.3.6.1.4.1.1466.115.121.1.12';
116 syntaxmap['2.5.5.2'] = '1.3.6.1.4.1.1466.115.121.1.38';
117 syntaxmap['2.5.5.3'] = '1.2.840.113556.1.4.1362';
118 syntaxmap['2.5.5.4'] = '1.2.840.113556.1.4.905';
119 syntaxmap['2.5.5.5'] = '1.3.6.1.4.1.1466.115.121.1.26';
120 syntaxmap['2.5.5.6'] = '1.3.6.1.4.1.1466.115.121.1.36';
121 syntaxmap['2.5.5.7'] = '1.2.840.113556.1.4.903';
122 syntaxmap['2.5.5.8'] = '1.3.6.1.4.1.1466.115.121.1.7';
123 syntaxmap['2.5.5.9'] = '1.3.6.1.4.1.1466.115.121.1.27';
124 syntaxmap['2.5.5.10'] = '1.3.6.1.4.1.1466.115.121.1.40';
125 syntaxmap['2.5.5.11'] = '1.3.6.1.4.1.1466.115.121.1.24';
126 syntaxmap['2.5.5.12'] = '1.3.6.1.4.1.1466.115.121.1.15';
127 syntaxmap['2.5.5.13'] = '1.3.6.1.4.1.1466.115.121.1.43';
128 syntaxmap['2.5.5.14'] = '1.2.840.113556.1.4.904';
129 syntaxmap['2.5.5.15'] = '1.2.840.113556.1.4.907';
130 syntaxmap['2.5.5.16'] = '1.2.840.113556.1.4.906';
131 syntaxmap['2.5.5.17'] = '1.3.6.1.4.1.1466.115.121.1.40';
134 map some attribute syntaxes from some apparently MS specific
135 syntaxes to the standard syntaxes
137 function map_attribute_syntax(s) {
138 if (syntaxmap[s] != undefined) {
146 fix a string DN to use ${BASEDN}
148 function fix_dn(dn) {
149 var s = strstr(dn, rootDse.defaultNamingContext);
153 return substr(dn, 0, strlen(dn) - strlen(s)) + "${BASEDN}";
157 dump an object as ldif
159 function write_ldif_one(o, attrs) {
161 printf("dn: CN=%s,CN=Schema,CN=Configuration,${BASEDN}\n", o.cn);
162 printf("cn: %s\n", o.cn);
163 printf("name: %s\n", o.cn);
164 for (i=0;i<attrs.length;i++) {
166 if (o[a] == undefined) {
169 /* special case for oMObjectClass, which is a binary object */
170 if (a == "oMObjectClass") {
171 printf("%s:: %s\n", a, o[a]);
175 if (typeof(v) == "string") {
179 for (j=0;j<v.length;j++) {
180 printf("%s: %s\n", a, fix_dn(v[j]));
187 dump an array of objects as ldif
189 function write_ldif(o, attrs) {
192 write_ldif_one(o[i], attrs);
198 create a testDN based an an example DN
199 the idea is to ensure we obey any structural rules
201 function create_testdn(exampleDN) {
202 var a = split(",", exampleDN);
208 find the properties of an objectclass
210 function find_objectclass_properties(ldb, o) {
211 var res = ldb.search(
212 sprintf("(ldapDisplayName=%s)", o.name),
213 rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, class_attrs);
214 assert(res != undefined);
215 assert(res.length == 1);
224 find the properties of an attribute
226 function find_attribute_properties(ldb, o) {
227 var res = ldb.search(
228 sprintf("(ldapDisplayName=%s)", o.name),
229 rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrib_attrs);
230 assert(res != undefined);
231 assert(res.length == 1);
235 /* special case for oMObjectClass, which is a binary object */
236 if (a == "oMObjectClass") {
237 o[a] = ldb.encode(msg[a]);
245 find the auto-created properties of an objectclass. Only works for classes
246 that can be created using just a DN and the objectclass
248 function find_objectclass_auto(ldb, o) {
249 if (o["exampleDN"] == undefined) {
252 var testdn = create_testdn(o.exampleDN);
255 dprintf("testdn is '%s'\n", testdn);
257 var ldif = "dn: " + testdn;
258 ldif = ldif + "\nobjectClass: " + o.name;
261 dprintf("error adding %s: %s\n", o.name, ldb.errstring());
262 dprintf("%s\n", ldif);
266 var res = ldb.search("", testdn, ldb.SCOPE_BASE);
267 ok = ldb.del(testdn);
272 attributes[a].autocreate = true;
278 look at auxiliary information from a class to intuit the existance of more
279 classes needed for a minimal schema
281 function expand_objectclass(ldb, o) {
282 var attrs = new Array("auxiliaryClass", "systemAuxiliaryClass",
283 "possSuperiors", "systemPossSuperiors",
285 var res = ldb.search(
286 sprintf("(&(objectClass=classSchema)(ldapDisplayName=%s))", o.name),
287 rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrs);
289 dprintf("Expanding class %s\n", o.name);
290 assert(res != undefined);
291 assert(res.length == 1);
293 for (a=0;a<attrs.length;a++) {
294 var aname = attrs[a];
295 if (msg[aname] == undefined) {
298 var list = msg[aname];
299 if (typeof(list) == "string") {
300 list = new Array(msg[aname]);
303 for (i=0;i<list.length;i++) {
305 if (objectclasses[name] == undefined) {
306 dprintf("Found new objectclass '%s'\n", name);
307 objectclasses[name] = obj_objectClass(ldb, name);
315 add the must and may attributes from an objectclass to the full list
318 function add_objectclass_attributes(ldb, class) {
319 var attrs = new Array("mustContain", "systemMustContain",
320 "mayContain", "systemMayContain");
322 for (i=0;i<attrs.length;i++) {
323 var aname = attrs[i];
324 if (class[aname] == undefined) {
327 var alist = class[aname];
328 if (typeof(alist) == "string") {
329 alist = new Array(alist);
332 var len = alist.length;
333 for (j=0;j<len;j++) {
335 if (attributes[a] == undefined) {
336 attributes[a] = obj_attribute(ldb, a);
344 process an individual record, working out what attributes it has
346 function walk_dn(ldb, dn) {
347 /* get a list of all possible attributes for this object */
348 var attrs = new Array("allowedAttributes");
349 var res = ldb.search("objectClass=*", dn, ldb.SCOPE_BASE, attrs);
350 if (res == undefined) {
351 dprintf("Unable to fetch allowedAttributes for '%s' - %s\n",
352 dn, ldb.errstring());
355 var allattrs = res[0].allowedAttributes;
356 res = ldb.search("objectClass=*", dn, ldb.SCOPE_BASE, allattrs);
357 if (res == undefined) {
358 dprintf("Unable to fetch all attributes for '%s' - %s\n",
359 dn, ldb.errstring());
365 if (attributes[a] == undefined) {
366 attributes[a] = obj_attribute(ldb, a);
372 walk a naming context, looking for all records
374 function walk_naming_context(ldb, namingContext) {
375 var attrs = new Array("objectClass");
376 var res = ldb.search("objectClass=*", namingContext, ldb.SCOPE_DEFAULT, attrs);
377 if (res == undefined) {
378 dprintf("Unable to fetch objectClasses for '%s' - %s\n",
379 namingContext, ldb.errstring());
383 for (r=0;r<res.length;r++) {
384 var msg = res[r].objectClass;
386 for (c=0;c<msg.length;c++) {
387 var objectClass = msg[c];
388 if (objectclasses[objectClass] == undefined) {
389 objectclasses[objectClass] = obj_objectClass(ldb, objectClass);
390 objectclasses[objectClass].exampleDN = res[r].dn;
393 walk_dn(ldb, res[r].dn);
398 trim the may attributes for an objectClass
400 function trim_objectclass_attributes(ldb, class) {
401 /* not implemented yet */
405 load the basic attributes of an objectClass
407 function build_objectclass(ldb, name) {
408 var attrs = new Array("name");
409 var res = ldb.search(
410 sprintf("(&(objectClass=classSchema)(ldapDisplayName=%s))", name),
411 rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrs);
412 if (res == undefined) {
413 dprintf("unknown class '%s'\n", name);
416 if (res.length == 0) {
417 dprintf("unknown class '%s'\n", name);
420 return obj_objectClass(ldb, name);
426 function list_append(a1, a2) {
428 if (a1 == undefined) {
431 if (a2 == undefined) {
434 for (i=0;i<a2.length;i++) {
435 a1[a1.length] = a2[i];
441 form a coalesced attribute list
443 function attribute_list(class, attr1, attr2) {
444 var a1 = class[attr1];
445 var a2 = class[attr2];
447 if (typeof(a1) == "string") {
450 if (typeof(a2) == "string") {
453 return list_append(a1, a2);
457 write out a list in aggregate form
459 function aggregate_list(name, list) {
460 if (list == undefined) {
464 printf("%s ( ", name);
465 for (i=0;i<list.length;i++) {
466 printf("%s ", list[i]);
467 if (i < (list.length - 1)) {
475 write the aggregate record for an objectclass
477 function write_aggregate_objectclass(class) {
478 printf("objectClasses: ( %s NAME '%s' ", class.governsID, class.name);
479 if (class['subClassOf'] != undefined) {
480 printf("SUP %s ", class['subClassOf']);
482 if (class.objectClassCategory == 1) {
483 printf("STRUCTURAL ");
484 } else if (class.objectClassCategory == 2) {
486 } else if (class.objectClassCategory == 3) {
487 printf("AUXILIARY ");
492 list = attribute_list(class, "systemMustContain", "mustContain");
493 aggregate_list("MUST", list);
495 list = attribute_list(class, "systemMayContain", "mayContain");
496 aggregate_list("MAY", list);
503 write the aggregate record for an ditcontentrule
505 function write_aggregate_ditcontentrule(class) {
506 var list = attribute_list(class, "auxiliaryClass", "systemAuxiliaryClass");
508 if (list == undefined) {
512 printf("dITContentRules: ( %s NAME '%s' ", class.governsID, class.name);
514 aggregate_list("AUX", list);
516 var may_list = undefined;
517 var must_list = undefined;
519 for (i=0;i<list.length;i++) {
522 list2 = attribute_list(objectclasses[c],
523 "mayContain", "systemMayContain");
524 may_list = list_append(may_list, list2);
525 list2 = attribute_list(objectclasses[c],
526 "mustContain", "systemMustContain");
527 must_list = list_append(must_list, list2);
530 aggregate_list("MUST", must_list);
531 aggregate_list("MAY", may_list);
537 write the aggregate record for an attribute
539 function write_aggregate_attribute(attrib) {
540 printf("attributeTypes: ( %s NAME '%s' SYNTAX '%s' ",
541 attrib.attributeID, attrib.name,
542 map_attribute_syntax(attrib.attributeSyntax));
543 if (attrib['isSingleValued'] == "TRUE") {
544 printf("SINGLE-VALUE ");
551 write the aggregate record
553 function write_aggregate() {
554 printf("dn: CN=Aggregate,CN=Schema,CN=Configuration,${BASEDN}\n");
555 print("objectClass: top
556 objectClass: subSchema
560 objectCategory: CN=SubSchema,CN=Schema,CN=Configuration,${BASEDN}
562 for (i in objectclasses) {
563 write_aggregate_objectclass(objectclasses[i]);
565 for (i in attributes) {
566 write_aggregate_attribute(attributes[i]);
568 for (i in objectclasses) {
569 write_aggregate_ditcontentrule(objectclasses[i]);
574 load a list from a file
576 function load_list(file) {
577 var sys = sys_init();
578 var s = sys.file_load(file);
579 var a = split("\n", s);
583 /* get the rootDSE */
584 var res = ldb.search("", "", ldb.SCOPE_BASE);
587 /* load the list of classes we are interested in */
588 var classes = load_list(classfile);
590 for (i=0;i<classes.length;i++) {
591 var classname = classes[i];
592 var class = build_objectclass(ldb, classname);
593 if (class != undefined) {
594 objectclasses[classname] = class;
600 expand the objectclass list as needed
602 for (i in objectclasses) {
603 expand_objectclass(ldb, objectclasses[i]);
607 find objectclass properties
609 for (i in objectclasses) {
610 find_objectclass_properties(ldb, objectclasses[i]);
614 trim the 'may' attribute lists to those really needed
616 for (i in objectclasses) {
617 trim_objectclass_attributes(ldb, objectclasses[i]);
621 form the full list of attributes
623 for (i in objectclasses) {
624 add_objectclass_attributes(ldb, objectclasses[i]);
627 /* and attribute properties */
628 for (i in attributes) {
629 find_attribute_properties(ldb, attributes[i]);
633 dump an ldif form of the attributes and objectclasses
635 write_ldif(attributes, attrib_attrs);
636 write_ldif(objectclasses, class_attrs);
640 if (verbose == undefined) {
645 dump list of objectclasses
647 printf("objectClasses:\n")
648 for (i in objectclasses) {
651 printf("attributes:\n")
652 for (i in attributes) {
656 printf("autocreated attributes:\n");
657 for (i in attributes) {
658 if (attributes[i].autocreate == true) {