mirror of
https://github.com/samba-team/samba.git
synced 2025-02-09 09:57:48 +03:00
This made Python 2's print behave like Python 3's print(). In some cases, where we had: from __future__ import print_function """Intended module documentation...""" this will have the side effect of making the intended module documentation work as the actual module documentation (i.e. becoming __doc__), because it is once again the first statement in the module. Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
266 lines
8.1 KiB
Python
Executable File
266 lines
8.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# Unix SMB/CIFS implementation.
|
|
# Copyright (C) Andrew Tridgell 2009
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
"""Tests the possibleInferiors generation in the schema_fsmo ldb module"""
|
|
|
|
import optparse
|
|
import sys
|
|
|
|
|
|
# Find right directory when running from source tree
|
|
sys.path.insert(0, "bin/python")
|
|
|
|
from samba import getopt as options, Ldb
|
|
import ldb
|
|
|
|
parser = optparse.OptionParser("possibleinferiors.py <URL> [<CLASS>]")
|
|
sambaopts = options.SambaOptions(parser)
|
|
parser.add_option_group(sambaopts)
|
|
credopts = options.CredentialsOptions(parser)
|
|
parser.add_option_group(credopts)
|
|
parser.add_option_group(options.VersionOptions(parser))
|
|
parser.add_option("--wspp", action="store_true")
|
|
|
|
opts, args = parser.parse_args()
|
|
|
|
if len(args) < 1:
|
|
parser.print_usage()
|
|
sys.exit(1)
|
|
|
|
url = args[0]
|
|
if (len(args) > 1):
|
|
objectclass = args[1]
|
|
else:
|
|
objectclass = None
|
|
|
|
|
|
def uniq_list(alist):
|
|
"""return a unique list"""
|
|
set = {}
|
|
return [set.setdefault(e, e) for e in alist if e not in set]
|
|
|
|
|
|
lp_ctx = sambaopts.get_loadparm()
|
|
|
|
creds = credopts.get_credentials(lp_ctx)
|
|
|
|
ldb_options = []
|
|
# use 'paged_search' module when connecting remotely
|
|
if url.lower().startswith("ldap://"):
|
|
ldb_options = ["modules:paged_searches"]
|
|
|
|
db = Ldb(url, credentials=creds, lp=lp_ctx, options=ldb_options)
|
|
|
|
# get the rootDSE
|
|
res = db.search(base="", expression="",
|
|
scope=ldb.SCOPE_BASE,
|
|
attrs=["schemaNamingContext"])
|
|
rootDse = res[0]
|
|
|
|
schema_base = rootDse["schemaNamingContext"][0]
|
|
|
|
|
|
def possible_inferiors_search(db, oc):
|
|
"""return the possible inferiors via a search for the possibleInferiors attribute"""
|
|
res = db.search(base=schema_base,
|
|
expression=("ldapDisplayName=%s" % oc),
|
|
attrs=["possibleInferiors"])
|
|
|
|
poss = []
|
|
if len(res) == 0 or res[0].get("possibleInferiors") is None:
|
|
return poss
|
|
for item in res[0]["possibleInferiors"]:
|
|
poss.append(str(item))
|
|
poss = uniq_list(poss)
|
|
poss.sort()
|
|
return poss
|
|
|
|
|
|
# see [MS-ADTS] section 3.1.1.4.5.21
|
|
# and section 3.1.1.4.2 for this algorithm
|
|
|
|
# !systemOnly=TRUE
|
|
# !objectClassCategory=2
|
|
# !objectClassCategory=3
|
|
|
|
def supclasses(classinfo, oc):
|
|
list = []
|
|
if oc == "top":
|
|
return list
|
|
if classinfo[oc].get("SUPCLASSES") is not None:
|
|
return classinfo[oc]["SUPCLASSES"]
|
|
res = classinfo[oc]["subClassOf"]
|
|
for r in res:
|
|
list.append(r)
|
|
list.extend(supclasses(classinfo, r))
|
|
classinfo[oc]["SUPCLASSES"] = list
|
|
return list
|
|
|
|
|
|
def auxclasses(classinfo, oclist):
|
|
list = []
|
|
if oclist == []:
|
|
return list
|
|
for oc in oclist:
|
|
if classinfo[oc].get("AUXCLASSES") is not None:
|
|
list.extend(classinfo[oc]["AUXCLASSES"])
|
|
else:
|
|
list2 = []
|
|
list2.extend(classinfo[oc]["systemAuxiliaryClass"])
|
|
list2.extend(auxclasses(classinfo, classinfo[oc]["systemAuxiliaryClass"]))
|
|
list2.extend(classinfo[oc]["auxiliaryClass"])
|
|
list2.extend(auxclasses(classinfo, classinfo[oc]["auxiliaryClass"]))
|
|
list2.extend(auxclasses(classinfo, supclasses(classinfo, oc)))
|
|
classinfo[oc]["AUXCLASSES"] = list2
|
|
list.extend(list2)
|
|
return list
|
|
|
|
|
|
def subclasses(classinfo, oclist):
|
|
list = []
|
|
for oc in oclist:
|
|
list.extend(classinfo[oc]["SUBCLASSES"])
|
|
return list
|
|
|
|
|
|
def posssuperiors(classinfo, oclist):
|
|
list = []
|
|
for oc in oclist:
|
|
if classinfo[oc].get("POSSSUPERIORS") is not None:
|
|
list.extend(classinfo[oc]["POSSSUPERIORS"])
|
|
else:
|
|
list2 = []
|
|
list2.extend(classinfo[oc]["systemPossSuperiors"])
|
|
list2.extend(classinfo[oc]["possSuperiors"])
|
|
list2.extend(posssuperiors(classinfo, supclasses(classinfo, oc)))
|
|
if opts.wspp:
|
|
# the WSPP docs suggest we should do this:
|
|
list2.extend(posssuperiors(classinfo, auxclasses(classinfo, [oc])))
|
|
else:
|
|
# but testing against w2k3 and w2k8 shows that we need to do this instead
|
|
list2.extend(subclasses(classinfo, list2))
|
|
classinfo[oc]["POSSSUPERIORS"] = list2
|
|
list.extend(list2)
|
|
return list
|
|
|
|
|
|
def pull_classinfo(db):
|
|
"""At startup we build a classinfo[] dictionary that holds all the information needed to construct the possible inferiors"""
|
|
classinfo = {}
|
|
res = db.search(base=schema_base,
|
|
expression="objectclass=classSchema",
|
|
attrs=["ldapDisplayName", "systemOnly", "objectClassCategory",
|
|
"possSuperiors", "systemPossSuperiors",
|
|
"auxiliaryClass", "systemAuxiliaryClass", "subClassOf"])
|
|
for r in res:
|
|
name = str(r["ldapDisplayName"][0])
|
|
classinfo[name] = {}
|
|
if str(r["systemOnly"]) == "TRUE":
|
|
classinfo[name]["systemOnly"] = True
|
|
else:
|
|
classinfo[name]["systemOnly"] = False
|
|
if r.get("objectClassCategory"):
|
|
classinfo[name]["objectClassCategory"] = int(r["objectClassCategory"][0])
|
|
else:
|
|
classinfo[name]["objectClassCategory"] = 0
|
|
for a in ["possSuperiors", "systemPossSuperiors",
|
|
"auxiliaryClass", "systemAuxiliaryClass",
|
|
"subClassOf"]:
|
|
classinfo[name][a] = []
|
|
if r.get(a):
|
|
for i in r[a]:
|
|
classinfo[name][a].append(str(i))
|
|
|
|
# build a list of subclasses for each class
|
|
def subclasses_recurse(subclasses, oc):
|
|
list = subclasses[oc]
|
|
for c in list:
|
|
list.extend(subclasses_recurse(subclasses, c))
|
|
return list
|
|
|
|
subclasses = {}
|
|
for oc in classinfo:
|
|
subclasses[oc] = []
|
|
for oc in classinfo:
|
|
for c in classinfo[oc]["subClassOf"]:
|
|
if not c == oc:
|
|
subclasses[c].append(oc)
|
|
for oc in classinfo:
|
|
classinfo[oc]["SUBCLASSES"] = uniq_list(subclasses_recurse(subclasses, oc))
|
|
|
|
return classinfo
|
|
|
|
|
|
def is_in_list(list, c):
|
|
for a in list:
|
|
if c == a:
|
|
return True
|
|
return False
|
|
|
|
|
|
def possible_inferiors_constructed(db, classinfo, c):
|
|
list = []
|
|
for oc in classinfo:
|
|
superiors = posssuperiors(classinfo, [oc])
|
|
if (is_in_list(superiors, c) and
|
|
classinfo[oc]["systemOnly"] == False and
|
|
classinfo[oc]["objectClassCategory"] != 2 and
|
|
classinfo[oc]["objectClassCategory"] != 3):
|
|
list.append(oc)
|
|
list = uniq_list(list)
|
|
list.sort()
|
|
return list
|
|
|
|
|
|
def test_class(db, classinfo, oc):
|
|
"""test to see if one objectclass returns the correct possibleInferiors"""
|
|
print("test: objectClass.%s" % oc)
|
|
poss1 = possible_inferiors_search(db, oc)
|
|
poss2 = possible_inferiors_constructed(db, classinfo, oc)
|
|
if poss1 != poss2:
|
|
print("failure: objectClass.%s [" % oc)
|
|
print("Returned incorrect list for objectclass %s" % oc)
|
|
print("search: %s" % poss1)
|
|
print("constructed: %s" % poss2)
|
|
for i in range(0, min(len(poss1), len(poss2))):
|
|
print("%30s %30s" % (poss1[i], poss2[i]))
|
|
print("]")
|
|
sys.exit(1)
|
|
else:
|
|
print("success: objectClass.%s" % oc)
|
|
|
|
|
|
def get_object_classes(db):
|
|
"""return a list of all object classes"""
|
|
list = []
|
|
for item in classinfo:
|
|
list.append(item)
|
|
return list
|
|
|
|
|
|
classinfo = pull_classinfo(db)
|
|
|
|
if objectclass is None:
|
|
for oc in get_object_classes(db):
|
|
test_class(db, classinfo, oc)
|
|
else:
|
|
test_class(db, classinfo, objectclass)
|
|
|
|
print("Lists match OK")
|