class Target: """Simple helper class that contains data for a specific SAM connection.""" def __init__(self, basedn, dn, lp): self.db = Ldb(lp=lp, session_info=system_session()) self.db.set_opaque("skip_allocate_sids", "true") self.basedn = basedn self.basedn_casefold = ldb.Dn(self.db, basedn).get_casefold() self.substvars = {"BASEDN": self.basedn} self.file = os.path.join(tempdir, "%s.ldb" % self.basedn_casefold) self.url = "tdb://" + self.file self._dn = dn def dn(self, rdn): return self._dn(self.basedn, rdn) def connect(self): return self.db.connect(self.url) def setup_data(self, path): self.add_ldif(read_datafile(path)) def subst(self, text): return substitute_var(text, self.substvars) def add_ldif(self, ldif): self.db.add_ldif(self.subst(ldif)) def modify_ldif(self, ldif): self.db.modify_ldif(self.subst(ldif))
def accountcontrol(lp, creds, username=None, value=0): """enable/disable an OpenChange user account. :param lp: Loadparm context :param creds: Credentials context :param username: Name of user to disable :param value: the control value """ names = guess_names_from_smbconf(lp, None, None) db = Ldb(url=os.path.join(lp.get("private dir"), lp.samdb_url()), session_info=system_session(), credentials=creds, lp=lp) user_dn = "CN=%s,CN=Users,%s" % (username, names.domaindn) extended_user = """ dn: %s changetype: modify replace: msExchUserAccountControl msExchUserAccountControl: %d """ % (user_dn, value) db.modify_ldif(extended_user) if value == 2: print "[+] Account %s disabled" % username else: print "[+] Account %s enabled" % username
def accountcontrol(lp, creds, username=None, value=0): """enable/disable an OpenChange user account. :param lp: Loadparm context :param creds: Credentials context :param username: Name of user to disable :param value: the control value """ names = guess_names_from_smbconf(lp, None, None) db = Ldb(url=get_ldb_url(lp, creds, names), session_info=system_session(), credentials=creds, lp=lp) user_dn = get_user_dn(db, names.domaindn, username) extended_user = """ dn: %s changetype: modify replace: msExchUserAccountControl msExchUserAccountControl: %d """ % (user_dn, value) db.modify_ldif(extended_user) if value == 2: print "[+] Account %s disabled" % username else: print "[+] Account %s enabled" % username
class Target: """Simple helper class that contains data for a specific SAM connection.""" def __init__(self, file, basedn, dn): self.file = os.path.join(tempdir, file) self.url = "tdb://" + self.file self.basedn = basedn self.substvars = {"BASEDN": self.basedn} self.db = Ldb(lp=cmdline_loadparm) self._dn = dn def dn(self, rdn): return self._dn(self.basedn, rdn) def connect(self): return self.db.connect(self.url) def setup_data(self, path): self.add_ldif(read_datafile(path)) def subst(self, text): return substitute_var(text, self.substvars) def add_ldif(self, ldif): self.db.add_ldif(self.subst(ldif)) def modify_ldif(self, ldif): self.db.modify_ldif(self.subst(ldif))
class Target: """Simple helper class that contains data for a specific SAM connection.""" def __init__(self, basedn, dn, lp): self.db = Ldb(lp=lp, session_info=system_session()) self.db.set_opaque("skip_allocate_sids", "true"); self.basedn = basedn self.basedn_casefold = ldb.Dn(self.db, basedn).get_casefold() self.substvars = {"BASEDN": self.basedn} self.file = os.path.join(tempdir, "%s.ldb" % self.basedn_casefold) self.url = "tdb://" + self.file self._dn = dn def dn(self, rdn): return self._dn(self.basedn, rdn) def connect(self): return self.db.connect(self.url) def setup_data(self, path): self.add_ldif(read_datafile(path)) def subst(self, text): return substitute_var(text, self.substvars) def add_ldif(self, ldif): self.db.add_ldif(self.subst(ldif)) def modify_ldif(self, ldif): self.db.modify_ldif(self.subst(ldif))
def ldif_to_samdb(dburl, lp, ldif_file, forced_local_dsa=None): """Routine to import all objects and attributes that are relevent to the KCC algorithms from a previously exported LDIF file. The point of this function is to allow a programmer/debugger to import an LDIF file with non-security relevent information that was previously extracted from a DC database. The LDIF file is used to create a temporary abbreviated database. The KCC algorithm can then run against this abbreviated database for debug or test verification that the topology generated is computationally the same between different OSes and algorithms. :param dburl: path to the temporary abbreviated db to create :param ldif_file: path to the ldif file to import """ if os.path.exists(dburl): raise LdifError("Specify a database (%s) that doesn't already exist." % dburl) # Use ["modules:"] as we are attempting to build a sam # database as opposed to start it here. tmpdb = Ldb(url=dburl, session_info=system_session(), lp=lp, options=["modules:"]) tmpdb.transaction_start() try: data = read_and_sub_file(ldif_file, None) tmpdb.add_ldif(data, None) if forced_local_dsa: tmpdb.modify_ldif("""dn: @ROOTDSE changetype: modify replace: dsServiceName dsServiceName: CN=NTDS Settings,%s """ % forced_local_dsa) tmpdb.add_ldif("""dn: @MODULES @LIST: rootdse,extended_dn_in,extended_dn_out_ldb,objectguid - """) except Exception as estr: tmpdb.transaction_cancel() raise LdifError("Failed to import %s: %s" % (ldif_file, estr)) tmpdb.transaction_commit() # We have an abbreviated list of options here because we have built # an abbreviated database. We use the rootdse and extended-dn # modules only during this re-open samdb = SamDB(url=dburl, session_info=system_session(), lp=lp) return samdb
def newuser(names, lp, creds, username=None): """extend user record with OpenChange settings. :param lp: Loadparm context :param creds: Credentials context :param names: provision names object. :param username: Name of user to extend """ db = Ldb(url=get_ldb_url(lp, creds, names), session_info=system_session(), credentials=creds, lp=lp) user_dn = get_user_dn(db, "CN=Users,%s" % names.domaindn, username) if user_dn: extended_user = """ dn: %(user_dn)s changetype: modify add: mailNickName mailNickname: %(username)s add: homeMDB homeMDB: CN=Mailbox Store (%(netbiosname)s),CN=First Storage Group,CN=InformationStore,CN=%(netbiosname)s,CN=Servers,CN=%(firstou)s,CN=Administrative Groups,CN=%(firstorg)s,CN=Microsoft Exchange,CN=Services,CN=Configuration,%(domaindn)s add: homeMTA homeMTA: CN=Mailbox Store (%(netbiosname)s),CN=First Storage Group,CN=InformationStore,CN=%(netbiosname)s,CN=Servers,CN=%(firstou)s,CN=Administrative Groups,CN=%(firstorg)s,CN=Microsoft Exchange,CN=Services,CN=Configuration,%(domaindn)s add: legacyExchangeDN legacyExchangeDN: /o=%(firstorg)s/ou=%(firstou)s/cn=Recipients/cn=%(username)s add: proxyAddresses proxyAddresses: =EX:/o=%(firstorg)s/ou=%(firstou)s/cn=Recipients/cn=%(username)s proxyAddresses: smtp:postmaster@%(dnsdomain)s proxyAddresses: X400:c=US;a= ;p=First Organizati;o=Exchange;s=%(username)s proxyAddresses: SMTP:%(username)s@%(dnsdomain)s replace: msExchUserAccountControl msExchUserAccountControl: 0 """ ldif_value = extended_user % {"user_dn": user_dn, "username": username, "netbiosname": names.netbiosname, "firstorg": names.firstorg, "firstou": names.firstou, "domaindn": names.domaindn, "dnsdomain": names.dnsdomain} db.modify_ldif(ldif_value) res = db.search(base=user_dn, scope=SCOPE_BASE, attrs=["*"]) if len(res) == 1: record = res[0] else: raise Exception("this should never happen as we just modified the record...") record_keys = map(lambda x: x.lower(), record.keys()) if "displayname" not in record_keys: extended_user = "******" % (user_dn, username) db.modify_ldif(extended_user) if "mail" not in record_keys: extended_user = "******" % (user_dn, username, names.dnsdomain) db.modify_ldif(extended_user) print "[+] User %s extended and enabled" % username else: print "[!] User '%s' not found" % username
def newuser(lp, creds, username=None): """extend user record with OpenChange settings. :param lp: Loadparm context :param creds: Credentials context :param username: Name of user to extend """ names = guess_names_from_smbconf(lp, None, None) db = Ldb(url=get_ldb_url(lp, creds, names), session_info=system_session(), credentials=creds, lp=lp) user_dn = get_user_dn(db, "CN=Users,%s" % names.domaindn, username) if user_dn: extended_user = """ dn: %(user_dn)s changetype: modify add: mailNickName mailNickname: %(username)s add: homeMDB homeMDB: CN=Mailbox Store (%(netbiosname)s),CN=First Storage Group,CN=InformationStore,CN=%(netbiosname)s,CN=Servers,CN=First Administrative Group,CN=Administrative Groups,CN=%(firstorg)s,CN=Microsoft Exchange,CN=Services,CN=Configuration,%(domaindn)s add: homeMTA homeMTA: CN=Mailbox Store (%(netbiosname)s),CN=First Storage Group,CN=InformationStore,CN=%(netbiosname)s,CN=Servers,CN=First Administrative Group,CN=Administrative Groups,CN=%(firstorg)s,CN=Microsoft Exchange,CN=Services,CN=Configuration,%(domaindn)s add: legacyExchangeDN legacyExchangeDN: /o=%(firstorg)s/ou=First Administrative Group/cn=Recipients/cn=%(username)s add: proxyAddresses proxyAddresses: =EX:/o=%(firstorg)s/ou=First Administrative Group/cn=Recipients/cn=%(username)s proxyAddresses: smtp:postmaster@%(dnsdomain)s proxyAddresses: X400:c=US;a= ;p=First Organizati;o=Exchange;s=%(username)s proxyAddresses: SMTP:%(username)s@%(dnsdomain)s replace: msExchUserAccountControl msExchUserAccountControl: 0 """ ldif_value = extended_user % {"user_dn": user_dn, "username": username, "netbiosname": names.netbiosname, "firstorg": names.firstorg, "domaindn": names.domaindn, "dnsdomain": names.dnsdomain} db.modify_ldif(ldif_value) res = db.search(base=user_dn, scope=SCOPE_BASE, attrs=["*"]) if len(res) == 1: record = res[0] else: raise Exception, \ "this should never happen as we just modified the record..." record_keys = map(lambda x: x.lower(), record.keys()) if "displayname" not in record_keys: extended_user = "******" % (user_dn, username) db.modify_ldif(extended_user) if "mail" not in record_keys: extended_user = "******" % (user_dn, username, names.dnsdomain) db.modify_ldif(extended_user) print "[+] User %s extended and enabled" % username else: print "[!] User '%s' not found" % username
def accountcontrol(names, lp, creds, username=None, value=0): """enable/disable an OpenChange user account. :param lp: Loadparm context :param creds: Credentials context :param names: Provision Names object :param username: Name of user to disable :param value: the control value """ db = Ldb(url=get_ldb_url(lp, creds, names), session_info=system_session(), credentials=creds, lp=lp) user_dn = get_user_dn(db, "CN=Users,%s" % names.domaindn, username) extended_user = """ dn: %s changetype: modify replace: msExchUserAccountControl msExchUserAccountControl: %d """ % (user_dn, value) db.modify_ldif(extended_user) if value == 2: print "[+] Account %s disabled" % username else: print "[+] Account %s enabled" % username
class MapTestCase(MapBaseTestCase): def setUp(self): super(MapTestCase, self).setUp() ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session()) ldb.set_opaque("skip_allocate_sids", "true") ldif = read_datafile("provision_samba3sam.ldif") ldb.add_ldif(self.samba4.subst(ldif)) self.setup_modules(ldb, self.samba3, self.samba4) del ldb self.ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session()) self.ldb.set_opaque("skip_allocate_sids", "true") def test_map_search(self): """Running search tests on mapped data.""" self.samba3.db.add({ "dn": "sambaDomainName=TESTS," + self.samba3.basedn, "objectclass": ["sambaDomain", "top"], "sambaSID": "S-1-5-21-4231626423-2410014848-2360679739", "sambaNextRid": "2000", "sambaDomainName": "TESTS" }) # Add a set of split records self.ldb.add_ldif(""" dn: """ + self.samba4.dn("cn=Domain Users") + """ objectClass: group cn: Domain Users objectSid: S-1-5-21-4231626423-2410014848-2360679739-513 """) # Add a set of split records self.ldb.add_ldif(""" dn: """ + self.samba4.dn("cn=X") + """ objectClass: user cn: X codePage: x revision: x dnsHostName: x nextRid: y lastLogon: x description: x objectSid: S-1-5-21-4231626423-2410014848-2360679739-1052 """) self.ldb.add({ "dn": self.samba4.dn("cn=Y"), "objectClass": "top", "cn": "Y", "codePage": "x", "revision": "x", "dnsHostName": "y", "nextRid": "y", "lastLogon": "y", "description": "x" }) self.ldb.add({ "dn": self.samba4.dn("cn=Z"), "objectClass": "top", "cn": "Z", "codePage": "x", "revision": "y", "dnsHostName": "z", "nextRid": "y", "lastLogon": "z", "description": "y" }) # Add a set of remote records self.samba3.db.add({ "dn": self.samba3.dn("cn=A"), "objectClass": "posixAccount", "cn": "A", "sambaNextRid": "x", "sambaBadPasswordCount": "x", "sambaLogonTime": "x", "description": "x", "sambaSID": "S-1-5-21-4231626423-2410014848-2360679739-1052", "sambaPrimaryGroupSID": "S-1-5-21-4231626423-2410014848-2360679739-512" }) self.samba3.db.add({ "dn": self.samba3.dn("cn=B"), "objectClass": "top", "cn": "B", "sambaNextRid": "x", "sambaBadPasswordCount": "x", "sambaLogonTime": "y", "description": "x" }) self.samba3.db.add({ "dn": self.samba3.dn("cn=C"), "objectClass": "top", "cn": "C", "sambaNextRid": "x", "sambaBadPasswordCount": "y", "sambaLogonTime": "z", "description": "y" }) # Testing search by DN # Search remote record by local DN dn = self.samba4.dn("cn=A") res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") # Search remote record by remote DN dn = self.samba3.dn("cn=A") res = self.samba3.db.search( dn, scope=SCOPE_BASE, attrs=["dnsHostName", "lastLogon", "sambaLogonTime"]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertTrue(not "dnsHostName" in res[0]) self.assertTrue(not "lastLogon" in res[0]) self.assertEquals(str(res[0]["sambaLogonTime"]), "x") # Search split record by local DN dn = self.samba4.dn("cn=X") res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertEquals(str(res[0]["dnsHostName"]), "x") self.assertEquals(str(res[0]["lastLogon"]), "x") # Search split record by remote DN dn = self.samba3.dn("cn=X") res = self.samba3.db.search( dn, scope=SCOPE_BASE, attrs=["dnsHostName", "lastLogon", "sambaLogonTime"]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertTrue(not "dnsHostName" in res[0]) self.assertTrue(not "lastLogon" in res[0]) self.assertEquals(str(res[0]["sambaLogonTime"]), "x") # Testing search by attribute # Search by ignored attribute res = self.ldb.search(expression="(revision=x)", scope=SCOPE_DEFAULT, attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 2) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[0]["dnsHostName"]), "x") self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Y")) self.assertEquals(str(res[1]["dnsHostName"]), "y") self.assertEquals(str(res[1]["lastLogon"]), "y") # Search by kept attribute res = self.ldb.search(expression="(description=y)", scope=SCOPE_DEFAULT, attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 2) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=C")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "z") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Z")) self.assertEquals(str(res[1]["dnsHostName"]), "z") self.assertEquals(str(res[1]["lastLogon"]), "z") # Search by renamed attribute res = self.ldb.search(expression="(badPwdCount=x)", scope=SCOPE_DEFAULT, attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 2) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) self.assertTrue(not "dnsHostName" in res[1]) self.assertEquals(str(res[1]["lastLogon"]), "y") # Search by converted attribute # TODO: # Using the SID directly in the parse tree leads to conversion # errors, letting the search fail with no results. #res = self.ldb.search("(objectSid=S-1-5-21-4231626423-2410014848-2360679739-1052)", scope=SCOPE_DEFAULT, attrs) res = self.ldb.search(expression="(objectSid=*)", base=None, scope=SCOPE_DEFAULT, attrs=["dnsHostName", "lastLogon", "objectSid"]) self.assertEquals(len(res), 4) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[1]["dnsHostName"]), "x") self.assertEquals(str(res[1]["lastLogon"]), "x") self.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-1052", res[1]["objectSid"]) self.assertTrue("objectSid" in res[1]) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-1052", res[0]["objectSid"]) self.assertTrue("objectSid" in res[0]) # Search by generated attribute # In most cases, this even works when the mapping is missing # a `convert_operator' by enumerating the remote db. res = self.ldb.search( expression="(primaryGroupID=512)", attrs=["dnsHostName", "lastLogon", "primaryGroupID"]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[0]["primaryGroupID"]), "512") # Note that Xs "objectSid" seems to be fine in the previous search for # "objectSid"... #res = ldb.search(expression="(primaryGroupID=*)", NULL, ldb. SCOPE_DEFAULT, attrs) #print len(res) + " results found" #for i in range(len(res)): # for (obj in res[i]) { # print obj + ": " + res[i][obj] # } # print "---" # # Search by remote name of renamed attribute */ res = self.ldb.search(expression="(sambaBadPasswordCount=*)", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 0) # Search by objectClass attrs = ["dnsHostName", "lastLogon", "objectClass"] res = self.ldb.search(expression="(objectClass=user)", attrs=attrs) self.assertEquals(len(res), 2) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[0]["objectClass"][0]), "user") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[1]["dnsHostName"]), "x") self.assertEquals(str(res[1]["lastLogon"]), "x") self.assertEquals(str(res[1]["objectClass"][0]), "user") # Prove that the objectClass is actually used for the search res = self.ldb.search( expression="(|(objectClass=user)(badPwdCount=x))", attrs=attrs) self.assertEquals(len(res), 3) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(res[0]["objectClass"][0], "user") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) self.assertTrue(not "dnsHostName" in res[1]) self.assertEquals(str(res[1]["lastLogon"]), "y") self.assertEquals(set(res[1]["objectClass"]), set(["top"])) self.assertEquals(str(res[2].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[2]["dnsHostName"]), "x") self.assertEquals(str(res[2]["lastLogon"]), "x") self.assertEquals(str(res[2]["objectClass"][0]), "user") # Testing search by parse tree # Search by conjunction of local attributes res = self.ldb.search(expression="(&(codePage=x)(revision=x))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 2) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[0]["dnsHostName"]), "x") self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Y")) self.assertEquals(str(res[1]["dnsHostName"]), "y") self.assertEquals(str(res[1]["lastLogon"]), "y") # Search by conjunction of remote attributes res = self.ldb.search(expression="(&(lastLogon=x)(description=x))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 2) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[1]["dnsHostName"]), "x") self.assertEquals(str(res[1]["lastLogon"]), "x") # Search by conjunction of local and remote attribute res = self.ldb.search(expression="(&(codePage=x)(description=x))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 2) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[0]["dnsHostName"]), "x") self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Y")) self.assertEquals(str(res[1]["dnsHostName"]), "y") self.assertEquals(str(res[1]["lastLogon"]), "y") # Search by conjunction of local and remote attribute w/o match attrs = ["dnsHostName", "lastLogon"] res = self.ldb.search(expression="(&(codePage=x)(nextRid=x))", attrs=attrs) self.assertEquals(len(res), 0) res = self.ldb.search(expression="(&(revision=x)(lastLogon=z))", attrs=attrs) self.assertEquals(len(res), 0) # Search by disjunction of local attributes res = self.ldb.search(expression="(|(revision=x)(dnsHostName=x))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 2) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[0]["dnsHostName"]), "x") self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Y")) self.assertEquals(str(res[1]["dnsHostName"]), "y") self.assertEquals(str(res[1]["lastLogon"]), "y") # Search by disjunction of remote attributes res = self.ldb.search(expression="(|(badPwdCount=x)(lastLogon=x))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 3) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertFalse("dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) self.assertFalse("dnsHostName" in res[1]) self.assertEquals(str(res[1]["lastLogon"]), "y") self.assertEquals(str(res[2].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[2]["dnsHostName"]), "x") self.assertEquals(str(res[2]["lastLogon"]), "x") # Search by disjunction of local and remote attribute res = self.ldb.search(expression="(|(revision=x)(lastLogon=y))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 3) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B")) self.assertFalse("dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "y") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[1]["dnsHostName"]), "x") self.assertEquals(str(res[1]["lastLogon"]), "x") self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Y")) self.assertEquals(str(res[2]["dnsHostName"]), "y") self.assertEquals(str(res[2]["lastLogon"]), "y") # Search by disjunction of local and remote attribute w/o match res = self.ldb.search(expression="(|(codePage=y)(nextRid=z))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 0) # Search by negated local attribute res = self.ldb.search(expression="(!(revision=x))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 6) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) self.assertTrue(not "dnsHostName" in res[1]) self.assertEquals(str(res[1]["lastLogon"]), "y") self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C")) self.assertTrue(not "dnsHostName" in res[2]) self.assertEquals(str(res[2]["lastLogon"]), "z") self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z")) self.assertEquals(str(res[3]["dnsHostName"]), "z") self.assertEquals(str(res[3]["lastLogon"]), "z") # Search by negated remote attribute res = self.ldb.search(expression="(!(description=x))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 4) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=C")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "z") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Z")) self.assertEquals(str(res[1]["dnsHostName"]), "z") self.assertEquals(str(res[1]["lastLogon"]), "z") # Search by negated conjunction of local attributes res = self.ldb.search(expression="(!(&(codePage=x)(revision=x)))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 6) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) self.assertTrue(not "dnsHostName" in res[1]) self.assertEquals(str(res[1]["lastLogon"]), "y") self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C")) self.assertTrue(not "dnsHostName" in res[2]) self.assertEquals(str(res[2]["lastLogon"]), "z") self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z")) self.assertEquals(str(res[3]["dnsHostName"]), "z") self.assertEquals(str(res[3]["lastLogon"]), "z") # Search by negated conjunction of remote attributes res = self.ldb.search(expression="(!(&(lastLogon=x)(description=x)))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 6) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "y") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=C")) self.assertTrue(not "dnsHostName" in res[1]) self.assertEquals(str(res[1]["lastLogon"]), "z") self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Y")) self.assertEquals(str(res[2]["dnsHostName"]), "y") self.assertEquals(str(res[2]["lastLogon"]), "y") self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z")) self.assertEquals(str(res[3]["dnsHostName"]), "z") self.assertEquals(str(res[3]["lastLogon"]), "z") # Search by negated conjunction of local and remote attribute res = self.ldb.search(expression="(!(&(codePage=x)(description=x)))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 6) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) self.assertTrue(not "dnsHostName" in res[1]) self.assertEquals(str(res[1]["lastLogon"]), "y") self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C")) self.assertTrue(not "dnsHostName" in res[2]) self.assertEquals(str(res[2]["lastLogon"]), "z") self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z")) self.assertEquals(str(res[3]["dnsHostName"]), "z") self.assertEquals(str(res[3]["lastLogon"]), "z") # Search by negated disjunction of local attributes res = self.ldb.search(expression="(!(|(revision=x)(dnsHostName=x)))", attrs=["dnsHostName", "lastLogon"]) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) self.assertTrue(not "dnsHostName" in res[1]) self.assertEquals(str(res[1]["lastLogon"]), "y") self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C")) self.assertTrue(not "dnsHostName" in res[2]) self.assertEquals(str(res[2]["lastLogon"]), "z") self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z")) self.assertEquals(str(res[3]["dnsHostName"]), "z") self.assertEquals(str(res[3]["lastLogon"]), "z") # Search by negated disjunction of remote attributes res = self.ldb.search(expression="(!(|(badPwdCount=x)(lastLogon=x)))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 5) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=C")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "z") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Y")) self.assertEquals(str(res[1]["dnsHostName"]), "y") self.assertEquals(str(res[1]["lastLogon"]), "y") self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z")) self.assertEquals(str(res[2]["dnsHostName"]), "z") self.assertEquals(str(res[2]["lastLogon"]), "z") # Search by negated disjunction of local and remote attribute res = self.ldb.search(expression="(!(|(revision=x)(lastLogon=y)))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 5) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=C")) self.assertTrue(not "dnsHostName" in res[1]) self.assertEquals(str(res[1]["lastLogon"]), "z") self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z")) self.assertEquals(str(res[2]["dnsHostName"]), "z") self.assertEquals(str(res[2]["lastLogon"]), "z") # Search by complex parse tree res = self.ldb.search( expression= "(|(&(revision=x)(dnsHostName=x))(!(&(description=x)(nextRid=y)))(badPwdCount=y))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 7) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) self.assertTrue(not "dnsHostName" in res[1]) self.assertEquals(str(res[1]["lastLogon"]), "y") self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C")) self.assertTrue(not "dnsHostName" in res[2]) self.assertEquals(str(res[2]["lastLogon"]), "z") self.assertEquals(str(res[3].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[3]["dnsHostName"]), "x") self.assertEquals(str(res[3]["lastLogon"]), "x") self.assertEquals(str(res[4].dn), self.samba4.dn("cn=Z")) self.assertEquals(str(res[4]["dnsHostName"]), "z") self.assertEquals(str(res[4]["lastLogon"]), "z") # Clean up dns = [ self.samba4.dn("cn=%s" % n) for n in ["A", "B", "C", "X", "Y", "Z"] ] for dn in dns: self.ldb.delete(dn) def test_map_modify_local(self): """Modification of local records.""" # Add local record dn = "cn=test,dc=idealx,dc=org" self.ldb.add({ "dn": dn, "cn": "test", "foo": "bar", "revision": "1", "description": "test" }) # Check it's there attrs = ["foo", "revision", "description"] res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertEquals(str(res[0]["foo"]), "bar") self.assertEquals(str(res[0]["revision"]), "1") self.assertEquals(str(res[0]["description"]), "test") # Check it's not in the local db res = self.samba4.db.search(expression="(cn=test)", scope=SCOPE_DEFAULT, attrs=attrs) self.assertEquals(len(res), 0) # Check it's not in the remote db res = self.samba3.db.search(expression="(cn=test)", scope=SCOPE_DEFAULT, attrs=attrs) self.assertEquals(len(res), 0) # Modify local record ldif = """ dn: """ + dn + """ replace: foo foo: baz replace: description description: foo """ self.ldb.modify_ldif(ldif) # Check in local db res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertEquals(str(res[0]["foo"]), "baz") self.assertEquals(str(res[0]["revision"]), "1") self.assertEquals(str(res[0]["description"]), "foo") # Rename local record dn2 = "cn=toast,dc=idealx,dc=org" self.ldb.rename(dn, dn2) # Check in local db res = self.ldb.search(dn2, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn2) self.assertEquals(str(res[0]["foo"]), "baz") self.assertEquals(str(res[0]["revision"]), "1") self.assertEquals(str(res[0]["description"]), "foo") # Delete local record self.ldb.delete(dn2) # Check it's gone res = self.ldb.search(dn2, scope=SCOPE_BASE) self.assertEquals(len(res), 0) def test_map_modify_remote_remote(self): """Modification of remote data of remote records""" # Add remote record dn = self.samba4.dn("cn=test") dn2 = self.samba3.dn("cn=test") self.samba3.db.add({ "dn": dn2, "cn": "test", "description": "foo", "sambaBadPasswordCount": "3", "sambaNextRid": "1001" }) # Check it's there res = self.samba3.db.search( dn2, scope=SCOPE_BASE, attrs=["description", "sambaBadPasswordCount", "sambaNextRid"]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn2) self.assertEquals(str(res[0]["description"]), "foo") self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "3") self.assertEquals(str(res[0]["sambaNextRid"]), "1001") # Check in mapped db attrs = ["description", "badPwdCount", "nextRid"] res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs, expression="") self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertEquals(str(res[0]["description"]), "foo") self.assertEquals(str(res[0]["badPwdCount"]), "3") self.assertEquals(str(res[0]["nextRid"]), "1001") # Check in local db res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 0) # Modify remote data of remote record ldif = """ dn: """ + dn + """ replace: description description: test replace: badPwdCount badPwdCount: 4 """ self.ldb.modify_ldif(ldif) # Check in mapped db res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=["description", "badPwdCount", "nextRid"]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertEquals(str(res[0]["description"]), "test") self.assertEquals(str(res[0]["badPwdCount"]), "4") self.assertEquals(str(res[0]["nextRid"]), "1001") # Check in remote db res = self.samba3.db.search( dn2, scope=SCOPE_BASE, attrs=["description", "sambaBadPasswordCount", "sambaNextRid"]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn2) self.assertEquals(str(res[0]["description"]), "test") self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4") self.assertEquals(str(res[0]["sambaNextRid"]), "1001") # Rename remote record dn2 = self.samba4.dn("cn=toast") self.ldb.rename(dn, dn2) # Check in mapped db dn = dn2 res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=["description", "badPwdCount", "nextRid"]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertEquals(str(res[0]["description"]), "test") self.assertEquals(str(res[0]["badPwdCount"]), "4") self.assertEquals(str(res[0]["nextRid"]), "1001") # Check in remote db dn2 = self.samba3.dn("cn=toast") res = self.samba3.db.search( dn2, scope=SCOPE_BASE, attrs=["description", "sambaBadPasswordCount", "sambaNextRid"]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn2) self.assertEquals(str(res[0]["description"]), "test") self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4") self.assertEquals(str(res[0]["sambaNextRid"]), "1001") # Delete remote record self.ldb.delete(dn) # Check in mapped db that it's removed res = self.ldb.search(dn, scope=SCOPE_BASE) self.assertEquals(len(res), 0) # Check in remote db res = self.samba3.db.search(dn2, scope=SCOPE_BASE) self.assertEquals(len(res), 0) def test_map_modify_remote_local(self): """Modification of local data of remote records""" # Add remote record (same as before) dn = self.samba4.dn("cn=test") dn2 = self.samba3.dn("cn=test") self.samba3.db.add({ "dn": dn2, "cn": "test", "description": "foo", "sambaBadPasswordCount": "3", "sambaNextRid": "1001" }) # Modify local data of remote record ldif = """ dn: """ + dn + """ add: revision revision: 1 replace: description description: test """ self.ldb.modify_ldif(ldif) # Check in mapped db attrs = ["revision", "description"] res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertEquals(str(res[0]["description"]), "test") self.assertEquals(str(res[0]["revision"]), "1") # Check in remote db res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn2) self.assertEquals(str(res[0]["description"]), "test") self.assertTrue(not "revision" in res[0]) # Check in local db res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertTrue(not "description" in res[0]) self.assertEquals(str(res[0]["revision"]), "1") # Delete (newly) split record self.ldb.delete(dn) def test_map_modify_split(self): """Testing modification of split records""" # Add split record dn = self.samba4.dn("cn=test") dn2 = self.samba3.dn("cn=test") self.ldb.add({ "dn": dn, "cn": "test", "description": "foo", "badPwdCount": "3", "nextRid": "1001", "revision": "1" }) # Check it's there attrs = ["description", "badPwdCount", "nextRid", "revision"] res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertEquals(str(res[0]["description"]), "foo") self.assertEquals(str(res[0]["badPwdCount"]), "3") self.assertEquals(str(res[0]["nextRid"]), "1001") self.assertEquals(str(res[0]["revision"]), "1") # Check in local db res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertTrue(not "description" in res[0]) self.assertTrue(not "badPwdCount" in res[0]) self.assertTrue(not "nextRid" in res[0]) self.assertEquals(str(res[0]["revision"]), "1") # Check in remote db attrs = [ "description", "sambaBadPasswordCount", "sambaNextRid", "revision" ] res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn2) self.assertEquals(str(res[0]["description"]), "foo") self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "3") self.assertEquals(str(res[0]["sambaNextRid"]), "1001") self.assertTrue(not "revision" in res[0]) # Modify of split record ldif = """ dn: """ + dn + """ replace: description description: test replace: badPwdCount badPwdCount: 4 replace: revision revision: 2 """ self.ldb.modify_ldif(ldif) # Check in mapped db attrs = ["description", "badPwdCount", "nextRid", "revision"] res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertEquals(str(res[0]["description"]), "test") self.assertEquals(str(res[0]["badPwdCount"]), "4") self.assertEquals(str(res[0]["nextRid"]), "1001") self.assertEquals(str(res[0]["revision"]), "2") # Check in local db res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertTrue(not "description" in res[0]) self.assertTrue(not "badPwdCount" in res[0]) self.assertTrue(not "nextRid" in res[0]) self.assertEquals(str(res[0]["revision"]), "2") # Check in remote db attrs = [ "description", "sambaBadPasswordCount", "sambaNextRid", "revision" ] res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn2) self.assertEquals(str(res[0]["description"]), "test") self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4") self.assertEquals(str(res[0]["sambaNextRid"]), "1001") self.assertTrue(not "revision" in res[0]) # Rename split record dn2 = self.samba4.dn("cn=toast") self.ldb.rename(dn, dn2) # Check in mapped db dn = dn2 attrs = ["description", "badPwdCount", "nextRid", "revision"] res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertEquals(str(res[0]["description"]), "test") self.assertEquals(str(res[0]["badPwdCount"]), "4") self.assertEquals(str(res[0]["nextRid"]), "1001") self.assertEquals(str(res[0]["revision"]), "2") # Check in local db res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertTrue(not "description" in res[0]) self.assertTrue(not "badPwdCount" in res[0]) self.assertTrue(not "nextRid" in res[0]) self.assertEquals(str(res[0]["revision"]), "2") # Check in remote db dn2 = self.samba3.dn("cn=toast") res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=[ "description", "sambaBadPasswordCount", "sambaNextRid", "revision" ]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn2) self.assertEquals(str(res[0]["description"]), "test") self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4") self.assertEquals(str(res[0]["sambaNextRid"]), "1001") self.assertTrue(not "revision" in res[0]) # Delete split record self.ldb.delete(dn) # Check in mapped db res = self.ldb.search(dn, scope=SCOPE_BASE) self.assertEquals(len(res), 0) # Check in local db res = self.samba4.db.search(dn, scope=SCOPE_BASE) self.assertEquals(len(res), 0) # Check in remote db res = self.samba3.db.search(dn2, scope=SCOPE_BASE) self.assertEquals(len(res), 0)
class Schema(object): def __init__(self, setup_path, domain_sid, schemadn=None, serverdn=None, files=None, prefixmap=None): """Load schema for the SamDB from the AD schema files and samba4_schema.ldif :param samdb: Load a schema into a SamDB. :param setup_path: Setup path function. :param schemadn: DN of the schema :param serverdn: DN of the server Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db """ self.schemadn = schemadn self.ldb = Ldb() self.schema_data = read_ms_schema( setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'), setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt')) if files is not None: for file in files: self.schema_data += open(file, 'r').read() self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn}) check_all_substituted(self.schema_data) self.schema_dn_modify = read_and_sub_file( setup_path("provision_schema_basedn_modify.ldif"), { "SCHEMADN": schemadn, "SERVERDN": serverdn, }) descr = b64encode(get_schema_descriptor(domain_sid)) self.schema_dn_add = read_and_sub_file( setup_path("provision_schema_basedn.ldif"), { "SCHEMADN": schemadn, "DESCRIPTOR": descr }) self.prefixmap_data = open(setup_path("prefixMap.txt"), 'r').read() if prefixmap is not None: for map in prefixmap: self.prefixmap_data += "%s\n" % map self.prefixmap_data = b64encode(self.prefixmap_data) # We don't actually add this ldif, just parse it prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % self.prefixmap_data self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data) def write_to_tmp_ldb(self, schemadb_path): self.ldb.connect(schemadb_path) self.ldb.transaction_start() self.ldb.add_ldif("""dn: @ATTRIBUTES linkID: INTEGER dn: @INDEXLIST @IDXATTR: linkID @IDXATTR: attributeSyntax """) # These bits of LDIF are supplied when the Schema object is created self.ldb.add_ldif(self.schema_dn_add) self.ldb.modify_ldif(self.schema_dn_modify) self.ldb.add_ldif(self.schema_data) self.ldb.transaction_commit() # Return a hash with the forward attribute as a key and the back as the value def linked_attributes(self): return get_linked_attributes(self.schemadn, self.ldb) def dnsyntax_attributes(self): return get_dnsyntax_attributes(self.schemadn, self.ldb)
class OpenChangeDBWithLdbBackend(object): """The OpenChange database.""" def __init__(self, url): self.url = url self.ldb = Ldb(self.url) self.nttime = samba.unix2nttime(int(time.time())) def reopen(self): self.ldb = Ldb(self.url) def remove(self): """Remove an existing OpenChangeDB file.""" if os.path.exists(self.url): os.remove(self.url) self.reopen() def setup(self, names=None): self.ldb.add_ldif(""" dn: @OPTIONS checkBaseOnSearch: TRUE dn: @INDEXLIST @IDXATTR: cn dn: @ATTRIBUTES cn: CASE_INSENSITIVE dn: CASE_INSENSITIVE """) self.reopen() if names: self.add_rootDSE(names.ocserverdn, names.firstorg, names.firstou) def add_rootDSE(self, ocserverdn, firstorg, firstou): self.ldb.add({ "dn": "@ROOTDSE", "defaultNamingContext": "CN=%s,CN=%s,%s" % (firstou, firstorg, ocserverdn), "rootDomainNamingContext": ocserverdn, "vendorName": "OpenChange Team (http://www.openchange.org)" }) def add_server(self, names): self.ldb.add({ "dn": names.ocserverdn, "objectClass": ["top", "server"], "cn": names.netbiosname, "GlobalCount": "1", "ChangeNumber": "1", "ReplicaID": "1" }) self.ldb.add({ "dn": "CN=%s,%s" % (names.firstorg, names.ocserverdn), "objectClass": ["top", "org"], "cn": names.firstorg }) self.ldb.add({ "dn": "CN=%s,CN=%s,%s" % (names.firstou, names.firstorg, names.ocserverdn), "objectClass": ["top", "ou"], "cn": names.firstou }) def add_root_public_folder(self, dn, fid, change_num, SystemIdx, childcount): self.ldb.add({ "dn": dn, "objectClass": ["publicfolder"], "cn": fid, "PidTagFolderId": fid, "PidTagChangeNumber": change_num, "PidTagDisplayName": "Public Folder Root", "PidTagCreationTime": "%d" % self.nttime, "PidTagLastModificationTime": "%d" % self.nttime, "PidTagSubFolders": str(childcount != 0).upper(), "PidTagFolderChildCount": str(childcount), "SystemIdx": str(SystemIdx) }) def add_sub_public_folder(self, dn, parentfid, fid, change_num, name, SystemIndex, childcount): self.ldb.add({ "dn": dn, "objectClass": ["publicfolder"], "cn": fid, "PidTagFolderId": fid, "PidTagParentFolderId": parentfid, "PidTagChangeNumber": change_num, "PidTagDisplayName": name, "PidTagCreationTime": "%d" % self.nttime, "PidTagLastModificationTime": "%d" % self.nttime, "PidTagAttributeHidden": str(0), "PidTagAttributeReadOnly": str(0), "PidTagAttributeSystem": str(0), "PidTagContainerClass": "IPF.Note (check this)", "PidTagSubFolders": str(childcount != 0).upper(), "PidTagFolderChildCount": str(childcount), "FolderType": str(1), "SystemIdx": str(SystemIndex) }) def add_one_public_folder(self, parent_fid, path, children, SystemIndex, names, dn_prefix=None): name = path[-1] GlobalCount = self.get_message_GlobalCount(names.netbiosname) ChangeNumber = self.get_message_ChangeNumber(names.netbiosname) ReplicaID = self.get_message_ReplicaID(names.netbiosname) if dn_prefix is None: dn_prefix = "CN=publicfolders,CN=%s,CN=%s,%s" % ( names.firstou, names.firstorg, names.ocserverdn) fid = gen_mailbox_folder_fid(GlobalCount, ReplicaID) dn = "CN=%s,%s" % (fid, dn_prefix) change_num = gen_mailbox_folder_fid(ChangeNumber, ReplicaID) childcount = len(children) print "\t* %-40s: 0x%.16x (%s)" % (name, int(fid, 10), fid) if parent_fid == 0: self.add_root_public_folder(dn, fid, change_num, SystemIndex, childcount) else: self.add_sub_public_folder(dn, parent_fid, fid, change_num, name, SystemIndex, childcount) GlobalCount += 1 self.set_message_GlobalCount(names.netbiosname, GlobalCount=GlobalCount) ChangeNumber += 1 self.set_message_ChangeNumber(names.netbiosname, ChangeNumber=ChangeNumber) for name, grandchildren in children.iteritems(): self.add_one_public_folder(fid, path + (name, ), grandchildren[0], grandchildren[1], names, dn) def add_public_folders(self, names): pfstoreGUID = str(uuid.uuid4()) self.ldb.add({ "dn": "CN=publicfolders,CN=%s,CN=%s,%s" % (names.firstou, names.firstorg, names.ocserverdn), "objectClass": ["container"], "cn": "publicfolders", "StoreGUID": pfstoreGUID, "ReplicaID": str(1) }) public_folders = _public_folders_meta(names) self.add_one_public_folder(0, ("Public Folder Root", ), public_folders[0], public_folders[1], names) def lookup_server(self, cn, attributes=[]): # Step 1. Search Server object filter = "(&(objectClass=server)(cn=%s))" % cn res = self.ldb.search("", scope=ldb.SCOPE_SUBTREE, expression=filter, attrs=attributes) if len(res) != 1: raise NoSuchServer(cn) return res[0] def lookup_mailbox_user(self, server, username, attributes=[]): """Check if a user already exists in openchange database. :param server: Server object name :param username: Username object :return: LDB Object of the user """ server_dn = self.lookup_server(server, []).dn # Step 2. Search User object filter = "(&(objectClass=mailbox)(cn=%s))" % (username) return self.ldb.search(server_dn, scope=ldb.SCOPE_SUBTREE, expression=filter, attrs=attributes) def lookup_public_folder(self, server, displayname, attributes=[]): """Retrieve the record for a public folder matching a specific display name :param server: Server Object Name :param displayname: Display Name of the Folder :param attributes: Requested Attributes :return: LDB Object of the Folder """ server_dn = self.lookup_server(server, []).dn filter = "(&(objectClass=publicfolder)(PidTagDisplayName=%s))" % ( displayname) return self.ldb.search(server_dn, scope=ldb.SCOPE_SUBTREE, expression=filter, attrs=attributes) def get_message_attribute(self, server, attribute): """Retrieve attribute value from given message database (server). :param server: Server object name """ return int(self.lookup_server(server, [attribute])[attribute][0], 10) def get_message_ReplicaID(self, server): """Retrieve current mailbox Replica ID for given message database (server). :param server: Server object name """ return self.get_message_attribute(server, "ReplicaID") def get_message_GlobalCount(self, server): """Retrieve current mailbox Global Count for given message database (server). :param server: Server object name """ return self.get_message_attribute(server, "GlobalCount") def set_message_GlobalCount(self, server, GlobalCount): """Update current mailbox GlobalCount for given message database (server). :param server: Server object name :param index: Mailbox new GlobalCount value """ server_dn = self.lookup_server(server, []).dn newGlobalCount = """ dn: %s changetype: modify replace: GlobalCount GlobalCount: %d """ % (server_dn, GlobalCount) self.ldb.transaction_start() try: self.ldb.modify_ldif(newGlobalCount) finally: self.ldb.transaction_commit() def get_message_ChangeNumber(self, server): """Retrieve current mailbox Global Count for given message database (server). :param server: Server object name """ return self.get_message_attribute(server, "ChangeNumber") def set_message_ChangeNumber(self, server, ChangeNumber): """Update current mailbox ChangeNumber for given message database (server). :param server: Server object name :param index: Mailbox new ChangeNumber value """ server_dn = self.lookup_server(server, []).dn newChangeNumber = """ dn: %s changetype: modify replace: ChangeNumber ChangeNumber: %d """ % (server_dn, ChangeNumber) self.ldb.transaction_start() try: self.ldb.modify_ldif(newChangeNumber) finally: self.ldb.transaction_commit()
class Samba3SamTestCase(MapBaseTestCase): def setUp(self): super(Samba3SamTestCase, self).setUp() ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session()) ldb.set_opaque("skip_allocate_sids", "true") self.samba3.setup_data("samba3.ldif") ldif = read_datafile("provision_samba3sam.ldif") ldb.add_ldif(self.samba4.subst(ldif)) self.setup_modules(ldb, self.samba3, self.samba4) del ldb self.ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session()) self.ldb.set_opaque("skip_allocate_sids", "true") def test_search_non_mapped(self): """Looking up by non-mapped attribute""" msg = self.ldb.search(expression="(cn=Administrator)") self.assertEquals(len(msg), 1) self.assertEquals(msg[0]["cn"], "Administrator") def test_search_non_mapped(self): """Looking up by mapped attribute""" msg = self.ldb.search(expression="(name=Backup Operators)") self.assertEquals(len(msg), 1) self.assertEquals(str(msg[0]["name"]), "Backup Operators") def test_old_name_of_renamed(self): """Looking up by old name of renamed attribute""" msg = self.ldb.search(expression="(displayName=Backup Operators)") self.assertEquals(len(msg), 0) def test_mapped_containing_sid(self): """Looking up mapped entry containing SID""" msg = self.ldb.search(expression="(cn=Replicator)") self.assertEquals(len(msg), 1) self.assertEquals(str(msg[0].dn), "cn=Replicator,ou=Groups,dc=vernstok,dc=nl") self.assertTrue("objectSid" in msg[0]) self.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-1052", msg[0]["objectSid"]) oc = set(msg[0]["objectClass"]) self.assertEquals(oc, set(["group"])) def test_search_by_objclass(self): """Looking up by objectClass""" msg = self.ldb.search( expression="(|(objectClass=user)(cn=Administrator))") self.assertEquals( set([str(m.dn) for m in msg]), set([ "unixName=Administrator,ou=Users,dc=vernstok,dc=nl", "unixName=nobody,ou=Users,dc=vernstok,dc=nl" ])) def test_s3sam_modify(self): # Adding a record that will be fallbacked self.ldb.add({ "dn": "cn=Foo", "foo": "bar", "blah": "Blie", "cn": "Foo", "showInAdvancedViewOnly": "TRUE" }) # Checking for existence of record (local) # TODO: This record must be searched in the local database, which is # currently only supported for base searches # msg = ldb.search(expression="(cn=Foo)", ['foo','blah','cn','showInAdvancedViewOnly')] # TODO: Actually, this version should work as well but doesn't... # # msg = self.ldb.search( expression="(cn=Foo)", base="cn=Foo", scope=SCOPE_BASE, attrs=['foo', 'blah', 'cn', 'showInAdvancedViewOnly']) self.assertEquals(len(msg), 1) self.assertEquals(str(msg[0]["showInAdvancedViewOnly"]), "TRUE") self.assertEquals(str(msg[0]["foo"]), "bar") self.assertEquals(str(msg[0]["blah"]), "Blie") # Adding record that will be mapped self.ldb.add({ "dn": "cn=Niemand,cn=Users,dc=vernstok,dc=nl", "objectClass": "user", "unixName": "bin", "sambaUnicodePwd": "geheim", "cn": "Niemand" }) # Checking for existence of record (remote) msg = self.ldb.search( expression="(unixName=bin)", attrs=['unixName', 'cn', 'dn', 'sambaUnicodePwd']) self.assertEquals(len(msg), 1) self.assertEquals(str(msg[0]["cn"]), "Niemand") self.assertEquals(str(msg[0]["sambaUnicodePwd"]), "geheim") # Checking for existence of record (local && remote) msg = self.ldb.search( expression="(&(unixName=bin)(sambaUnicodePwd=geheim))", attrs=['unixName', 'cn', 'dn', 'sambaUnicodePwd']) self.assertEquals(len(msg), 1) # TODO: should check with more records self.assertEquals(str(msg[0]["cn"]), "Niemand") self.assertEquals(str(msg[0]["unixName"]), "bin") self.assertEquals(str(msg[0]["sambaUnicodePwd"]), "geheim") # Checking for existence of record (local || remote) msg = self.ldb.search( expression="(|(unixName=bin)(sambaUnicodePwd=geheim))", attrs=['unixName', 'cn', 'dn', 'sambaUnicodePwd']) #print "got %d replies" % len(msg) self.assertEquals(len(msg), 1) # TODO: should check with more records self.assertEquals(str(msg[0]["cn"]), "Niemand") self.assertEquals(str(msg[0]["unixName"]), "bin") self.assertEquals(str(msg[0]["sambaUnicodePwd"]), "geheim") # Checking for data in destination database msg = self.samba3.db.search(expression="(cn=Niemand)") self.assertTrue(len(msg) >= 1) self.assertEquals(str(msg[0]["sambaSID"]), "S-1-5-21-4231626423-2410014848-2360679739-2001") self.assertEquals(str(msg[0]["displayName"]), "Niemand") # Adding attribute... self.ldb.modify_ldif(""" dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl changetype: modify add: description description: Blah """) # Checking whether changes are still there... msg = self.ldb.search(expression="(cn=Niemand)") self.assertTrue(len(msg) >= 1) self.assertEquals(str(msg[0]["cn"]), "Niemand") self.assertEquals(str(msg[0]["description"]), "Blah") # Modifying attribute... self.ldb.modify_ldif(""" dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl changetype: modify replace: description description: Blie """) # Checking whether changes are still there... msg = self.ldb.search(expression="(cn=Niemand)") self.assertTrue(len(msg) >= 1) self.assertEquals(str(msg[0]["description"]), "Blie") # Deleting attribute... self.ldb.modify_ldif(""" dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl changetype: modify delete: description """) # Checking whether changes are no longer there... msg = self.ldb.search(expression="(cn=Niemand)") self.assertTrue(len(msg) >= 1) self.assertTrue(not "description" in msg[0]) # Renaming record... self.ldb.rename("cn=Niemand,cn=Users,dc=vernstok,dc=nl", "cn=Niemand2,cn=Users,dc=vernstok,dc=nl") # Checking whether DN has changed... msg = self.ldb.search(expression="(cn=Niemand2)") self.assertEquals(len(msg), 1) self.assertEquals(str(msg[0].dn), "cn=Niemand2,cn=Users,dc=vernstok,dc=nl") # Deleting record... self.ldb.delete("cn=Niemand2,cn=Users,dc=vernstok,dc=nl") # Checking whether record is gone... msg = self.ldb.search(expression="(cn=Niemand2)") self.assertEquals(len(msg), 0)
class MapTestCase(MapBaseTestCase): def setUp(self): super(MapTestCase, self).setUp() ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session()) ldb.set_opaque("skip_allocate_sids", "true"); ldif = read_datafile("provision_samba3sam.ldif") ldb.add_ldif(self.samba4.subst(ldif)) self.setup_modules(ldb, self.samba3, self.samba4) del ldb self.ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session()) self.ldb.set_opaque("skip_allocate_sids", "true"); def test_map_search(self): """Running search tests on mapped data.""" self.samba3.db.add({ "dn": "sambaDomainName=TESTS," + self.samba3.basedn, "objectclass": ["sambaDomain", "top"], "sambaSID": "S-1-5-21-4231626423-2410014848-2360679739", "sambaNextRid": "2000", "sambaDomainName": "TESTS" }) # Add a set of split records self.ldb.add_ldif(""" dn: """+ self.samba4.dn("cn=Domain Users") + """ objectClass: group cn: Domain Users objectSid: S-1-5-21-4231626423-2410014848-2360679739-513 """) # Add a set of split records self.ldb.add_ldif(""" dn: """+ self.samba4.dn("cn=X") + """ objectClass: user cn: X codePage: x revision: x dnsHostName: x nextRid: y lastLogon: x description: x objectSid: S-1-5-21-4231626423-2410014848-2360679739-1052 """) self.ldb.add({ "dn": self.samba4.dn("cn=Y"), "objectClass": "top", "cn": "Y", "codePage": "x", "revision": "x", "dnsHostName": "y", "nextRid": "y", "lastLogon": "y", "description": "x"}) self.ldb.add({ "dn": self.samba4.dn("cn=Z"), "objectClass": "top", "cn": "Z", "codePage": "x", "revision": "y", "dnsHostName": "z", "nextRid": "y", "lastLogon": "z", "description": "y"}) # Add a set of remote records self.samba3.db.add({ "dn": self.samba3.dn("cn=A"), "objectClass": "posixAccount", "cn": "A", "sambaNextRid": "x", "sambaBadPasswordCount": "x", "sambaLogonTime": "x", "description": "x", "sambaSID": "S-1-5-21-4231626423-2410014848-2360679739-1052", "sambaPrimaryGroupSID": "S-1-5-21-4231626423-2410014848-2360679739-512"}) self.samba3.db.add({ "dn": self.samba3.dn("cn=B"), "objectClass": "top", "cn": "B", "sambaNextRid": "x", "sambaBadPasswordCount": "x", "sambaLogonTime": "y", "description": "x"}) self.samba3.db.add({ "dn": self.samba3.dn("cn=C"), "objectClass": "top", "cn": "C", "sambaNextRid": "x", "sambaBadPasswordCount": "y", "sambaLogonTime": "z", "description": "y"}) # Testing search by DN # Search remote record by local DN dn = self.samba4.dn("cn=A") res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") # Search remote record by remote DN dn = self.samba3.dn("cn=A") res = self.samba3.db.search(dn, scope=SCOPE_BASE, attrs=["dnsHostName", "lastLogon", "sambaLogonTime"]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertTrue(not "dnsHostName" in res[0]) self.assertTrue(not "lastLogon" in res[0]) self.assertEquals(str(res[0]["sambaLogonTime"]), "x") # Search split record by local DN dn = self.samba4.dn("cn=X") res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertEquals(str(res[0]["dnsHostName"]), "x") self.assertEquals(str(res[0]["lastLogon"]), "x") # Search split record by remote DN dn = self.samba3.dn("cn=X") res = self.samba3.db.search(dn, scope=SCOPE_BASE, attrs=["dnsHostName", "lastLogon", "sambaLogonTime"]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertTrue(not "dnsHostName" in res[0]) self.assertTrue(not "lastLogon" in res[0]) self.assertEquals(str(res[0]["sambaLogonTime"]), "x") # Testing search by attribute # Search by ignored attribute res = self.ldb.search(expression="(revision=x)", scope=SCOPE_DEFAULT, attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 2) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[0]["dnsHostName"]), "x") self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Y")) self.assertEquals(str(res[1]["dnsHostName"]), "y") self.assertEquals(str(res[1]["lastLogon"]), "y") # Search by kept attribute res = self.ldb.search(expression="(description=y)", scope=SCOPE_DEFAULT, attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 2) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=C")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "z") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Z")) self.assertEquals(str(res[1]["dnsHostName"]), "z") self.assertEquals(str(res[1]["lastLogon"]), "z") # Search by renamed attribute res = self.ldb.search(expression="(badPwdCount=x)", scope=SCOPE_DEFAULT, attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 2) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) self.assertTrue(not "dnsHostName" in res[1]) self.assertEquals(str(res[1]["lastLogon"]), "y") # Search by converted attribute # TODO: # Using the SID directly in the parse tree leads to conversion # errors, letting the search fail with no results. #res = self.ldb.search("(objectSid=S-1-5-21-4231626423-2410014848-2360679739-1052)", scope=SCOPE_DEFAULT, attrs) res = self.ldb.search(expression="(objectSid=*)", base=None, scope=SCOPE_DEFAULT, attrs=["dnsHostName", "lastLogon", "objectSid"]) self.assertEquals(len(res), 4) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[1]["dnsHostName"]), "x") self.assertEquals(str(res[1]["lastLogon"]), "x") self.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-1052", res[1]["objectSid"]) self.assertTrue("objectSid" in res[1]) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-1052", res[0]["objectSid"]) self.assertTrue("objectSid" in res[0]) # Search by generated attribute # In most cases, this even works when the mapping is missing # a `convert_operator' by enumerating the remote db. res = self.ldb.search(expression="(primaryGroupID=512)", attrs=["dnsHostName", "lastLogon", "primaryGroupID"]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[0]["primaryGroupID"]), "512") # Note that Xs "objectSid" seems to be fine in the previous search for # "objectSid"... #res = ldb.search(expression="(primaryGroupID=*)", NULL, ldb. SCOPE_DEFAULT, attrs) #print len(res) + " results found" #for i in range(len(res)): # for (obj in res[i]) { # print obj + ": " + res[i][obj] # } # print "---" # # Search by remote name of renamed attribute */ res = self.ldb.search(expression="(sambaBadPasswordCount=*)", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 0) # Search by objectClass attrs = ["dnsHostName", "lastLogon", "objectClass"] res = self.ldb.search(expression="(objectClass=user)", attrs=attrs) self.assertEquals(len(res), 2) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[0]["objectClass"][0]), "user") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[1]["dnsHostName"]), "x") self.assertEquals(str(res[1]["lastLogon"]), "x") self.assertEquals(str(res[1]["objectClass"][0]), "user") # Prove that the objectClass is actually used for the search res = self.ldb.search(expression="(|(objectClass=user)(badPwdCount=x))", attrs=attrs) self.assertEquals(len(res), 3) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(res[0]["objectClass"][0], "user") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) self.assertTrue(not "dnsHostName" in res[1]) self.assertEquals(str(res[1]["lastLogon"]), "y") self.assertEquals(set(res[1]["objectClass"]), set(["top"])) self.assertEquals(str(res[2].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[2]["dnsHostName"]), "x") self.assertEquals(str(res[2]["lastLogon"]), "x") self.assertEquals(str(res[2]["objectClass"][0]), "user") # Testing search by parse tree # Search by conjunction of local attributes res = self.ldb.search(expression="(&(codePage=x)(revision=x))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 2) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[0]["dnsHostName"]), "x") self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Y")) self.assertEquals(str(res[1]["dnsHostName"]), "y") self.assertEquals(str(res[1]["lastLogon"]), "y") # Search by conjunction of remote attributes res = self.ldb.search(expression="(&(lastLogon=x)(description=x))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 2) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[1]["dnsHostName"]), "x") self.assertEquals(str(res[1]["lastLogon"]), "x") # Search by conjunction of local and remote attribute res = self.ldb.search(expression="(&(codePage=x)(description=x))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 2) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[0]["dnsHostName"]), "x") self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Y")) self.assertEquals(str(res[1]["dnsHostName"]), "y") self.assertEquals(str(res[1]["lastLogon"]), "y") # Search by conjunction of local and remote attribute w/o match attrs = ["dnsHostName", "lastLogon"] res = self.ldb.search(expression="(&(codePage=x)(nextRid=x))", attrs=attrs) self.assertEquals(len(res), 0) res = self.ldb.search(expression="(&(revision=x)(lastLogon=z))", attrs=attrs) self.assertEquals(len(res), 0) # Search by disjunction of local attributes res = self.ldb.search(expression="(|(revision=x)(dnsHostName=x))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 2) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[0]["dnsHostName"]), "x") self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Y")) self.assertEquals(str(res[1]["dnsHostName"]), "y") self.assertEquals(str(res[1]["lastLogon"]), "y") # Search by disjunction of remote attributes res = self.ldb.search(expression="(|(badPwdCount=x)(lastLogon=x))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 3) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertFalse("dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) self.assertFalse("dnsHostName" in res[1]) self.assertEquals(str(res[1]["lastLogon"]), "y") self.assertEquals(str(res[2].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[2]["dnsHostName"]), "x") self.assertEquals(str(res[2]["lastLogon"]), "x") # Search by disjunction of local and remote attribute res = self.ldb.search(expression="(|(revision=x)(lastLogon=y))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 3) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B")) self.assertFalse("dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "y") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[1]["dnsHostName"]), "x") self.assertEquals(str(res[1]["lastLogon"]), "x") self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Y")) self.assertEquals(str(res[2]["dnsHostName"]), "y") self.assertEquals(str(res[2]["lastLogon"]), "y") # Search by disjunction of local and remote attribute w/o match res = self.ldb.search(expression="(|(codePage=y)(nextRid=z))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 0) # Search by negated local attribute res = self.ldb.search(expression="(!(revision=x))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 6) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) self.assertTrue(not "dnsHostName" in res[1]) self.assertEquals(str(res[1]["lastLogon"]), "y") self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C")) self.assertTrue(not "dnsHostName" in res[2]) self.assertEquals(str(res[2]["lastLogon"]), "z") self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z")) self.assertEquals(str(res[3]["dnsHostName"]), "z") self.assertEquals(str(res[3]["lastLogon"]), "z") # Search by negated remote attribute res = self.ldb.search(expression="(!(description=x))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 4) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=C")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "z") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Z")) self.assertEquals(str(res[1]["dnsHostName"]), "z") self.assertEquals(str(res[1]["lastLogon"]), "z") # Search by negated conjunction of local attributes res = self.ldb.search(expression="(!(&(codePage=x)(revision=x)))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 6) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) self.assertTrue(not "dnsHostName" in res[1]) self.assertEquals(str(res[1]["lastLogon"]), "y") self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C")) self.assertTrue(not "dnsHostName" in res[2]) self.assertEquals(str(res[2]["lastLogon"]), "z") self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z")) self.assertEquals(str(res[3]["dnsHostName"]), "z") self.assertEquals(str(res[3]["lastLogon"]), "z") # Search by negated conjunction of remote attributes res = self.ldb.search(expression="(!(&(lastLogon=x)(description=x)))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 6) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "y") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=C")) self.assertTrue(not "dnsHostName" in res[1]) self.assertEquals(str(res[1]["lastLogon"]), "z") self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Y")) self.assertEquals(str(res[2]["dnsHostName"]), "y") self.assertEquals(str(res[2]["lastLogon"]), "y") self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z")) self.assertEquals(str(res[3]["dnsHostName"]), "z") self.assertEquals(str(res[3]["lastLogon"]), "z") # Search by negated conjunction of local and remote attribute res = self.ldb.search(expression="(!(&(codePage=x)(description=x)))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 6) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) self.assertTrue(not "dnsHostName" in res[1]) self.assertEquals(str(res[1]["lastLogon"]), "y") self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C")) self.assertTrue(not "dnsHostName" in res[2]) self.assertEquals(str(res[2]["lastLogon"]), "z") self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z")) self.assertEquals(str(res[3]["dnsHostName"]), "z") self.assertEquals(str(res[3]["lastLogon"]), "z") # Search by negated disjunction of local attributes res = self.ldb.search(expression="(!(|(revision=x)(dnsHostName=x)))", attrs=["dnsHostName", "lastLogon"]) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) self.assertTrue(not "dnsHostName" in res[1]) self.assertEquals(str(res[1]["lastLogon"]), "y") self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C")) self.assertTrue(not "dnsHostName" in res[2]) self.assertEquals(str(res[2]["lastLogon"]), "z") self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z")) self.assertEquals(str(res[3]["dnsHostName"]), "z") self.assertEquals(str(res[3]["lastLogon"]), "z") # Search by negated disjunction of remote attributes res = self.ldb.search(expression="(!(|(badPwdCount=x)(lastLogon=x)))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 5) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=C")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "z") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Y")) self.assertEquals(str(res[1]["dnsHostName"]), "y") self.assertEquals(str(res[1]["lastLogon"]), "y") self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z")) self.assertEquals(str(res[2]["dnsHostName"]), "z") self.assertEquals(str(res[2]["lastLogon"]), "z") # Search by negated disjunction of local and remote attribute res = self.ldb.search(expression="(!(|(revision=x)(lastLogon=y)))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 5) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=C")) self.assertTrue(not "dnsHostName" in res[1]) self.assertEquals(str(res[1]["lastLogon"]), "z") self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z")) self.assertEquals(str(res[2]["dnsHostName"]), "z") self.assertEquals(str(res[2]["lastLogon"]), "z") # Search by complex parse tree res = self.ldb.search(expression="(|(&(revision=x)(dnsHostName=x))(!(&(description=x)(nextRid=y)))(badPwdCount=y))", attrs=["dnsHostName", "lastLogon"]) self.assertEquals(len(res), 7) res = sorted(res, key=attrgetter('dn')) self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) self.assertTrue(not "dnsHostName" in res[0]) self.assertEquals(str(res[0]["lastLogon"]), "x") self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) self.assertTrue(not "dnsHostName" in res[1]) self.assertEquals(str(res[1]["lastLogon"]), "y") self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C")) self.assertTrue(not "dnsHostName" in res[2]) self.assertEquals(str(res[2]["lastLogon"]), "z") self.assertEquals(str(res[3].dn), self.samba4.dn("cn=X")) self.assertEquals(str(res[3]["dnsHostName"]), "x") self.assertEquals(str(res[3]["lastLogon"]), "x") self.assertEquals(str(res[4].dn), self.samba4.dn("cn=Z")) self.assertEquals(str(res[4]["dnsHostName"]), "z") self.assertEquals(str(res[4]["lastLogon"]), "z") # Clean up dns = [self.samba4.dn("cn=%s" % n) for n in ["A","B","C","X","Y","Z"]] for dn in dns: self.ldb.delete(dn) def test_map_modify_local(self): """Modification of local records.""" # Add local record dn = "cn=test,dc=idealx,dc=org" self.ldb.add({"dn": dn, "cn": "test", "foo": "bar", "revision": "1", "description": "test"}) # Check it's there attrs = ["foo", "revision", "description"] res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertEquals(str(res[0]["foo"]), "bar") self.assertEquals(str(res[0]["revision"]), "1") self.assertEquals(str(res[0]["description"]), "test") # Check it's not in the local db res = self.samba4.db.search(expression="(cn=test)", scope=SCOPE_DEFAULT, attrs=attrs) self.assertEquals(len(res), 0) # Check it's not in the remote db res = self.samba3.db.search(expression="(cn=test)", scope=SCOPE_DEFAULT, attrs=attrs) self.assertEquals(len(res), 0) # Modify local record ldif = """ dn: """ + dn + """ replace: foo foo: baz replace: description description: foo """ self.ldb.modify_ldif(ldif) # Check in local db res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertEquals(str(res[0]["foo"]), "baz") self.assertEquals(str(res[0]["revision"]), "1") self.assertEquals(str(res[0]["description"]), "foo") # Rename local record dn2 = "cn=toast,dc=idealx,dc=org" self.ldb.rename(dn, dn2) # Check in local db res = self.ldb.search(dn2, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn2) self.assertEquals(str(res[0]["foo"]), "baz") self.assertEquals(str(res[0]["revision"]), "1") self.assertEquals(str(res[0]["description"]), "foo") # Delete local record self.ldb.delete(dn2) # Check it's gone res = self.ldb.search(dn2, scope=SCOPE_BASE) self.assertEquals(len(res), 0) def test_map_modify_remote_remote(self): """Modification of remote data of remote records""" # Add remote record dn = self.samba4.dn("cn=test") dn2 = self.samba3.dn("cn=test") self.samba3.db.add({"dn": dn2, "cn": "test", "description": "foo", "sambaBadPasswordCount": "3", "sambaNextRid": "1001"}) # Check it's there res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=["description", "sambaBadPasswordCount", "sambaNextRid"]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn2) self.assertEquals(str(res[0]["description"]), "foo") self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "3") self.assertEquals(str(res[0]["sambaNextRid"]), "1001") # Check in mapped db attrs = ["description", "badPwdCount", "nextRid"] res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs, expression="") self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertEquals(str(res[0]["description"]), "foo") self.assertEquals(str(res[0]["badPwdCount"]), "3") self.assertEquals(str(res[0]["nextRid"]), "1001") # Check in local db res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 0) # Modify remote data of remote record ldif = """ dn: """ + dn + """ replace: description description: test replace: badPwdCount badPwdCount: 4 """ self.ldb.modify_ldif(ldif) # Check in mapped db res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=["description", "badPwdCount", "nextRid"]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertEquals(str(res[0]["description"]), "test") self.assertEquals(str(res[0]["badPwdCount"]), "4") self.assertEquals(str(res[0]["nextRid"]), "1001") # Check in remote db res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=["description", "sambaBadPasswordCount", "sambaNextRid"]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn2) self.assertEquals(str(res[0]["description"]), "test") self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4") self.assertEquals(str(res[0]["sambaNextRid"]), "1001") # Rename remote record dn2 = self.samba4.dn("cn=toast") self.ldb.rename(dn, dn2) # Check in mapped db dn = dn2 res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=["description", "badPwdCount", "nextRid"]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertEquals(str(res[0]["description"]), "test") self.assertEquals(str(res[0]["badPwdCount"]), "4") self.assertEquals(str(res[0]["nextRid"]), "1001") # Check in remote db dn2 = self.samba3.dn("cn=toast") res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=["description", "sambaBadPasswordCount", "sambaNextRid"]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn2) self.assertEquals(str(res[0]["description"]), "test") self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4") self.assertEquals(str(res[0]["sambaNextRid"]), "1001") # Delete remote record self.ldb.delete(dn) # Check in mapped db that it's removed res = self.ldb.search(dn, scope=SCOPE_BASE) self.assertEquals(len(res), 0) # Check in remote db res = self.samba3.db.search(dn2, scope=SCOPE_BASE) self.assertEquals(len(res), 0) def test_map_modify_remote_local(self): """Modification of local data of remote records""" # Add remote record (same as before) dn = self.samba4.dn("cn=test") dn2 = self.samba3.dn("cn=test") self.samba3.db.add({"dn": dn2, "cn": "test", "description": "foo", "sambaBadPasswordCount": "3", "sambaNextRid": "1001"}) # Modify local data of remote record ldif = """ dn: """ + dn + """ add: revision revision: 1 replace: description description: test """ self.ldb.modify_ldif(ldif) # Check in mapped db attrs = ["revision", "description"] res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertEquals(str(res[0]["description"]), "test") self.assertEquals(str(res[0]["revision"]), "1") # Check in remote db res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn2) self.assertEquals(str(res[0]["description"]), "test") self.assertTrue(not "revision" in res[0]) # Check in local db res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertTrue(not "description" in res[0]) self.assertEquals(str(res[0]["revision"]), "1") # Delete (newly) split record self.ldb.delete(dn) def test_map_modify_split(self): """Testing modification of split records""" # Add split record dn = self.samba4.dn("cn=test") dn2 = self.samba3.dn("cn=test") self.ldb.add({ "dn": dn, "cn": "test", "description": "foo", "badPwdCount": "3", "nextRid": "1001", "revision": "1"}) # Check it's there attrs = ["description", "badPwdCount", "nextRid", "revision"] res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertEquals(str(res[0]["description"]), "foo") self.assertEquals(str(res[0]["badPwdCount"]), "3") self.assertEquals(str(res[0]["nextRid"]), "1001") self.assertEquals(str(res[0]["revision"]), "1") # Check in local db res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertTrue(not "description" in res[0]) self.assertTrue(not "badPwdCount" in res[0]) self.assertTrue(not "nextRid" in res[0]) self.assertEquals(str(res[0]["revision"]), "1") # Check in remote db attrs = ["description", "sambaBadPasswordCount", "sambaNextRid", "revision"] res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn2) self.assertEquals(str(res[0]["description"]), "foo") self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "3") self.assertEquals(str(res[0]["sambaNextRid"]), "1001") self.assertTrue(not "revision" in res[0]) # Modify of split record ldif = """ dn: """ + dn + """ replace: description description: test replace: badPwdCount badPwdCount: 4 replace: revision revision: 2 """ self.ldb.modify_ldif(ldif) # Check in mapped db attrs = ["description", "badPwdCount", "nextRid", "revision"] res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertEquals(str(res[0]["description"]), "test") self.assertEquals(str(res[0]["badPwdCount"]), "4") self.assertEquals(str(res[0]["nextRid"]), "1001") self.assertEquals(str(res[0]["revision"]), "2") # Check in local db res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertTrue(not "description" in res[0]) self.assertTrue(not "badPwdCount" in res[0]) self.assertTrue(not "nextRid" in res[0]) self.assertEquals(str(res[0]["revision"]), "2") # Check in remote db attrs = ["description", "sambaBadPasswordCount", "sambaNextRid", "revision"] res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn2) self.assertEquals(str(res[0]["description"]), "test") self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4") self.assertEquals(str(res[0]["sambaNextRid"]), "1001") self.assertTrue(not "revision" in res[0]) # Rename split record dn2 = self.samba4.dn("cn=toast") self.ldb.rename(dn, dn2) # Check in mapped db dn = dn2 attrs = ["description", "badPwdCount", "nextRid", "revision"] res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertEquals(str(res[0]["description"]), "test") self.assertEquals(str(res[0]["badPwdCount"]), "4") self.assertEquals(str(res[0]["nextRid"]), "1001") self.assertEquals(str(res[0]["revision"]), "2") # Check in local db res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn) self.assertTrue(not "description" in res[0]) self.assertTrue(not "badPwdCount" in res[0]) self.assertTrue(not "nextRid" in res[0]) self.assertEquals(str(res[0]["revision"]), "2") # Check in remote db dn2 = self.samba3.dn("cn=toast") res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=["description", "sambaBadPasswordCount", "sambaNextRid", "revision"]) self.assertEquals(len(res), 1) self.assertEquals(str(res[0].dn), dn2) self.assertEquals(str(res[0]["description"]), "test") self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4") self.assertEquals(str(res[0]["sambaNextRid"]), "1001") self.assertTrue(not "revision" in res[0]) # Delete split record self.ldb.delete(dn) # Check in mapped db res = self.ldb.search(dn, scope=SCOPE_BASE) self.assertEquals(len(res), 0) # Check in local db res = self.samba4.db.search(dn, scope=SCOPE_BASE) self.assertEquals(len(res), 0) # Check in remote db res = self.samba3.db.search(dn2, scope=SCOPE_BASE) self.assertEquals(len(res), 0)
class Samba3SamTestCase(MapBaseTestCase): def setUp(self): super(Samba3SamTestCase, self).setUp() ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session()) ldb.set_opaque("skip_allocate_sids", "true"); self.samba3.setup_data("samba3.ldif") ldif = read_datafile("provision_samba3sam.ldif") ldb.add_ldif(self.samba4.subst(ldif)) self.setup_modules(ldb, self.samba3, self.samba4) del ldb self.ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session()) self.ldb.set_opaque("skip_allocate_sids", "true"); def test_search_non_mapped(self): """Looking up by non-mapped attribute""" msg = self.ldb.search(expression="(cn=Administrator)") self.assertEquals(len(msg), 1) self.assertEquals(msg[0]["cn"], "Administrator") def test_search_non_mapped(self): """Looking up by mapped attribute""" msg = self.ldb.search(expression="(name=Backup Operators)") self.assertEquals(len(msg), 1) self.assertEquals(str(msg[0]["name"]), "Backup Operators") def test_old_name_of_renamed(self): """Looking up by old name of renamed attribute""" msg = self.ldb.search(expression="(displayName=Backup Operators)") self.assertEquals(len(msg), 0) def test_mapped_containing_sid(self): """Looking up mapped entry containing SID""" msg = self.ldb.search(expression="(cn=Replicator)") self.assertEquals(len(msg), 1) self.assertEquals(str(msg[0].dn), "cn=Replicator,ou=Groups,dc=vernstok,dc=nl") self.assertTrue("objectSid" in msg[0]) self.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-1052", msg[0]["objectSid"]) oc = set(msg[0]["objectClass"]) self.assertEquals(oc, set(["group"])) def test_search_by_objclass(self): """Looking up by objectClass""" msg = self.ldb.search(expression="(|(objectClass=user)(cn=Administrator))") self.assertEquals(set([str(m.dn) for m in msg]), set(["unixName=Administrator,ou=Users,dc=vernstok,dc=nl", "unixName=nobody,ou=Users,dc=vernstok,dc=nl"])) def test_s3sam_modify(self): # Adding a record that will be fallbacked self.ldb.add({ "dn": "cn=Foo", "foo": "bar", "blah": "Blie", "cn": "Foo", "showInAdvancedViewOnly": "TRUE"}) # Checking for existence of record (local) # TODO: This record must be searched in the local database, which is # currently only supported for base searches # msg = ldb.search(expression="(cn=Foo)", ['foo','blah','cn','showInAdvancedViewOnly')] # TODO: Actually, this version should work as well but doesn't... # # msg = self.ldb.search(expression="(cn=Foo)", base="cn=Foo", scope=SCOPE_BASE, attrs=['foo','blah','cn','showInAdvancedViewOnly']) self.assertEquals(len(msg), 1) self.assertEquals(str(msg[0]["showInAdvancedViewOnly"]), "TRUE") self.assertEquals(str(msg[0]["foo"]), "bar") self.assertEquals(str(msg[0]["blah"]), "Blie") # Adding record that will be mapped self.ldb.add({"dn": "cn=Niemand,cn=Users,dc=vernstok,dc=nl", "objectClass": "user", "unixName": "bin", "sambaUnicodePwd": "geheim", "cn": "Niemand"}) # Checking for existence of record (remote) msg = self.ldb.search(expression="(unixName=bin)", attrs=['unixName','cn','dn', 'sambaUnicodePwd']) self.assertEquals(len(msg), 1) self.assertEquals(str(msg[0]["cn"]), "Niemand") self.assertEquals(str(msg[0]["sambaUnicodePwd"]), "geheim") # Checking for existence of record (local && remote) msg = self.ldb.search(expression="(&(unixName=bin)(sambaUnicodePwd=geheim))", attrs=['unixName','cn','dn', 'sambaUnicodePwd']) self.assertEquals(len(msg), 1) # TODO: should check with more records self.assertEquals(str(msg[0]["cn"]), "Niemand") self.assertEquals(str(msg[0]["unixName"]), "bin") self.assertEquals(str(msg[0]["sambaUnicodePwd"]), "geheim") # Checking for existence of record (local || remote) msg = self.ldb.search(expression="(|(unixName=bin)(sambaUnicodePwd=geheim))", attrs=['unixName','cn','dn', 'sambaUnicodePwd']) #print "got %d replies" % len(msg) self.assertEquals(len(msg), 1) # TODO: should check with more records self.assertEquals(str(msg[0]["cn"]), "Niemand") self.assertEquals(str(msg[0]["unixName"]), "bin") self.assertEquals(str(msg[0]["sambaUnicodePwd"]), "geheim") # Checking for data in destination database msg = self.samba3.db.search(expression="(cn=Niemand)") self.assertTrue(len(msg) >= 1) self.assertEquals(str(msg[0]["sambaSID"]), "S-1-5-21-4231626423-2410014848-2360679739-2001") self.assertEquals(str(msg[0]["displayName"]), "Niemand") # Adding attribute... self.ldb.modify_ldif(""" dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl changetype: modify add: description description: Blah """) # Checking whether changes are still there... msg = self.ldb.search(expression="(cn=Niemand)") self.assertTrue(len(msg) >= 1) self.assertEquals(str(msg[0]["cn"]), "Niemand") self.assertEquals(str(msg[0]["description"]), "Blah") # Modifying attribute... self.ldb.modify_ldif(""" dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl changetype: modify replace: description description: Blie """) # Checking whether changes are still there... msg = self.ldb.search(expression="(cn=Niemand)") self.assertTrue(len(msg) >= 1) self.assertEquals(str(msg[0]["description"]), "Blie") # Deleting attribute... self.ldb.modify_ldif(""" dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl changetype: modify delete: description """) # Checking whether changes are no longer there... msg = self.ldb.search(expression="(cn=Niemand)") self.assertTrue(len(msg) >= 1) self.assertTrue(not "description" in msg[0]) # Renaming record... self.ldb.rename("cn=Niemand,cn=Users,dc=vernstok,dc=nl", "cn=Niemand2,cn=Users,dc=vernstok,dc=nl") # Checking whether DN has changed... msg = self.ldb.search(expression="(cn=Niemand2)") self.assertEquals(len(msg), 1) self.assertEquals(str(msg[0].dn), "cn=Niemand2,cn=Users,dc=vernstok,dc=nl") # Deleting record... self.ldb.delete("cn=Niemand2,cn=Users,dc=vernstok,dc=nl") # Checking whether record is gone... msg = self.ldb.search(expression="(cn=Niemand2)") self.assertEquals(len(msg), 0)
class Schema(object): def __init__(self, setup_path, domain_sid, schemadn=None, serverdn=None, files=None, prefixmap=None): """Load schema for the SamDB from the AD schema files and samba4_schema.ldif :param samdb: Load a schema into a SamDB. :param setup_path: Setup path function. :param schemadn: DN of the schema :param serverdn: DN of the server Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db """ self.schemadn = schemadn self.ldb = Ldb() self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_R2_Attributes.txt'), setup_path('ad-schema/MS-AD_Schema_2K8_R2_Classes.txt')) if files is not None: for file in files: self.schema_data += open(file, 'r').read() self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn}) check_all_substituted(self.schema_data) self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"), {"SCHEMADN": schemadn, "SERVERDN": serverdn, }) descr = b64encode(get_schema_descriptor(domain_sid)) self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"), {"SCHEMADN": schemadn, "DESCRIPTOR": descr }) self.prefixmap_data = open(setup_path("prefixMap.txt"), 'r').read() if prefixmap is not None: for map in prefixmap: self.prefixmap_data += "%s\n" % map self.prefixmap_data = b64encode(self.prefixmap_data) # We don't actually add this ldif, just parse it prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % self.prefixmap_data self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data) def write_to_tmp_ldb(self, schemadb_path): self.ldb.connect(schemadb_path) self.ldb.transaction_start() self.ldb.add_ldif("""dn: @ATTRIBUTES linkID: INTEGER dn: @INDEXLIST @IDXATTR: linkID @IDXATTR: attributeSyntax """) # These bits of LDIF are supplied when the Schema object is created self.ldb.add_ldif(self.schema_dn_add) self.ldb.modify_ldif(self.schema_dn_modify) self.ldb.add_ldif(self.schema_data) self.ldb.transaction_commit() # Return a hash with the forward attribute as a key and the back as the value def linked_attributes(self): return get_linked_attributes(self.schemadn, self.ldb) def dnsyntax_attributes(self): return get_dnsyntax_attributes(self.schemadn, self.ldb)
class OpenChangeDBWithLdbBackend(object): """The OpenChange database.""" def __init__(self, url): self.url = url self.ldb = Ldb(self.url) self.nttime = samba.unix2nttime(int(time.time())) def reopen(self): self.ldb = Ldb(self.url) def remove(self): """Remove an existing OpenChangeDB file.""" if os.path.exists(self.url): os.remove(self.url) self.reopen() def setup(self, names=None): self.ldb.add_ldif(""" dn: @OPTIONS checkBaseOnSearch: TRUE dn: @INDEXLIST @IDXATTR: cn dn: @ATTRIBUTES cn: CASE_INSENSITIVE dn: CASE_INSENSITIVE """) self.reopen() if names: self.add_rootDSE(names.ocserverdn, names.firstorg, names.firstou) def add_rootDSE(self, ocserverdn, firstorg, firstou): self.ldb.add({"dn": "@ROOTDSE", "defaultNamingContext": "CN=%s,CN=%s,%s" % (firstou, firstorg, ocserverdn), "rootDomainNamingContext": ocserverdn, "vendorName": "OpenChange Team (http://www.openchange.org)"}) def add_server(self, names): self.ldb.add({"dn": names.ocserverdn, "objectClass": ["top", "server"], "cn": names.netbiosname, "GlobalCount": "1", "ChangeNumber": "1", "ReplicaID": "1"}) self.ldb.add({"dn": "CN=%s,%s" % (names.firstorg, names.ocserverdn), "objectClass": ["top", "org"], "cn": names.firstorg}) self.ldb.add({"dn": "CN=%s,CN=%s,%s" % (names.firstou, names.firstorg, names.ocserverdn), "objectClass": ["top", "ou"], "cn": names.firstou}) def add_root_public_folder(self, dn, fid, change_num, SystemIdx, childcount): self.ldb.add({"dn": dn, "objectClass": ["publicfolder"], "cn": fid, "PidTagFolderId": fid, "PidTagChangeNumber": change_num, "PidTagDisplayName": "Public Folder Root", "PidTagCreationTime": "%d" % self.nttime, "PidTagLastModificationTime": "%d" % self.nttime, "PidTagSubFolders": str(childcount != 0).upper(), "PidTagFolderChildCount": str(childcount), "SystemIdx": str(SystemIdx)}) def add_sub_public_folder(self, dn, parentfid, fid, change_num, name, SystemIndex, childcount): self.ldb.add({"dn": dn, "objectClass": ["publicfolder"], "cn": fid, "PidTagFolderId": fid, "PidTagParentFolderId": parentfid, "PidTagChangeNumber": change_num, "PidTagDisplayName": name, "PidTagCreationTime": "%d" % self.nttime, "PidTagLastModificationTime": "%d" % self.nttime, "PidTagAttributeHidden": str(0), "PidTagAttributeReadOnly": str(0), "PidTagAttributeSystem": str(0), "PidTagContainerClass": "IPF.Note (check this)", "PidTagSubFolders": str(childcount != 0).upper(), "PidTagFolderChildCount": str(childcount), "FolderType": str(1), "SystemIdx": str(SystemIndex)}) def add_one_public_folder(self, parent_fid, path, children, SystemIndex, names, dn_prefix = None): name = path[-1] GlobalCount = self.get_message_GlobalCount(names.netbiosname) ChangeNumber = self.get_message_ChangeNumber(names.netbiosname) ReplicaID = self.get_message_ReplicaID(names.netbiosname) if dn_prefix is None: dn_prefix = "CN=publicfolders,CN=%s,CN=%s,%s" % (names.firstou, names.firstorg, names.ocserverdn) fid = gen_mailbox_folder_fid(GlobalCount, ReplicaID) dn = "CN=%s,%s" % (fid, dn_prefix) change_num = gen_mailbox_folder_fid(ChangeNumber, ReplicaID) childcount = len(children) print "\t* %-40s: 0x%.16x (%s)" % (name, int(fid, 10), fid) if parent_fid == 0: self.add_root_public_folder(dn, fid, change_num, SystemIndex, childcount) else: self.add_sub_public_folder(dn, parent_fid, fid, change_num, name, SystemIndex, childcount) GlobalCount += 1 self.set_message_GlobalCount(names.netbiosname, GlobalCount=GlobalCount) ChangeNumber += 1 self.set_message_ChangeNumber(names.netbiosname, ChangeNumber=ChangeNumber) for name, grandchildren in children.iteritems(): self.add_one_public_folder(fid, path + (name,), grandchildren[0], grandchildren[1], names, dn) def add_public_folders(self, names): pfstoreGUID = str(uuid.uuid4()) self.ldb.add({"dn": "CN=publicfolders,CN=%s,CN=%s,%s" % (names.firstou, names.firstorg, names.ocserverdn), "objectClass": ["container"], "cn": "publicfolders", "StoreGUID": pfstoreGUID, "ReplicaID": str(1)}) public_folders = _public_folders_meta(names) self.add_one_public_folder(0, ("Public Folder Root",), public_folders[0], public_folders[1], names) def lookup_server(self, cn, attributes=[]): # Step 1. Search Server object filter = "(&(objectClass=server)(cn=%s))" % cn res = self.ldb.search("", scope=ldb.SCOPE_SUBTREE, expression=filter, attrs=attributes) if len(res) != 1: raise NoSuchServer(cn) return res[0] def lookup_mailbox_user(self, server, username, attributes=[]): """Check if a user already exists in openchange database. :param server: Server object name :param username: Username object :return: LDB Object of the user """ server_dn = self.lookup_server(server, []).dn # Step 2. Search User object filter = "(&(objectClass=mailbox)(cn=%s))" % (username) return self.ldb.search(server_dn, scope=ldb.SCOPE_SUBTREE, expression=filter, attrs=attributes) def lookup_public_folder(self, server, displayname, attributes=[]): """Retrieve the record for a public folder matching a specific display name :param server: Server Object Name :param displayname: Display Name of the Folder :param attributes: Requested Attributes :return: LDB Object of the Folder """ server_dn = self.lookup_server(server, []).dn filter = "(&(objectClass=publicfolder)(PidTagDisplayName=%s))" % (displayname) return self.ldb.search(server_dn, scope=ldb.SCOPE_SUBTREE, expression=filter, attrs=attributes) def get_message_attribute(self, server, attribute): """Retrieve attribute value from given message database (server). :param server: Server object name """ return int(self.lookup_server(server, [attribute])[attribute][0], 10) def get_message_ReplicaID(self, server): """Retrieve current mailbox Replica ID for given message database (server). :param server: Server object name """ return self.get_message_attribute(server, "ReplicaID") def get_message_GlobalCount(self, server): """Retrieve current mailbox Global Count for given message database (server). :param server: Server object name """ return self.get_message_attribute(server, "GlobalCount") def set_message_GlobalCount(self, server, GlobalCount): """Update current mailbox GlobalCount for given message database (server). :param server: Server object name :param index: Mailbox new GlobalCount value """ server_dn = self.lookup_server(server, []).dn newGlobalCount = """ dn: %s changetype: modify replace: GlobalCount GlobalCount: %d """ % (server_dn, GlobalCount) self.ldb.transaction_start() try: self.ldb.modify_ldif(newGlobalCount) finally: self.ldb.transaction_commit() def get_message_ChangeNumber(self, server): """Retrieve current mailbox Global Count for given message database (server). :param server: Server object name """ return self.get_message_attribute(server, "ChangeNumber") def set_message_ChangeNumber(self, server, ChangeNumber): """Update current mailbox ChangeNumber for given message database (server). :param server: Server object name :param index: Mailbox new ChangeNumber value """ server_dn = self.lookup_server(server, []).dn newChangeNumber = """ dn: %s changetype: modify replace: ChangeNumber ChangeNumber: %d """ % (server_dn, ChangeNumber) self.ldb.transaction_start() try: self.ldb.modify_ldif(newChangeNumber) finally: self.ldb.transaction_commit()
def newuser(names, lp, creds, username=None, mail=None): """extend user record with OpenChange settings. :param lp: Loadparm context :param creds: Credentials context :param names: provision names object. :param username: Name of user to extend :param mail: The user email address. If not specified, it will be set to <samAccountName>@<dnsdomain> """ db = Ldb(url=get_ldb_url(lp, creds, names), session_info=system_session(), credentials=creds, lp=lp) user_dn = get_user_dn(db, "CN=Users,%s" % names.domaindn, username) if user_dn: if mail: smtp_user = mail else: smtp_user = username if '@' not in smtp_user: smtp_user += '@%s' % names.dnsdomain mail_domain = names.dnsdomain else: mail_domain = smtp_user.split('@')[1] extended_user = """ dn: %(user_dn)s changetype: modify add: mailNickName mailNickname: %(username)s add: homeMDB homeMDB: CN=Mailbox Store (%(netbiosname)s),CN=First Storage Group,CN=InformationStore,CN=%(netbiosname)s,CN=Servers,CN=%(firstou)s,CN=Administrative Groups,CN=%(firstorg)s,CN=Microsoft Exchange,CN=Services,CN=Configuration,%(domaindn)s add: homeMTA homeMTA: CN=Mailbox Store (%(netbiosname)s),CN=First Storage Group,CN=InformationStore,CN=%(netbiosname)s,CN=Servers,CN=%(firstou)s,CN=Administrative Groups,CN=%(firstorg)s,CN=Microsoft Exchange,CN=Services,CN=Configuration,%(domaindn)s add: legacyExchangeDN legacyExchangeDN: /o=%(firstorg)s/ou=%(firstou)s/cn=Recipients/cn=%(username)s add: proxyAddresses proxyAddresses: =EX:/o=%(firstorg)s/ou=%(firstou)s/cn=Recipients/cn=%(username)s proxyAddresses: smtp:postmaster@%(mail_domain)s proxyAddresses: X400:c=US;a= ;p=%(firstorg_x400)s;o=%(firstou_x400)s;s=%(username)s proxyAddresses: SMTP:%(smtp_user)s replace: msExchUserAccountControl msExchUserAccountControl: 0 """ ldif_value = extended_user % {"user_dn": user_dn, "username": username, "netbiosname": names.netbiosname, "firstorg": names.firstorg, "firstorg_x400": names.firstorg[:16], "firstou": names.firstou, "firstou_x400": names.firstou[:64], "domaindn": names.domaindn, "dnsdomain": names.dnsdomain, "smtp_user": smtp_user, "mail_domain": mail_domain} db.modify_ldif(ldif_value) res = db.search(base=user_dn, scope=SCOPE_BASE, attrs=["*"]) if len(res) == 1: record = res[0] else: raise Exception("this should never happen as we just modified the record...") record_keys = map(lambda x: x.lower(), record.keys()) if "displayname" not in record_keys: extended_user = "******" % (user_dn, username) db.modify_ldif(extended_user) if "mail" not in record_keys: extended_user = "******" % (user_dn, smtp_user) db.modify_ldif(extended_user) print "[+] User %s extended and enabled" % username else: print "[!] User '%s' not found" % username