test_user.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. # -*- coding: utf-8 -*-
  2. """
  3. tests for user state
  4. user absent
  5. user present
  6. user present with custom homedir
  7. """
  8. from __future__ import absolute_import, print_function, unicode_literals
  9. import os
  10. import sys
  11. from random import randint
  12. import pytest
  13. import salt.utils.files
  14. import salt.utils.platform
  15. from tests.support.case import ModuleCase
  16. from tests.support.helpers import (
  17. destructiveTest,
  18. requires_system_grains,
  19. skip_if_not_root,
  20. slowTest,
  21. )
  22. from tests.support.mixins import SaltReturnAssertsMixin
  23. from tests.support.unit import skipIf
  24. try:
  25. import grp
  26. except ImportError:
  27. grp = None
  28. if salt.utils.platform.is_darwin():
  29. USER = "macuser"
  30. GROUP = "macuser"
  31. GID = randint(400, 500)
  32. NOGROUPGID = randint(400, 500)
  33. elif salt.utils.platform.is_windows():
  34. USER = "winuser"
  35. GROUP = "winuser"
  36. GID = randint(400, 500)
  37. NOGROUPGID = randint(400, 500)
  38. else:
  39. USER = "nobody"
  40. GROUP = "nobody"
  41. GID = "nobody"
  42. NOGROUPGID = "nogroup"
  43. @destructiveTest
  44. @skip_if_not_root
  45. @pytest.mark.windows_whitelisted
  46. class UserTest(ModuleCase, SaltReturnAssertsMixin):
  47. """
  48. test for user absent
  49. """
  50. user_name = "salt-test"
  51. alt_group = "salt-test-altgroup"
  52. user_home = (
  53. "/var/lib/{0}".format(user_name)
  54. if not salt.utils.platform.is_windows()
  55. else os.path.join("tmp", user_name)
  56. )
  57. @slowTest
  58. def test_user_absent(self):
  59. ret = self.run_state("user.absent", name="unpossible")
  60. self.assertSaltTrueReturn(ret)
  61. @slowTest
  62. def test_user_if_present(self):
  63. ret = self.run_state("user.present", name=USER)
  64. self.assertSaltTrueReturn(ret)
  65. @slowTest
  66. def test_user_if_present_with_gid(self):
  67. if self.run_function("group.info", [USER]):
  68. ret = self.run_state("user.present", name=USER, gid=GID)
  69. elif self.run_function("group.info", ["nogroup"]):
  70. ret = self.run_state("user.present", name=USER, gid=NOGROUPGID)
  71. else:
  72. self.skipTest("Neither 'nobody' nor 'nogroup' are valid groups")
  73. self.assertSaltTrueReturn(ret)
  74. @slowTest
  75. def test_user_not_present(self):
  76. """
  77. This is a DESTRUCTIVE TEST it creates a new user on the minion.
  78. And then destroys that user.
  79. Assume that it will break any system you run it on.
  80. """
  81. ret = self.run_state("user.present", name=self.user_name)
  82. self.assertSaltTrueReturn(ret)
  83. @slowTest
  84. def test_user_present_when_home_dir_does_not_18843(self):
  85. """
  86. This is a DESTRUCTIVE TEST it creates a new user on the minion.
  87. And then destroys that user.
  88. Assume that it will break any system you run it on.
  89. """
  90. if salt.utils.platform.is_darwin():
  91. HOMEDIR = "/Users/home_of_" + self.user_name
  92. else:
  93. HOMEDIR = "/home/home_of_" + self.user_name
  94. ret = self.run_state("user.present", name=self.user_name, home=HOMEDIR)
  95. self.assertSaltTrueReturn(ret)
  96. self.run_function("file.absent", name=HOMEDIR)
  97. ret = self.run_state("user.present", name=self.user_name, home=HOMEDIR)
  98. self.assertSaltTrueReturn(ret)
  99. @requires_system_grains
  100. @slowTest
  101. def test_user_present_nondefault(self, grains=None):
  102. """
  103. This is a DESTRUCTIVE TEST it creates a new user on the on the minion.
  104. """
  105. ret = self.run_state("user.present", name=self.user_name, home=self.user_home)
  106. self.assertSaltTrueReturn(ret)
  107. ret = self.run_function("user.info", [self.user_name])
  108. self.assertReturnNonEmptySaltType(ret)
  109. if salt.utils.platform.is_windows():
  110. group_name = self.run_function("user.list_groups", [self.user_name])
  111. else:
  112. group_name = grp.getgrgid(ret["gid"]).gr_name
  113. if not salt.utils.platform.is_darwin() and not salt.utils.platform.is_windows():
  114. self.assertTrue(os.path.isdir(self.user_home))
  115. if grains["os_family"] in ("Suse",):
  116. self.assertEqual(group_name, "users")
  117. elif grains["os_family"] == "MacOS":
  118. self.assertEqual(group_name, "staff")
  119. elif salt.utils.platform.is_windows():
  120. self.assertEqual([], group_name)
  121. else:
  122. self.assertEqual(group_name, self.user_name)
  123. @skipIf(
  124. salt.utils.platform.is_windows(), "windows minion does not support usergroup"
  125. )
  126. @slowTest
  127. def test_user_present_usergroup_false(self):
  128. """
  129. This is a DESTRUCTIVE TEST it creates a new user on the on the minion.
  130. This is a unit test, NOT an integration test. We create a group of the
  131. same name as the user beforehand, so it should all run smoothly.
  132. """
  133. # MacOS users' primary group defaults to staff (20), not the name of
  134. # user
  135. ret = self.run_state("group.present", name=self.user_name)
  136. self.assertSaltTrueReturn(ret)
  137. if salt.utils.platform.is_darwin():
  138. gid = grp.getgrnam("staff").gr_gid
  139. else:
  140. gid = self.user_name
  141. ret = self.run_state(
  142. "user.present",
  143. name=self.user_name,
  144. gid=gid,
  145. usergroup=False,
  146. home=self.user_home,
  147. )
  148. self.assertSaltTrueReturn(ret)
  149. ret = self.run_function("user.info", [self.user_name])
  150. self.assertReturnNonEmptySaltType(ret)
  151. group_name = grp.getgrgid(ret["gid"]).gr_name
  152. if not salt.utils.platform.is_darwin():
  153. self.assertTrue(os.path.isdir(self.user_home))
  154. self.assertEqual(group_name, self.user_name)
  155. ret = self.run_state("user.absent", name=self.user_name)
  156. self.assertSaltTrueReturn(ret)
  157. ret = self.run_state("group.absent", name=self.user_name)
  158. self.assertSaltTrueReturn(ret)
  159. @skipIf(
  160. salt.utils.platform.is_windows(),
  161. "windows minion does not support gid_from_name",
  162. )
  163. @skipIf(
  164. salt.utils.platform.is_windows(), "windows minion does not support usergroup"
  165. )
  166. def test_user_present_usergroup(self):
  167. """
  168. This is a DESTRUCTIVE TEST it creates a new user on the on the minion.
  169. This is a unit test, NOT an integration test.
  170. """
  171. ret = self.run_state(
  172. "user.present", name=self.user_name, usergroup=True, home=self.user_home
  173. )
  174. self.assertSaltTrueReturn(ret)
  175. ret = self.run_function("user.info", [self.user_name])
  176. self.assertReturnNonEmptySaltType(ret)
  177. group_name = grp.getgrgid(ret["gid"]).gr_name
  178. if not salt.utils.platform.is_darwin():
  179. self.assertTrue(os.path.isdir(self.user_home))
  180. if salt.utils.platform.is_darwin():
  181. group_name = "staff"
  182. else:
  183. group_name = self.user_name
  184. self.assertEqual(group_name, group_name)
  185. ret = self.run_state("user.absent", name=self.user_name)
  186. self.assertSaltTrueReturn(ret)
  187. if not salt.utils.platform.is_darwin():
  188. ret = self.run_state("group.absent", name=self.user_name)
  189. self.assertSaltTrueReturn(ret)
  190. @skipIf(
  191. sys.getfilesystemencoding().startswith("ANSI"),
  192. "A system encoding which supports Unicode characters must be set. Current setting is: {0}. Try setting $LANG='en_US.UTF-8'".format(
  193. sys.getfilesystemencoding()
  194. ),
  195. )
  196. @slowTest
  197. def test_user_present_unicode(self):
  198. """
  199. This is a DESTRUCTIVE TEST it creates a new user on the on the minion.
  200. It ensures that unicode GECOS data will be properly handled, without
  201. any encoding-related failures.
  202. """
  203. ret = self.run_state(
  204. "user.present",
  205. name=self.user_name,
  206. fullname="Sålt Test",
  207. roomnumber="①②③",
  208. workphone="١٢٣٤",
  209. homephone="६७८",
  210. )
  211. self.assertSaltTrueReturn(ret)
  212. # Ensure updating a user also works
  213. ret = self.run_state(
  214. "user.present",
  215. name=self.user_name,
  216. fullname="Sølt Test",
  217. roomnumber="①③②",
  218. workphone="٣٤١٢",
  219. homephone="६८७",
  220. )
  221. self.assertSaltTrueReturn(ret)
  222. @skipIf(
  223. salt.utils.platform.is_windows(),
  224. "windows minion does not support roomnumber or phone",
  225. )
  226. @slowTest
  227. def test_user_present_gecos(self):
  228. """
  229. This is a DESTRUCTIVE TEST it creates a new user on the on the minion.
  230. It ensures that numeric GECOS data will be properly coerced to strings,
  231. otherwise the state will fail because the GECOS fields are written as
  232. strings (and show up in the user.info output as such). Thus the
  233. comparison will fail, since '12345' != 12345.
  234. """
  235. ret = self.run_state(
  236. "user.present",
  237. name=self.user_name,
  238. fullname=12345,
  239. roomnumber=123,
  240. workphone=1234567890,
  241. homephone=1234567890,
  242. )
  243. self.assertSaltTrueReturn(ret)
  244. @skipIf(
  245. salt.utils.platform.is_windows(),
  246. "windows minion does not support roomnumber or phone",
  247. )
  248. @slowTest
  249. def test_user_present_gecos_none_fields(self):
  250. """
  251. This is a DESTRUCTIVE TEST it creates a new user on the on the minion.
  252. It ensures that if no GECOS data is supplied, the fields will be coerced
  253. into empty strings as opposed to the string "None".
  254. """
  255. ret = self.run_state(
  256. "user.present",
  257. name=self.user_name,
  258. fullname=None,
  259. roomnumber=None,
  260. workphone=None,
  261. homephone=None,
  262. )
  263. self.assertSaltTrueReturn(ret)
  264. ret = self.run_function("user.info", [self.user_name])
  265. self.assertReturnNonEmptySaltType(ret)
  266. self.assertEqual("", ret["fullname"])
  267. # MacOS does not supply the following GECOS fields
  268. if not salt.utils.platform.is_darwin():
  269. self.assertEqual("", ret["roomnumber"])
  270. self.assertEqual("", ret["workphone"])
  271. self.assertEqual("", ret["homephone"])
  272. @skipIf(
  273. salt.utils.platform.is_windows(), "windows minion does not support createhome"
  274. )
  275. @slowTest
  276. def test_user_present_home_directory_created(self):
  277. """
  278. This is a DESTRUCTIVE TEST it creates a new user on the minion.
  279. It ensures that the home directory is created.
  280. """
  281. ret = self.run_state("user.present", name=self.user_name, createhome=True)
  282. self.assertSaltTrueReturn(ret)
  283. user_info = self.run_function("user.info", [self.user_name])
  284. self.assertTrue(os.path.exists(user_info["home"]))
  285. @skipIf(not salt.utils.platform.is_linux(), "only supported on linux")
  286. def test_user_present_gid_from_name(self):
  287. """
  288. Test that gid_from_name results in warning, while it is on a
  289. deprecation path.
  290. """
  291. # Add the user
  292. ret = self.run_state("user.present", name=self.user_name, gid_from_name=True)
  293. self.assertSaltTrueReturn(ret)
  294. ret = ret[next(iter(ret))]
  295. expected = [
  296. "The 'gid_from_name' argument in the user.present state has been "
  297. "replaced with 'usergroup'. Update your SLS file to get rid of "
  298. "this warning."
  299. ]
  300. assert ret["warnings"] == expected, ret["warnings"]
  301. @skipIf(not salt.utils.platform.is_linux(), "only supported on linux")
  302. def test_user_present_gid_from_name_and_usergroup(self):
  303. """
  304. Test that gid_from_name results in warning, while it is on a
  305. deprecation path.
  306. """
  307. # Add the user
  308. ret = self.run_state(
  309. "user.present", name=self.user_name, usergroup=True, gid_from_name=True
  310. )
  311. self.assertSaltTrueReturn(ret)
  312. ret = ret[next(iter(ret))]
  313. expected = [
  314. "The 'gid_from_name' argument in the user.present state has been "
  315. "replaced with 'usergroup'. Ignoring since 'usergroup' was also "
  316. "used."
  317. ]
  318. assert ret["warnings"] == expected, ret["warnings"]
  319. @skipIf(
  320. salt.utils.platform.is_windows() or salt.utils.platform.is_darwin(),
  321. "groups/gid not fully supported",
  322. )
  323. def test_user_present_change_gid_but_keep_group(self):
  324. """
  325. This tests the case in which the default group is changed at the same
  326. time as it is also moved into the "groups" list.
  327. """
  328. try:
  329. # Add the groups
  330. ret = self.run_state("group.present", name=self.user_name)
  331. self.assertSaltTrueReturn(ret)
  332. ret = self.run_state("group.present", name=self.alt_group)
  333. self.assertSaltTrueReturn(ret)
  334. user_name_gid = self.run_function("file.group_to_gid", [self.user_name])
  335. alt_group_gid = self.run_function("file.group_to_gid", [self.alt_group])
  336. # Add the user
  337. ret = self.run_state("user.present", name=self.user_name, gid=alt_group_gid)
  338. # Check that the initial user addition set the gid and groups as
  339. # expected.
  340. new_gid = self.run_function("file.group_to_gid", [self.user_name])
  341. uinfo = self.run_function("user.info", [self.user_name])
  342. assert uinfo["gid"] == alt_group_gid, uinfo["gid"]
  343. assert uinfo["groups"] == [self.alt_group], uinfo["groups"]
  344. # Now change the gid and move alt_group to the groups list in the
  345. # same salt run.
  346. ret = self.run_state(
  347. "user.present",
  348. name=self.user_name,
  349. gid=user_name_gid,
  350. groups=[self.alt_group],
  351. allow_gid_change=True,
  352. )
  353. self.assertSaltTrueReturn(ret)
  354. # Be sure that we did what we intended
  355. new_gid = self.run_function("file.group_to_gid", [self.user_name])
  356. uinfo = self.run_function("user.info", [self.user_name])
  357. assert uinfo["gid"] == new_gid, uinfo["gid"]
  358. assert uinfo["groups"] == [self.user_name, self.alt_group], uinfo["groups"]
  359. finally:
  360. # Do the cleanup here so we don't have to put all of this in the
  361. # tearDown to be executed after each test.
  362. self.assertSaltTrueReturn(
  363. self.run_state("user.absent", name=self.user_name)
  364. )
  365. self.assertSaltTrueReturn(
  366. self.run_state("group.absent", name=self.user_name)
  367. )
  368. self.assertSaltTrueReturn(
  369. self.run_state("group.absent", name=self.alt_group)
  370. )
  371. def tearDown(self):
  372. if salt.utils.platform.is_darwin():
  373. check_user = self.run_function("user.list_users")
  374. if USER in check_user:
  375. del_user = self.run_function("user.delete", [USER], remove=True)
  376. self.assertSaltTrueReturn(self.run_state("user.absent", name=self.user_name))
  377. self.assertSaltTrueReturn(self.run_state("group.absent", name=self.user_name))
  378. @destructiveTest
  379. @skip_if_not_root
  380. @skipIf(not salt.utils.platform.is_windows(), "Windows only tests")
  381. @pytest.mark.windows_whitelisted
  382. class WinUserTest(ModuleCase, SaltReturnAssertsMixin):
  383. """
  384. test for user absent
  385. """
  386. def tearDown(self):
  387. self.assertSaltTrueReturn(self.run_state("user.absent", name=USER))
  388. @slowTest
  389. def test_user_present_existing(self):
  390. ret = self.run_state(
  391. "user.present",
  392. name=USER,
  393. win_homedrive="U:",
  394. win_profile="C:\\User\\{0}".format(USER),
  395. win_logonscript="C:\\logon.vbs",
  396. win_description="Test User Account",
  397. )
  398. self.assertSaltTrueReturn(ret)
  399. ret = self.run_state(
  400. "user.present",
  401. name=USER,
  402. win_homedrive="R:",
  403. win_profile="C:\\Users\\{0}".format(USER),
  404. win_logonscript="C:\\Windows\\logon.vbs",
  405. win_description="Temporary Account",
  406. )
  407. self.assertSaltTrueReturn(ret)
  408. self.assertSaltStateChangesEqual(ret, "R:", keys=["homedrive"])
  409. self.assertSaltStateChangesEqual(
  410. ret, "C:\\Users\\{0}".format(USER), keys=["profile"]
  411. )
  412. self.assertSaltStateChangesEqual(
  413. ret, "C:\\Windows\\logon.vbs", keys=["logonscript"]
  414. )
  415. self.assertSaltStateChangesEqual(ret, "Temporary Account", keys=["description"])