PageRenderTime 39ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/provd/devices/tests/test_ident.py

https://gitlab.com/xivo.solutions/xivo-provisioning
Python | 362 lines | 258 code | 89 blank | 15 comment | 5 complexity | 558b63b9256d3b12d6df18f08d7456d2 MD5 | raw file
  1. # -*- coding: utf-8 -*-
  2. # Copyright (C) 2010-2016 Avencall
  3. #
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program. If not, see <http://www.gnu.org/licenses/>
  16. from hamcrest import assert_that, equal_to, has_entry
  17. from mock import Mock, patch
  18. from twisted.internet import defer
  19. from twisted.trial import unittest
  20. from provd.devices import ident
  21. from provd.devices.ident import LastSeenUpdater, VotingUpdater, _RequestHelper, \
  22. RemoveOutdatedIpDeviceUpdater, AddDeviceRetriever
  23. class TestAddDeviceRetriever(unittest.TestCase):
  24. def setUp(self):
  25. self.app = Mock()
  26. self.dev_retriever = AddDeviceRetriever(self.app)
  27. @patch('provd.devices.ident.log_security_msg')
  28. @defer.inlineCallbacks
  29. def test_retrieve_log_security_event(self, mock_log_security_msg):
  30. device_id = u'some-id'
  31. device_ip = u'169.254.1.1'
  32. dev_info = {
  33. u'ip': device_ip,
  34. }
  35. self.app.dev_insert.return_value = defer.succeed(device_id)
  36. device = yield self.dev_retriever.retrieve(dev_info)
  37. mock_log_security_msg.assert_called_once_with('New device created automatically from %s: %s', device_ip,
  38. device_id)
  39. expected_device = dict(dev_info)
  40. expected_device[u'added'] = u'auto'
  41. assert_that(device, equal_to(expected_device))
  42. class TestLastSeenUpdater(unittest.TestCase):
  43. def setUp(self):
  44. self.updater = LastSeenUpdater()
  45. def test_last_seen_updater_set_on_conflict(self):
  46. dev_infos = [
  47. {'k1': 'v1'},
  48. {'k1': 'v2'},
  49. ]
  50. for dev_info in dev_infos:
  51. self.updater.update(dev_info)
  52. self.assertEqual(self.updater.dev_info, {'k1': 'v2'})
  53. def test_last_seen_updater_noop_on_nonconflict(self):
  54. dev_infos = [
  55. {'k1': 'v1'},
  56. {'k2': 'v2'},
  57. ]
  58. for dev_info in dev_infos:
  59. self.updater.update(dev_info)
  60. self.assertEqual(self.updater.dev_info, {'k1': 'v1', 'k2': 'v2'})
  61. class TestVotingUpdater(unittest.TestCase):
  62. def setUp(self):
  63. self.updater = VotingUpdater()
  64. def test_voting_updater_votes_for_only_if_only_one(self):
  65. dev_infos = [
  66. {'k1': 'v1'},
  67. ]
  68. for dev_info in dev_infos:
  69. self.updater.update(dev_info)
  70. self.assertEqual(self.updater.dev_info, {'k1': 'v1'})
  71. def test_voting_updater_votes_for_highest_1(self):
  72. dev_infos = [
  73. {'k1': 'v1'},
  74. {'k1': 'v1'},
  75. {'k1': 'v2'},
  76. ]
  77. for dev_info in dev_infos:
  78. self.updater.update(dev_info)
  79. self.assertEqual(self.updater.dev_info, {'k1': 'v1'})
  80. def test_voting_updater_votes_for_highest_2(self):
  81. dev_infos = [
  82. {'k1': 'v2'},
  83. {'k1': 'v1'},
  84. {'k1': 'v1'},
  85. ]
  86. for dev_info in dev_infos:
  87. self.updater.update(dev_info)
  88. self.assertEqual(self.updater.dev_info, {'k1': 'v1'})
  89. class TestRemoveOutdatedIpDeviceUpdater(unittest.TestCase):
  90. def setUp(self):
  91. self.app = Mock()
  92. self.app.nat = 0
  93. self.dev_updater = RemoveOutdatedIpDeviceUpdater(self.app)
  94. @defer.inlineCallbacks
  95. def test_nat_disabled(self):
  96. device = {
  97. u'id': u'abc',
  98. }
  99. dev_info = {
  100. u'ip': u'1.1.1.1',
  101. }
  102. self.app.dev_find.return_value = defer.succeed([])
  103. yield self.dev_updater.update(device, dev_info, 'http', Mock())
  104. self.app.dev_find.assert_called_once_with({u'ip': u'1.1.1.1', u'id': {'$ne': u'abc'}})
  105. @defer.inlineCallbacks
  106. def test_nat_enabled(self):
  107. device = {
  108. u'id': u'abc',
  109. }
  110. dev_info = {
  111. u'ip': u'1.1.1.1',
  112. }
  113. self.app.nat = 1
  114. yield self.dev_updater.update(device, dev_info, 'http', Mock())
  115. self.assertFalse(self.app.dev_find.called)
  116. class TestRequestHelper(unittest.TestCase):
  117. def setUp(self):
  118. self.app = Mock()
  119. self.request = Mock()
  120. self.request_type = Mock()
  121. self.helper = _RequestHelper(self.app, self.request, self.request_type, 1)
  122. @defer.inlineCallbacks
  123. def test_extract_device_info_no_info(self):
  124. extractor = self._new_dev_info_extractor_mock(None)
  125. dev_info = yield self.helper.extract_device_info(extractor)
  126. extractor.extract.assert_called_once_with(self.request, self.request_type)
  127. assert_that(dev_info, equal_to({}))
  128. @defer.inlineCallbacks
  129. def test_extract_device_info_with_info(self):
  130. expected = {'a': 1}
  131. extractor = self._new_dev_info_extractor_mock(expected)
  132. dev_info = yield self.helper.extract_device_info(extractor)
  133. extractor.extract.assert_called_once_with(self.request, self.request_type)
  134. assert_that(dev_info, equal_to(expected))
  135. @defer.inlineCallbacks
  136. def test_retrieve_device_no_device(self):
  137. retriever = self._new_dev_retriever_mock(None)
  138. dev_info = {}
  139. device = yield self.helper.retrieve_device(retriever, dev_info)
  140. retriever.retrieve.assert_called_once_with(dev_info)
  141. assert_that(device, equal_to(None))
  142. @defer.inlineCallbacks
  143. def test_retrieve_device_with_device(self):
  144. expected = {u'id': u'a'}
  145. retriever = self._new_dev_retriever_mock(expected)
  146. dev_info = Mock()
  147. device = yield self.helper.retrieve_device(retriever, dev_info)
  148. retriever.retrieve.assert_called_once_with(dev_info)
  149. assert_that(device, equal_to(expected))
  150. @defer.inlineCallbacks
  151. def test_update_device_no_device(self):
  152. dev_updater = self._new_dev_updater_mock()
  153. device = None
  154. dev_info = {}
  155. yield self.helper.update_device(dev_updater, device, dev_info)
  156. self.assertFalse(dev_updater.update.called)
  157. @defer.inlineCallbacks
  158. def test_update_device_on_no_device_change_and_no_remote_state_update(self):
  159. dev_updater = self._new_dev_updater_mock()
  160. device = {u'id': u'a'}
  161. dev_info = {}
  162. yield self.helper.update_device(dev_updater, device, dev_info)
  163. dev_updater.update.assert_called_once_with(device, dev_info, self.request, self.request_type)
  164. self.assertFalse(self.app.cfg_retrieve.called)
  165. self.assertFalse(self.app.dev_update.called)
  166. @defer.inlineCallbacks
  167. def test_update_device_on_no_device_change_and_remote_state_update(self):
  168. dev_updater = self._new_dev_updater_mock()
  169. device = {
  170. u'id': u'a',
  171. u'configured': True,
  172. u'plugin': u'foo',
  173. u'config': u'a',
  174. }
  175. dev_info = {}
  176. self.request = Mock()
  177. self.request.path = '001122334455.cfg'
  178. self.request_type = 'http'
  179. plugin = Mock()
  180. plugin.get_remote_state_trigger_filename.return_value = '001122334455.cfg'
  181. self.app.pg_mgr.get.return_value = plugin
  182. self.app.cfg_retrieve.return_value = {
  183. u'raw_config': {
  184. u'sip_lines': {
  185. u'1': {
  186. u'username': 'foobar',
  187. }
  188. }
  189. }
  190. }
  191. self.helper = _RequestHelper(self.app, self.request, self.request_type, 1)
  192. yield self.helper.update_device(dev_updater, device, dev_info)
  193. dev_updater.update.assert_called_once_with(device, dev_info, self.request, self.request_type)
  194. self.app.cfg_retrieve.assert_called_once_with(device[u'config'])
  195. self.app.dev_update.assert_called_once_with(device)
  196. assert_that(device, has_entry(u'remote_state_sip_username', u'foobar'))
  197. @defer.inlineCallbacks
  198. def test_update_device_on_device_change_and_remote_state_update(self):
  199. dev_updater = self._new_dev_updater_mock({u'vendor': u'xivo'})
  200. device = {
  201. u'id': u'a',
  202. u'configured': True,
  203. u'plugin': u'foo',
  204. u'config': u'a',
  205. }
  206. dev_info = {}
  207. self.request = Mock()
  208. self.request.path = '001122334455.cfg'
  209. self.request_type = 'http'
  210. plugin = Mock()
  211. plugin.get_remote_state_trigger_filename.return_value = '001122334455.cfg'
  212. self.app.pg_mgr.get.return_value = plugin
  213. self.app.cfg_retrieve.return_value = {
  214. u'raw_config': {
  215. u'sip_lines': {
  216. u'1': {
  217. u'username': 'foobar',
  218. }
  219. }
  220. }
  221. }
  222. self.helper = _RequestHelper(self.app, self.request, self.request_type, 1)
  223. yield self.helper.update_device(dev_updater, device, dev_info)
  224. dev_updater.update.assert_called_once_with(device, dev_info, self.request, self.request_type)
  225. self.app.dev_update.assert_called_once_with(device, pre_update_hook=self.helper._pre_update_hook)
  226. def test_get_plugin_id_no_device(self):
  227. device = None
  228. pg_id = self.helper.get_plugin_id(device)
  229. assert_that(pg_id, equal_to(None))
  230. def test_get_plugin_id_no_plugin_key(self):
  231. device = {u'id': u'a'}
  232. pg_id = self.helper.get_plugin_id(device)
  233. assert_that(pg_id, equal_to(None))
  234. def test_get_plugin_id_ok(self):
  235. device = {u'id': u'a', u'plugin': u'xivo-foo'}
  236. pg_id = self.helper.get_plugin_id(device)
  237. assert_that(pg_id, equal_to(u'xivo-foo'))
  238. def _new_dev_info_extractor_mock(self, return_value):
  239. dev_info_extractor = Mock()
  240. dev_info_extractor.extract.return_value = defer.succeed(return_value)
  241. return dev_info_extractor
  242. def _new_dev_retriever_mock(self, return_value):
  243. dev_retriever = Mock()
  244. dev_retriever.retrieve.return_value = defer.succeed(return_value)
  245. return dev_retriever
  246. def _new_dev_updater_mock(self, device_update={}):
  247. dev_updater = Mock()
  248. def update_fun(device, dev_info, request, request_type):
  249. device.update(device_update)
  250. return defer.succeed(None)
  251. dev_updater.update.side_effect = update_fun
  252. return dev_updater
  253. class TestLogSensitiveRequest(unittest.TestCase):
  254. def setUp(self):
  255. self.ip = '169.254.0.1'
  256. self.filename = 'foobar.cfg'
  257. self.request_type = ident.REQUEST_TYPE_HTTP
  258. self.request = Mock()
  259. self.request.getClientIP.return_value = self.ip
  260. self.request.path = '/{}'.format(self.filename)
  261. self.plugin = Mock()
  262. @patch('provd.devices.ident.log_security_msg')
  263. def test_no_log_when_plugin_doesnt_have_method(self, mock_log_security_msg):
  264. del self.plugin.is_sensitive_filename
  265. ident._log_sensitive_request(self.plugin, self.request, self.request_type)
  266. assert_that(mock_log_security_msg.called, equal_to(False))
  267. @patch('provd.devices.ident.log_security_msg')
  268. def test_log_when_sensitive_filename(self, mock_log_security_msg):
  269. self.plugin.is_sensitive_filename.return_value = True
  270. ident._log_sensitive_request(self.plugin, self.request, self.request_type)
  271. self.plugin.is_sensitive_filename.assert_called_once_with(self.filename)
  272. mock_log_security_msg.assert_called_once_with('Sensitive file requested from %s: %s',
  273. self.ip, self.filename)