Example #1
0
class SchemaTests(samba.tests.TestCase):

    def setUp(self):
        super(SchemaTests, self).setUp()
        self.ldb = SamDB(host, credentials=creds,
            session_info=system_session(lp), lp=lp, options=ldb_options)
        self.base_dn = self.ldb.domain_dn()
        self.schema_dn = self.ldb.get_schema_basedn().get_linearized()

    def test_generated_schema(self):
        """Testing we can read the generated schema via LDAP"""
        res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE,
                attrs=["objectClasses", "attributeTypes", "dITContentRules"])
        self.assertEquals(len(res), 1)
        self.assertTrue("dITContentRules" in res[0])
        self.assertTrue("objectClasses" in res[0])
        self.assertTrue("attributeTypes" in res[0])

    def test_generated_schema_is_operational(self):
        """Testing we don't get the generated schema via LDAP by default"""
        # Must keep the "*" form
        res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE,
                              attrs=["*"])
        self.assertEquals(len(res), 1)
        self.assertFalse("dITContentRules" in res[0])
        self.assertFalse("objectClasses" in res[0])
        self.assertFalse("attributeTypes" in res[0])

    def test_schemaUpdateNow(self):
        """Testing schemaUpdateNow"""
        attr_name = "test-Attr" + time.strftime("%s", time.gmtime())
        attr_ldap_display_name = attr_name.replace("-", "")

        ldif = """
dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
objectClass: top
objectClass: attributeSchema
adminDescription: """ + attr_name + """
adminDisplayName: """ + attr_name + """
cn: """ + attr_name + """
attributeId: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9940
attributeSyntax: 2.5.5.12
omSyntax: 64
instanceType: 4
isSingleValued: TRUE
systemOnly: FALSE
"""
        self.ldb.add_ldif(ldif)
        # We must do a schemaUpdateNow otherwise it's not 100% sure that the schema
        # will contain the new attribute
        ldif = """
dn:
changetype: modify
add: schemaUpdateNow
schemaUpdateNow: 1
"""
        self.ldb.modify_ldif(ldif)

        # Search for created attribute
        res = []
        res = self.ldb.search("cn=%s,%s" % (attr_name, self.schema_dn), scope=SCOPE_BASE,
                              attrs=["lDAPDisplayName","schemaIDGUID"])
        self.assertEquals(len(res), 1)
        self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_display_name)
        self.assertTrue("schemaIDGUID" in res[0])

        class_name = "test-Class" + time.strftime("%s", time.gmtime())
        class_ldap_display_name = class_name.replace("-", "")

        # First try to create a class with a wrong "defaultObjectCategory"
        ldif = """
dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
objectClass: top
objectClass: classSchema
defaultObjectCategory: CN=_
adminDescription: """ + class_name + """
adminDisplayName: """ + class_name + """
cn: """ + class_name + """
governsId: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9939
instanceType: 4
objectClassCategory: 1
subClassOf: organizationalPerson
systemFlags: 16
rDNAttID: cn
systemMustContain: cn
systemMustContain: """ + attr_ldap_display_name + """
systemOnly: FALSE
"""
        try:
                 self.ldb.add_ldif(ldif)
                 self.fail()
        except LdbError, (num, _):
                 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        ldif = """
dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
objectClass: top
objectClass: classSchema
adminDescription: """ + class_name + """
adminDisplayName: """ + class_name + """
cn: """ + class_name + """
governsId: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9939
instanceType: 4
objectClassCategory: 1
subClassOf: organizationalPerson
systemFlags: 16
rDNAttID: cn
systemMustContain: cn
systemMustContain: """ + attr_ldap_display_name + """
systemOnly: FALSE
"""
        self.ldb.add_ldif(ldif)

        # Search for created objectclass
        res = []
        res = self.ldb.search("cn=%s,%s" % (class_name, self.schema_dn), scope=SCOPE_BASE,
                              attrs=["lDAPDisplayName", "defaultObjectCategory", "schemaIDGUID", "distinguishedName"])
        self.assertEquals(len(res), 1)
        self.assertEquals(res[0]["lDAPDisplayName"][0], class_ldap_display_name)
        self.assertEquals(res[0]["defaultObjectCategory"][0], res[0]["distinguishedName"][0])
        self.assertTrue("schemaIDGUID" in res[0])

        ldif = """
dn:
changetype: modify
add: schemaUpdateNow
schemaUpdateNow: 1
"""
        self.ldb.modify_ldif(ldif)

        object_name = "obj" + time.strftime("%s", time.gmtime())

        ldif = """
dn: CN=%s,CN=Users,%s"""% (object_name, self.base_dn) + """
objectClass: organizationalPerson
objectClass: person
objectClass: """ + class_ldap_display_name + """
objectClass: top
cn: """ + object_name + """
instanceType: 4
objectCategory: CN=%s,%s"""% (class_name, self.schema_dn) + """
distinguishedName: CN=%s,CN=Users,%s"""% (object_name, self.base_dn) + """
name: """ + object_name + """
""" + attr_ldap_display_name + """: test
"""
        self.ldb.add_ldif(ldif)

        # Search for created object
        res = []
        res = self.ldb.search("cn=%s,cn=Users,%s" % (object_name, self.base_dn), scope=SCOPE_BASE, attrs=["dn"])
        self.assertEquals(len(res), 1)
        # Delete the object
        delete_force(self.ldb, "cn=%s,cn=Users,%s" % (object_name, self.base_dn))
Example #2
0
class Schema(object):

    def __init__(self, domain_sid, invocationid=None, schemadn=None,
                 files=None, override_prefixmap=None, additional_prefixmap=None):
        from samba.provision import setup_path

        """Load schema for the SamDB from the AD schema files and
        samba4_schema.ldif

        :param samdb: Load a schema into a SamDB.
        :param schemadn: DN of the schema

        Returns the schema data loaded, to avoid double-parsing when then
        needing to add it to the db
        """

        self.schemadn = schemadn
        # We need to have the am_rodc=False just to keep some warnings quiet -
        # this isn't a real SAM, so it's meaningless.
        self.ldb = SamDB(global_schema=False, am_rodc=False)
        if invocationid is not None:
            self.ldb.set_invocation_id(invocationid)

        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})

        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})

        if override_prefixmap is not None:
            self.prefixmap_data = override_prefixmap
        else:
            self.prefixmap_data = open(setup_path("prefixMap.txt"), 'r').read()

        if additional_prefixmap is not None:
            for map in additional_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: %s\nprefixMap:: %s\n\n" % (self.schemadn, self.prefixmap_data)
        self.set_from_ldif(prefixmap_ldif, self.schema_data, self.schemadn)

    def set_from_ldif(self, pf, df, dn):
        dsdb._dsdb_set_schema_from_ldif(self.ldb, pf, df, dn)

    def write_to_tmp_ldb(self, schemadb_path):
        self.ldb.connect(url=schemadb_path)
        self.ldb.transaction_start()
        try:
            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)
        except:
            self.ldb.transaction_cancel()
            raise
        else:
            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)

    def convert_to_openldap(self, target, mapping):
        return dsdb._dsdb_convert_schema_to_openldap(self.ldb, target, mapping)
Example #3
0
class SchemaTests_msDS_IntId(samba.tests.TestCase):

    def setUp(self):
        super(SchemaTests_msDS_IntId, self).setUp()
        self.ldb = SamDB(host, credentials=creds,
            session_info=system_session(lp), lp=lp, options=ldb_options)
        res = self.ldb.search(base="", expression="", scope=SCOPE_BASE,
                         attrs=["schemaNamingContext", "defaultNamingContext",
                                "forestFunctionality"])
        self.assertEquals(len(res), 1)
        self.schema_dn = res[0]["schemaNamingContext"][0]
        self.base_dn = res[0]["defaultNamingContext"][0]
        self.forest_level = int(res[0]["forestFunctionality"][0])

    def _ldap_schemaUpdateNow(self):
        ldif = """
dn:
changetype: modify
add: schemaUpdateNow
schemaUpdateNow: 1
"""
        self.ldb.modify_ldif(ldif)

    def _make_obj_names(self, prefix):
        class_name = prefix + time.strftime("%s", time.gmtime())
        class_ldap_name = class_name.replace("-", "")
        class_dn = "CN=%s,%s" % (class_name, self.schema_dn)
        return (class_name, class_ldap_name, class_dn)

    def _is_schema_base_object(self, ldb_msg):
        """Test systemFlags for SYSTEM_FLAG_SCHEMA_BASE_OBJECT (16)"""
        systemFlags = 0
        if "systemFlags" in ldb_msg:
            systemFlags = int(ldb_msg["systemFlags"][0])
        return (systemFlags & 16) != 0

    def _make_attr_ldif(self, attr_name, attr_dn):
        ldif = """
dn: """ + attr_dn + """
objectClass: top
objectClass: attributeSchema
adminDescription: """ + attr_name + """
adminDisplayName: """ + attr_name + """
cn: """ + attr_name + """
attributeId: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9940
attributeSyntax: 2.5.5.12
omSyntax: 64
instanceType: 4
isSingleValued: TRUE
systemOnly: FALSE
"""
        return ldif

    def test_msDS_IntId_on_attr(self):
        """Testing msDs-IntId creation for Attributes.
        See MS-ADTS - 3.1.1.Attributes

        This test should verify that:
        - Creating attribute with 'msDS-IntId' fails with ERR_UNWILLING_TO_PERFORM
        - Adding 'msDS-IntId' on existing attribute fails with ERR_CONSTRAINT_VIOLATION
        - Creating attribute with 'msDS-IntId' set and FLAG_SCHEMA_BASE_OBJECT flag
          set fails with ERR_UNWILLING_TO_PERFORM
        - Attributes created with FLAG_SCHEMA_BASE_OBJECT not set have
          'msDS-IntId' attribute added internally
        """

        # 1. Create attribute without systemFlags
        # msDS-IntId should be created if forest functional
        # level is >= DS_DOMAIN_FUNCTION_2003
        # and missing otherwise
        (attr_name, attr_ldap_name, attr_dn) = self._make_obj_names("msDS-IntId-Attr-1-")
        ldif = self._make_attr_ldif(attr_name, attr_dn)

        # try to add msDS-IntId during Attribute creation
        ldif_fail = ldif + "msDS-IntId: -1993108831\n"
        try:
            self.ldb.add_ldif(ldif_fail)
            self.fail("Adding attribute with preset msDS-IntId should fail")
        except LdbError, (num, _):
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

        # add the new attribute and update schema
        self.ldb.add_ldif(ldif)
        self._ldap_schemaUpdateNow()

        # Search for created attribute
        res = []
        res = self.ldb.search(attr_dn, scope=SCOPE_BASE,
                              attrs=["lDAPDisplayName", "msDS-IntId", "systemFlags"])
        self.assertEquals(len(res), 1)
        self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_name)
        if self.forest_level >= DS_DOMAIN_FUNCTION_2003:
            if self._is_schema_base_object(res[0]):
                self.assertTrue("msDS-IntId" not in res[0])
            else:
                self.assertTrue("msDS-IntId" in res[0])
        else:
            self.assertTrue("msDS-IntId" not in res[0])

        msg = Message()
        msg.dn = Dn(self.ldb, attr_dn)
        msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
        try:
            self.ldb.modify(msg)
            self.fail("Modifying msDS-IntId should return error")
        except LdbError, (num, _):
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
Example #4
0
class SchemaTests(samba.tests.TestCase):

    def setUp(self):
        super(SchemaTests, self).setUp()
        self.ldb = SamDB(host, credentials=creds,
            session_info=system_session(lp), lp=lp, options=ldb_options)
        self.base_dn = self.ldb.domain_dn()
        self.schema_dn = self.ldb.get_schema_basedn().get_linearized()

    def test_generated_schema(self):
        """Testing we can read the generated schema via LDAP"""
        res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE,
                attrs=["objectClasses", "attributeTypes", "dITContentRules"])
        self.assertEquals(len(res), 1)
        self.assertTrue("dITContentRules" in res[0])
        self.assertTrue("objectClasses" in res[0])
        self.assertTrue("attributeTypes" in res[0])

    def test_generated_schema_is_operational(self):
        """Testing we don't get the generated schema via LDAP by default"""
        # Must keep the "*" form
        res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE,
                              attrs=["*"])
        self.assertEquals(len(res), 1)
        self.assertFalse("dITContentRules" in res[0])
        self.assertFalse("objectClasses" in res[0])
        self.assertFalse("attributeTypes" in res[0])

    def test_schemaUpdateNow(self):
        """Testing schemaUpdateNow"""
        attr_name = "test-Attr" + time.strftime("%s", time.gmtime())
        attr_ldap_display_name = attr_name.replace("-", "")

        ldif = """
dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
objectClass: top
objectClass: attributeSchema
adminDescription: """ + attr_name + """
adminDisplayName: """ + attr_name + """
cn: """ + attr_name + """
attributeId: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9940
attributeSyntax: 2.5.5.12
omSyntax: 64
instanceType: 4
isSingleValued: TRUE
systemOnly: FALSE
"""
        self.ldb.add_ldif(ldif)
        # We must do a schemaUpdateNow otherwise it's not 100% sure that the schema
        # will contain the new attribute
        ldif = """
dn:
changetype: modify
add: schemaUpdateNow
schemaUpdateNow: 1
"""
        self.ldb.modify_ldif(ldif)

        # Search for created attribute
        res = []
        res = self.ldb.search("cn=%s,%s" % (attr_name, self.schema_dn), scope=SCOPE_BASE,
                              attrs=["lDAPDisplayName","schemaIDGUID", "msDS-IntID"])
        self.assertEquals(len(res), 1)
        self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_display_name)
        self.assertTrue("schemaIDGUID" in res[0])
        if "msDS-IntId" in res[0]:
            msDS_IntId = int(res[0]["msDS-IntId"][0])
            if msDS_IntId < 0:
                msDS_IntId += (1 << 32)
        else:
            msDS_IntId = None

        class_name = "test-Class" + time.strftime("%s", time.gmtime())
        class_ldap_display_name = class_name.replace("-", "")

        # First try to create a class with a wrong "defaultObjectCategory"
        ldif = """
dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
objectClass: top
objectClass: classSchema
defaultObjectCategory: CN=_
adminDescription: """ + class_name + """
adminDisplayName: """ + class_name + """
cn: """ + class_name + """
governsId: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9939
instanceType: 4
objectClassCategory: 1
subClassOf: organizationalPerson
systemFlags: 16
rDNAttID: cn
systemMustContain: cn
systemMustContain: """ + attr_ldap_display_name + """
systemOnly: FALSE
"""
        try:
                 self.ldb.add_ldif(ldif)
                 self.fail()
        except LdbError, (num, _):
                 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        ldif = """
dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
objectClass: top
objectClass: classSchema
adminDescription: """ + class_name + """
adminDisplayName: """ + class_name + """
cn: """ + class_name + """
governsId: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9939
instanceType: 4
objectClassCategory: 1
subClassOf: organizationalPerson
systemFlags: 16
rDNAttID: cn
systemMustContain: cn
systemMustContain: """ + attr_ldap_display_name + """
systemOnly: FALSE
"""
        self.ldb.add_ldif(ldif)

        # Search for created objectclass
        res = []
        res = self.ldb.search("cn=%s,%s" % (class_name, self.schema_dn), scope=SCOPE_BASE,
                              attrs=["lDAPDisplayName", "defaultObjectCategory", "schemaIDGUID", "distinguishedName"])
        self.assertEquals(len(res), 1)
        self.assertEquals(res[0]["lDAPDisplayName"][0], class_ldap_display_name)
        self.assertEquals(res[0]["defaultObjectCategory"][0], res[0]["distinguishedName"][0])
        self.assertTrue("schemaIDGUID" in res[0])

        ldif = """
dn:
changetype: modify
add: schemaUpdateNow
schemaUpdateNow: 1
"""
        self.ldb.modify_ldif(ldif)

        object_name = "obj" + time.strftime("%s", time.gmtime())

        ldif = """
dn: CN=%s,CN=Users,%s"""% (object_name, self.base_dn) + """
objectClass: organizationalPerson
objectClass: person
objectClass: """ + class_ldap_display_name + """
objectClass: top
cn: """ + object_name + """
instanceType: 4
objectCategory: CN=%s,%s"""% (class_name, self.schema_dn) + """
distinguishedName: CN=%s,CN=Users,%s"""% (object_name, self.base_dn) + """
name: """ + object_name + """
""" + attr_ldap_display_name + """: test
"""
        self.ldb.add_ldif(ldif)

        # Search for created object
        obj_res = self.ldb.search("cn=%s,cn=Users,%s" % (object_name, self.base_dn), scope=SCOPE_BASE, attrs=["replPropertyMetaData"])

        self.assertEquals(len(obj_res), 1)
        self.assertTrue("replPropertyMetaData" in obj_res[0])
        val = obj_res[0]["replPropertyMetaData"][0]
        repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, str(val))
        obj = repl.ctr

        # Windows 2000 functional level won't have this.  It is too
        # hard to work it out from the prefixmap however, so we skip
        # this test in that case.
        if msDS_IntId is not None:
            found = False
            for o in repl.ctr.array:
                if o.attid == msDS_IntId:
                    found = True
                    break
            self.assertTrue(found, "Did not find 0x%08x in replPropertyMetaData" % msDS_IntId)
        # Delete the object
        delete_force(self.ldb, "cn=%s,cn=Users,%s" % (object_name, self.base_dn))
Example #5
0
class Schema(object):

    def __init__(self, domain_sid, invocationid=None, schemadn=None,
                 files=None, override_prefixmap=None, additional_prefixmap=None):
        from samba.provision import setup_path

        """Load schema for the SamDB from the AD schema files and
        samba4_schema.ldif

        :param samdb: Load a schema into a SamDB.
        :param schemadn: DN of the schema

        Returns the schema data loaded, to avoid double-parsing when then
        needing to add it to the db
        """

        self.schemadn = schemadn
        # We need to have the am_rodc=False just to keep some warnings quiet -
        # this isn't a real SAM, so it's meaningless.
        self.ldb = SamDB(global_schema=False, am_rodc=False)
        if invocationid is not None:
            self.ldb.set_invocation_id(invocationid)

        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})

        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})

        if override_prefixmap is not None:
            self.prefixmap_data = override_prefixmap
        else:
            self.prefixmap_data = open(setup_path("prefixMap.txt"), 'r').read()

        if additional_prefixmap is not None:
            for map in additional_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: %s\nprefixMap:: %s\n\n" % (self.schemadn, self.prefixmap_data)
        self.set_from_ldif(prefixmap_ldif, self.schema_data, self.schemadn)

    def set_from_ldif(self, pf, df, dn):
        dsdb._dsdb_set_schema_from_ldif(self.ldb, pf, df, dn)

    def write_to_tmp_ldb(self, schemadb_path):
        self.ldb.connect(url=schemadb_path)
        self.ldb.transaction_start()
        try:
            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)
        except:
            self.ldb.transaction_cancel()
            raise
        else:
            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)

    def convert_to_openldap(self, target, mapping):
        return dsdb._dsdb_convert_schema_to_openldap(self.ldb, target, mapping)
class UserTests(samba.tests.TestCase):

    def add_if_possible(self, *args, **kwargs):
        """In these tests sometimes things are left in the database
        deliberately, so we don't worry if we fail to add them a second
        time."""
        try:
            self.ldb.add(*args, **kwargs)
        except LdbError:
            pass

    def setUp(self):
        super(UserTests, self).setUp()
        self.state = GlobalState  # the class itself, not an instance
        self.lp = lp
        self.ldb = SamDB(host, credentials=creds,
                         session_info=system_session(lp), lp=lp)
        self.base_dn = self.ldb.domain_dn()
        self.ou = "OU=pid%s,%s" % (os.getpid(), self.base_dn)
        self.ou_users = "OU=users,%s" % self.ou
        self.ou_groups = "OU=groups,%s" % self.ou
        self.ou_computers = "OU=computers,%s" % self.ou

        self.state.test_number += 1
        random.seed(self.state.test_number)

    def tearDown(self):
        super(UserTests, self).tearDown()

    def test_00_00_do_nothing(self):
        # this gives us an idea of the overhead
        pass

    def test_00_01_do_nothing_relevant(self):
        # takes around 1 second on i7-4770
        j = 0
        for i in range(30000000):
            j += i

    def test_00_02_do_nothing_sleepily(self):
        time.sleep(1)

    def test_00_03_add_ous_and_groups(self):
        # initialise the database
        for dn in (self.ou,
                   self.ou_users,
                   self.ou_groups,
                   self.ou_computers):
            self.ldb.add({
                "dn": dn,
                "objectclass": "organizationalUnit"
            })

        for i in range(N_GROUPS):
            self.ldb.add({
                "dn": "cn=g%d,%s" % (i, self.ou_groups),
                "objectclass": "group"
            })

        self.state.n_groups = N_GROUPS

    def _add_users(self, start, end):
        for i in range(start, end):
            self.ldb.add({
                "dn": "cn=u%d,%s" % (i, self.ou_users),
                "objectclass": "user"
            })

    def _add_users_ldif(self, start, end):
        lines = []
        for i in range(start, end):
            lines.append("dn: cn=u%d,%s" % (i, self.ou_users))
            lines.append("objectclass: user")
            lines.append("")
        self.ldb.add_ldif('\n'.join(lines))

    def _test_join(self):
        tmpdir = tempfile.mkdtemp()
        if '://' in host:
            server = host.split('://', 1)[1]
        else:
            server = host
        cmd = cmd_sambatool.subcommands['domain'].subcommands['join']
        result = cmd._run("samba-tool domain join",
                          creds.get_realm(),
                          "dc", "-U%s%%%s" % (creds.get_username(),
                                              creds.get_password()),
                          '--targetdir=%s' % tmpdir,
                          '--server=%s' % server)

        shutil.rmtree(tmpdir)

    def _test_unindexed_search(self):
        expressions = [
            ('(&(objectclass=user)(description='
             'Built-in account for adminstering the computer/domain))'),
            '(description=Built-in account for adminstering the computer/domain)',
            '(objectCategory=*)',
            '(samaccountname=Administrator*)'
        ]
        for expression in expressions:
            t = time.time()
            for i in range(25):
                self.ldb.search(self.ou,
                                expression=expression,
                                scope=SCOPE_SUBTREE,
                                attrs=['cn'])
            print('%d %s took %s' % (i, expression,
                                     time.time() - t),
                  file=sys.stderr)

    def _test_indexed_search(self):
        expressions = ['(objectclass=group)',
                       '(samaccountname=Administrator)'
        ]
        for expression in expressions:
            t = time.time()
            for i in range(4000):
                self.ldb.search(self.ou,
                                expression=expression,
                                scope=SCOPE_SUBTREE,
                                attrs=['cn'])
            print('%d runs %s took %s' % (i, expression,
                                          time.time() - t),
                  file=sys.stderr)

    def _test_base_search(self):
        for dn in [self.base_dn, self.ou, self.ou_users,
                   self.ou_groups, self.ou_computers]:
            for i in range(4000):
                try:
                    self.ldb.search(dn,
                                    scope=SCOPE_BASE,
                                    attrs=['cn'])
                except LdbError as e:
                    (num, msg) = e.args
                    if num != ERR_NO_SUCH_OBJECT:
                        raise

    def _test_base_search_failing(self):
        pattern = 'missing%d' + self.ou
        for i in range(4000):
            try:
                self.ldb.search(pattern % i,
                                scope=SCOPE_BASE,
                                attrs=['cn'])
            except LdbError as (num, msg):
                if num != ERR_NO_SUCH_OBJECT:
                    raise
Example #7
0
class Schema(object):

    # the schema files (and corresponding object version) that we know about
    base_schemas = {
        "2008_R2_old": ("MS-AD_Schema_2K8_R2_Attributes.txt",
                        "MS-AD_Schema_2K8_R2_Classes.txt", 47),
        "2008_R2": ("Attributes_for_AD_DS__Windows_Server_2008_R2.ldf",
                    "Classes_for_AD_DS__Windows_Server_2008_R2.ldf", 47),
        "2012": ("AD_DS_Attributes__Windows_Server_2012.ldf",
                 "AD_DS_Classes__Windows_Server_2012.ldf", 56),
        "2012_R2": ("AD_DS_Attributes__Windows_Server_2012_R2.ldf",
                    "AD_DS_Classes__Windows_Server_2012_R2.ldf", 69),
    }

    def __init__(self,
                 domain_sid,
                 invocationid=None,
                 schemadn=None,
                 files=None,
                 override_prefixmap=None,
                 additional_prefixmap=None,
                 base_schema=None):
        from samba.provision import setup_path
        """Load schema for the SamDB from the AD schema files and
        samba4_schema.ldif

        :param samdb: Load a schema into a SamDB.
        :param schemadn: DN of the schema

        Returns the schema data loaded, to avoid double-parsing when then
        needing to add it to the db
        """

        if base_schema is None:
            base_schema = Schema.default_base_schema()

        self.base_schema = base_schema

        self.schemadn = schemadn
        # We need to have the am_rodc=False just to keep some warnings quiet -
        # this isn't a real SAM, so it's meaningless.
        self.ldb = SamDB(global_schema=False, am_rodc=False)
        if invocationid is not None:
            self.ldb.set_invocation_id(invocationid)

        self.schema_data = read_ms_schema(
            setup_path('ad-schema/%s' % Schema.base_schemas[base_schema][0]),
            setup_path('ad-schema/%s' % Schema.base_schemas[base_schema][1]))

        if files is not None:
            for file in files:
                self.schema_data += open(file, 'rb').read()

        self.schema_data = substitute_var(self.schema_data,
                                          {"SCHEMADN": schemadn})
        check_all_substituted(self.schema_data)

        schema_version = str(Schema.get_version(base_schema))
        self.schema_dn_modify = read_and_sub_file(
            setup_path("provision_schema_basedn_modify.ldif"), {
                "SCHEMADN": schemadn,
                "OBJVERSION": schema_version
            })

        descr = b64encode(get_schema_descriptor(domain_sid)).decode('utf8')
        self.schema_dn_add = read_and_sub_file(
            setup_path("provision_schema_basedn.ldif"), {
                "SCHEMADN": schemadn,
                "DESCRIPTOR": descr
            })

        if override_prefixmap is not None:
            self.prefixmap_data = override_prefixmap
        else:
            self.prefixmap_data = open(setup_path("prefixMap.txt"),
                                       'rb').read()

        if additional_prefixmap is not None:
            for map in additional_prefixmap:
                self.prefixmap_data += "%s\n" % map

        self.prefixmap_data = b64encode(self.prefixmap_data).decode('utf8')

        # We don't actually add this ldif, just parse it
        prefixmap_ldif = "dn: %s\nprefixMap:: %s\n\n" % (self.schemadn,
                                                         self.prefixmap_data)
        self.set_from_ldif(prefixmap_ldif, self.schema_data, self.schemadn)

    @staticmethod
    def default_base_schema():
        """Returns the default base schema to use"""
        return "2008_R2"

    @staticmethod
    def get_version(base_schema):
        """Returns the base schema's object version, e.g. 47 for 2008_R2"""
        return Schema.base_schemas[base_schema][2]

    def set_from_ldif(self, pf, df, dn):
        dsdb._dsdb_set_schema_from_ldif(self.ldb, pf, df, dn)

    def write_to_tmp_ldb(self, schemadb_path):
        self.ldb.connect(url=schemadb_path)
        self.ldb.transaction_start()
        try:
            # These are actually ignored, as the schema has been forced
            # when the ldb object was created, and that overrides this
            self.ldb.add_ldif("""dn: @ATTRIBUTES
linkID: INTEGER

dn: @INDEXLIST
@IDXATTR: linkID
@IDXATTR: attributeSyntax
@IDXGUID: objectGUID
""")

            schema_dn_add = self.schema_dn_add \
            + "objectGUID: 24e2ca70-b093-4ae8-84c0-2d7ac652a1b8\n"

            # These bits of LDIF are supplied when the Schema object is created
            self.ldb.add_ldif(schema_dn_add)
            self.ldb.modify_ldif(self.schema_dn_modify)
            self.ldb.add_ldif(self.schema_data)
        except:
            self.ldb.transaction_cancel()
            raise
        else:
            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)

    def convert_to_openldap(self, target, mapping):
        return dsdb._dsdb_convert_schema_to_openldap(self.ldb, target, mapping)
Example #8
0
class Schema(object):

    # the schema files (and corresponding object version) that we know about
    base_schemas = {
       "2008_R2_old" : ("MS-AD_Schema_2K8_R2_Attributes.txt",
                        "MS-AD_Schema_2K8_R2_Classes.txt",
                        47),
       "2008_R2" : ("Attributes_for_AD_DS__Windows_Server_2008_R2.ldf",
                    "Classes_for_AD_DS__Windows_Server_2008_R2.ldf",
                    47),
       "2012"    : ("AD_DS_Attributes__Windows_Server_2012.ldf",
                    "AD_DS_Classes__Windows_Server_2012.ldf",
                    56),
       "2012_R2" : ("AD_DS_Attributes__Windows_Server_2012_R2.ldf",
                    "AD_DS_Classes__Windows_Server_2012_R2.ldf",
                    69),
    }

    def __init__(self, domain_sid, invocationid=None, schemadn=None,
                 files=None, override_prefixmap=None, additional_prefixmap=None,
                 base_schema=None):
        from samba.provision import setup_path

        """Load schema for the SamDB from the AD schema files and
        samba4_schema.ldif

        :param samdb: Load a schema into a SamDB.
        :param schemadn: DN of the schema

        Returns the schema data loaded, to avoid double-parsing when then
        needing to add it to the db
        """

        if base_schema is None:
            base_schema = Schema.default_base_schema()

        self.base_schema = base_schema

        self.schemadn = schemadn
        # We need to have the am_rodc=False just to keep some warnings quiet -
        # this isn't a real SAM, so it's meaningless.
        self.ldb = SamDB(global_schema=False, am_rodc=False)
        if invocationid is not None:
            self.ldb.set_invocation_id(invocationid)

        self.schema_data = read_ms_schema(
            setup_path('ad-schema/%s' % Schema.base_schemas[base_schema][0]),
            setup_path('ad-schema/%s' % Schema.base_schemas[base_schema][1]))

        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)

        schema_version = str(Schema.get_version(base_schema))
        self.schema_dn_modify = read_and_sub_file(
            setup_path("provision_schema_basedn_modify.ldif"),
            {"SCHEMADN": schemadn, "OBJVERSION" : schema_version})

        descr = b64encode(get_schema_descriptor(domain_sid)).decode('utf8')
        self.schema_dn_add = read_and_sub_file(
            setup_path("provision_schema_basedn.ldif"),
            {"SCHEMADN": schemadn, "DESCRIPTOR": descr})

        if override_prefixmap is not None:
            self.prefixmap_data = override_prefixmap
        else:
            self.prefixmap_data = open(setup_path("prefixMap.txt"), 'r').read()

        if additional_prefixmap is not None:
            for map in additional_prefixmap:
                self.prefixmap_data += "%s\n" % map

        self.prefixmap_data = b64encode(self.prefixmap_data).decode('utf8')

        # We don't actually add this ldif, just parse it
        prefixmap_ldif = "dn: %s\nprefixMap:: %s\n\n" % (self.schemadn, self.prefixmap_data)
        self.set_from_ldif(prefixmap_ldif, self.schema_data, self.schemadn)

    @staticmethod
    def default_base_schema():
        """Returns the default base schema to use"""
        return "2008_R2"

    @staticmethod
    def get_version(base_schema):
        """Returns the base schema's object version, e.g. 47 for 2008_R2"""
        return Schema.base_schemas[base_schema][2]

    def set_from_ldif(self, pf, df, dn):
        dsdb._dsdb_set_schema_from_ldif(self.ldb, pf, df, dn)

    def write_to_tmp_ldb(self, schemadb_path):
        self.ldb.connect(url=schemadb_path)
        self.ldb.transaction_start()
        try:
            # These are actually ignored, as the schema has been forced
            # when the ldb object was created, and that overrides this
            self.ldb.add_ldif("""dn: @ATTRIBUTES
linkID: INTEGER

dn: @INDEXLIST
@IDXATTR: linkID
@IDXATTR: attributeSyntax
@IDXGUID: objectGUID
""")

            schema_dn_add = self.schema_dn_add \
                            + "objectGUID: 24e2ca70-b093-4ae8-84c0-2d7ac652a1b8\n"

            # These bits of LDIF are supplied when the Schema object is created
            self.ldb.add_ldif(schema_dn_add)
            self.ldb.modify_ldif(self.schema_dn_modify)
            self.ldb.add_ldif(self.schema_data)
        except:
            self.ldb.transaction_cancel()
            raise
        else:
            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)

    def convert_to_openldap(self, target, mapping):
        return dsdb._dsdb_convert_schema_to_openldap(self.ldb, target, mapping)
Example #9
0
class UserTests(samba.tests.TestCase):
    def add_if_possible(self, *args, **kwargs):
        """In these tests sometimes things are left in the database
        deliberately, so we don't worry if we fail to add them a second
        time."""
        try:
            self.ldb.add(*args, **kwargs)
        except LdbError:
            pass

    def setUp(self):
        super(UserTests, self).setUp()
        self.state = GlobalState  # the class itself, not an instance
        self.lp = lp
        self.ldb = SamDB(host,
                         credentials=creds,
                         session_info=system_session(lp),
                         lp=lp)
        self.base_dn = self.ldb.domain_dn()
        self.ou = "OU=pid%s,%s" % (os.getpid(), self.base_dn)
        self.ou_users = "OU=users,%s" % self.ou
        self.ou_groups = "OU=groups,%s" % self.ou
        self.ou_computers = "OU=computers,%s" % self.ou

        for dn in (self.ou, self.ou_users, self.ou_groups, self.ou_computers):
            self.add_if_possible({
                "dn": dn,
                "objectclass": "organizationalUnit"
            })

    def tearDown(self):
        super(UserTests, self).tearDown()

    def test_00_00_do_nothing(self):
        # this gives us an idea of the overhead
        pass

    def _prepare_n_groups(self, n):
        self.state.n_groups = n
        for i in range(n):
            self.add_if_possible({
                "dn": "cn=g%d,%s" % (i, self.ou_groups),
                "objectclass": "group"
            })

    def _add_users(self, start, end):
        for i in range(start, end):
            self.ldb.add({
                "dn": "cn=u%d,%s" % (i, self.ou_users),
                "objectclass": "user"
            })

    def _add_users_ldif(self, start, end):
        lines = []
        for i in range(start, end):
            lines.append("dn: cn=u%d,%s" % (i, self.ou_users))
            lines.append("objectclass: user")
            lines.append("")
        self.ldb.add_ldif('\n'.join(lines))

    def _test_unindexed_search(self):
        expressions = [(
            '(&(objectclass=user)(description='
            'Built-in account for adminstering the computer/domain))'
        ), '(description=Built-in account for adminstering the computer/domain)',
                       '(objectCategory=*)', '(samaccountname=Administrator*)']
        for expression in expressions:
            t = time.time()
            for i in range(50):
                self.ldb.search(self.ou,
                                expression=expression,
                                scope=SCOPE_SUBTREE,
                                attrs=['cn'])
            print('%d %s took %s' % (i, expression, time.time() - t),
                  file=sys.stderr)

    def _test_indexed_search(self):
        expressions = ['(objectclass=group)', '(samaccountname=Administrator)']
        for expression in expressions:
            t = time.time()
            for i in range(10000):
                self.ldb.search(self.ou,
                                expression=expression,
                                scope=SCOPE_SUBTREE,
                                attrs=['cn'])
            print('%d runs %s took %s' % (i, expression, time.time() - t),
                  file=sys.stderr)

    def _test_complex_search(self):
        classes = ['samaccountname', 'objectCategory', 'dn', 'member']
        values = ['*', '*t*', 'g*', 'user']
        comparators = ['=', '<=', '>=']  # '~=' causes error
        maybe_not = ['!(', '']
        joiners = ['&', '|']

        # The number of permuations is 18432, which is not huge but
        # would take hours to search. So we take a sample.
        all_permutations = list(
            itertools.product(joiners, classes, classes, values, values,
                              comparators, comparators, maybe_not, maybe_not))
        random.seed(1)

        for (j, c1, c2, v1, v2, o1, o2, n1,
             n2) in random.sample(all_permutations, 100):
            expression = ''.join([
                '(', j, '(', n1, c1, o1, v1, '))' if n1 else ')', '(', n2, c2,
                o2, v2, '))' if n2 else ')', ')'
            ])
            print(expression)
            self.ldb.search(self.ou,
                            expression=expression,
                            scope=SCOPE_SUBTREE,
                            attrs=['cn'])

    def _test_member_search(self, rounds=10):
        expressions = []
        for d in range(50):
            expressions.append('(member=cn=u%d,%s)' % (d + 500, self.ou_users))
            expressions.append('(member=u%d*)' % (d + 700, ))
        for i in range(N_GROUPS):
            expressions.append('(memberOf=cn=g%d,%s)' % (i, self.ou_groups))
            expressions.append('(memberOf=cn=g%d*)' % (i, ))
            expressions.append('(memberOf=cn=*%s*)' % self.ou_groups)

        for expression in expressions:
            t = time.time()
            for i in range(rounds):
                self.ldb.search(self.ou,
                                expression=expression,
                                scope=SCOPE_SUBTREE,
                                attrs=['cn'])
            print('%d runs %s took %s' % (i, expression, time.time() - t),
                  file=sys.stderr)

    def _test_add_many_users(self, n=BATCH_SIZE):
        s = self.state.next_user_id
        e = s + n
        self._add_users(s, e)
        self.state.next_user_id = e

    def _test_add_many_users_ldif(self, n=BATCH_SIZE):
        s = self.state.next_user_id
        e = s + n
        self._add_users_ldif(s, e)
        self.state.next_user_id = e

    def _link_user_and_group(self, u, g):
        m = Message()
        m.dn = Dn(self.ldb, "CN=g%d,%s" % (g, self.ou_groups))
        m["member"] = MessageElement("cn=u%d,%s" % (u, self.ou_users),
                                     FLAG_MOD_ADD, "member")
        self.ldb.modify(m)

    def _test_link_many_users(self, n=BATCH_SIZE):
        self._prepare_n_groups(N_GROUPS)
        s = self.state.next_linked_user
        e = s + n
        for i in range(s, e):
            # put everyone in group 0, and one other group
            g = i % (N_GROUPS - 1) + 1
            self._link_user_and_group(i, g)
            self._link_user_and_group(i, 0)
        self.state.next_linked_user = e

    test_00_01_adding_users_1000 = _test_add_many_users

    test_00_10_complex_search_1k_users = _test_complex_search
    test_00_11_unindexed_search_1k_users = _test_unindexed_search
    test_00_12_indexed_search_1k_users = _test_indexed_search
    test_00_13_member_search_1k_users = _test_member_search

    test_01_02_adding_users_2000_ldif = _test_add_many_users_ldif
    test_01_03_adding_users_3000 = _test_add_many_users

    test_01_10_complex_search_3k_users = _test_complex_search
    test_01_11_unindexed_search_3k_users = _test_unindexed_search
    test_01_12_indexed_search_3k_users = _test_indexed_search

    def test_01_13_member_search_3k_users(self):
        self._test_member_search(rounds=5)

    test_02_01_link_users_1000 = _test_link_many_users
    test_02_02_link_users_2000 = _test_link_many_users
    test_02_03_link_users_3000 = _test_link_many_users

    test_03_10_complex_search_linked_users = _test_complex_search
    test_03_11_unindexed_search_linked_users = _test_unindexed_search
    test_03_12_indexed_search_linked_users = _test_indexed_search

    def test_03_13_member_search_linked_users(self):
        self._test_member_search(rounds=2)
Example #10
0
objectClass: nTDSSiteSettings
cn: NTDS Site Settings
showInAdvancedViewOnly: TRUE
name: NTDS Site Settings
objectCategory: CN=NTDS-Site-Settings,CN=Schema,CN=Configuration,%(samba4_ldap_base)s

dn: CN=Servers,CN=%(branchsite_name)s,CN=Sites,CN=Configuration,%(samba4_ldap_base)s
objectClass: serversContainer
cn: Servers
showInAdvancedViewOnly: TRUE
name: Servers
systemFlags: 33554432
objectCategory: CN=Servers-Container,CN=Schema,CN=Configuration,%(samba4_ldap_base)s
''' % ldif_dict

	samdb.add_ldif(site_add_ldif)
	print("created site %s" % opts.site)

	if opts.sitelink and not opts.createsitelink:
		# and add it to the sitelink
		sitelink_modify_ldif = '''
dn: CN=%(sitelink)s,CN=IP,CN=Inter-Site Transports,CN=Sites,CN=Configuration,%(samba4_ldap_base)s
changetype: modify
add: siteList
siteList: CN=%(branchsite_name)s,CN=Sites,CN=Configuration,%(samba4_ldap_base)s
''' % ldif_dict
		samdb.modify_ldif(sitelink_modify_ldif)
		print("added site %s to sitelink %s" % (opts.site, opts.sitelink))

elif opts.site:
	res = samdb.search("CN=Configuration,%s" % samba4_ldap_base, scope=ldb.SCOPE_SUBTREE, expression="(&(objectClass=site)(cn=%s))" % opts.site)
Example #11
0
class UserTests(samba.tests.TestCase):
    def add_if_possible(self, *args, **kwargs):
        """In these tests sometimes things are left in the database
        deliberately, so we don't worry if we fail to add them a second
        time."""
        try:
            self.ldb.add(*args, **kwargs)
        except LdbError:
            pass

    def setUp(self):
        super(UserTests, self).setUp()
        self.state = GlobalState  # the class itself, not an instance
        self.lp = lp
        self.ldb = SamDB(host,
                         credentials=creds,
                         session_info=system_session(lp),
                         lp=lp)
        self.base_dn = self.ldb.domain_dn()
        self.ou = "OU=pid%s,%s" % (os.getpid(), self.base_dn)
        self.ou_users = "OU=users,%s" % self.ou
        self.ou_groups = "OU=groups,%s" % self.ou
        self.ou_computers = "OU=computers,%s" % self.ou

        self.state.test_number += 1
        random.seed(self.state.test_number)

    def tearDown(self):
        super(UserTests, self).tearDown()

    def test_00_00_do_nothing(self):
        # this gives us an idea of the overhead
        pass

    def test_00_01_do_nothing_relevant(self):
        # takes around 1 second on i7-4770
        j = 0
        for i in range(30000000):
            j += i

    def test_00_02_do_nothing_sleepily(self):
        time.sleep(1)

    def test_00_03_add_ous_and_groups(self):
        # initialise the database
        for dn in (self.ou, self.ou_users, self.ou_groups, self.ou_computers):
            self.ldb.add({"dn": dn, "objectclass": "organizationalUnit"})

        for i in range(N_GROUPS):
            self.ldb.add({
                "dn": "cn=g%d,%s" % (i, self.ou_groups),
                "objectclass": "group"
            })

        self.state.n_groups = N_GROUPS

    def _add_users(self, start, end):
        for i in range(start, end):
            self.ldb.add({
                "dn": "cn=u%d,%s" % (i, self.ou_users),
                "objectclass": "user"
            })

    def _add_users_ldif(self, start, end):
        lines = []
        for i in range(start, end):
            lines.append("dn: cn=u%d,%s" % (i, self.ou_users))
            lines.append("objectclass: user")
            lines.append("")
        self.ldb.add_ldif('\n'.join(lines))

    def _test_join(self):
        tmpdir = tempfile.mkdtemp()
        if '://' in host:
            server = host.split('://', 1)[1]
        else:
            server = host
        cmd = cmd_sambatool.subcommands['domain'].subcommands['join']
        result = cmd._run(
            "samba-tool domain join", creds.get_realm(), "dc",
            "-U%s%%%s" % (creds.get_username(), creds.get_password()),
            '--targetdir=%s' % tmpdir, '--server=%s' % server)

        shutil.rmtree(tmpdir)

    def _test_unindexed_search(self):
        expressions = [(
            '(&(objectclass=user)(description='
            'Built-in account for adminstering the computer/domain))'
        ), '(description=Built-in account for adminstering the computer/domain)',
                       '(objectCategory=*)', '(samaccountname=Administrator*)']
        for expression in expressions:
            t = time.time()
            for i in range(25):
                self.ldb.search(self.ou,
                                expression=expression,
                                scope=SCOPE_SUBTREE,
                                attrs=['cn'])
            print('%d %s took %s' % (i, expression, time.time() - t),
                  file=sys.stderr)

    def _test_indexed_search(self):
        expressions = ['(objectclass=group)', '(samaccountname=Administrator)']
        for expression in expressions:
            t = time.time()
            for i in range(4000):
                self.ldb.search(self.ou,
                                expression=expression,
                                scope=SCOPE_SUBTREE,
                                attrs=['cn'])
            print('%d runs %s took %s' % (i, expression, time.time() - t),
                  file=sys.stderr)

    def _test_base_search(self):
        for dn in [
                self.base_dn, self.ou, self.ou_users, self.ou_groups,
                self.ou_computers
        ]:
            for i in range(4000):
                try:
                    self.ldb.search(dn, scope=SCOPE_BASE, attrs=['cn'])
                except LdbError as e:
                    (num, msg) = e.args
                    if num != ERR_NO_SUCH_OBJECT:
                        raise

    def _test_base_search_failing(self):
        pattern = 'missing%d' + self.ou
        for i in range(4000):
            try:
                self.ldb.search(pattern % i, scope=SCOPE_BASE, attrs=['cn'])
            except LdbError as e:
                (num, msg) = e
                if num != ERR_NO_SUCH_OBJECT:
                    raise

    def search_expression_list(self,
                               expressions,
                               rounds,
                               attrs=['cn'],
                               scope=SCOPE_SUBTREE):
        for expression in expressions:
            t = time.time()
            for i in range(rounds):
                self.ldb.search(self.ou,
                                expression=expression,
                                scope=SCOPE_SUBTREE,
                                attrs=['cn'])
            print('%d runs %s took %s' % (i, expression, time.time() - t),
                  file=sys.stderr)

    def _test_complex_search(self, n=100):
        classes = ['samaccountname', 'objectCategory', 'dn', 'member']
        values = ['*', '*t*', 'g*', 'user']
        comparators = ['=', '<=', '>=']  # '~=' causes error
        maybe_not = ['!(', '']
        joiners = ['&', '|']

        # The number of permuations is 18432, which is not huge but
        # would take hours to search. So we take a sample.
        all_permutations = list(
            itertools.product(joiners, classes, classes, values, values,
                              comparators, comparators, maybe_not, maybe_not))

        expressions = []

        for (j, c1, c2, v1, v2, o1, o2, n1,
             n2) in random.sample(all_permutations, n):
            expression = ''.join([
                '(', j, '(', n1, c1, o1, v1, '))' if n1 else ')', '(', n2, c2,
                o2, v2, '))' if n2 else ')', ')'
            ])
            expressions.append(expression)

        self.search_expression_list(expressions, 1)

    def _test_member_search(self, rounds=10):
        expressions = []
        for d in range(20):
            expressions.append('(member=cn=u%d,%s)' % (d + 500, self.ou_users))
            expressions.append('(member=u%d*)' % (d + 700, ))

        self.search_expression_list(expressions, rounds)

    def _test_memberof_search(self, rounds=200):
        expressions = []
        for i in range(min(self.state.n_groups, rounds)):
            expressions.append('(memberOf=cn=g%d,%s)' % (i, self.ou_groups))
            expressions.append('(memberOf=cn=g%d*)' % (i, ))
            expressions.append('(memberOf=cn=*%s*)' % self.ou_groups)

        self.search_expression_list(expressions, 2)

    def _test_add_many_users(self, n=BATCH_SIZE):
        s = self.state.next_user_id
        e = s + n
        self._add_users(s, e)
        self.state.next_user_id = e

    def _test_add_many_users_ldif(self, n=BATCH_SIZE):
        s = self.state.next_user_id
        e = s + n
        self._add_users_ldif(s, e)
        self.state.next_user_id = e

    def _link_user_and_group(self, u, g):
        link = (u, g)
        if link in self.state.active_links:
            return False

        m = Message()
        m.dn = Dn(self.ldb, "CN=g%d,%s" % (g, self.ou_groups))
        m["member"] = MessageElement("cn=u%d,%s" % (u, self.ou_users),
                                     FLAG_MOD_ADD, "member")
        self.ldb.modify(m)
        self.state.active_links.add(link)
        return True

    def _unlink_user_and_group(self, u, g):
        link = (u, g)
        if link not in self.state.active_links:
            return False

        user = "******" % (u, self.ou_users)
        group = "CN=g%d,%s" % (g, self.ou_groups)
        m = Message()
        m.dn = Dn(self.ldb, group)
        m["member"] = MessageElement(user, FLAG_MOD_DELETE, "member")
        self.ldb.modify(m)
        self.state.active_links.remove(link)
        return True

    def _test_link_many_users(self, n=LINK_BATCH_SIZE):
        # this links unevenly, putting more users in the first group
        # and fewer in the last.
        ng = self.state.n_groups
        nu = self.state.next_user_id
        while n:
            u = random.randrange(nu)
            g = random.randrange(random.randrange(ng) + 1)
            if self._link_user_and_group(u, g):
                n -= 1

    def _test_link_many_users_batch(self, n=(LINK_BATCH_SIZE * 10)):
        # this links unevenly, putting more users in the first group
        # and fewer in the last.
        ng = self.state.n_groups
        nu = self.state.next_user_id
        messages = []
        for g in range(ng):
            m = Message()
            m.dn = Dn(self.ldb, "CN=g%d,%s" % (g, self.ou_groups))
            messages.append(m)

        while n:
            u = random.randrange(nu)
            g = random.randrange(random.randrange(ng) + 1)
            link = (u, g)
            if link in self.state.active_links:
                continue
            m = messages[g]
            m["member%s" % u] = MessageElement(
                "cn=u%d,%s" % (u, self.ou_users), FLAG_MOD_ADD, "member")
            self.state.active_links.add(link)
            n -= 1

        for m in messages:
            try:
                self.ldb.modify(m)
            except LdbError as e:
                print(e)
                print(m)

    def _test_remove_some_links(self, n=(LINK_BATCH_SIZE // 2)):
        victims = random.sample(list(self.state.active_links), n)
        for x in victims:
            self._unlink_user_and_group(*x)

    test_00_11_join_empty_dc = _test_join

    test_00_12_adding_users_2000 = _test_add_many_users

    test_00_20_join_unlinked_2k_users = _test_join
    test_00_21_unindexed_search_2k_users = _test_unindexed_search
    test_00_22_indexed_search_2k_users = _test_indexed_search

    test_00_23_complex_search_2k_users = _test_complex_search
    test_00_24_member_search_2k_users = _test_member_search
    test_00_25_memberof_search_2k_users = _test_memberof_search

    test_00_27_base_search_2k_users = _test_base_search
    test_00_28_base_search_failing_2k_users = _test_base_search_failing

    test_01_01_link_2k_users = _test_link_many_users
    test_01_02_link_2k_users_batch = _test_link_many_users_batch

    test_02_10_join_2k_linked_dc = _test_join
    test_02_11_unindexed_search_2k_linked_dc = _test_unindexed_search
    test_02_12_indexed_search_2k_linked_dc = _test_indexed_search

    test_04_01_remove_some_links_2k = _test_remove_some_links

    test_05_01_adding_users_after_links_4k_ldif = _test_add_many_users_ldif

    test_06_04_link_users_4k = _test_link_many_users
    test_06_05_link_users_4k_batch = _test_link_many_users_batch

    test_07_01_adding_users_after_links_6k = _test_add_many_users

    def _test_ldif_well_linked_group(self, link_chance=1.0):
        g = self.state.n_groups
        self.state.n_groups += 1
        lines = ["dn: CN=g%d,%s" % (g, self.ou_groups), "objectclass: group"]

        for i in range(self.state.next_user_id):
            if random.random() <= link_chance:
                lines.append("member: cn=u%d,%s" % (i, self.ou_users))
                self.state.active_links.add((i, g))

        lines.append("")
        self.ldb.add_ldif('\n'.join(lines))

    test_09_01_add_fully_linked_group = _test_ldif_well_linked_group

    def test_09_02_add_exponentially_diminishing_linked_groups(self):
        linkage = 0.8
        while linkage > 0.01:
            self._test_ldif_well_linked_group(linkage)
            linkage *= 0.75

    test_09_04_link_users_6k = _test_link_many_users

    test_10_01_unindexed_search_6k_users = _test_unindexed_search
    test_10_02_indexed_search_6k_users = _test_indexed_search

    test_10_27_base_search_6k_users = _test_base_search
    test_10_28_base_search_failing_6k_users = _test_base_search_failing

    def test_10_03_complex_search_6k_users(self):
        self._test_complex_search(n=50)

    def test_10_04_member_search_6k_users(self):
        self._test_member_search(rounds=1)

    def test_10_05_memberof_search_6k_users(self):
        self._test_memberof_search(rounds=5)

    test_11_02_join_full_dc = _test_join

    test_12_01_remove_some_links_6k = _test_remove_some_links

    def _test_delete_many_users(self, n=DELETE_BATCH_SIZE):
        e = self.state.next_user_id
        s = max(0, e - n)
        self.state.next_user_id = s
        for i in range(s, e):
            self.ldb.delete("cn=u%d,%s" % (i, self.ou_users))

        for x in tuple(self.state.active_links):
            if s >= x[0] > e:
                self.state.active_links.remove(x)

    test_20_01_delete_users_6k = _test_delete_many_users

    def test_21_01_delete_10_groups(self):
        for i in range(self.state.n_groups - 10, self.state.n_groups):
            self.ldb.delete("cn=g%d,%s" % (i, self.ou_groups))
        self.state.n_groups -= 10
        for x in tuple(self.state.active_links):
            if x[1] >= self.state.n_groups:
                self.state.active_links.remove(x)

    test_21_02_delete_users_5950 = _test_delete_many_users

    def test_22_01_delete_all_groups(self):
        for i in range(self.state.n_groups):
            self.ldb.delete("cn=g%d,%s" % (i, self.ou_groups))
        self.state.n_groups = 0
        self.state.active_links = set()

    # XXX assert the state is as we think, using searches

    def test_23_01_delete_users_5900_after_groups(self):
        # we do not delete everything because it takes too long
        n = 4 * DELETE_BATCH_SIZE
        self._test_delete_many_users(n=n)

    test_24_02_join_after_partial_cleanup = _test_join
Example #12
0
class UserTests(samba.tests.TestCase):
    def add_if_possible(self, *args, **kwargs):
        """In these tests sometimes things are left in the database
        deliberately, so we don't worry if we fail to add them a second
        time."""
        try:
            self.ldb.add(*args, **kwargs)
        except LdbError:
            pass

    def setUp(self):
        super(UserTests, self).setUp()
        self.state = GlobalState  # the class itself, not an instance
        self.lp = lp
        self.ldb = SamDB(host,
                         credentials=creds,
                         session_info=system_session(lp),
                         lp=lp)
        self.base_dn = self.ldb.domain_dn()
        self.ou = "OU=pid%s,%s" % (os.getpid(), self.base_dn)
        self.ou_users = "OU=users,%s" % self.ou
        self.ou_groups = "OU=groups,%s" % self.ou
        self.ou_computers = "OU=computers,%s" % self.ou

        self.state.test_number += 1
        random.seed(self.state.test_number)

    def tearDown(self):
        super(UserTests, self).tearDown()

    def test_00_00_do_nothing(self):
        # this gives us an idea of the overhead
        pass

    def test_00_01_do_nothing_relevant(self):
        # takes around 1 second on i7-4770
        j = 0
        for i in range(30000000):
            j += i

    def test_00_02_do_nothing_sleepily(self):
        time.sleep(1)

    def test_00_03_add_ous_and_groups(self):
        # initialise the database
        for dn in (self.ou, self.ou_users, self.ou_groups, self.ou_computers):
            self.ldb.add({"dn": dn, "objectclass": "organizationalUnit"})

        for i in range(N_GROUPS):
            self.ldb.add({
                "dn": "cn=g%d,%s" % (i, self.ou_groups),
                "objectclass": "group"
            })

        self.state.n_groups = N_GROUPS

    def _add_users(self, start, end):
        for i in range(start, end):
            self.ldb.add({
                "dn": "cn=u%d,%s" % (i, self.ou_users),
                "objectclass": "user"
            })

    def _add_users_ldif(self, start, end):
        lines = []
        for i in range(start, end):
            lines.append("dn: cn=u%d,%s" % (i, self.ou_users))
            lines.append("objectclass: user")
            lines.append("")
        self.ldb.add_ldif('\n'.join(lines))

    def _test_join(self):
        tmpdir = tempfile.mkdtemp()
        if '://' in host:
            server = host.split('://', 1)[1]
        else:
            server = host
        cmd = cmd_sambatool.subcommands['domain'].subcommands['join']
        result = cmd._run(
            "samba-tool domain join", creds.get_realm(), "dc",
            "-U%s%%%s" % (creds.get_username(), creds.get_password()),
            '--targetdir=%s' % tmpdir, '--server=%s' % server)

        shutil.rmtree(tmpdir)

    def _test_unindexed_search(self):
        expressions = [(
            '(&(objectclass=user)(description='
            'Built-in account for adminstering the computer/domain))'
        ), '(description=Built-in account for adminstering the computer/domain)',
                       '(objectCategory=*)', '(samaccountname=Administrator*)']
        for expression in expressions:
            t = time.time()
            for i in range(25):
                self.ldb.search(self.ou,
                                expression=expression,
                                scope=SCOPE_SUBTREE,
                                attrs=['cn'])
            print >> sys.stderr, '%d %s took %s' % (i, expression,
                                                    time.time() - t)

    def _test_indexed_search(self):
        expressions = ['(objectclass=group)', '(samaccountname=Administrator)']
        for expression in expressions:
            t = time.time()
            for i in range(4000):
                self.ldb.search(self.ou,
                                expression=expression,
                                scope=SCOPE_SUBTREE,
                                attrs=['cn'])
            print >> sys.stderr, '%d runs %s took %s' % (i, expression,
                                                         time.time() - t)

    def _test_base_search(self):
        for dn in [
                self.base_dn, self.ou, self.ou_users, self.ou_groups,
                self.ou_computers
        ]:
            for i in range(4000):
                try:
                    self.ldb.search(dn, scope=SCOPE_BASE, attrs=['cn'])
                except LdbError as (num, msg):
                    if num != 32:
                        raise
Example #13
0
class UserTests(samba.tests.TestCase):

    def add_if_possible(self, *args, **kwargs):
        """In these tests sometimes things are left in the database
        deliberately, so we don't worry if we fail to add them a second
        time."""
        try:
            self.ldb.add(*args, **kwargs)
        except LdbError:
            pass

    def setUp(self):
        super(UserTests, self).setUp()
        self.state = GlobalState  # the class itself, not an instance
        self.lp = lp
        self.ldb = SamDB(host, credentials=creds,
                         session_info=system_session(lp), lp=lp)
        self.base_dn = self.ldb.domain_dn()
        self.ou = "OU=pid%s,%s" % (os.getpid(), self.base_dn)
        self.ou_users = "OU=users,%s" % self.ou
        self.ou_groups = "OU=groups,%s" % self.ou
        self.ou_computers = "OU=computers,%s" % self.ou

        for dn in (self.ou, self.ou_users, self.ou_groups,
                   self.ou_computers):
            self.add_if_possible({
                "dn": dn,
                "objectclass": "organizationalUnit"})

    def tearDown(self):
        super(UserTests, self).tearDown()

    def test_00_00_do_nothing(self):
        # this gives us an idea of the overhead
        pass

    def _prepare_n_groups(self, n):
        self.state.n_groups = n
        for i in range(n):
            self.add_if_possible({
                "dn": "cn=g%d,%s" % (i, self.ou_groups),
                "objectclass": "group"})

    def _add_users(self, start, end):
        for i in range(start, end):
            self.ldb.add({
                "dn": "cn=u%d,%s" % (i, self.ou_users),
                "objectclass": "user"})

    def _add_users_ldif(self, start, end):
        lines = []
        for i in range(start, end):
            lines.append("dn: cn=u%d,%s" % (i, self.ou_users))
            lines.append("objectclass: user")
            lines.append("")
        self.ldb.add_ldif('\n'.join(lines))

    def _test_unindexed_search(self):
        expressions = [
            ('(&(objectclass=user)(description='
             'Built-in account for adminstering the computer/domain))'),
            '(description=Built-in account for adminstering the computer/domain)',
            '(objectCategory=*)',
            '(samaccountname=Administrator*)'
        ]
        for expression in expressions:
            t = time.time()
            for i in range(50):
                self.ldb.search(self.ou,
                                expression=expression,
                                scope=SCOPE_SUBTREE,
                                attrs=['cn'])
            print >> sys.stderr, '%d %s took %s' % (i, expression,
                                                    time.time() - t)

    def _test_indexed_search(self):
        expressions = ['(objectclass=group)',
                       '(samaccountname=Administrator)'
        ]
        for expression in expressions:
            t = time.time()
            for i in range(10000):
                self.ldb.search(self.ou,
                                expression=expression,
                                scope=SCOPE_SUBTREE,
                                attrs=['cn'])
            print >> sys.stderr, '%d runs %s took %s' % (i, expression,
                                                         time.time() - t)

    def _test_complex_search(self):
        classes = ['samaccountname', 'objectCategory', 'dn', 'member']
        values = ['*', '*t*', 'g*', 'user']
        comparators = ['=', '<=', '>='] # '~=' causes error
        maybe_not = ['!(', '']
        joiners = ['&', '|']

        # The number of permuations is 18432, which is not huge but
        # would take hours to search. So we take a sample.
        all_permutations = list(itertools.product(joiners,
                                                  classes, classes,
                                                  values, values,
                                                  comparators, comparators,
                                                  maybe_not, maybe_not))
        random.seed(1)



        for (j, c1, c2, v1, v2,
             o1, o2, n1, n2) in random.sample(all_permutations, 100):
            expression = ''.join(['(', j,
                                  '(', n1, c1, o1, v1,
                                  '))' if n1 else ')',
                                  '(', n2, c2, o2, v2,
                                  '))' if n2 else ')',
                                  ')'])
            print expression
            self.ldb.search(self.ou,
                            expression=expression,
                            scope=SCOPE_SUBTREE,
                            attrs=['cn'])

    def _test_member_search(self, rounds=10):
        expressions = []
        for d in range(50):
            expressions.append('(member=cn=u%d,%s)' % (d + 500, self.ou_users))
            expressions.append('(member=u%d*)' % (d + 700,))
        for i in range(N_GROUPS):
            expressions.append('(memberOf=cn=g%d,%s)' % (i, self.ou_groups))
            expressions.append('(memberOf=cn=g%d*)' % (i,))
            expressions.append('(memberOf=cn=*%s*)' % self.ou_groups)

        for expression in expressions:
            t = time.time()
            for i in range(rounds):
                self.ldb.search(self.ou,
                                expression=expression,
                                scope=SCOPE_SUBTREE,
                                attrs=['cn'])
            print >> sys.stderr, '%d runs %s took %s' % (i, expression,
                                                         time.time() - t)

    def _test_add_many_users(self, n=BATCH_SIZE):
        s = self.state.next_user_id
        e = s + n
        self._add_users(s, e)
        self.state.next_user_id = e

    def _test_add_many_users_ldif(self, n=BATCH_SIZE):
        s = self.state.next_user_id
        e = s + n
        self._add_users_ldif(s, e)
        self.state.next_user_id = e

    def _link_user_and_group(self, u, g):
        m = Message()
        m.dn = Dn(self.ldb, "CN=g%d,%s" % (g, self.ou_groups))
        m["member"] = MessageElement("cn=u%d,%s" % (u, self.ou_users),
                                     FLAG_MOD_ADD, "member")
        self.ldb.modify(m)

    def _test_link_many_users(self, n=BATCH_SIZE):
        self._prepare_n_groups(N_GROUPS)
        s = self.state.next_linked_user
        e = s + n
        for i in range(s, e):
            # put everyone in group 0, and one other group
            g = i % (N_GROUPS - 1) + 1
            self._link_user_and_group(i, g)
            self._link_user_and_group(i, 0)
        self.state.next_linked_user = e


    test_00_01_adding_users_1000 = _test_add_many_users

    test_00_10_complex_search_1k_users = _test_complex_search
    test_00_11_unindexed_search_1k_users = _test_unindexed_search
    test_00_12_indexed_search_1k_users = _test_indexed_search
    test_00_13_member_search_1k_users = _test_member_search

    test_01_02_adding_users_2000_ldif = _test_add_many_users_ldif
    test_01_03_adding_users_3000 = _test_add_many_users

    test_01_10_complex_search_3k_users = _test_complex_search
    test_01_11_unindexed_search_3k_users = _test_unindexed_search
    test_01_12_indexed_search_3k_users = _test_indexed_search
    def test_01_13_member_search_3k_users(self):
        self._test_member_search(rounds=5)

    test_02_01_link_users_1000 = _test_link_many_users
    test_02_02_link_users_2000 = _test_link_many_users
    test_02_03_link_users_3000 = _test_link_many_users

    test_03_10_complex_search_linked_users = _test_complex_search
    test_03_11_unindexed_search_linked_users = _test_unindexed_search
    test_03_12_indexed_search_linked_users = _test_indexed_search
    def test_03_13_member_search_linked_users(self):
        self._test_member_search(rounds=2)
objectClass: nTDSSiteSettings
cn: NTDS Site Settings
showInAdvancedViewOnly: TRUE
name: NTDS Site Settings
objectCategory: CN=NTDS-Site-Settings,CN=Schema,CN=Configuration,%(samba4_ldap_base)s

dn: CN=Servers,CN=%(branchsite_name)s,CN=Sites,CN=Configuration,%(samba4_ldap_base)s
objectClass: serversContainer
cn: Servers
showInAdvancedViewOnly: TRUE
name: Servers
systemFlags: 33554432
objectCategory: CN=Servers-Container,CN=Schema,CN=Configuration,%(samba4_ldap_base)s
''' % ldif_dict

	samdb.add_ldif(site_add_ldif)
	print "created site %s" % opts.site

	if opts.sitelink and not opts.createsitelink:
		## and add it to the sitelink
		sitelink_modify_ldif='''
dn: CN=%(sitelink)s,CN=IP,CN=Inter-Site Transports,CN=Sites,CN=Configuration,%(samba4_ldap_base)s
changetype: modify
add: siteList
siteList: CN=%(branchsite_name)s,CN=Sites,CN=Configuration,%(samba4_ldap_base)s
''' % ldif_dict
		samdb.modify_ldif(sitelink_modify_ldif)
		print "added site %s to sitelink %s" % (opts.site, opts.sitelink)

elif opts.site:
	res = samdb.search("CN=Configuration,%s" % samba4_ldap_base, scope=ldb.SCOPE_SUBTREE, expression="(&(objectClass=site)(cn=%s))" % opts.site)