ctdb-scripts: Improve update and listing code
[samba4-gss.git] / python / samba / tests / samba_tool / contact.py
blob39e962316923fc290d304dc7eef93f1354d1d539
1 # Unix SMB/CIFS implementation.
3 # Tests for samba-tool contact management commands
5 # Copyright (C) Bjoern Baumbach <bbaumbach@samba.org> 2019
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 import os
22 import ldb
23 from samba.tests.samba_tool.base import SambaToolCmdTest
25 class ContactCmdTestCase(SambaToolCmdTest):
26 """Tests for samba-tool contact subcommands"""
27 contacts = []
28 samdb = None
30 def setUp(self):
31 super().setUp()
32 self.creds = "-U%s%%%s" % (os.environ["DC_USERNAME"],
33 os.environ["DC_PASSWORD"])
34 self.samdb = self.getSamDB("-H",
35 "ldap://%s" % os.environ["DC_SERVER"],
36 self.creds)
37 contact = None
38 self.contacts = []
40 contact = self._randomContact({"expectedname": "contact1",
41 "name": "contact1"})
42 self.contacts.append(contact)
44 # No 'name' is given here, so the name will be made from givenname.
45 contact = self._randomContact({"expectedname": "contact2",
46 "givenName": "contact2"})
47 self.contacts.append(contact)
49 contact = self._randomContact({"expectedname": "contact3",
50 "name": "contact3",
51 "displayName": "contact3displayname",
52 "givenName": "not_contact3",
53 "initials": "I",
54 "sn": "not_contact3",
55 "mobile": "12345"})
56 self.contacts.append(contact)
58 # No 'name' is given here, so the name will be made from the
59 # sn, initials and givenName attributes.
60 contact = self._randomContact({"expectedname": "James T. Kirk",
61 "sn": "Kirk",
62 "initials": "T",
63 "givenName": "James"})
64 self.contacts.append(contact)
66 # setup the 4 contacts and ensure they are correct
67 for contact in self.contacts:
68 (result, out, err) = self._create_contact(contact)
70 self.assertCmdSuccess(result, out, err)
71 self.assertNotIn(
72 "ERROR", err, "There shouldn't be any error message")
73 self.assertIn("Contact '%s' added successfully" %
74 contact["expectedname"], out)
76 found = self._find_contact(contact["expectedname"])
78 self.assertIsNotNone(found)
80 contactname = contact["expectedname"]
81 self.assertEqual("%s" % found.get("name"), contactname)
82 self.assertEqual("%s" % found.get("description"),
83 contact["description"])
85 def tearDown(self):
86 super().tearDown()
87 # clean up all the left over contacts, just in case
88 for contact in self.contacts:
89 if self._find_contact(contact["expectedname"]):
90 (result, out, err) = self.runsubcmd(
91 "contact", "delete", "%s" % contact["expectedname"])
92 self.assertCmdSuccess(result, out, err,
93 "Failed to delete contact '%s'" %
94 contact["expectedname"])
96 def test_newcontact(self):
97 """This tests the "contact create" and "contact delete" commands"""
98 # try to create all the contacts again, this should fail
99 for contact in self.contacts:
100 (result, out, err) = self._create_contact(contact)
101 self.assertCmdFail(result, "Succeeded to create existing contact")
102 self.assertIn("already exists", err)
104 # try to delete all the contacts we just added
105 for contact in self.contacts:
106 (result, out, err) = self.runsubcmd("contact", "delete", "%s" %
107 contact["expectedname"])
108 self.assertCmdSuccess(result, out, err,
109 "Failed to delete contact '%s'" %
110 contact["expectedname"])
111 found = self._find_contact(contact["expectedname"])
112 self.assertIsNone(found,
113 "Deleted contact '%s' still exists" %
114 contact["expectedname"])
116 # test creating contacts in an specified OU
117 parentou = self._randomOU({"name": "testOU"})
118 (result, out, err) = self._create_ou(parentou)
119 self.assertCmdSuccess(result, out, err)
121 for contact in self.contacts:
122 (result, out, err) = self._create_contact(contact, ou="OU=testOU")
124 self.assertCmdSuccess(result, out, err)
125 self.assertEqual(err, "", "There shouldn't be any error message")
126 self.assertIn("Contact '%s' added successfully" %
127 contact["expectedname"], out)
129 found = self._find_contact(contact["expectedname"])
131 contactname = contact["expectedname"]
132 self.assertEqual("%s" % found.get("name"), contactname)
133 self.assertEqual("%s" % found.get("description"),
134 contact["description"])
136 # try to delete all the contacts we just added, by DN
137 for contact in self.contacts:
138 expecteddn = ldb.Dn(self.samdb,
139 "CN=%s,OU=%s,%s" %
140 (contact["expectedname"],
141 parentou["name"],
142 self.samdb.domain_dn()))
143 (result, out, err) = self.runsubcmd("contact", "delete", "%s" %
144 expecteddn)
145 self.assertCmdSuccess(result, out, err,
146 "Failed to delete contact '%s'" %
147 contact["expectedname"])
148 found = self._find_contact(contact["expectedname"])
149 self.assertIsNone(found,
150 "Deleted contact '%s' still exists" %
151 contact["expectedname"])
153 (result, out, err) = self.runsubcmd("ou", "delete",
154 "OU=%s" % parentou["name"])
155 self.assertCmdSuccess(result, out, err,
156 "Failed to delete ou '%s'" % parentou["name"])
158 # creating contacts, again for further tests
159 for contact in self.contacts:
160 (result, out, err) = self._create_contact(contact)
162 self.assertCmdSuccess(result, out, err)
163 self.assertEqual(err, "", "There shouldn't be any error message")
164 self.assertIn("Contact '%s' added successfully" %
165 contact["expectedname"], out)
167 found = self._find_contact(contact["expectedname"])
169 contactname = contact["expectedname"]
170 self.assertEqual("%s" % found.get("name"), contactname)
171 self.assertEqual("%s" % found.get("description"),
172 contact["description"])
174 def test_list(self):
175 (result, out, err) = self.runsubcmd("contact", "list")
176 self.assertCmdSuccess(result, out, err, "Error running list")
178 search_filter = "(objectClass=contact)"
179 contactlist = self.samdb.search(base=self.samdb.domain_dn(),
180 scope=ldb.SCOPE_SUBTREE,
181 expression=search_filter,
182 attrs=["name"])
184 self.assertTrue(len(contactlist) > 0, "no contacts found in samdb")
186 for contactobj in contactlist:
187 name = contactobj.get("name", idx=0)
188 self.assertMatch(out, str(name),
189 "contact '%s' not found" % name)
191 def test_list_full_dn(self):
192 (result, out, err) = self.runsubcmd("contact", "list", "--full-dn")
193 self.assertCmdSuccess(result, out, err, "Error running list")
195 search_filter = "(objectClass=contact)"
196 contactlist = self.samdb.search(base=self.samdb.domain_dn(),
197 scope=ldb.SCOPE_SUBTREE,
198 expression=search_filter,
199 attrs=["dn"])
201 self.assertTrue(len(contactlist) > 0, "no contacts found in samdb")
203 for contactobj in contactlist:
204 self.assertMatch(out, str(contactobj.dn),
205 "contact '%s' not found" % str(contactobj.dn))
207 def test_list_base_dn(self):
208 base_dn = str(self.samdb.domain_dn())
209 (result, out, err) = self.runsubcmd("contact", "list",
210 "-b", base_dn)
211 self.assertCmdSuccess(result, out, err, "Error running list")
213 search_filter = "(objectClass=contact)"
214 contactlist = self.samdb.search(base=base_dn,
215 scope=ldb.SCOPE_SUBTREE,
216 expression=search_filter,
217 attrs=["name"])
219 self.assertTrue(len(contactlist) > 0, "no contacts found in samdb")
221 for contactobj in contactlist:
222 name = contactobj.get("name", idx=0)
223 self.assertMatch(out, str(name),
224 "contact '%s' not found" % name)
226 def test_move(self):
227 parentou = self._randomOU({"name": "parentOU"})
228 (result, out, err) = self._create_ou(parentou)
229 self.assertCmdSuccess(result, out, err)
231 for contact in self.contacts:
232 olddn = self._find_contact(contact["expectedname"]).get("dn")
234 (result, out, err) = self.runsubcmd("contact", "move",
235 "%s" % contact["expectedname"],
236 "OU=%s" % parentou["name"])
237 self.assertCmdSuccess(result, out, err,
238 "Failed to move contact '%s'" %
239 contact["expectedname"])
240 self.assertEqual(err, "", "There shouldn't be any error message")
241 self.assertIn('Moved contact "%s"' % contact["expectedname"], out)
243 found = self._find_contact(contact["expectedname"])
244 self.assertNotEqual(found.get("dn"), olddn,
245 ("Moved contact '%s' still exists with the "
246 "same dn" % contact["expectedname"]))
247 contactname = contact["expectedname"]
248 newexpecteddn = ldb.Dn(self.samdb,
249 "CN=%s,OU=%s,%s" %
250 (contactname,
251 parentou["name"],
252 self.samdb.domain_dn()))
253 self.assertEqual(found.get("dn"), newexpecteddn,
254 "Moved contact '%s' does not exist" %
255 contact["expectedname"])
257 (result, out, err) = self.runsubcmd("contact", "move",
258 "%s" % contact["expectedname"],
259 "%s" % olddn.parent())
260 self.assertCmdSuccess(result, out, err,
261 "Failed to move contact '%s'" %
262 contact["expectedname"])
264 (result, out, err) = self.runsubcmd("ou", "delete",
265 "OU=%s" % parentou["name"])
266 self.assertCmdSuccess(result, out, err,
267 "Failed to delete ou '%s'" % parentou["name"])
269 def test_rename_givenname_initials_surname(self):
270 """rename and remove given name, initials and surname for all contacts"""
271 for contact in self.contacts:
272 name = contact["name"] if "name" in contact else contact["expectedname"]
274 new_givenname = "new_given_name_of_" + name
275 new_initials = "A"
276 new_surname = "new_surname_of_" + name
277 new_cn = "new_cn_of_" + name
278 expected_cn = "%s %s. %s" % (new_givenname, new_initials, new_surname)
280 # rename given name, initials and surname
281 (result, out, err) = self.runsubcmd("contact", "rename", name,
282 "--reset-cn",
283 "--surname=%s" % new_surname,
284 "--initials=%s" % new_initials,
285 "--given-name=%s" % new_givenname)
286 self.assertCmdSuccess(result, out, err)
287 self.assertEqual(err, "", "Shouldn't be any error messages")
288 self.assertIn('successfully', out)
290 found = self._find_contact(expected_cn)
291 self.assertEqual("%s" % found.get("givenName"), new_givenname)
292 self.assertEqual("%s" % found.get("initials"), new_initials)
293 self.assertEqual("%s" % found.get("sn"), new_surname)
294 self.assertEqual("%s" % found.get("name"), expected_cn)
295 self.assertEqual("%s" % found.get("cn"), expected_cn)
297 # remove given name, initials and surname
298 # (must force new cn, because en empty new CN throws an error)
299 (result, out, err) = self.runsubcmd("contact", "rename", expected_cn,
300 "--force-new-cn=%s" % expected_cn,
301 "--surname=",
302 "--initials=",
303 "--given-name=")
304 self.assertCmdSuccess(result, out, err)
305 self.assertEqual(err, "", "Shouldn't be any error messages")
306 self.assertIn('successfully', out)
308 found = self._find_contact(expected_cn)
309 self.assertEqual(found.get("givenName"), None)
310 self.assertEqual(found.get("initials"), None)
311 self.assertEqual(found.get("sn"), None)
313 # reset changes (initials are already removed)
314 old_surname = contact["sn"] if "sn" in contact else ""
315 old_initials = contact["initials"] if "initials" in contact else ""
316 old_givenname = contact["givenName"] if "givenName" in contact else ""
317 old_cn = contact["cn"] if "cn" in contact else name
318 (result, out, err) = self.runsubcmd("contact", "rename", expected_cn,
319 "--force-new-cn=%s" % old_cn,
320 "--surname=%s" % old_surname,
321 "--initials=%s" % old_initials,
322 "--given-name=%s" % old_givenname)
323 self.assertCmdSuccess(result, out, err)
325 def test_rename_cn(self):
326 """rename and try to remove the cn of all contacts"""
327 for contact in self.contacts:
328 name = contact["name"] if "name" in contact else contact["expectedname"]
329 new_cn = "new_cn_of_" + name
331 # rename cn
332 (result, out, err) = self.runsubcmd("contact", "rename", name,
333 "--force-new-cn=%s" % new_cn)
334 self.assertCmdSuccess(result, out, err)
335 self.assertEqual(err, "", "Shouldn't be any error messages")
336 self.assertIn('successfully', out)
338 found = self._find_contact(new_cn)
339 self.assertEqual("%s" % found.get("cn"), new_cn)
341 # trying to remove cn (throws an error)
342 (result, out, err) = self.runsubcmd("contact", "rename", new_cn,
343 "--force-new-cn=")
344 self.assertCmdFail(result)
345 self.assertIn('Failed to rename contact', err)
346 self.assertIn("delete protected attribute", err)
348 # reset changes (cn must be the name)
349 (result, out, err) = self.runsubcmd("contact", "rename", new_cn,
350 "--force-new-cn=%s" % name)
351 self.assertCmdSuccess(result, out, err)
354 def test_rename_mailaddress_displayname(self):
355 """rename and remove the mail and the displayname attribute of all contacts"""
356 for contact in self.contacts:
357 name = contact["name"] if "name" in contact else contact["expectedname"]
358 new_mail = "new_mailaddress_of_" + name
359 new_displayname = "new displayname of " + name
361 # change mail and displayname
362 (result, out, err) = self.runsubcmd("contact", "rename", name,
363 "--mail-address=%s"
364 % new_mail,
365 "--display-name=%s"
366 % new_displayname)
367 self.assertCmdSuccess(result, out, err)
368 self.assertEqual(err, "", "Shouldn't be any error messages")
369 self.assertIn('successfully', out)
371 found = self._find_contact(name)
372 self.assertEqual("%s" % found.get("mail"), new_mail)
373 self.assertEqual("%s" % found.get("displayName"), new_displayname)
375 # remove mail and displayname
376 (result, out, err) = self.runsubcmd("contact", "rename", name,
377 "--mail-address=",
378 "--display-name=")
379 self.assertCmdSuccess(result, out, err)
380 self.assertEqual(err, "", "Shouldn't be any error messages")
381 self.assertIn('successfully', out)
383 found = self._find_contact(name)
384 self.assertEqual(found.get("mail"), None)
385 self.assertEqual(found.get("displayName"), None)
387 # reset changes
388 old_mail = contact["givenName"] if "givenName" in contact else ""
389 old_displayname = contact["cn"] if "cn" in contact else ""
390 (result, out, err) = self.runsubcmd("contact", "rename", name,
391 "--mail-address=%s" % old_mail,
392 "--display-name=%s" % old_displayname)
393 self.assertCmdSuccess(result, out, err)
395 def _randomContact(self, base=None):
396 """Create a contact with random attribute values, you can specify base
397 attributes"""
398 if base is None:
399 base = {}
401 # No name attributes are given here, because the object name will
402 # be made from the sn, givenName and initials attributes, if no name
403 # is given.
404 contact = {
405 "description": self.randomName(count=100),
407 contact.update(base)
408 return contact
410 def _randomOU(self, base=None):
411 """Create an ou with random attribute values, you can specify base
412 attributes."""
413 if base is None:
414 base = {}
416 ou = {
417 "name": self.randomName(),
418 "description": self.randomName(count=100),
420 ou.update(base)
421 return ou
423 def _create_contact(self, contact, ou=None):
424 args = ""
426 if "name" in contact:
427 args += '{0}'.format(contact['name'])
429 args += ' {0}'.format(self.creds)
431 if ou is not None:
432 args += ' --ou={0}'.format(ou)
434 if "description" in contact:
435 args += ' --description={0}'.format(contact["description"])
436 if "sn" in contact:
437 args += ' --surname={0}'.format(contact["sn"])
438 if "initials" in contact:
439 args += ' --initials={0}'.format(contact["initials"])
440 if "givenName" in contact:
441 args += ' --given-name={0}'.format(contact["givenName"])
442 if "displayName" in contact:
443 args += ' --display-name={0}'.format(contact["displayName"])
444 if "mobile" in contact:
445 args += ' --mobile-number={0}'.format(contact["mobile"])
447 args = args.split()
449 return self.runsubcmd('contact', 'create', *args)
451 def _create_ou(self, ou):
452 return self.runsubcmd("ou",
453 "create",
454 "OU=%s" % ou["name"],
455 "--description=%s" % ou["description"])
457 def _find_contact(self, name):
458 contactname = name
459 search_filter = ("(&(objectClass=contact)(name=%s))" %
460 ldb.binary_encode(contactname))
461 contactlist = self.samdb.search(base=self.samdb.domain_dn(),
462 scope=ldb.SCOPE_SUBTREE,
463 expression=search_filter,
464 attrs=[])
465 if contactlist:
466 return contactlist[0]
467 else:
468 return None