3 # create schema.ldif (as a string) from WSPP documentation
5 # based on minschema.py and minschema_wspp
15 # bit positions as labeled in the docs
16 bitFields
["searchflags"] = {
18 'fPDNTATTINDEX': 30, # PI
20 'fPRESERVEONDELETE': 28, # PR
22 'fTUPLEINDEX': 26, # TP
23 'fSUBTREEATTINDEX': 25, # ST
24 'fCONFIDENTIAL': 24, # CF
25 'fNEVERVALUEAUDIT': 23, # NV
26 'fRODCAttribute': 22, # RO
29 # missing in ADTS but required by LDIF
30 'fRODCFilteredAttribute': 22, # RO ?
31 'fCONFIDENTAIL': 24, # typo
32 'fRODCFILTEREDATTRIBUTE': 22 # case
36 bitFields
["systemflags"] = {
37 'FLAG_ATTR_NOT_REPLICATED': 31, 'FLAG_CR_NTDS_NC': 31, # NR
38 'FLAG_ATTR_REQ_PARTIAL_SET_MEMBER': 30, 'FLAG_CR_NTDS_DOMAIN': 30, # PS
39 'FLAG_ATTR_IS_CONSTRUCTED': 29, 'FLAG_CR_NTDS_NOT_GC_REPLICATED': 29, # CS
40 'FLAG_ATTR_IS_OPERATIONAL': 28, # OP
41 'FLAG_SCHEMA_BASE_OBJECT': 27, # BS
42 'FLAG_ATTR_IS_RDN': 26, # RD
43 'FLAG_DISALLOW_MOVE_ON_DELETE': 6, # DE
44 'FLAG_DOMAIN_DISALLOW_MOVE': 5, # DM
45 'FLAG_DOMAIN_DISALLOW_RENAME': 4, # DR
46 'FLAG_CONFIG_ALLOW_LIMITED_MOVE': 3, # AL
47 'FLAG_CONFIG_ALLOW_MOVE': 2, # AM
48 'FLAG_CONFIG_ALLOW_RENAME': 1, # AR
49 'FLAG_DISALLOW_DELETE': 0 # DD
53 bitFields
["schemaflagsex"] = {
54 'FLAG_ATTR_IS_CRITICAL': 31
59 '1.3.12.2.1011.28.0.702' : base64
.b64encode('\x2B\x0C\x02\x87\x73\x1C\x00\x85\x3E'),
60 '1.2.840.113556.1.1.1.12': base64
.b64encode('\x2A\x86\x48\x86\xF7\x14\x01\x01\x01\x0C'),
61 '2.6.6.1.2.5.11.29' : base64
.b64encode('\x56\x06\x01\x02\x05\x0B\x1D'),
62 '1.2.840.113556.1.1.1.11': base64
.b64encode('\x2A\x86\x48\x86\xF7\x14\x01\x01\x01\x0B'),
63 '1.3.12.2.1011.28.0.714' : base64
.b64encode('\x2B\x0C\x02\x87\x73\x1C\x00\x85\x4A'),
64 '1.3.12.2.1011.28.0.732' : base64
.b64encode('\x2B\x0C\x02\x87\x73\x1C\x00\x85\x5C'),
65 '1.2.840.113556.1.1.1.6' : base64
.b64encode('\x2A\x86\x48\x86\xF7\x14\x01\x01\x01\x06')
68 # separated by commas in docs, and must be broken up
69 multivalued_attrs
= set(["auxiliaryclass","maycontain","mustcontain","posssuperiors",
70 "systemauxiliaryclass","systemmaycontain","systemmustcontain",
71 "systemposssuperiors"])
73 def __read_folded_line(f
, buffer):
74 """ reads a line from an LDIF file, unfolding it"""
83 # cannot fold an empty line
84 assert(line
!= "" and line
!= "\n")
94 # eof, definitely won't be folded
97 # marks end of a folded line
98 # line contains the now unfolded line
99 # buffer contains the start of the next possibly folded line
103 return (line
, buffer)
106 def __read_raw_entries(f
):
107 """reads an LDIF entry, only unfolding lines"""
109 # will not match options after the attribute type
110 attr_type_re
= re
.compile("^([A-Za-z]+[A-Za-z0-9-]*):")
118 (l
, buffer) = __read_folded_line(f
, buffer)
123 if l
== "\n" or l
== "":
126 m
= attr_type_re
.match(l
)
134 print >>sys
.stderr
, "Invalid line: %s" % l
,
145 """fix a string DN to use ${SCHEMADN}"""
148 if dn
.find("<RootDomainDN>") != -1:
149 dn
= dn
.replace("\n ", "")
150 dn
= dn
.replace(" ", "")
151 return dn
.replace("CN=Schema,CN=Configuration,<RootDomainDN>", "${SCHEMADN}")
155 def __convert_bitfield(key
, value
):
156 """Evaluate the OR expression in 'value'"""
157 assert(isinstance(value
, str))
159 value
= value
.replace("\n ", "")
160 value
= value
.replace(" ", "")
163 # some attributes already have numeric values
167 flags
= value
.split("|")
169 bitpos
= bitFields
[key
][f
]
170 o
= o |
(1 << (31 - bitpos
))
174 def __write_ldif_one(entry
):
175 """Write out entry as LDIF"""
179 if isinstance(l
[1], str):
184 if l
[0].lower() == 'omobjectclass':
185 out
.append("%s:: %s" % (l
[0], l
[1]))
189 out
.append("%s: %s" % (l
[0], v
))
192 return "\n".join(out
)
194 def __transform_entry(entry
, objectClass
):
195 """Perform transformations required to convert the LDIF-like schema
196 file entries to LDIF, including Samba-specific stuff."""
198 entry
= [l
.split(":", 1) for l
in entry
]
207 if not cn
and key
== "cn":
210 if key
in multivalued_attrs
:
211 # unlike LDIF, these are comma-separated
212 l
[1] = l
[1].replace("\n ", "")
213 l
[1] = l
[1].replace(" ", "")
215 l
[1] = l
[1].split(",")
218 l
[1] = __convert_bitfield(key
, l
[1])
220 if key
== "omobjectclass":
221 l
[1] = oMObjectClassBER
[l
[1].strip()]
223 if isinstance(l
[1], str):
228 entry
.insert(0, ["dn", "CN=%s,${SCHEMADN}" % cn
])
229 entry
.insert(1, ["objectClass", ["top", objectClass
]])
230 entry
.insert(2, ["cn", cn
])
231 entry
.insert(2, ["objectGUID", str(uuid
.uuid4())])
232 entry
.insert(2, ["adminDescription", cn
])
233 entry
.insert(2, ["adminDisplayName", cn
])
243 def __parse_schema_file(filename
, objectClass
):
244 """Load and transform a schema file."""
248 f
= open(filename
, "rU")
249 for entry
in __read_raw_entries(f
):
250 out
.append(__write_ldif_one(__transform_entry(entry
, objectClass
)))
252 return "\n\n".join(out
)
255 def read_ms_schema(attr_file
, classes_file
, dump_attributes
= True, dump_classes
= True, debug
= False):
256 """Read WSPP documentation-derived schema files."""
262 attr_ldif
= __parse_schema_file(attr_file
, "attributeSchema")
264 classes_ldif
= __parse_schema_file(classes_file
, "classSchema")
266 return attr_ldif
+ "\n\n" + classes_ldif
+ "\n\n"
268 if __name__
== '__main__':
272 attr_file
= sys
.argv
[1]
273 classes_file
= sys
.argv
[2]
275 print >>sys
.stderr
, "Usage: %s attr-file.txt classes-file.txt" % (sys
.argv
[0])
278 print read_ms_schema(attr_file
, classes_file
)