test_wtmp.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. # coding: utf-8
  2. # Python libs
  3. from __future__ import absolute_import
  4. import datetime
  5. import logging
  6. # Salt testing libs
  7. from tests.support.unit import skipIf, TestCase
  8. from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock, mock_open
  9. from tests.support.mixins import LoaderModuleMockMixin
  10. # Salt libs
  11. import salt.beacons.wtmp as wtmp
  12. from salt.ext import six
  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 = (7, 6216, 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', b's/14', 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', 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', 0, 0, 0, 1506101523, 353882, 0, 0, 0, 16777216)
  21. log = logging.getLogger(__name__)
  22. @skipIf(NO_MOCK, NO_MOCK_REASON)
  23. class WTMPBeaconTestCase(TestCase, LoaderModuleMockMixin):
  24. '''
  25. Test case for salt.beacons.[s]
  26. '''
  27. def setup_loader_modules(self):
  28. return {
  29. wtmp: {
  30. '__context__': {'wtmp.loc': 2},
  31. '__salt__': {},
  32. }
  33. }
  34. def test_non_list_config(self):
  35. config = {}
  36. ret = wtmp.validate(config)
  37. self.assertEqual(ret, (False, 'Configuration for wtmp beacon must'
  38. ' be a list.'))
  39. def test_empty_config(self):
  40. config = [{}]
  41. ret = wtmp.validate(config)
  42. self.assertEqual(ret, (True, 'Valid beacon configuration'))
  43. def test_no_match(self):
  44. config = [{'users': {'gareth': {'time_range': {'end': '09-22-2017 5pm',
  45. 'start': '09-22-2017 3pm'}}}}
  46. ]
  47. ret = wtmp.validate(config)
  48. self.assertEqual(ret, (True, 'Valid beacon configuration'))
  49. with patch('salt.utils.files.fopen', mock_open(b'')) as m_open:
  50. ret = wtmp.beacon(config)
  51. call_args = next(six.itervalues(m_open.filehandles))[0].call.args
  52. assert call_args == (wtmp.WTMP, 'rb'), call_args
  53. assert ret == [], ret
  54. def test_invalid_users(self):
  55. config = [{'users': ['gareth']}]
  56. ret = wtmp.validate(config)
  57. self.assertEqual(ret, (False, 'User configuration for wtmp beacon must be a dictionary.'))
  58. def test_invalid_groups(self):
  59. config = [{'groups': ['docker']}]
  60. ret = wtmp.validate(config)
  61. self.assertEqual(ret, (False, 'Group configuration for wtmp beacon must be a dictionary.'))
  62. def test_default_invalid_time_range(self):
  63. config = [{'defaults': {'time_range': {'start': '3pm'}}}]
  64. ret = wtmp.validate(config)
  65. self.assertEqual(ret, (False, 'The time_range parameter for wtmp beacon must contain start & end options.'))
  66. def test_users_invalid_time_range(self):
  67. config = [{'users': {'gareth': {'time_range': {'start': '3pm'}}}}]
  68. ret = wtmp.validate(config)
  69. self.assertEqual(ret, (False, 'The time_range parameter for wtmp beacon must contain start & end options.'))
  70. def test_groups_invalid_time_range(self):
  71. config = [{'groups': {'docker': {'time_range': {'start': '3pm'}}}}]
  72. ret = wtmp.validate(config)
  73. self.assertEqual(ret, (False, 'The time_range parameter for wtmp beacon must contain start & end options.'))
  74. def test_match(self):
  75. with patch('salt.utils.files.fopen',
  76. mock_open(read_data=raw)):
  77. with patch('struct.unpack',
  78. MagicMock(return_value=pack)):
  79. config = [{'users': {'gareth': {}}}]
  80. ret = wtmp.validate(config)
  81. self.assertEqual(ret, (True, 'Valid beacon configuration'))
  82. _expected = [{'PID': 6216,
  83. 'action': 'login',
  84. 'line': 'pts/14',
  85. 'session': 0,
  86. 'time': 0,
  87. 'exit_status': 0,
  88. 'inittab': 's/14',
  89. 'type': 7,
  90. 'addr': 1506101523,
  91. 'hostname': '::1',
  92. 'user': 'gareth'}]
  93. ret = wtmp.beacon(config)
  94. log.debug('{}'.format(ret))
  95. self.assertEqual(ret, _expected)
  96. @skipIf(not _TIME_SUPPORTED, 'dateutil.parser is missing.')
  97. def test_match_time(self):
  98. with patch('salt.utils.files.fopen',
  99. mock_open(read_data=raw)):
  100. mock_now = datetime.datetime(2017, 9, 22, 16, 0, 0, 0)
  101. with patch('datetime.datetime', MagicMock()), \
  102. patch('datetime.datetime.now',
  103. MagicMock(return_value=mock_now)):
  104. with patch('struct.unpack',
  105. MagicMock(return_value=pack)):
  106. config = [{'users': {'gareth': {'time': {'end': '09-22-2017 5pm',
  107. 'start': '09-22-2017 3pm'}}}}
  108. ]
  109. ret = wtmp.validate(config)
  110. self.assertEqual(ret, (True, 'Valid beacon configuration'))
  111. _expected = [{'PID': 6216,
  112. 'action': 'login',
  113. 'line': 'pts/14',
  114. 'session': 0,
  115. 'time': 0,
  116. 'exit_status': 0,
  117. 'inittab': 's/14',
  118. 'type': 7,
  119. 'addr': 1506101523,
  120. 'hostname': '::1',
  121. 'user': 'gareth'}]
  122. ret = wtmp.beacon(config)
  123. self.assertEqual(ret, _expected)
  124. def test_match_group(self):
  125. for groupadd in ('salt.modules.aix_group',
  126. 'salt.modules.mac_group',
  127. 'salt.modules.pw_group',
  128. 'salt.modules.solaris_group',
  129. 'salt.modules.win_groupadd'):
  130. mock_group_info = {'passwd': 'x',
  131. 'gid': 100,
  132. 'name': 'users',
  133. 'members': ['gareth']}
  134. with patch('salt.utils.files.fopen',
  135. mock_open(read_data=raw)):
  136. with patch('time.time',
  137. MagicMock(return_value=1506121200)):
  138. with patch('struct.unpack',
  139. MagicMock(return_value=pack)):
  140. with patch('{0}.info'.format(groupadd),
  141. new=MagicMock(return_value=mock_group_info)):
  142. config = [{'group': {'users': {'time': {'end': '09-22-2017 5pm',
  143. 'start': '09-22-2017 3pm'}}}}
  144. ]
  145. ret = wtmp.validate(config)
  146. self.assertEqual(ret,
  147. (True, 'Valid beacon configuration'))
  148. _expected = [{'PID': 6216,
  149. 'action': 'login',
  150. 'line': 'pts/14',
  151. 'session': 0,
  152. 'time': 0,
  153. 'exit_status': 0,
  154. 'inittab': 's/14',
  155. 'type': 7,
  156. 'addr': 1506101523,
  157. 'hostname': '::1',
  158. 'user': 'gareth'}]
  159. ret = wtmp.beacon(config)
  160. self.assertEqual(ret, _expected)