pyldb: avoid segfault when adding an element with no name
[kai/samba-autobuild/.git] / source4 / scripting / bin / fullschema
1 #!/usr/bin/env python3
2
3 # Works out the full schema
4 #
5 from __future__ import print_function
6
7 import base64
8 import optparse
9 import sys
10
11 # Find right directory when running from source tree
12 sys.path.insert(0, "bin/python")
13
14 import samba
15 from samba import getopt as options, Ldb
16 from ldb import SCOPE_SUBTREE, SCOPE_BASE
17 import sys
18
19 parser = optparse.OptionParser("fullschema <URL>")
20 sambaopts = options.SambaOptions(parser)
21 parser.add_option_group(sambaopts)
22 credopts = options.CredentialsOptions(parser)
23 parser.add_option_group(credopts)
24 parser.add_option_group(options.VersionOptions(parser))
25 parser.add_option("--dump-classes", action="store_true")
26 parser.add_option("--dump-attributes", action="store_true")
27
28 opts, args = parser.parse_args()
29 opts.dump_all = True
30
31 if opts.dump_classes:
32     opts.dump_all = False
33 if opts.dump_attributes:
34     opts.dump_all = False
35 if opts.dump_all:
36     opts.dump_classes = True
37     opts.dump_attributes = True
38
39 if len(args) != 1:
40     parser.print_usage()
41     sys.exit(1)
42
43 url = args[0]
44
45 lp_ctx = sambaopts.get_loadparm()
46
47 creds = credopts.get_credentials(lp_ctx)
48 ldb = Ldb(url, credentials=creds, lp=lp_ctx, options=["modules:paged_searches"])
49
50 # the attributes we need for objectclasses
51 class_attrs = ["objectClass", 
52                "cn",
53                "subClassOf", 
54                "governsID", 
55                "possSuperiors", 
56                "possibleInferiors",
57                "mayContain",
58                "mustContain",
59                "auxiliaryClass",
60                "rDNAttID",
61                "adminDisplayName",
62                "adminDescription",
63                "objectClassCategory",
64                "lDAPDisplayName",
65                "schemaIDGUID",
66                "systemOnly",
67                "systemPossSuperiors",
68                "systemMayContain",
69                "systemMustContain",
70                "systemAuxiliaryClass",
71                "defaultSecurityDescriptor",
72                "systemFlags",
73                "defaultHidingValue",
74                "defaultObjectCategory", 
75                
76                # this attributes are not used by w2k3
77                "schemaFlagsEx",
78                "msDs-IntId",
79                "msDs-Schema-Extensions",
80                "classDisplayName",
81                "isDefunct"]
82
83 attrib_attrs = ["objectClass",
84                 "cn",
85                 "attributeID", 
86                 "attributeSyntax",
87                 "isSingleValued",
88                 "rangeLower",
89                 "rangeUpper",
90                 "mAPIID",
91                 "linkID",
92                 "adminDisplayName",
93                 "oMObjectClass",
94                 "adminDescription",
95                 "oMSyntax", 
96                 "searchFlags",
97                 "extendedCharsAllowed",
98                 "lDAPDisplayName",
99                 "schemaIDGUID",
100                 "attributeSecurityGUID",
101                 "systemOnly",
102                 "systemFlags",
103                 "isMemberOfPartialAttributeSet",
104                 
105                 # this attributes are not used by w2k3
106                 "schemaFlagsEx",
107                 "msDs-IntId",
108                 "msDs-Schema-Extensions",
109                 "classDisplayName",
110                 "isEphemeral",
111                 "isDefunct"]
112
113 class Objectclass(dict):
114
115     def __init__(self, ldb, name):
116         """create an objectclass object"""
117         self.name = name
118
119
120 class Attribute(dict):
121
122     def __init__(self, ldb, name):
123         """create an attribute object"""
124         self.name = name
125         self["cn"] = get_object_cn(ldb, name)
126
127
128
129 def fix_dn(dn):
130     """fix a string DN to use ${SCHEMADN}"""
131     return dn.replace(rootDse["schemaNamingContext"][0], "${SCHEMADN}")
132
133
134 def write_ldif_one(o, attrs):
135     """dump an object as ldif"""
136     print("dn: CN=%s,${SCHEMADN}" % o["cn"])
137     for a in attrs:
138         if not o.has_key(a):
139             continue
140         # special case for oMObjectClass, which is a binary object
141         v = o[a]
142         list = []
143         for j in v:
144             value = fix_dn(j)
145             list.append(value)
146         list.sort()
147         for j in list:
148             value = fix_dn(j)
149             if a != "cn":
150                 if a == "oMObjectClass":
151                     print("%s:: %s" % (a, base64.b64encode(value)).decode('utf8'))
152                 elif a.endswith("GUID"):
153                     print("%s: %s" % (a, ldb.schema_format_value(a, value)))
154                 else:
155                     print("%s: %s" % (a, value))
156     print()
157
158
159 # get the rootDSE
160 res = ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["schemaNamingContext"])
161 rootDse = res[0]
162
163 if opts.dump_attributes:
164     res = ldb.search(expression="objectClass=attributeSchema", 
165                      base=rootDse["schemaNamingContext"][0], scope=SCOPE_SUBTREE,attrs=attrib_attrs,
166                      controls=["server_sort:1:0:cn"])
167     
168     for msg in res:
169         o = Objectclass(ldb, msg["ldapDisplayName"])
170         for a in msg:
171             o[a] = msg[a]
172         write_ldif_one(o, attrib_attrs)
173             
174 if opts.dump_classes:
175     res = ldb.search(expression="objectClass=classSchema", 
176                      base=rootDse["schemaNamingContext"][0], scope=SCOPE_SUBTREE,attrs=class_attrs,
177                      controls=["server_sort:1:0:cn"])
178
179     for msg in res:
180         o = Objectclass(ldb, msg["ldapDisplayName"])
181         for a in msg:
182             o[a] = msg[a]
183         write_ldif_one(o, class_attrs)
184