test_user.py 15 KB

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