summaryrefslogtreecommitdiff
path: root/testprogs/ejs/minschema.js
diff options
context:
space:
mode:
Diffstat (limited to 'testprogs/ejs/minschema.js')
-rwxr-xr-xtestprogs/ejs/minschema.js804
1 files changed, 804 insertions, 0 deletions
diff --git a/testprogs/ejs/minschema.js b/testprogs/ejs/minschema.js
new file mode 100755
index 0000000000..f088501c1d
--- /dev/null
+++ b/testprogs/ejs/minschema.js
@@ -0,0 +1,804 @@
+#!/bin/sh
+exec smbscript "$0" ${1+"$@"}
+/*
+ work out the minimal schema for a set of objectclasses
+*/
+
+libinclude("base.js");
+
+var ldb = ldb_init();
+
+var options = GetOptions(ARGV,
+ "POPT_AUTOHELP",
+ "POPT_COMMON_SAMBA",
+ "POPT_COMMON_CREDENTIALS",
+ "verbose",
+ "classes",
+ "attributes");
+if (options == undefined) {
+ println("Failed to parse options");
+ return -1;
+}
+verbose = options["verbose"];
+dump_all = "yes";
+dump_classes = options["classes"];
+dump_attributes = options["attributes"];
+
+if (dump_classes != undefined) {
+ dump_all = undefined;
+}
+if (dump_attributes != undefined) {
+ dump_all = undefined;
+}
+if (dump_all != undefined) {
+ dump_classes = "yes";
+ dump_attributes = "yes";
+}
+
+if (options.ARGV.length != 2) {
+ println("Usage: minschema.js <URL> <classfile>");
+ return -1;
+}
+
+var url = options.ARGV[0];
+var classfile = options.ARGV[1];
+
+/* use command line creds if available */
+ldb.credentials = options.get_credentials();
+
+var ok = ldb.connect(url);
+assert(ok);
+
+objectclasses = new Object();
+attributes = new Object();
+rootDse = new Object();
+
+objectclasses_expanded = new Object();
+
+/* the attributes we need for objectclasses */
+class_attrs = new Array("objectClass",
+ "subClassOf",
+ "governsID",
+ "possSuperiors",
+ "possibleInferiors",
+ "mayContain",
+ "mustContain",
+ "auxiliaryClass",
+ "rDNAttID",
+ "showInAdvancedViewOnly",
+ "adminDisplayName",
+ "adminDescription",
+ "objectClassCategory",
+ "lDAPDisplayName",
+ "schemaIDGUID",
+ "systemOnly",
+ "systemPossSuperiors",
+ "systemMayContain",
+ "systemMustContain",
+ "systemAuxiliaryClass",
+ "defaultSecurityDescriptor",
+ "systemFlags",
+ "defaultHidingValue",
+ "defaultObjectCategory",
+
+ /* this attributes are not used by w2k3 */
+ "schemaFlagsEx",
+ "msDs-IntId",
+ "msDs-Schema-Extensions",
+ "classDisplayName",
+ "isDefunct");
+
+
+attrib_attrs = new Array("objectClass",
+ "attributeID",
+ "attributeSyntax",
+ "isSingleValued",
+ "rangeLower",
+ "rangeUpper",
+ "mAPIID",
+ "linkID",
+ "showInAdvancedViewOnly",
+ "adminDisplayName",
+ "oMObjectClass",
+ "adminDescription",
+ "oMSyntax",
+ "searchFlags",
+ "extendedCharsAllowed",
+ "lDAPDisplayName",
+ "schemaIDGUID",
+ "attributeSecurityGUID",
+ "systemOnly",
+ "systemFlags",
+ "isMemberOfPartialAttributeSet",
+
+ /* this attributes are not used by w2k3 */
+ "schemaFlagsEx",
+ "msDs-IntId",
+ "msDs-Schema-Extensions",
+ "classDisplayName",
+ "isEphemeral",
+ "isDefunct");
+
+/*
+ notes:
+
+ objectClassCategory
+ 1: structural
+ 2: abstract
+ 3: auxiliary
+*/
+
+
+/*
+ print only if verbose is set
+*/
+function dprintf() {
+ if (verbose != undefined) {
+ print(vsprintf(arguments));
+ }
+}
+
+function get_object_cn(ldb, name) {
+ var attrs = new Array("cn");
+
+ var res = ldb.search(sprintf("(ldapDisplayName=%s)", name), rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrs);
+ assert(res != undefined);
+ assert(res.msgs.length == 1);
+
+ var cn = res.msgs[0]["cn"];
+ assert(cn != undefined);
+ if (typeof(cn) == "string") {
+ return cn;
+ }
+ return cn[0];
+}
+/*
+ create an objectclass object
+*/
+function obj_objectClass(ldb, name) {
+ var o = new Object();
+ o.name = name;
+ o.cn = get_object_cn(ldb, name);
+ return o;
+}
+
+/*
+ create an attribute object
+*/
+function obj_attribute(ldb, name) {
+ var o = new Object();
+ o.name = name;
+ o.cn = get_object_cn(ldb, name);
+ return o;
+}
+
+
+syntaxmap = new Object();
+
+syntaxmap['2.5.5.1'] = '1.3.6.1.4.1.1466.115.121.1.12';
+syntaxmap['2.5.5.2'] = '1.3.6.1.4.1.1466.115.121.1.38';
+syntaxmap['2.5.5.3'] = '1.2.840.113556.1.4.1362';
+syntaxmap['2.5.5.4'] = '1.2.840.113556.1.4.905';
+syntaxmap['2.5.5.5'] = '1.3.6.1.4.1.1466.115.121.1.26';
+syntaxmap['2.5.5.6'] = '1.3.6.1.4.1.1466.115.121.1.36';
+syntaxmap['2.5.5.7'] = '1.2.840.113556.1.4.903';
+syntaxmap['2.5.5.8'] = '1.3.6.1.4.1.1466.115.121.1.7';
+syntaxmap['2.5.5.9'] = '1.3.6.1.4.1.1466.115.121.1.27';
+syntaxmap['2.5.5.10'] = '1.3.6.1.4.1.1466.115.121.1.40';
+syntaxmap['2.5.5.11'] = '1.3.6.1.4.1.1466.115.121.1.24';
+syntaxmap['2.5.5.12'] = '1.3.6.1.4.1.1466.115.121.1.15';
+syntaxmap['2.5.5.13'] = '1.3.6.1.4.1.1466.115.121.1.43';
+syntaxmap['2.5.5.14'] = '1.2.840.113556.1.4.904';
+syntaxmap['2.5.5.15'] = '1.2.840.113556.1.4.907';
+syntaxmap['2.5.5.16'] = '1.2.840.113556.1.4.906';
+syntaxmap['2.5.5.17'] = '1.3.6.1.4.1.1466.115.121.1.40';
+
+/*
+ map some attribute syntaxes from some apparently MS specific
+ syntaxes to the standard syntaxes
+*/
+function map_attribute_syntax(s) {
+ if (syntaxmap[s] != undefined) {
+ return syntaxmap[s];
+ }
+ return s;
+}
+
+
+/*
+ fix a string DN to use ${SCHEMADN}
+*/
+function fix_dn(dn) {
+ var s = strstr(dn, rootDse.schemaNamingContext);
+ if (s == NULL) {
+ return dn;
+ }
+ return substr(dn, 0, strlen(dn) - strlen(s)) + "${SCHEMADN}";
+}
+
+/*
+ dump an object as ldif
+*/
+function write_ldif_one(o, attrs) {
+ var i;
+ printf("dn: CN=%s,${SCHEMADN}\n", o.cn);
+ for (i=0;i<attrs.length;i++) {
+ var a = attrs[i];
+ if (o[a] == undefined) {
+ continue;
+ }
+ /* special case for oMObjectClass, which is a binary object */
+ if (a == "oMObjectClass") {
+ printf("%s:: %s\n", a, o[a]);
+ continue;
+ }
+ var v = o[a];
+ if (typeof(v) == "string") {
+ v = new Array(v);
+ }
+ var j;
+ for (j=0;j<v.length;j++) {
+ printf("%s: %s\n", a, fix_dn(v[j]));
+ }
+ }
+ printf("\n");
+}
+
+/*
+ dump an array of objects as ldif
+*/
+function write_ldif(o, attrs) {
+ var i;
+ for (i in o) {
+ write_ldif_one(o[i], attrs);
+ }
+}
+
+
+/*
+ create a testDN based an an example DN
+ the idea is to ensure we obey any structural rules
+*/
+function create_testdn(exampleDN) {
+ var a = split(",", exampleDN);
+ a[0] = "CN=TestDN";
+ return join(",", a);
+}
+
+/*
+ find the properties of an objectclass
+ */
+function find_objectclass_properties(ldb, o) {
+ var res = ldb.search(
+ sprintf("(ldapDisplayName=%s)", o.name),
+ rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, class_attrs);
+ assert(res != undefined);
+ assert(res.msgs.length == 1);
+ var msg = res.msgs[0];
+ var a;
+ for (a in msg) {
+ o[a] = msg[a];
+ }
+}
+
+/*
+ find the properties of an attribute
+ */
+function find_attribute_properties(ldb, o) {
+ var res = ldb.search(
+ sprintf("(ldapDisplayName=%s)", o.name),
+ rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrib_attrs);
+ assert(res != undefined);
+ assert(res.msgs.length == 1);
+ var msg = res.msgs[0];
+ var a;
+ for (a in msg) {
+ /* special case for oMObjectClass, which is a binary object */
+ if (a == "oMObjectClass") {
+ o[a] = ldb.encode(msg[a]);
+ continue;
+ }
+ o[a] = msg[a];
+ }
+}
+
+/*
+ find the auto-created properties of an objectclass. Only works for classes
+ that can be created using just a DN and the objectclass
+ */
+function find_objectclass_auto(ldb, o) {
+ if (o["exampleDN"] == undefined) {
+ return;
+ }
+ var testdn = create_testdn(o.exampleDN);
+ var ok;
+
+ dprintf("testdn is '%s'\n", testdn);
+
+ var ldif = "dn: " + testdn;
+ ldif = ldif + "\nobjectClass: " + o.name;
+ ok = ldb.add(ldif);
+ if (ok.error != 0) {
+ dprintf("error adding %s: %s\n", o.name, ok.errstr);
+ dprintf("%s\n", ldif);
+ return;
+ }
+
+ var res = ldb.search("", testdn, ldb.SCOPE_BASE);
+ ok = ldb.del(testdn);
+ assert(ok.error == 0);
+
+ var a;
+ for (a in res.msgs[0]) {
+ attributes[a].autocreate = true;
+ }
+}
+
+
+/*
+ look at auxiliary information from a class to intuit the existance of more
+ classes needed for a minimal schema
+*/
+function expand_objectclass(ldb, o) {
+ var attrs = new Array("auxiliaryClass", "systemAuxiliaryClass",
+ "possSuperiors", "systemPossSuperiors",
+ "subClassOf");
+ var res = ldb.search(
+ sprintf("(&(objectClass=classSchema)(ldapDisplayName=%s))", o.name),
+ rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrs);
+ var a;
+ dprintf("Expanding class %s\n", o.name);
+ assert(res != undefined);
+ assert(res.msgs.length == 1);
+ var msg = res.msgs[0];
+ for (a=0;a<attrs.length;a++) {
+ var aname = attrs[a];
+ if (msg[aname] == undefined) {
+ continue;
+ }
+ var list = msg[aname];
+ if (typeof(list) == "string") {
+ list = new Array(msg[aname]);
+ }
+ var i;
+ for (i=0;i<list.length;i++) {
+ var name = list[i];
+ if (objectclasses[name] == undefined) {
+ dprintf("Found new objectclass '%s'\n", name);
+ objectclasses[name] = obj_objectClass(ldb, name);
+ }
+ }
+ }
+}
+
+
+/*
+ add the must and may attributes from an objectclass to the full list
+ of attributes
+*/
+function add_objectclass_attributes(ldb, class) {
+ var attrs = new Array("mustContain", "systemMustContain",
+ "mayContain", "systemMayContain");
+ var i;
+ for (i=0;i<attrs.length;i++) {
+ var aname = attrs[i];
+ if (class[aname] == undefined) {
+ continue;
+ }
+ var alist = class[aname];
+ if (typeof(alist) == "string") {
+ alist = new Array(alist);
+ }
+ var j;
+ var len = alist.length;
+ for (j=0;j<len;j++) {
+ var a = alist[j];
+ if (attributes[a] == undefined) {
+ attributes[a] = obj_attribute(ldb, a);
+ }
+ }
+ }
+}
+
+
+/*
+ process an individual record, working out what attributes it has
+*/
+function walk_dn(ldb, dn) {
+ /* get a list of all possible attributes for this object */
+ var attrs = new Array("allowedAttributes");
+ var res = ldb.search("objectClass=*", dn, ldb.SCOPE_BASE, attrs);
+ if (res.error != 0) {
+ dprintf("Unable to fetch allowedAttributes for '%s' - %s\n",
+ dn, res.errstr);
+ return;
+ }
+ var allattrs = res.msgs[0].allowedAttributes;
+ res = ldb.search("objectClass=*", dn, ldb.SCOPE_BASE, allattrs);
+ if (res.error != 0) {
+ dprintf("Unable to fetch all attributes for '%s' - %s\n",
+ dn, res.errstr);
+ return;
+ }
+ var a;
+ var msg = res.msgs[0];
+ for (a in msg) {
+ if (attributes[a] == undefined) {
+ attributes[a] = obj_attribute(ldb, a);
+ }
+ }
+}
+
+/*
+ walk a naming context, looking for all records
+*/
+function walk_naming_context(ldb, namingContext) {
+ var attrs = new Array("objectClass");
+ var res = ldb.search("objectClass=*", namingContext, ldb.SCOPE_DEFAULT, attrs);
+ if (res.error != 0) {
+ dprintf("Unable to fetch objectClasses for '%s' - %s\n",
+ namingContext, res.errstr);
+ return;
+ }
+ var r;
+ for (r=0;r<res.msgs.length;r++) {
+ var msg = res.msgs[r].objectClass;
+ var c;
+ for (c=0;c<msg.length;c++) {
+ var objectClass = msg[c];
+ if (objectclasses[objectClass] == undefined) {
+ objectclasses[objectClass] = obj_objectClass(ldb, objectClass);
+ objectclasses[objectClass].exampleDN = res.msgs[r].dn;
+ }
+ }
+ walk_dn(ldb, res.msgs[r].dn);
+ }
+}
+
+/*
+ trim the may attributes for an objectClass
+*/
+function trim_objectclass_attributes(ldb, class) {
+ var i,j,n;
+
+ /* trim possibleInferiors,
+ * include only the classes we extracted */
+ var possinf = class["possibleInferiors"];
+ if (possinf != undefined) {
+ var newpossinf = new Array();
+ if (typeof(possinf) == "string") {
+ possinf = new Array(possinf);
+ }
+ n = 0;
+ for (j = 0;j < possinf.length; j++) {
+ var x = possinf[j];
+ if (objectclasses[x] != undefined) {
+ newpossinf[n] = x;
+ n++;
+ }
+ }
+ class["possibleInferiors"] = newpossinf;
+ }
+
+ /* trim systemMayContain,
+ * remove duplicates */
+ var sysmay = class["systemMayContain"];
+ if (sysmay != undefined) {
+ var newsysmay = new Array();
+ if (typeof(sysmay) == "string") {
+ sysmay = new Array(sysmay);
+ }
+ for (j = 0;j < sysmay.length; j++) {
+ var x = sysmay[j];
+ var dup = false;
+ if (newsysmay[0] == undefined) {
+ newsysmay[0] = x;
+ } else {
+ for (n = 0; n < newsysmay.length; n++) {
+ if (newsysmay[n] == x) {
+ dup = true;
+ }
+ }
+ if (dup == false) {
+ newsysmay[n] = x;
+ }
+ }
+ }
+ class["systemMayContain"] = newsysmay;
+ }
+
+ /* trim mayContain,
+ * remove duplicates */
+ var may = class["mayContain"];
+ if (may != undefined) {
+ var newmay = new Array();
+ if (typeof(may) == "string") {
+ may = new Array(may);
+ }
+ for (j = 0;j < may.length; j++) {
+ var x = may[j];
+ var dup = false;
+ if (newmay[0] == undefined) {
+ newmay[0] = x;
+ } else {
+ for (n = 0; n < newmay.length; n++) {
+ if (newmay[n] == x) {
+ dup = true;
+ }
+ }
+ if (dup == false) {
+ newmay[n] = x;
+ }
+ }
+ }
+ class["mayContain"] = newmay;
+ }
+}
+
+/*
+ load the basic attributes of an objectClass
+*/
+function build_objectclass(ldb, name) {
+ var attrs = new Array("name");
+ var res = ldb.search(
+ sprintf("(&(objectClass=classSchema)(ldapDisplayName=%s))", name),
+ rootDse.schemaNamingContext, ldb.SCOPE_SUBTREE, attrs);
+ if (res.error != 0) {
+ dprintf("unknown class '%s'\n", name);
+ return undefined;
+ }
+ if (res.msgs.length == 0) {
+ dprintf("unknown class '%s'\n", name);
+ return undefined;
+ }
+ return obj_objectClass(ldb, name);
+}
+
+/*
+ append 2 lists
+*/
+function list_append(a1, a2) {
+ var i;
+ if (a1 == undefined) {
+ return a2;
+ }
+ if (a2 == undefined) {
+ return a1;
+ }
+ for (i=0;i<a2.length;i++) {
+ a1[a1.length] = a2[i];
+ }
+ return a1;
+}
+
+/*
+ form a coalesced attribute list
+*/
+function attribute_list(class, attr1, attr2) {
+ var a1 = class[attr1];
+ var a2 = class[attr2];
+ if (typeof(a1) == "string") {
+ a1 = new Array(a1);
+ }
+ if (typeof(a2) == "string") {
+ a2 = new Array(a2);
+ }
+ return list_append(a1, a2);
+}
+
+/*
+ write out a list in aggregate form
+*/
+function aggregate_list(name, list) {
+ if (list == undefined) {
+ return;
+ }
+ var i;
+ printf("%s ( ", name);
+ for (i=0;i<list.length;i++) {
+ printf("%s ", list[i]);
+ if (i < (list.length - 1)) {
+ printf("$ ");
+ }
+ }
+ printf(") ");
+}
+
+/*
+ write the aggregate record for an objectclass
+*/
+function write_aggregate_objectclass(class) {
+ printf("objectClasses: ( %s NAME '%s' ", class.governsID, class.name);
+ if (class['subClassOf'] != undefined) {
+ printf("SUP %s ", class['subClassOf']);
+ }
+ if (class.objectClassCategory == 1) {
+ printf("STRUCTURAL ");
+ } else if (class.objectClassCategory == 2) {
+ printf("ABSTRACT ");
+ } else if (class.objectClassCategory == 3) {
+ printf("AUXILIARY ");
+ }
+
+ var list;
+
+ list = attribute_list(class, "systemMustContain", "mustContain");
+ aggregate_list("MUST", list);
+
+ list = attribute_list(class, "systemMayContain", "mayContain");
+ aggregate_list("MAY", list);
+
+ printf(")\n");
+}
+
+
+/*
+ write the aggregate record for an ditcontentrule
+*/
+function write_aggregate_ditcontentrule(class) {
+ var list = attribute_list(class, "auxiliaryClass", "systemAuxiliaryClass");
+ var i;
+ if (list == undefined) {
+ return;
+ }
+
+ printf("dITContentRules: ( %s NAME '%s' ", class.governsID, class.name);
+
+ aggregate_list("AUX", list);
+
+ var may_list = undefined;
+ var must_list = undefined;
+
+ for (i=0;i<list.length;i++) {
+ var c = list[i];
+ var list2;
+ list2 = attribute_list(objectclasses[c],
+ "mayContain", "systemMayContain");
+ may_list = list_append(may_list, list2);
+ list2 = attribute_list(objectclasses[c],
+ "mustContain", "systemMustContain");
+ must_list = list_append(must_list, list2);
+ }
+
+ aggregate_list("MUST", must_list);
+ aggregate_list("MAY", may_list);
+
+ printf(")\n");
+}
+
+/*
+ write the aggregate record for an attribute
+*/
+function write_aggregate_attribute(attrib) {
+ printf("attributeTypes: ( %s NAME '%s' SYNTAX '%s' ",
+ attrib.attributeID, attrib.name,
+ map_attribute_syntax(attrib.attributeSyntax));
+ if (attrib['isSingleValued'] == "TRUE") {
+ printf("SINGLE-VALUE ");
+ }
+ if (attrib['systemOnly'] == "TRUE") {
+ printf("NO-USER-MODIFICATION ");
+ }
+
+ printf(")\n");
+}
+
+
+
+/*
+ load a list from a file
+*/
+function load_list(file) {
+ var sys = sys_init();
+ var s = sys.file_load(file);
+ var a = split("\n", s);
+ return a;
+}
+
+/* get the rootDSE */
+var res = ldb.search("", "", ldb.SCOPE_BASE);
+rootDse = res.msgs[0];
+
+/* load the list of classes we are interested in */
+var classes = load_list(classfile);
+var i;
+for (i=0;i<classes.length;i++) {
+ var classname = classes[i];
+ var class = build_objectclass(ldb, classname);
+ if (class != undefined) {
+ objectclasses[classname] = class;
+ }
+}
+
+
+/*
+ expand the objectclass list as needed
+*/
+var num_classes = 0;
+var expanded = 0;
+/* calculate the actual number of classes */
+for (i in objectclasses) {
+ num_classes++;
+}
+/* so EJS do not have while nor the break statement
+ cannot find any other way than doing more loops
+ than necessary to recursively expand all classes
+ */
+var inf;
+for (inf = 0;inf < 500; inf++) {
+ if (expanded < num_classes) {
+ for (i in objectclasses) {
+ var n = objectclasses[i];
+ if (objectclasses_expanded[i] != "DONE") {
+ expand_objectclass(ldb, objectclasses[i]);
+ objectclasses_expanded[i] = "DONE";
+ expanded++;
+ }
+ }
+ /* recalculate the actual number of classes */
+ num_classes = 0;
+ for (i in objectclasses) {
+ num_classes++;
+ }
+ }
+}
+
+/*
+ find objectclass properties
+*/
+for (i in objectclasses) {
+ find_objectclass_properties(ldb, objectclasses[i]);
+}
+
+/*
+ form the full list of attributes
+*/
+for (i in objectclasses) {
+ add_objectclass_attributes(ldb, objectclasses[i]);
+}
+
+/* and attribute properties */
+for (i in attributes) {
+ find_attribute_properties(ldb, attributes[i]);
+}
+
+/*
+ trim the 'may' attribute lists to those really needed
+*/
+for (i in objectclasses) {
+ trim_objectclass_attributes(ldb, objectclasses[i]);
+}
+
+/*
+ dump an ldif form of the attributes and objectclasses
+*/
+if (dump_attributes != undefined) {
+ write_ldif(attributes, attrib_attrs);
+}
+if (dump_classes != undefined) {
+ write_ldif(objectclasses, class_attrs);
+}
+if (verbose == undefined) {
+ exit(0);
+}
+
+/*
+ dump list of objectclasses
+*/
+printf("objectClasses:\n")
+for (i in objectclasses) {
+ printf("\t%s\n", i);
+}
+printf("attributes:\n")
+for (i in attributes) {
+ printf("\t%s\n", i);
+}
+
+printf("autocreated attributes:\n");
+for (i in attributes) {
+ if (attributes[i].autocreate == true) {
+ printf("\t%s\n", i);
+ }
+}
+
+return 0;