2 # create schema.ldif (as a string) from WSPP documentation
4 # based on minschema.py and minschema_wspp
13 # bit positions as labeled in the docs
14 bitFields["searchflags"] = {
16 'fPDNTATTINDEX': 30, # PI
18 'fPRESERVEONDELETE': 28, # PR
20 'fTUPLEINDEX': 26, # TP
21 'fSUBTREEATTINDEX': 25, # ST
22 'fCONFIDENTIAL': 24, # CF
23 'fNEVERVALUEAUDIT': 23, # NV
24 'fRODCAttribute': 22, # RO
27 # missing in ADTS but required by LDIF
28 'fRODCFilteredAttribute': 22, # RO ?
29 'fCONFIDENTAIL': 24, # typo
30 'fRODCFILTEREDATTRIBUTE': 22 # case
34 bitFields["systemflags"] = {
35 'FLAG_ATTR_NOT_REPLICATED': 31, 'FLAG_CR_NTDS_NC': 31, # NR
36 'FLAG_ATTR_REQ_PARTIAL_SET_MEMBER': 30, 'FLAG_CR_NTDS_DOMAIN': 30, # PS
37 'FLAG_ATTR_IS_CONSTRUCTED': 29, 'FLAG_CR_NTDS_NOT_GC_REPLICATED': 29, # CS
38 'FLAG_ATTR_IS_OPERATIONAL': 28, # OP
39 'FLAG_SCHEMA_BASE_OBJECT': 27, # BS
40 'FLAG_ATTR_IS_RDN': 26, # RD
41 'FLAG_DISALLOW_MOVE_ON_DELETE': 6, # DE
42 'FLAG_DOMAIN_DISALLOW_MOVE': 5, # DM
43 'FLAG_DOMAIN_DISALLOW_RENAME': 4, # DR
44 'FLAG_CONFIG_ALLOW_LIMITED_MOVE': 3, # AL
45 'FLAG_CONFIG_ALLOW_MOVE': 2, # AM
46 'FLAG_CONFIG_ALLOW_RENAME': 1, # AR
47 'FLAG_DISALLOW_DELETE': 0 # DD
51 bitFields["schemaflagsex"] = {
52 'FLAG_ATTR_IS_CRITICAL': 31
57 '1.3.12.2.1011.28.0.702' : base64.b64encode('\x2B\x0C\x02\x87\x73\x1C\x00\x85\x3E'),
58 '1.2.840.113556.1.1.1.12': base64.b64encode('\x2A\x86\x48\x86\xF7\x14\x01\x01\x01\x0C'),
59 '2.6.6.1.2.5.11.29' : base64.b64encode('\x56\x06\x01\x02\x05\x0B\x1D'),
60 '1.2.840.113556.1.1.1.11': base64.b64encode('\x2A\x86\x48\x86\xF7\x14\x01\x01\x01\x0B'),
61 '1.3.12.2.1011.28.0.714' : base64.b64encode('\x2B\x0C\x02\x87\x73\x1C\x00\x85\x4A'),
62 '1.3.12.2.1011.28.0.732' : base64.b64encode('\x2B\x0C\x02\x87\x73\x1C\x00\x85\x5C'),
63 '1.2.840.113556.1.1.1.6' : base64.b64encode('\x2A\x86\x48\x86\xF7\x14\x01\x01\x01\x06')
66 # separated by commas in docs, and must be broken up
67 multivalued_attrs = set(["auxiliaryclass","maycontain","mustcontain","posssuperiors",
68 "systemauxiliaryclass","systemmaycontain","systemmustcontain",
69 "systemposssuperiors"])
71 def __read_folded_line(f, buffer):
72 """ reads a line from an LDIF file, unfolding it"""
81 # cannot fold an empty line
82 assert(line != "" and line != "\n")
92 # eof, definitely won't be folded
95 # marks end of a folded line
96 # line contains the now unfolded line
97 # buffer contains the start of the next possibly folded line
101 return (line, buffer)
104 def __read_raw_entries(f):
105 """reads an LDIF entry, only unfolding lines"""
107 # will not match options after the attribute type
108 attr_type_re = re.compile("^([A-Za-z]+[A-Za-z0-9-]*):")
116 (l, buffer) = __read_folded_line(f, buffer)
121 if l == "\n" or l == "":
124 m = attr_type_re.match(l)
132 print >>sys.stderr, "Invalid line: %s" % l,
143 """fix a string DN to use ${SCHEMADN}"""
146 if dn.find("<RootDomainDN>") != -1:
147 dn = dn.replace("\n ", "")
148 dn = dn.replace(" ", "")
149 return dn.replace("CN=Schema,CN=Configuration,<RootDomainDN>", "${SCHEMADN}")
153 def __convert_bitfield(key, value):
154 """Evaluate the OR expression in 'value'"""
155 assert(isinstance(value, str))
157 value = value.replace("\n ", "")
158 value = value.replace(" ", "")
161 # some attributes already have numeric values
165 flags = value.split("|")
167 bitpos = bitFields[key][f]
168 o = o | (1 << (31 - bitpos))
172 def __write_ldif_one(entry):
173 """Write out entry as LDIF"""
177 if isinstance(l[1], str):
182 if l[0].lower() == 'omobjectclass':
183 out.append("%s:: %s" % (l[0], l[1]))
187 out.append("%s: %s" % (l[0], v))
190 return "\n".join(out)
192 def __transform_entry(entry, objectClass):
193 """Perform transformations required to convert the LDIF-like schema
194 file entries to LDIF, including Samba-specific stuff."""
196 entry = [l.split(":", 1) for l in entry]
205 if not cn and key == "cn":
208 if key in multivalued_attrs:
209 # unlike LDIF, these are comma-separated
210 l[1] = l[1].replace("\n ", "")
211 l[1] = l[1].replace(" ", "")
213 l[1] = l[1].split(",")
216 l[1] = __convert_bitfield(key, l[1])
218 if key == "omobjectclass":
219 l[1] = oMObjectClassBER[l[1].strip()]
221 if isinstance(l[1], str):
226 entry.insert(0, ["dn", "CN=%s,${SCHEMADN}" % cn])
227 entry.insert(1, ["objectClass", ["top", objectClass]])
231 def __parse_schema_file(filename, objectClass):
232 """Load and transform a schema file."""
236 f = open(filename, "rU")
237 for entry in __read_raw_entries(f):
238 out.append(__write_ldif_one(__transform_entry(entry, objectClass)))
240 return "\n\n".join(out)
243 def read_ms_schema(attr_file, classes_file, dump_attributes = True, dump_classes = True, debug = False):
244 """Read WSPP documentation-derived schema files."""
250 attr_ldif = __parse_schema_file(attr_file, "attributeSchema")
252 classes_ldif = __parse_schema_file(classes_file, "classSchema")
254 return attr_ldif + "\n\n" + classes_ldif + "\n\n"