Example #1
0
        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))
Example #2
0
        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))
Example #3
0
        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, 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))
Example #5
0
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
Example #6
0
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
Example #7
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)
Example #8
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)
Example #9
0
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()
Example #10
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)
Example #11
0
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()
Example #12
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)