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/>.
23 from samba
.tests
.samba_tool
.base
import SambaToolCmdTest
25 class ContactCmdTestCase(SambaToolCmdTest
):
26 """Tests for samba-tool contact subcommands"""
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"],
40 contact
= self
._randomContact
({"expectedname": "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",
51 "displayName": "contact3displayname",
52 "givenName": "not_contact3",
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",
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
)
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"])
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
,
140 (contact
["expectedname"],
142 self
.samdb
.domain_dn()))
143 (result
, out
, err
) = self
.runsubcmd("contact", "delete", "%s" %
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"])
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
,
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
,
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",
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
,
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
)
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
,
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
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
,
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
,
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
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
,
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
,
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
,
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)
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
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
405 "description": self
.randomName(count
=100),
410 def _randomOU(self
, base
=None):
411 """Create an ou with random attribute values, you can specify base
417 "name": self
.randomName(),
418 "description": self
.randomName(count
=100),
423 def _create_contact(self
, contact
, ou
=None):
426 if "name" in contact
:
427 args
+= '{0}'.format(contact
['name'])
429 args
+= ' {0}'.format(self
.creds
)
432 args
+= ' --ou={0}'.format(ou
)
434 if "description" in contact
:
435 args
+= ' --description={0}'.format(contact
["description"])
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"])
449 return self
.runsubcmd('contact', 'create', *args
)
451 def _create_ou(self
, ou
):
452 return self
.runsubcmd("ou",
454 "OU=%s" % ou
["name"],
455 "--description=%s" % ou
["description"])
457 def _find_contact(self
, 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
,
466 return contactlist
[0]