2005-10-14 07:38:16 +00:00
#!/bin/sh
exec smbscript "$0" $ { 1 + "$@" }
/ *
2005-11-01 07:07:48 +00:00
work out the minimal schema for a set of objectclasses
2005-10-14 07:38:16 +00:00
* /
libinclude ( "base.js" ) ;
var ldb = ldb _init ( ) ;
var options = GetOptions ( ARGV ,
"POPT_AUTOHELP" ,
"POPT_COMMON_SAMBA" ,
2005-11-01 07:07:48 +00:00
"POPT_COMMON_CREDENTIALS" ,
"verbose" ) ;
2005-10-14 07:38:16 +00:00
if ( options == undefined ) {
println ( "Failed to parse options" ) ;
return - 1 ;
}
2005-11-01 07:07:48 +00:00
verbose = options [ "verbose" ] ;
2005-10-14 07:38:16 +00:00
2005-11-01 07:07:48 +00:00
if ( options . ARGV . length != 2 ) {
println ( "Usage: minschema.js <URL> <classfile>" ) ;
2005-10-14 07:38:16 +00:00
return - 1 ;
}
var url = options . ARGV [ 0 ] ;
2005-11-01 07:07:48 +00:00
var classfile = options . ARGV [ 1 ] ;
2005-10-14 07:38:16 +00:00
var ok = ldb . connect ( url ) ;
assert ( ok ) ;
objectclasses = new Object ( ) ;
attributes = new Object ( ) ;
2005-11-01 07:07:48 +00:00
rootDse = new Object ( ) ;
/* the attributes we need for objectclasses */
class _attrs = new Array ( "objectClass" ,
"auxiliaryClass" , "systemAuxiliaryClass" ,
"possSuperiors" , "systemPossSuperiors" ,
"lDAPDisplayName" , "governsID" ,
"rDNAttID" , "mustContain" , "systemMustContain" ,
"mayContain" , "systemMayContain" ,
"objectClassCategory" , "subClassOf" ,
"defaultObjectCategory" , "defaultHidingValue" ,
"systemFlags" , "systemOnly" , "defaultSecurityDescriptor" ,
2005-11-02 02:32:25 +00:00
"objectCategory" , "possibleInferiors" , "displaySpecification" ,
"schemaIDGUID" ) ;
2005-11-01 07:07:48 +00:00
attrib _attrs = new Array ( "objectClass" , "lDAPDisplayName" ,
"isSingleValued" , "linkID" , "systemFlags" , "systemOnly" ,
"schemaIDGUID" , "adminDisplayName" , "attributeID" ,
"attributeSyntax" ) ;
/ *
notes :
objectClassCategory
1 : structural
2 : abstract
3 : auxiliary
* /
/ *
print only if verbose is set
* /
function dprintf ( ) {
if ( verbose != undefined ) {
print ( vsprintf ( arguments ) ) ;
}
}
/ *
create an objectclass object
* /
function obj _objectClass ( name ) {
var o = new Object ( ) ;
o . name = name ;
return o ;
}
/ *
create an attribute object
* /
function obj _attribute ( name ) {
var o = new Object ( ) ;
o . name = name ;
return o ;
}
2005-11-02 02:32:25 +00:00
syntaxmap = new Object ( ) ;
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' ;
/ *
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 ;
}
2005-11-01 07:07:48 +00:00
/ *
fix a string DN to use $ { BASEDN }
* /
function fix _dn ( dn ) {
var s = strstr ( dn , rootDse . defaultNamingContext ) ;
if ( s == NULL ) {
return dn ;
}
return substr ( dn , 0 , strlen ( dn ) - strlen ( s ) ) + "${BASEDN}" ;
}
/ *
dump an object as ldif
* /
function write _ldif _one ( o , attrs ) {
var i ;
printf ( "dn: CN=%s,CN=Schema,CN=Configuration,${BASEDN}\n" , o . name ) ;
2005-11-02 02:32:25 +00:00
printf ( "cn: %s\n" , o . name ) ;
2005-11-01 07:07:48 +00:00
printf ( "name: %s\n" , o . name ) ;
for ( i = 0 ; i < attrs . length ; i ++ ) {
var a = attrs [ i ] ;
if ( o [ a ] == undefined ) {
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 . length == 1 ) ;
var msg = res [ 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 . length == 1 ) ;
var msg = res [ 0 ] ;
var a ;
for ( a in msg ) {
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 ) {
dprintf ( "error adding %s: %s\n" , o . name , ldb . errstring ( ) ) ;
dprintf ( "%s\n" , ldif ) ;
return ;
}
var res = ldb . search ( "" , testdn , ldb . SCOPE _BASE ) ;
ok = ldb . del ( testdn ) ;
assert ( ok ) ;
var a ;
for ( a in res [ 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 . length == 1 ) ;
var msg = res [ 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 ( 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 ( a ) ;
}
}
}
}
2005-10-14 07:38:16 +00:00
/ *
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 == undefined ) {
2005-11-01 07:07:48 +00:00
dprintf ( "Unable to fetch allowedAttributes for '%s' - %s\n" ,
2005-10-14 07:38:16 +00:00
dn , ldb . errstring ( ) ) ;
return ;
}
var allattrs = res [ 0 ] . allowedAttributes ;
res = ldb . search ( "objectClass=*" , dn , ldb . SCOPE _BASE , allattrs ) ;
if ( res == undefined ) {
2005-11-01 07:07:48 +00:00
dprintf ( "Unable to fetch all attributes for '%s' - %s\n" ,
2005-10-14 07:38:16 +00:00
dn , ldb . errstring ( ) ) ;
return ;
}
var a ;
var msg = res [ 0 ] ;
for ( a in msg ) {
2005-11-01 07:07:48 +00:00
if ( attributes [ a ] == undefined ) {
attributes [ a ] = obj _attribute ( a ) ;
}
2005-10-14 07:38:16 +00:00
}
}
/ *
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 == undefined ) {
2005-11-01 07:07:48 +00:00
dprintf ( "Unable to fetch objectClasses for '%s' - %s\n" ,
2005-10-14 07:38:16 +00:00
namingContext , ldb . errstring ( ) ) ;
return ;
}
var r ;
for ( r = 0 ; r < res . length ; r ++ ) {
var msg = res [ r ] . objectClass ;
var c ;
for ( c = 0 ; c < msg . length ; c ++ ) {
var objectClass = msg [ c ] ;
2005-11-01 07:07:48 +00:00
if ( objectclasses [ objectClass ] == undefined ) {
objectclasses [ objectClass ] = obj _objectClass ( objectClass ) ;
objectclasses [ objectClass ] . exampleDN = res [ r ] . dn ;
}
2005-10-14 07:38:16 +00:00
}
walk _dn ( ldb , res [ r ] . dn ) ;
}
}
/ *
2005-11-01 07:07:48 +00:00
trim the may attributes for an objectClass
* /
function trim _objectclass _attributes ( ldb , class ) {
/* not implemented yet */
}
/ *
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 == undefined ) {
dprintf ( "unknown class '%s'\n" , name ) ;
return undefined ;
}
if ( res . length == 0 ) {
dprintf ( "unknown class '%s'\n" , name ) ;
return undefined ;
}
return obj _objectClass ( 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 ] ;
var i ;
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
2005-10-14 07:38:16 +00:00
* /
2005-11-01 07:07:48 +00:00
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" ) ;
}
2005-10-14 07:38:16 +00:00
/ *
2005-11-01 07:07:48 +00:00
write the aggregate record for an ditcontentrule
2005-10-14 07:38:16 +00:00
* /
2005-11-01 07:07:48 +00:00
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' " ,
2005-11-02 02:32:25 +00:00
attrib . attributeID , attrib . name ,
map _attribute _syntax ( attrib . attributeSyntax ) ) ;
2005-11-01 07:07:48 +00:00
if ( attrib [ 'isSingleValued' ] == "TRUE" ) {
printf ( "SINGLE-VALUE " ) ;
}
printf ( ")\n" ) ;
}
/ *
write the aggregate record
* /
function write _aggregate ( ) {
printf ( "dn: CN=Aggregate,CN=Schema,CN=Configuration,${BASEDN}\n" ) ;
print ( " objectClass : top
objectClass : subSchema
cn : Aggregate
instanceType : 4
name : Aggregate
objectCategory : CN = SubSchema , CN = Schema , CN = Configuration , $ { BASEDN }
" ) ;
for ( i in objectclasses ) {
write _aggregate _objectclass ( objectclasses [ i ] ) ;
}
for ( i in attributes ) {
write _aggregate _attribute ( attributes [ i ] ) ;
}
for ( i in objectclasses ) {
write _aggregate _ditcontentrule ( objectclasses [ i ] ) ;
}
}
/ *
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 [ 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
* /
for ( i in objectclasses ) {
expand _objectclass ( ldb , objectclasses [ i ] ) ;
}
/ *
find objectclass properties
* /
for ( i in objectclasses ) {
find _objectclass _properties ( ldb , objectclasses [ i ] ) ;
}
/ *
trim the 'may' attribute lists to those really needed
* /
for ( i in objectclasses ) {
trim _objectclass _attributes ( 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 ] ) ;
}
/ *
dump an ldif form of the attributes and objectclasses
* /
write _ldif ( attributes , attrib _attrs ) ;
write _ldif ( objectclasses , class _attrs ) ;
write _aggregate ( ) ;
if ( verbose == undefined ) {
exit ( 0 ) ;
2005-10-14 07:38:16 +00:00
}
/ *
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 ) ;
}
2005-11-01 07:07:48 +00:00
printf ( "autocreated attributes:\n" ) ;
for ( i in attributes ) {
if ( attributes [ i ] . autocreate == true ) {
printf ( "\t%s\n" , i ) ;
}
}
2005-10-14 07:38:16 +00:00
return 0 ;