test_auth.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. # -*- coding: utf-8 -*-
  2. '''
  3. :codeauthor: Mike Place <mp@saltstack.com>
  4. '''
  5. # Import pytohn libs
  6. from __future__ import absolute_import, print_function, unicode_literals
  7. # Import Salt Testing libs
  8. from tests.support.unit import TestCase, skipIf, WAR_ROOM_SKIP
  9. from tests.support.mock import patch, call, NO_MOCK, NO_MOCK_REASON, MagicMock
  10. # Import Salt libraries
  11. import salt.master
  12. from tests.support.case import ModuleCase
  13. from salt import auth
  14. import salt.utils.platform
  15. @skipIf(NO_MOCK, NO_MOCK_REASON)
  16. class LoadAuthTestCase(TestCase):
  17. def setUp(self): # pylint: disable=W0221
  18. patches = (
  19. ('salt.payload.Serial', None),
  20. ('salt.loader.auth', dict(return_value={'pam.auth': 'fake_func_str', 'pam.groups': 'fake_groups_function_str'})),
  21. ('salt.loader.eauth_tokens', dict(return_value={'localfs.mk_token': 'fake_func_mktok',
  22. 'localfs.get_token': 'fake_func_gettok',
  23. 'localfs.rm_roken': 'fake_func_rmtok'}))
  24. )
  25. for mod, mock in patches:
  26. if mock:
  27. patcher = patch(mod, **mock)
  28. else:
  29. patcher = patch(mod)
  30. patcher.start()
  31. self.addCleanup(patcher.stop)
  32. self.lauth = auth.LoadAuth({}) # Load with empty opts
  33. def test_load_name(self):
  34. valid_eauth_load = {'username': 'test_user',
  35. 'show_timeout': False,
  36. 'test_password': '',
  37. 'eauth': 'pam'}
  38. # Test a case where the loader auth doesn't have the auth type
  39. without_auth_type = dict(valid_eauth_load)
  40. without_auth_type.pop('eauth')
  41. ret = self.lauth.load_name(without_auth_type)
  42. self.assertEqual(ret, '', "Did not bail when the auth loader didn't have the auth type.")
  43. # Test a case with valid params
  44. with patch('salt.utils.args.arg_lookup',
  45. MagicMock(return_value={'args': ['username', 'password']})) as format_call_mock:
  46. expected_ret = call('fake_func_str')
  47. ret = self.lauth.load_name(valid_eauth_load)
  48. format_call_mock.assert_has_calls((expected_ret,), any_order=True)
  49. self.assertEqual(ret, 'test_user')
  50. def test_get_groups(self):
  51. valid_eauth_load = {'username': 'test_user',
  52. 'show_timeout': False,
  53. 'test_password': '',
  54. 'eauth': 'pam'}
  55. with patch('salt.utils.args.format_call') as format_call_mock:
  56. expected_ret = call('fake_groups_function_str', {
  57. 'username': 'test_user',
  58. 'test_password': '',
  59. 'show_timeout': False,
  60. 'eauth': 'pam'
  61. }, expected_extra_kws=auth.AUTH_INTERNAL_KEYWORDS)
  62. self.lauth.get_groups(valid_eauth_load)
  63. format_call_mock.assert_has_calls((expected_ret,), any_order=True)
  64. @skipIf(WAR_ROOM_SKIP, 'WAR ROOM TEMPORARY SKIP')
  65. class MasterACLTestCase(ModuleCase):
  66. '''
  67. A class to check various aspects of the publisher ACL system
  68. '''
  69. def setUp(self):
  70. self.fire_event_mock = MagicMock(return_value='dummy_tag')
  71. self.addCleanup(delattr, self, 'fire_event_mock')
  72. opts = self.get_temp_config('master')
  73. patches = (
  74. ('zmq.Context', MagicMock()),
  75. ('salt.payload.Serial.dumps', MagicMock()),
  76. ('salt.master.tagify', MagicMock()),
  77. ('salt.utils.event.SaltEvent.fire_event', self.fire_event_mock),
  78. ('salt.auth.LoadAuth.time_auth', MagicMock(return_value=True)),
  79. ('salt.minion.MasterMinion', MagicMock()),
  80. ('salt.utils.verify.check_path_traversal', MagicMock()),
  81. ('salt.client.get_local_client', MagicMock(return_value=opts['conf_file'])),
  82. )
  83. for mod, mock in patches:
  84. patcher = patch(mod, mock)
  85. patcher.start()
  86. self.addCleanup(patcher.stop)
  87. opts['publisher_acl'] = {}
  88. opts['publisher_acl_blacklist'] = {}
  89. opts['master_job_cache'] = ''
  90. opts['sign_pub_messages'] = False
  91. opts['con_cache'] = ''
  92. opts['external_auth'] = {}
  93. opts['external_auth']['pam'] = \
  94. {'test_user': [{'*': ['test.ping']},
  95. {'minion_glob*': ['foo.bar']},
  96. {'minion_func_test': ['func_test.*']}],
  97. 'test_group%': [{'*': ['test.echo']}],
  98. 'test_user_mminion': [{'target_minion': ['test.ping']}],
  99. '*': [{'my_minion': ['my_mod.my_func']}],
  100. 'test_user_func': [{'*': [{'test.echo': {'args': ['MSG:.*']}},
  101. {'test.echo': {'kwargs': {'text': 'KWMSG:.*',
  102. 'anything': '.*',
  103. 'none': None}}},
  104. {'my_mod.*': {'args': ['a.*', 'b.*'],
  105. 'kwargs': {'kwa': 'kwa.*',
  106. 'kwb': 'kwb'}}}]},
  107. {'minion1': [{'test.echo': {'args': ['TEST',
  108. None,
  109. 'TEST.*']}},
  110. {'test.empty': {}}]}
  111. ]
  112. }
  113. self.clear = salt.master.ClearFuncs(opts, MagicMock())
  114. self.addCleanup(delattr, self, 'clear')
  115. # overwrite the _send_pub method so we don't have to serialize MagicMock
  116. self.clear._send_pub = lambda payload: True
  117. # make sure to return a JID, instead of a mock
  118. self.clear.mminion.returners = {'.prep_jid': lambda x: 1}
  119. self.valid_clear_load = {'tgt_type': 'glob',
  120. 'jid': '',
  121. 'cmd': 'publish',
  122. 'tgt': 'test_minion',
  123. 'kwargs':
  124. {'username': 'test_user',
  125. 'password': 'test_password',
  126. 'show_timeout': False,
  127. 'eauth': 'pam',
  128. 'show_jid': False},
  129. 'ret': '',
  130. 'user': 'test_user',
  131. 'key': '',
  132. 'arg': '',
  133. 'fun': 'test.ping',
  134. }
  135. self.addCleanup(delattr, self, 'valid_clear_load')
  136. @skipIf(salt.utils.platform.is_windows(), 'PAM eauth not available on Windows')
  137. def test_master_publish_name(self):
  138. '''
  139. Test to ensure a simple name can auth against a given function.
  140. This tests to ensure test_user can access test.ping but *not* sys.doc
  141. '''
  142. _check_minions_return = {'minions': ['some_minions'], 'missing': []}
  143. with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)):
  144. # Can we access test.ping?
  145. self.clear.publish(self.valid_clear_load)
  146. self.assertEqual(self.fire_event_mock.call_args[0][0]['fun'], 'test.ping')
  147. # Are we denied access to sys.doc?
  148. sys_doc_load = self.valid_clear_load
  149. sys_doc_load['fun'] = 'sys.doc'
  150. self.clear.publish(sys_doc_load)
  151. self.assertNotEqual(self.fire_event_mock.call_args[0][0]['fun'], 'sys.doc') # If sys.doc were to fire, this would match
  152. def test_master_publish_group(self):
  153. '''
  154. Tests to ensure test_group can access test.echo but *not* sys.doc
  155. '''
  156. _check_minions_return = {'minions': ['some_minions'], 'missing': []}
  157. with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)):
  158. self.valid_clear_load['kwargs']['user'] = 'new_user'
  159. self.valid_clear_load['fun'] = 'test.echo'
  160. self.valid_clear_load['arg'] = 'hello'
  161. with patch('salt.auth.LoadAuth.get_groups', return_value=['test_group', 'second_test_group']):
  162. self.clear.publish(self.valid_clear_load)
  163. # Did we fire test.echo?
  164. self.assertEqual(self.fire_event_mock.call_args[0][0]['fun'], 'test.echo')
  165. # Request sys.doc
  166. self.valid_clear_load['fun'] = 'sys.doc'
  167. # Did we fire it?
  168. self.assertNotEqual(self.fire_event_mock.call_args[0][0]['fun'], 'sys.doc')
  169. def test_master_publish_some_minions(self):
  170. '''
  171. Tests to ensure we can only target minions for which we
  172. have permission with publisher acl.
  173. Note that in order for these sorts of tests to run correctly that
  174. you should NOT patch check_minions!
  175. '''
  176. self.valid_clear_load['kwargs']['username'] = 'test_user_mminion'
  177. self.valid_clear_load['user'] = 'test_user_mminion'
  178. self.clear.publish(self.valid_clear_load)
  179. self.assertEqual(self.fire_event_mock.mock_calls, [])
  180. def test_master_not_user_glob_all(self):
  181. '''
  182. Test to ensure that we DO NOT access to a given
  183. function to all users with publisher acl. ex:
  184. '*':
  185. my_minion:
  186. - my_func
  187. Yes, this seems like a bit of a no-op test but it's
  188. here to document that this functionality
  189. is NOT supported currently.
  190. WARNING: Do not patch this wit
  191. '''
  192. self.valid_clear_load['kwargs']['username'] = 'NOT_A_VALID_USERNAME'
  193. self.valid_clear_load['user'] = 'NOT_A_VALID_USERNAME'
  194. self.valid_clear_load['fun'] = 'test.ping'
  195. self.clear.publish(self.valid_clear_load)
  196. self.assertEqual(self.fire_event_mock.mock_calls, [])
  197. @skipIf(salt.utils.platform.is_windows(), 'PAM eauth not available on Windows')
  198. def test_master_minion_glob(self):
  199. '''
  200. Test to ensure we can allow access to a given
  201. function for a user to a subset of minions
  202. selected by a glob. ex:
  203. test_user:
  204. 'minion_glob*':
  205. - glob_mod.glob_func
  206. This test is a bit tricky, because ultimately the real functionality
  207. lies in what's returned from check_minions, but this checks a limited
  208. amount of logic on the way there as well. Note the inline patch.
  209. '''
  210. requested_function = 'foo.bar'
  211. requested_tgt = 'minion_glob1'
  212. self.valid_clear_load['tgt'] = requested_tgt
  213. self.valid_clear_load['fun'] = requested_function
  214. _check_minions_return = {'minions': ['minion_glob1'], 'missing': []}
  215. with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)): # Assume that there is a listening minion match
  216. self.clear.publish(self.valid_clear_load)
  217. self.assertTrue(self.fire_event_mock.called, 'Did not fire {0} for minion tgt {1}'.format(requested_function, requested_tgt))
  218. self.assertEqual(self.fire_event_mock.call_args[0][0]['fun'], requested_function, 'Did not fire {0} for minion glob'.format(requested_function))
  219. def test_master_function_glob(self):
  220. '''
  221. Test to ensure that we can allow access to a given
  222. set of functions in an execution module as selected
  223. by a glob. ex:
  224. my_user:
  225. my_minion:
  226. 'test.*'
  227. '''
  228. # Unimplemented
  229. pass
  230. @skipIf(salt.utils.platform.is_windows(), 'PAM eauth not available on Windows')
  231. def test_args_empty_spec(self):
  232. '''
  233. Test simple arg restriction allowed.
  234. 'test_user_func':
  235. minion1:
  236. - test.empty:
  237. '''
  238. _check_minions_return = {'minions': ['minion1'], 'missing': []}
  239. with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)):
  240. self.valid_clear_load['kwargs'].update({'username': 'test_user_func'})
  241. self.valid_clear_load.update({'user': 'test_user_func',
  242. 'tgt': 'minion1',
  243. 'fun': 'test.empty',
  244. 'arg': ['TEST']})
  245. self.clear.publish(self.valid_clear_load)
  246. self.assertEqual(self.fire_event_mock.call_args[0][0]['fun'], 'test.empty')
  247. @skipIf(salt.utils.platform.is_windows(), 'PAM eauth not available on Windows')
  248. def test_args_simple_match(self):
  249. '''
  250. Test simple arg restriction allowed.
  251. 'test_user_func':
  252. minion1:
  253. - test.echo:
  254. args:
  255. - 'TEST'
  256. - 'TEST.*'
  257. '''
  258. _check_minions_return = {'minions': ['minion1'], 'missing': []}
  259. with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)):
  260. self.valid_clear_load['kwargs'].update({'username': 'test_user_func'})
  261. self.valid_clear_load.update({'user': 'test_user_func',
  262. 'tgt': 'minion1',
  263. 'fun': 'test.echo',
  264. 'arg': ['TEST', 'any', 'TEST ABC']})
  265. self.clear.publish(self.valid_clear_load)
  266. self.assertEqual(self.fire_event_mock.call_args[0][0]['fun'], 'test.echo')
  267. @skipIf(salt.utils.platform.is_windows(), 'PAM eauth not available on Windows')
  268. def test_args_more_args(self):
  269. '''
  270. Test simple arg restriction allowed to pass unlisted args.
  271. 'test_user_func':
  272. minion1:
  273. - test.echo:
  274. args:
  275. - 'TEST'
  276. - 'TEST.*'
  277. '''
  278. _check_minions_return = {'minions': ['minion1'], 'missing': []}
  279. with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)):
  280. self.valid_clear_load['kwargs'].update({'username': 'test_user_func'})
  281. self.valid_clear_load.update({'user': 'test_user_func',
  282. 'tgt': 'minion1',
  283. 'fun': 'test.echo',
  284. 'arg': ['TEST',
  285. 'any',
  286. 'TEST ABC',
  287. 'arg 3',
  288. {'kwarg1': 'val1',
  289. '__kwarg__': True}]})
  290. self.clear.publish(self.valid_clear_load)
  291. self.assertEqual(self.fire_event_mock.call_args[0][0]['fun'], 'test.echo')
  292. def test_args_simple_forbidden(self):
  293. '''
  294. Test simple arg restriction forbidden.
  295. 'test_user_func':
  296. minion1:
  297. - test.echo:
  298. args:
  299. - 'TEST'
  300. - 'TEST.*'
  301. '''
  302. _check_minions_return = {'minions': ['minion1'], 'missing': []}
  303. with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)):
  304. self.valid_clear_load['kwargs'].update({'username': 'test_user_func'})
  305. # Wrong last arg
  306. self.valid_clear_load.update({'user': 'test_user_func',
  307. 'tgt': 'minion1',
  308. 'fun': 'test.echo',
  309. 'arg': ['TEST', 'any', 'TESLA']})
  310. self.clear.publish(self.valid_clear_load)
  311. self.assertEqual(self.fire_event_mock.mock_calls, [])
  312. # Wrong first arg
  313. self.valid_clear_load['arg'] = ['TES', 'any', 'TEST1234']
  314. self.clear.publish(self.valid_clear_load)
  315. self.assertEqual(self.fire_event_mock.mock_calls, [])
  316. # Missing the last arg
  317. self.valid_clear_load['arg'] = ['TEST', 'any']
  318. self.clear.publish(self.valid_clear_load)
  319. self.assertEqual(self.fire_event_mock.mock_calls, [])
  320. # No args
  321. self.valid_clear_load['arg'] = []
  322. self.clear.publish(self.valid_clear_load)
  323. self.assertEqual(self.fire_event_mock.mock_calls, [])
  324. @skipIf(salt.utils.platform.is_windows(), 'PAM eauth not available on Windows')
  325. def test_args_kwargs_match(self):
  326. '''
  327. Test simple kwargs restriction allowed.
  328. 'test_user_func':
  329. '*':
  330. - test.echo:
  331. kwargs:
  332. text: 'KWMSG:.*'
  333. '''
  334. _check_minions_return = {'minions': ['some_minions'], 'missing': []}
  335. with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)):
  336. self.valid_clear_load['kwargs'].update({'username': 'test_user_func'})
  337. self.valid_clear_load.update({'user': 'test_user_func',
  338. 'tgt': '*',
  339. 'fun': 'test.echo',
  340. 'arg': [{'text': 'KWMSG: a message',
  341. 'anything': 'hello all',
  342. 'none': 'hello none',
  343. '__kwarg__': True}]})
  344. self.clear.publish(self.valid_clear_load)
  345. self.assertEqual(self.fire_event_mock.call_args[0][0]['fun'], 'test.echo')
  346. def test_args_kwargs_mismatch(self):
  347. '''
  348. Test simple kwargs restriction allowed.
  349. 'test_user_func':
  350. '*':
  351. - test.echo:
  352. kwargs:
  353. text: 'KWMSG:.*'
  354. '''
  355. _check_minions_return = {'minions': ['some_minions'], 'missing': []}
  356. with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)):
  357. self.valid_clear_load['kwargs'].update({'username': 'test_user_func'})
  358. self.valid_clear_load.update({'user': 'test_user_func',
  359. 'tgt': '*',
  360. 'fun': 'test.echo'})
  361. # Wrong kwarg value
  362. self.valid_clear_load['arg'] = [{'text': 'KWMSG a message',
  363. 'anything': 'hello all',
  364. 'none': 'hello none',
  365. '__kwarg__': True}]
  366. self.clear.publish(self.valid_clear_load)
  367. self.assertEqual(self.fire_event_mock.mock_calls, [])
  368. # Missing kwarg value
  369. self.valid_clear_load['arg'] = [{'anything': 'hello all',
  370. 'none': 'hello none',
  371. '__kwarg__': True}]
  372. self.clear.publish(self.valid_clear_load)
  373. self.assertEqual(self.fire_event_mock.mock_calls, [])
  374. self.valid_clear_load['arg'] = [{'__kwarg__': True}]
  375. self.clear.publish(self.valid_clear_load)
  376. self.assertEqual(self.fire_event_mock.mock_calls, [])
  377. self.valid_clear_load['arg'] = [{}]
  378. self.clear.publish(self.valid_clear_load)
  379. self.assertEqual(self.fire_event_mock.mock_calls, [])
  380. self.valid_clear_load['arg'] = []
  381. self.clear.publish(self.valid_clear_load)
  382. self.assertEqual(self.fire_event_mock.mock_calls, [])
  383. # Missing kwarg allowing any value
  384. self.valid_clear_load['arg'] = [{'text': 'KWMSG: a message',
  385. 'none': 'hello none',
  386. '__kwarg__': True}]
  387. self.clear.publish(self.valid_clear_load)
  388. self.assertEqual(self.fire_event_mock.mock_calls, [])
  389. self.valid_clear_load['arg'] = [{'text': 'KWMSG: a message',
  390. 'anything': 'hello all',
  391. '__kwarg__': True}]
  392. self.clear.publish(self.valid_clear_load)
  393. self.assertEqual(self.fire_event_mock.mock_calls, [])
  394. @skipIf(salt.utils.platform.is_windows(), 'PAM eauth not available on Windows')
  395. def test_args_mixed_match(self):
  396. '''
  397. Test mixed args and kwargs restriction allowed.
  398. 'test_user_func':
  399. '*':
  400. - 'my_mod.*':
  401. args:
  402. - 'a.*'
  403. - 'b.*'
  404. kwargs:
  405. 'kwa': 'kwa.*'
  406. 'kwb': 'kwb'
  407. '''
  408. _check_minions_return = {'minions': ['some_minions'], 'missing': []}
  409. with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)):
  410. self.valid_clear_load['kwargs'].update({'username': 'test_user_func'})
  411. self.valid_clear_load.update({'user': 'test_user_func',
  412. 'tgt': '*',
  413. 'fun': 'my_mod.some_func',
  414. 'arg': ['alpha',
  415. 'beta',
  416. 'gamma',
  417. {'kwa': 'kwarg #1',
  418. 'kwb': 'kwb',
  419. 'one_more': 'just one more',
  420. '__kwarg__': True}]})
  421. self.clear.publish(self.valid_clear_load)
  422. self.assertEqual(self.fire_event_mock.call_args[0][0]['fun'],
  423. 'my_mod.some_func')
  424. def test_args_mixed_mismatch(self):
  425. '''
  426. Test mixed args and kwargs restriction forbidden.
  427. 'test_user_func':
  428. '*':
  429. - 'my_mod.*':
  430. args:
  431. - 'a.*'
  432. - 'b.*'
  433. kwargs:
  434. 'kwa': 'kwa.*'
  435. 'kwb': 'kwb'
  436. '''
  437. _check_minions_return = {'minions': ['some_minions'], 'missing': []}
  438. with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)):
  439. self.valid_clear_load['kwargs'].update({'username': 'test_user_func'})
  440. self.valid_clear_load.update({'user': 'test_user_func',
  441. 'tgt': '*',
  442. 'fun': 'my_mod.some_func'})
  443. # Wrong arg value
  444. self.valid_clear_load['arg'] = ['alpha',
  445. 'gamma',
  446. {'kwa': 'kwarg #1',
  447. 'kwb': 'kwb',
  448. 'one_more': 'just one more',
  449. '__kwarg__': True}]
  450. self.clear.publish(self.valid_clear_load)
  451. self.assertEqual(self.fire_event_mock.mock_calls, [])
  452. # Wrong kwarg value
  453. self.valid_clear_load['arg'] = ['alpha',
  454. 'beta',
  455. 'gamma',
  456. {'kwa': 'kkk',
  457. 'kwb': 'kwb',
  458. 'one_more': 'just one more',
  459. '__kwarg__': True}]
  460. self.clear.publish(self.valid_clear_load)
  461. self.assertEqual(self.fire_event_mock.mock_calls, [])
  462. # Missing arg
  463. self.valid_clear_load['arg'] = ['alpha',
  464. {'kwa': 'kwarg #1',
  465. 'kwb': 'kwb',
  466. 'one_more': 'just one more',
  467. '__kwarg__': True}]
  468. self.clear.publish(self.valid_clear_load)
  469. self.assertEqual(self.fire_event_mock.mock_calls, [])
  470. # Missing kwarg
  471. self.valid_clear_load['arg'] = ['alpha',
  472. 'beta',
  473. 'gamma',
  474. {'kwa': 'kwarg #1',
  475. 'one_more': 'just one more',
  476. '__kwarg__': True}]
  477. self.clear.publish(self.valid_clear_load)
  478. self.assertEqual(self.fire_event_mock.mock_calls, [])
  479. class AuthACLTestCase(ModuleCase):
  480. '''
  481. A class to check various aspects of the publisher ACL system
  482. '''
  483. def setUp(self):
  484. self.auth_check_mock = MagicMock(return_value=True)
  485. opts = self.get_temp_config('master')
  486. patches = (
  487. ('salt.minion.MasterMinion', MagicMock()),
  488. ('salt.utils.verify.check_path_traversal', MagicMock()),
  489. ('salt.utils.minions.CkMinions.auth_check', self.auth_check_mock),
  490. ('salt.auth.LoadAuth.time_auth', MagicMock(return_value=True)),
  491. ('salt.client.get_local_client', MagicMock(return_value=opts['conf_file'])),
  492. )
  493. for mod, mock in patches:
  494. patcher = patch(mod, mock)
  495. patcher.start()
  496. self.addCleanup(patcher.stop)
  497. self.addCleanup(delattr, self, 'auth_check_mock')
  498. opts['publisher_acl'] = {}
  499. opts['publisher_acl_blacklist'] = {}
  500. opts['master_job_cache'] = ''
  501. opts['sign_pub_messages'] = False
  502. opts['con_cache'] = ''
  503. opts['external_auth'] = {}
  504. opts['external_auth']['pam'] = {'test_user': [{'alpha_minion': ['test.ping']}]}
  505. self.clear = salt.master.ClearFuncs(opts, MagicMock())
  506. self.addCleanup(delattr, self, 'clear')
  507. # overwrite the _send_pub method so we don't have to serialize MagicMock
  508. self.clear._send_pub = lambda payload: True
  509. # make sure to return a JID, instead of a mock
  510. self.clear.mminion.returners = {'.prep_jid': lambda x: 1}
  511. self.valid_clear_load = {'tgt_type': 'glob',
  512. 'jid': '',
  513. 'cmd': 'publish',
  514. 'tgt': 'test_minion',
  515. 'kwargs':
  516. {'username': 'test_user',
  517. 'password': 'test_password',
  518. 'show_timeout': False,
  519. 'eauth': 'pam',
  520. 'show_jid': False},
  521. 'ret': '',
  522. 'user': 'test_user',
  523. 'key': '',
  524. 'arg': '',
  525. 'fun': 'test.ping',
  526. }
  527. self.addCleanup(delattr, self, 'valid_clear_load')
  528. @skipIf(WAR_ROOM_SKIP, 'WAR ROOM TEMPORARY SKIP')
  529. @skipIf(salt.utils.platform.is_windows(), 'PAM eauth not available on Windows')
  530. def test_acl_simple_allow(self):
  531. self.clear.publish(self.valid_clear_load)
  532. self.assertEqual(self.auth_check_mock.call_args[0][0],
  533. [{'alpha_minion': ['test.ping']}])
  534. def test_acl_simple_deny(self):
  535. with patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=[{'beta_minion': ['test.ping']}])):
  536. self.clear.publish(self.valid_clear_load)
  537. self.assertEqual(self.auth_check_mock.call_args[0][0],
  538. [{'beta_minion': ['test.ping']}])