auth/spnego: rename 'nt_status' to 'status' in gensec_spnego_create_negTokenInit()
[Samba.git] / python / samba / ms_schema.py
blob245ce3f2a60832e89d6ff54498245f42c0c794eb
1 # create schema.ldif (as a string) from WSPP documentation
3 # based on minschema.py and minschema_wspp
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 """Generate LDIF from WSPP documentation."""
20 import re
21 import base64
22 import uuid
24 bitFields = {}
26 # ADTS: 2.2.9
27 # bit positions as labeled in the docs
28 bitFields["searchflags"] = {
29 'fATTINDEX': 31, # IX
30 'fPDNTATTINDEX': 30, # PI
31 'fANR': 29, # AR
32 'fPRESERVEONDELETE': 28, # PR
33 'fCOPY': 27, # CP
34 'fTUPLEINDEX': 26, # TP
35 'fSUBTREEATTINDEX': 25, # ST
36 'fCONFIDENTIAL': 24, # CF
37 'fNEVERVALUEAUDIT': 23, # NV
38 'fRODCAttribute': 22, # RO
41 # missing in ADTS but required by LDIF
42 'fRODCFilteredAttribute': 22, # RO ?
43 'fCONFIDENTAIL': 24, # typo
44 'fRODCFILTEREDATTRIBUTE': 22 # case
47 # ADTS: 2.2.10
48 bitFields["systemflags"] = {
49 'FLAG_ATTR_NOT_REPLICATED': 31, 'FLAG_CR_NTDS_NC': 31, # NR
50 'FLAG_ATTR_REQ_PARTIAL_SET_MEMBER': 30, 'FLAG_CR_NTDS_DOMAIN': 30, # PS
51 'FLAG_ATTR_IS_CONSTRUCTED': 29, 'FLAG_CR_NTDS_NOT_GC_REPLICATED': 29, # CS
52 'FLAG_ATTR_IS_OPERATIONAL': 28, # OP
53 'FLAG_SCHEMA_BASE_OBJECT': 27, # BS
54 'FLAG_ATTR_IS_RDN': 26, # RD
55 'FLAG_DISALLOW_MOVE_ON_DELETE': 6, # DE
56 'FLAG_DOMAIN_DISALLOW_MOVE': 5, # DM
57 'FLAG_DOMAIN_DISALLOW_RENAME': 4, # DR
58 'FLAG_CONFIG_ALLOW_LIMITED_MOVE': 3, # AL
59 'FLAG_CONFIG_ALLOW_MOVE': 2, # AM
60 'FLAG_CONFIG_ALLOW_RENAME': 1, # AR
61 'FLAG_DISALLOW_DELETE': 0 # DD
64 # ADTS: 2.2.11
65 bitFields["schemaflagsex"] = {
66 'FLAG_ATTR_IS_CRITICAL': 31
69 # ADTS: 3.1.1.2.2.2
70 oMObjectClassBER = {
71 '1.3.12.2.1011.28.0.702' : base64.b64encode('\x2B\x0C\x02\x87\x73\x1C\x00\x85\x3E'),
72 '1.2.840.113556.1.1.1.12': base64.b64encode('\x2A\x86\x48\x86\xF7\x14\x01\x01\x01\x0C'),
73 '2.6.6.1.2.5.11.29' : base64.b64encode('\x56\x06\x01\x02\x05\x0B\x1D'),
74 '1.2.840.113556.1.1.1.11': base64.b64encode('\x2A\x86\x48\x86\xF7\x14\x01\x01\x01\x0B'),
75 '1.3.12.2.1011.28.0.714' : base64.b64encode('\x2B\x0C\x02\x87\x73\x1C\x00\x85\x4A'),
76 '1.3.12.2.1011.28.0.732' : base64.b64encode('\x2B\x0C\x02\x87\x73\x1C\x00\x85\x5C'),
77 '1.2.840.113556.1.1.1.6' : base64.b64encode('\x2A\x86\x48\x86\xF7\x14\x01\x01\x01\x06')
80 # separated by commas in docs, and must be broken up
81 multivalued_attrs = set(["auxiliaryclass","maycontain","mustcontain","posssuperiors",
82 "systemauxiliaryclass","systemmaycontain","systemmustcontain",
83 "systemposssuperiors"])
85 def __read_folded_line(f, buffer):
86 """ reads a line from an LDIF file, unfolding it"""
87 line = buffer
89 while True:
90 l = f.readline()
92 if l[:1] == " ":
93 # continued line
95 # cannot fold an empty line
96 assert(line != "" and line != "\n")
98 # preserves '\n '
99 line = line + l
100 else:
101 # non-continued line
102 if line == "":
103 line = l
105 if l == "":
106 # eof, definitely won't be folded
107 break
108 else:
109 # marks end of a folded line
110 # line contains the now unfolded line
111 # buffer contains the start of the next possibly folded line
112 buffer = l
113 break
115 return (line, buffer)
118 def __read_raw_entries(f):
119 """reads an LDIF entry, only unfolding lines"""
120 import sys
122 # will not match options after the attribute type
123 attr_type_re = re.compile("^([A-Za-z]+[A-Za-z0-9-]*):")
125 buffer = ""
127 while True:
128 entry = []
130 while True:
131 (l, buffer) = __read_folded_line(f, buffer)
133 if l[:1] == "#":
134 continue
136 if l == "\n" or l == "":
137 break
139 m = attr_type_re.match(l)
141 if m:
142 if l[-1:] == "\n":
143 l = l[:-1]
145 entry.append(l)
146 else:
147 print >>sys.stderr, "Invalid line: %s" % l,
148 sys.exit(1)
150 if len(entry):
151 yield entry
153 if l == "":
154 break
157 def fix_dn(dn):
158 """fix a string DN to use ${SCHEMADN}"""
160 # folding?
161 if dn.find("<RootDomainDN>") != -1:
162 dn = dn.replace("\n ", "")
163 dn = dn.replace(" ", "")
164 return dn.replace("CN=Schema,CN=Configuration,<RootDomainDN>", "${SCHEMADN}")
165 else:
166 return dn
168 def __convert_bitfield(key, value):
169 """Evaluate the OR expression in 'value'"""
170 assert(isinstance(value, str))
172 value = value.replace("\n ", "")
173 value = value.replace(" ", "")
175 try:
176 # some attributes already have numeric values
177 o = int(value)
178 except ValueError:
179 o = 0
180 flags = value.split("|")
181 for f in flags:
182 bitpos = bitFields[key][f]
183 o = o | (1 << (31 - bitpos))
185 return str(o)
187 def __write_ldif_one(entry):
188 """Write out entry as LDIF"""
189 out = []
191 for l in entry:
192 if isinstance(l[1], str):
193 vl = [l[1]]
194 else:
195 vl = l[1]
197 if l[0].lower() == 'omobjectclass':
198 out.append("%s:: %s" % (l[0], l[1]))
199 continue
201 for v in vl:
202 out.append("%s: %s" % (l[0], v))
205 return "\n".join(out)
207 def __transform_entry(entry, objectClass):
208 """Perform transformations required to convert the LDIF-like schema
209 file entries to LDIF, including Samba-specific stuff."""
211 entry = [l.split(":", 1) for l in entry]
213 cn = ""
215 for l in entry:
216 key = l[0].lower()
217 l[1] = l[1].lstrip()
218 l[1] = l[1].rstrip()
220 if not cn and key == "cn":
221 cn = l[1]
223 if key in multivalued_attrs:
224 # unlike LDIF, these are comma-separated
225 l[1] = l[1].replace("\n ", "")
226 l[1] = l[1].replace(" ", "")
228 l[1] = l[1].split(",")
230 if key in bitFields:
231 l[1] = __convert_bitfield(key, l[1])
233 if key == "omobjectclass":
234 l[1] = oMObjectClassBER[l[1].strip()]
236 if isinstance(l[1], str):
237 l[1] = fix_dn(l[1])
240 assert(cn)
241 entry.insert(0, ["dn", "CN=%s,${SCHEMADN}" % cn])
242 entry.insert(1, ["objectClass", ["top", objectClass]])
243 entry.insert(2, ["cn", cn])
244 entry.insert(2, ["objectGUID", str(uuid.uuid4())])
245 entry.insert(2, ["adminDescription", cn])
246 entry.insert(2, ["adminDisplayName", cn])
248 for l in entry:
249 key = l[0].lower()
251 if key == "cn":
252 entry.remove(l)
254 return entry
256 def __parse_schema_file(filename, objectClass):
257 """Load and transform a schema file."""
259 out = []
261 f = open(filename, "rU")
262 for entry in __read_raw_entries(f):
263 out.append(__write_ldif_one(__transform_entry(entry, objectClass)))
265 return "\n\n".join(out)
268 def read_ms_schema(attr_file, classes_file, dump_attributes = True, dump_classes = True, debug = False):
269 """Read WSPP documentation-derived schema files."""
271 attr_ldif = ""
272 classes_ldif = ""
274 if dump_attributes:
275 attr_ldif = __parse_schema_file(attr_file, "attributeSchema")
276 if dump_classes:
277 classes_ldif = __parse_schema_file(classes_file, "classSchema")
279 return attr_ldif + "\n\n" + classes_ldif + "\n\n"
281 if __name__ == '__main__':
282 import sys
284 try:
285 attr_file = sys.argv[1]
286 classes_file = sys.argv[2]
287 except IndexError:
288 print >>sys.stderr, "Usage: %s attr-file.txt classes-file.txt" % (sys.argv[0])
289 sys.exit(1)
291 print read_ms_schema(attr_file, classes_file)