test_wtmp.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. # coding: utf-8
  2. # Python libs
  3. from __future__ import absolute_import
  4. import datetime
  5. import logging
  6. # Salt libs
  7. import salt.beacons.wtmp as wtmp
  8. from salt.ext import six
  9. from tests.support.mixins import LoaderModuleMockMixin
  10. from tests.support.mock import MagicMock, mock_open, patch
  11. # Salt testing libs
  12. from tests.support.unit import TestCase, skipIf
  13. # pylint: disable=import-error
  14. try:
  15. import dateutil.parser as dateutil_parser # pylint: disable=unused-import
  16. _TIME_SUPPORTED = True
  17. except ImportError:
  18. _TIME_SUPPORTED = False
  19. raw = b"\x07\x00\x00\x00H\x18\x00\x00pts/14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00s/14gareth\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00::1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13I\xc5YZf\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
  20. pack = (
  21. 7,
  22. 6216,
  23. b"pts/14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
  24. b"s/14",
  25. b"gareth\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
  26. b"::1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
  27. 0,
  28. 0,
  29. 0,
  30. 1506101523,
  31. 353882,
  32. 0,
  33. 0,
  34. 0,
  35. 16777216,
  36. )
  37. log = logging.getLogger(__name__)
  38. class WTMPBeaconTestCase(TestCase, LoaderModuleMockMixin):
  39. """
  40. Test case for salt.beacons.[s]
  41. """
  42. def setup_loader_modules(self):
  43. return {wtmp: {"__context__": {"wtmp.loc": 2}, "__salt__": {}}}
  44. def test_non_list_config(self):
  45. config = {}
  46. ret = wtmp.validate(config)
  47. self.assertEqual(ret, (False, "Configuration for wtmp beacon must be a list."))
  48. def test_empty_config(self):
  49. config = [{}]
  50. ret = wtmp.validate(config)
  51. self.assertEqual(ret, (True, "Valid beacon configuration"))
  52. def test_no_match(self):
  53. config = [
  54. {
  55. "users": {
  56. "gareth": {
  57. "time_range": {
  58. "end": "09-22-2017 5pm",
  59. "start": "09-22-2017 3pm",
  60. }
  61. }
  62. }
  63. }
  64. ]
  65. ret = wtmp.validate(config)
  66. self.assertEqual(ret, (True, "Valid beacon configuration"))
  67. with patch("salt.utils.files.fopen", mock_open(b"")) as m_open:
  68. ret = wtmp.beacon(config)
  69. call_args = next(six.itervalues(m_open.filehandles))[0].call.args
  70. assert call_args == (wtmp.WTMP, "rb"), call_args
  71. assert ret == [], ret
  72. def test_invalid_users(self):
  73. config = [{"users": ["gareth"]}]
  74. ret = wtmp.validate(config)
  75. self.assertEqual(
  76. ret, (False, "User configuration for wtmp beacon must be a dictionary.")
  77. )
  78. def test_invalid_groups(self):
  79. config = [{"groups": ["docker"]}]
  80. ret = wtmp.validate(config)
  81. self.assertEqual(
  82. ret, (False, "Group configuration for wtmp beacon must be a dictionary.")
  83. )
  84. def test_default_invalid_time_range(self):
  85. config = [{"defaults": {"time_range": {"start": "3pm"}}}]
  86. ret = wtmp.validate(config)
  87. self.assertEqual(
  88. ret,
  89. (
  90. False,
  91. "The time_range parameter for wtmp beacon must contain start & end options.",
  92. ),
  93. )
  94. def test_users_invalid_time_range(self):
  95. config = [{"users": {"gareth": {"time_range": {"start": "3pm"}}}}]
  96. ret = wtmp.validate(config)
  97. self.assertEqual(
  98. ret,
  99. (
  100. False,
  101. "The time_range parameter for wtmp beacon must contain start & end options.",
  102. ),
  103. )
  104. def test_groups_invalid_time_range(self):
  105. config = [{"groups": {"docker": {"time_range": {"start": "3pm"}}}}]
  106. ret = wtmp.validate(config)
  107. self.assertEqual(
  108. ret,
  109. (
  110. False,
  111. "The time_range parameter for wtmp beacon must contain start & end options.",
  112. ),
  113. )
  114. def test_match(self):
  115. with patch("salt.utils.files.fopen", mock_open(read_data=raw)):
  116. with patch("struct.unpack", MagicMock(return_value=pack)):
  117. config = [{"users": {"gareth": {}}}]
  118. ret = wtmp.validate(config)
  119. self.assertEqual(ret, (True, "Valid beacon configuration"))
  120. _expected = [
  121. {
  122. "PID": 6216,
  123. "action": "login",
  124. "line": "pts/14",
  125. "session": 0,
  126. "time": 0,
  127. "exit_status": 0,
  128. "inittab": "s/14",
  129. "type": 7,
  130. "addr": 1506101523,
  131. "hostname": "::1",
  132. "user": "gareth",
  133. }
  134. ]
  135. ret = wtmp.beacon(config)
  136. log.debug("{}".format(ret))
  137. self.assertEqual(ret, _expected)
  138. @skipIf(not _TIME_SUPPORTED, "dateutil.parser is missing.")
  139. def test_match_time(self):
  140. with patch("salt.utils.files.fopen", mock_open(read_data=raw)):
  141. mock_now = datetime.datetime(2017, 9, 22, 16, 0, 0, 0)
  142. with patch("datetime.datetime", MagicMock()), patch(
  143. "datetime.datetime.now", MagicMock(return_value=mock_now)
  144. ):
  145. with patch("struct.unpack", MagicMock(return_value=pack)):
  146. config = [
  147. {
  148. "users": {
  149. "gareth": {
  150. "time": {
  151. "end": "09-22-2017 5pm",
  152. "start": "09-22-2017 3pm",
  153. }
  154. }
  155. }
  156. }
  157. ]
  158. ret = wtmp.validate(config)
  159. self.assertEqual(ret, (True, "Valid beacon configuration"))
  160. _expected = [
  161. {
  162. "PID": 6216,
  163. "action": "login",
  164. "line": "pts/14",
  165. "session": 0,
  166. "time": 0,
  167. "exit_status": 0,
  168. "inittab": "s/14",
  169. "type": 7,
  170. "addr": 1506101523,
  171. "hostname": "::1",
  172. "user": "gareth",
  173. }
  174. ]
  175. ret = wtmp.beacon(config)
  176. self.assertEqual(ret, _expected)
  177. def test_match_group(self):
  178. for groupadd in (
  179. "salt.modules.aix_group",
  180. "salt.modules.mac_group",
  181. "salt.modules.pw_group",
  182. "salt.modules.solaris_group",
  183. "salt.modules.win_groupadd",
  184. ):
  185. mock_group_info = {
  186. "passwd": "x",
  187. "gid": 100,
  188. "name": "users",
  189. "members": ["gareth"],
  190. }
  191. with patch("salt.utils.files.fopen", mock_open(read_data=raw)):
  192. with patch("time.time", MagicMock(return_value=1506121200)):
  193. with patch("struct.unpack", MagicMock(return_value=pack)):
  194. with patch(
  195. "{0}.info".format(groupadd),
  196. new=MagicMock(return_value=mock_group_info),
  197. ):
  198. config = [
  199. {
  200. "group": {
  201. "users": {
  202. "time": {
  203. "end": "09-22-2017 5pm",
  204. "start": "09-22-2017 3pm",
  205. }
  206. }
  207. }
  208. }
  209. ]
  210. ret = wtmp.validate(config)
  211. self.assertEqual(ret, (True, "Valid beacon configuration"))
  212. _expected = [
  213. {
  214. "PID": 6216,
  215. "action": "login",
  216. "line": "pts/14",
  217. "session": 0,
  218. "time": 0,
  219. "exit_status": 0,
  220. "inittab": "s/14",
  221. "type": 7,
  222. "addr": 1506101523,
  223. "hostname": "::1",
  224. "user": "gareth",
  225. }
  226. ]
  227. ret = wtmp.beacon(config)
  228. self.assertEqual(ret, _expected)