# Unix SMB/CIFS implementation. # # Tests for samba-tool contact management commands # # Copyright (C) Bjoern Baumbach 2019 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import os import ldb from samba.tests.samba_tool.base import SambaToolCmdTest class ContactCmdTestCase(SambaToolCmdTest): """Tests for samba-tool contact subcommands""" contacts = [] samdb = None def setUp(self): super().setUp() self.creds = "-U%s%%%s" % (os.environ["DC_USERNAME"], os.environ["DC_PASSWORD"]) self.samdb = self.getSamDB("-H", "ldap://%s" % os.environ["DC_SERVER"], self.creds) contact = None self.contacts = [] contact = self._randomContact({"expectedname": "contact1", "name": "contact1"}) self.contacts.append(contact) # No 'name' is given here, so the name will be made from givenname. contact = self._randomContact({"expectedname": "contact2", "givenName": "contact2"}) self.contacts.append(contact) contact = self._randomContact({"expectedname": "contact3", "name": "contact3", "displayName": "contact3displayname", "givenName": "not_contact3", "initials": "I", "sn": "not_contact3", "mobile": "12345"}) self.contacts.append(contact) # No 'name' is given here, so the name will be made from the the # sn, initials and givenName attributes. contact = self._randomContact({"expectedname": "James T. Kirk", "sn": "Kirk", "initials": "T", "givenName": "James"}) self.contacts.append(contact) # setup the 4 contacts and ensure they are correct for contact in self.contacts: (result, out, err) = self._create_contact(contact) self.assertCmdSuccess(result, out, err) self.assertNotIn( "ERROR", err, "There shouldn't be any error message") self.assertIn("Contact '%s' added successfully" % contact["expectedname"], out) found = self._find_contact(contact["expectedname"]) self.assertIsNotNone(found) contactname = contact["expectedname"] self.assertEqual("%s" % found.get("name"), contactname) self.assertEqual("%s" % found.get("description"), contact["description"]) def tearDown(self): super().tearDown() # clean up all the left over contacts, just in case for contact in self.contacts: if self._find_contact(contact["expectedname"]): (result, out, err) = self.runsubcmd( "contact", "delete", "%s" % contact["expectedname"]) self.assertCmdSuccess(result, out, err, "Failed to delete contact '%s'" % contact["expectedname"]) def test_newcontact(self): """This tests the "contact create" and "contact delete" commands""" # try to create all the contacts again, this should fail for contact in self.contacts: (result, out, err) = self._create_contact(contact) self.assertCmdFail(result, "Succeeded to create existing contact") self.assertIn("already exists", err) # try to delete all the contacts we just added for contact in self.contacts: (result, out, err) = self.runsubcmd("contact", "delete", "%s" % contact["expectedname"]) self.assertCmdSuccess(result, out, err, "Failed to delete contact '%s'" % contact["expectedname"]) found = self._find_contact(contact["expectedname"]) self.assertIsNone(found, "Deleted contact '%s' still exists" % contact["expectedname"]) # test creating contacts in an specified OU parentou = self._randomOU({"name": "testOU"}) (result, out, err) = self._create_ou(parentou) self.assertCmdSuccess(result, out, err) for contact in self.contacts: (result, out, err) = self._create_contact(contact, ou="OU=testOU") self.assertCmdSuccess(result, out, err) self.assertEqual(err, "", "There shouldn't be any error message") self.assertIn("Contact '%s' added successfully" % contact["expectedname"], out) found = self._find_contact(contact["expectedname"]) contactname = contact["expectedname"] self.assertEqual("%s" % found.get("name"), contactname) self.assertEqual("%s" % found.get("description"), contact["description"]) # try to delete all the contacts we just added, by DN for contact in self.contacts: expecteddn = ldb.Dn(self.samdb, "CN=%s,OU=%s,%s" % (contact["expectedname"], parentou["name"], self.samdb.domain_dn())) (result, out, err) = self.runsubcmd("contact", "delete", "%s" % expecteddn) self.assertCmdSuccess(result, out, err, "Failed to delete contact '%s'" % contact["expectedname"]) found = self._find_contact(contact["expectedname"]) self.assertIsNone(found, "Deleted contact '%s' still exists" % contact["expectedname"]) (result, out, err) = self.runsubcmd("ou", "delete", "OU=%s" % parentou["name"]) self.assertCmdSuccess(result, out, err, "Failed to delete ou '%s'" % parentou["name"]) # creating contacts, again for further tests for contact in self.contacts: (result, out, err) = self._create_contact(contact) self.assertCmdSuccess(result, out, err) self.assertEqual(err, "", "There shouldn't be any error message") self.assertIn("Contact '%s' added successfully" % contact["expectedname"], out) found = self._find_contact(contact["expectedname"]) contactname = contact["expectedname"] self.assertEqual("%s" % found.get("name"), contactname) self.assertEqual("%s" % found.get("description"), contact["description"]) def test_list(self): (result, out, err) = self.runsubcmd("contact", "list") self.assertCmdSuccess(result, out, err, "Error running list") search_filter = "(objectClass=contact)" contactlist = self.samdb.search(base=self.samdb.domain_dn(), scope=ldb.SCOPE_SUBTREE, expression=search_filter, attrs=["name"]) self.assertTrue(len(contactlist) > 0, "no contacts found in samdb") for contactobj in contactlist: name = contactobj.get("name", idx=0) self.assertMatch(out, str(name), "contact '%s' not found" % name) def test_list_full_dn(self): (result, out, err) = self.runsubcmd("contact", "list", "--full-dn") self.assertCmdSuccess(result, out, err, "Error running list") search_filter = "(objectClass=contact)" contactlist = self.samdb.search(base=self.samdb.domain_dn(), scope=ldb.SCOPE_SUBTREE, expression=search_filter, attrs=["dn"]) self.assertTrue(len(contactlist) > 0, "no contacts found in samdb") for contactobj in contactlist: self.assertMatch(out, str(contactobj.dn), "contact '%s' not found" % str(contactobj.dn)) def test_list_base_dn(self): base_dn = str(self.samdb.domain_dn()) (result, out, err) = self.runsubcmd("contact", "list", "-b", base_dn) self.assertCmdSuccess(result, out, err, "Error running list") search_filter = "(objectClass=contact)" contactlist = self.samdb.search(base=base_dn, scope=ldb.SCOPE_SUBTREE, expression=search_filter, attrs=["name"]) self.assertTrue(len(contactlist) > 0, "no contacts found in samdb") for contactobj in contactlist: name = contactobj.get("name", idx=0) self.assertMatch(out, str(name), "contact '%s' not found" % name) def test_move(self): parentou = self._randomOU({"name": "parentOU"}) (result, out, err) = self._create_ou(parentou) self.assertCmdSuccess(result, out, err) for contact in self.contacts: olddn = self._find_contact(contact["expectedname"]).get("dn") (result, out, err) = self.runsubcmd("contact", "move", "%s" % contact["expectedname"], "OU=%s" % parentou["name"]) self.assertCmdSuccess(result, out, err, "Failed to move contact '%s'" % contact["expectedname"]) self.assertEqual(err, "", "There shouldn't be any error message") self.assertIn('Moved contact "%s"' % contact["expectedname"], out) found = self._find_contact(contact["expectedname"]) self.assertNotEqual(found.get("dn"), olddn, ("Moved contact '%s' still exists with the " "same dn" % contact["expectedname"])) contactname = contact["expectedname"] newexpecteddn = ldb.Dn(self.samdb, "CN=%s,OU=%s,%s" % (contactname, parentou["name"], self.samdb.domain_dn())) self.assertEqual(found.get("dn"), newexpecteddn, "Moved contact '%s' does not exist" % contact["expectedname"]) (result, out, err) = self.runsubcmd("contact", "move", "%s" % contact["expectedname"], "%s" % olddn.parent()) self.assertCmdSuccess(result, out, err, "Failed to move contact '%s'" % contact["expectedname"]) (result, out, err) = self.runsubcmd("ou", "delete", "OU=%s" % parentou["name"]) self.assertCmdSuccess(result, out, err, "Failed to delete ou '%s'" % parentou["name"]) def test_rename_givenname_initials_surname(self): """rename and remove given name, initials and surname for all contacts""" for contact in self.contacts: name = contact["name"] if "name" in contact else contact["expectedname"] new_givenname = "new_given_name_of_" + name new_initials = "A" new_surname = "new_surname_of_" + name new_cn = "new_cn_of_" + name expected_cn = "%s %s. %s" % (new_givenname, new_initials, new_surname) # rename given name, initials and surname (result, out, err) = self.runsubcmd("contact", "rename", name, "--reset-cn", "--surname=%s" % new_surname, "--initials=%s" % new_initials, "--given-name=%s" % new_givenname) self.assertCmdSuccess(result, out, err) self.assertEqual(err, "", "Shouldn't be any error messages") self.assertIn('successfully', out) found = self._find_contact(expected_cn) self.assertEqual("%s" % found.get("givenName"), new_givenname) self.assertEqual("%s" % found.get("initials"), new_initials) self.assertEqual("%s" % found.get("sn"), new_surname) self.assertEqual("%s" % found.get("name"), expected_cn) self.assertEqual("%s" % found.get("cn"), expected_cn) # remove given name, initials and surname # (must force new cn, because en empty new CN throws an error) (result, out, err) = self.runsubcmd("contact", "rename", expected_cn, "--force-new-cn=%s" % expected_cn, "--surname=", "--initials=", "--given-name=") self.assertCmdSuccess(result, out, err) self.assertEqual(err, "", "Shouldn't be any error messages") self.assertIn('successfully', out) found = self._find_contact(expected_cn) self.assertEqual(found.get("givenName"), None) self.assertEqual(found.get("initials"), None) self.assertEqual(found.get("sn"), None) # reset changes (initials are already removed) old_surname = contact["sn"] if "sn" in contact else "" old_initials = contact["initials"] if "initials" in contact else "" old_givenname = contact["givenName"] if "givenName" in contact else "" old_cn = contact["cn"] if "cn" in contact else name (result, out, err) = self.runsubcmd("contact", "rename", expected_cn, "--force-new-cn=%s" % old_cn, "--surname=%s" % old_surname, "--initials=%s" % old_initials, "--given-name=%s" % old_givenname) self.assertCmdSuccess(result, out, err) def test_rename_cn(self): """rename and try to remove the cn of all contacts""" for contact in self.contacts: name = contact["name"] if "name" in contact else contact["expectedname"] new_cn = "new_cn_of_" + name # rename cn (result, out, err) = self.runsubcmd("contact", "rename", name, "--force-new-cn=%s" % new_cn) self.assertCmdSuccess(result, out, err) self.assertEqual(err, "", "Shouldn't be any error messages") self.assertIn('successfully', out) found = self._find_contact(new_cn) self.assertEqual("%s" % found.get("cn"), new_cn) # trying to remove cn (throws an error) (result, out, err) = self.runsubcmd("contact", "rename", new_cn, "--force-new-cn=") self.assertCmdFail(result) self.assertIn('Failed to rename contact', err) self.assertIn("delete protected attribute", err) # reset changes (cn must be the name) (result, out, err) = self.runsubcmd("contact", "rename", new_cn, "--force-new-cn=%s" % name) self.assertCmdSuccess(result, out, err) def test_rename_mailaddress_displayname(self): """rename and remove the mail and the displayname attribute of all contacts""" for contact in self.contacts: name = contact["name"] if "name" in contact else contact["expectedname"] new_mail = "new_mailaddress_of_" + name new_displayname = "new displayname of " + name # change mail and displayname (result, out, err) = self.runsubcmd("contact", "rename", name, "--mail-address=%s" % new_mail, "--display-name=%s" % new_displayname) self.assertCmdSuccess(result, out, err) self.assertEqual(err, "", "Shouldn't be any error messages") self.assertIn('successfully', out) found = self._find_contact(name) self.assertEqual("%s" % found.get("mail"), new_mail) self.assertEqual("%s" % found.get("displayName"), new_displayname) # remove mail and displayname (result, out, err) = self.runsubcmd("contact", "rename", name, "--mail-address=", "--display-name=") self.assertCmdSuccess(result, out, err) self.assertEqual(err, "", "Shouldn't be any error messages") self.assertIn('successfully', out) found = self._find_contact(name) self.assertEqual(found.get("mail"), None) self.assertEqual(found.get("displayName"), None) # reset changes old_mail = contact["givenName"] if "givenName" in contact else "" old_displayname = contact["cn"] if "cn" in contact else "" (result, out, err) = self.runsubcmd("contact", "rename", name, "--mail-address=%s" % old_mail, "--display-name=%s" % old_displayname) self.assertCmdSuccess(result, out, err) def _randomContact(self, base=None): """Create a contact with random attribute values, you can specify base attributes""" if base is None: base = {} # No name attributes are given here, because the object name will # be made from the sn, givenName and initials attributes, if no name # is given. contact = { "description": self.randomName(count=100), } contact.update(base) return contact def _randomOU(self, base=None): """Create an ou with random attribute values, you can specify base attributes.""" if base is None: base = {} ou = { "name": self.randomName(), "description": self.randomName(count=100), } ou.update(base) return ou def _create_contact(self, contact, ou=None): args = "" if "name" in contact: args += '{0}'.format(contact['name']) args += ' {0}'.format(self.creds) if ou is not None: args += ' --ou={0}'.format(ou) if "description" in contact: args += ' --description={0}'.format(contact["description"]) if "sn" in contact: args += ' --surname={0}'.format(contact["sn"]) if "initials" in contact: args += ' --initials={0}'.format(contact["initials"]) if "givenName" in contact: args += ' --given-name={0}'.format(contact["givenName"]) if "displayName" in contact: args += ' --display-name={0}'.format(contact["displayName"]) if "mobile" in contact: args += ' --mobile-number={0}'.format(contact["mobile"]) args = args.split() return self.runsubcmd('contact', 'create', *args) def _create_ou(self, ou): return self.runsubcmd("ou", "create", "OU=%s" % ou["name"], "--description=%s" % ou["description"]) def _find_contact(self, name): contactname = name search_filter = ("(&(objectClass=contact)(name=%s))" % ldb.binary_encode(contactname)) contactlist = self.samdb.search(base=self.samdb.domain_dn(), scope=ldb.SCOPE_SUBTREE, expression=search_filter, attrs=[]) if contactlist: return contactlist[0] else: return None