test_master.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. # -*- coding: utf-8 -*-
  2. from __future__ import absolute_import
  3. import salt.config
  4. import salt.master
  5. from tests.support.helpers import slowTest
  6. from tests.support.mock import MagicMock, patch
  7. from tests.support.unit import TestCase
  8. class TransportMethodsTest(TestCase):
  9. def test_transport_methods(self):
  10. class Foo(salt.master.TransportMethods):
  11. expose_methods = ["bar"]
  12. def bar(self):
  13. pass
  14. def bang(self):
  15. pass
  16. foo = Foo()
  17. assert foo.get_method("bar") is not None
  18. assert foo.get_method("bang") is None
  19. def test_aes_funcs_white(self):
  20. """
  21. Validate methods exposed on AESFuncs exist and are callable
  22. """
  23. opts = salt.config.master_config(None)
  24. aes_funcs = salt.master.AESFuncs(opts)
  25. for name in aes_funcs.expose_methods:
  26. func = getattr(aes_funcs, name, None)
  27. assert callable(func)
  28. def test_aes_funcs_black(self):
  29. """
  30. Validate methods on AESFuncs that should not be called remotely
  31. """
  32. opts = salt.config.master_config(None)
  33. aes_funcs = salt.master.AESFuncs(opts)
  34. # Any callable that should not explicitly be allowed should be added
  35. # here.
  36. blacklist_methods = [
  37. "_AESFuncs__setup_fileserver",
  38. "_AESFuncs__verify_load",
  39. "_AESFuncs__verify_minion",
  40. "_AESFuncs__verify_minion_publish",
  41. "__class__",
  42. "__delattr__",
  43. "__dir__",
  44. "__eq__",
  45. "__format__",
  46. "__ge__",
  47. "__getattribute__",
  48. "__gt__",
  49. "__hash__",
  50. "__init__",
  51. "__init_subclass__",
  52. "__le__",
  53. "__lt__",
  54. "__ne__",
  55. "__new__",
  56. "__reduce__",
  57. "__reduce_ex__",
  58. "__repr__",
  59. "__setattr__",
  60. "__sizeof__",
  61. "__str__",
  62. "__subclasshook__",
  63. "get_method",
  64. "run_func",
  65. ]
  66. for name in dir(aes_funcs):
  67. if name in aes_funcs.expose_methods:
  68. continue
  69. if not callable(getattr(aes_funcs, name)):
  70. continue
  71. assert name in blacklist_methods, name
  72. def test_clear_funcs_white(self):
  73. """
  74. Validate methods exposed on ClearFuncs exist and are callable
  75. """
  76. opts = salt.config.master_config(None)
  77. clear_funcs = salt.master.ClearFuncs(opts, {})
  78. for name in clear_funcs.expose_methods:
  79. func = getattr(clear_funcs, name, None)
  80. assert callable(func)
  81. def test_clear_funcs_black(self):
  82. """
  83. Validate methods on ClearFuncs that should not be called remotely
  84. """
  85. opts = salt.config.master_config(None)
  86. clear_funcs = salt.master.ClearFuncs(opts, {})
  87. blacklist_methods = [
  88. "__class__",
  89. "__delattr__",
  90. "__dir__",
  91. "__eq__",
  92. "__format__",
  93. "__ge__",
  94. "__getattribute__",
  95. "__gt__",
  96. "__hash__",
  97. "__init__",
  98. "__init_subclass__",
  99. "__le__",
  100. "__lt__",
  101. "__ne__",
  102. "__new__",
  103. "__reduce__",
  104. "__reduce_ex__",
  105. "__repr__",
  106. "__setattr__",
  107. "__sizeof__",
  108. "__str__",
  109. "__subclasshook__",
  110. "_prep_auth_info",
  111. "_prep_jid",
  112. "_prep_pub",
  113. "_send_pub",
  114. "_send_ssh_pub",
  115. "get_method",
  116. ]
  117. for name in dir(clear_funcs):
  118. if name in clear_funcs.expose_methods:
  119. continue
  120. if not callable(getattr(clear_funcs, name)):
  121. continue
  122. assert name in blacklist_methods, name
  123. class ClearFuncsTestCase(TestCase):
  124. """
  125. TestCase for salt.master.ClearFuncs class
  126. """
  127. @classmethod
  128. def setUpClass(cls):
  129. opts = salt.config.master_config(None)
  130. cls.clear_funcs = salt.master.ClearFuncs(opts, {})
  131. @classmethod
  132. def tearDownClass(cls):
  133. del cls.clear_funcs
  134. def test_get_method(self):
  135. assert getattr(self.clear_funcs, "_send_pub", None) is not None
  136. assert self.clear_funcs.get_method("_send_pub") is None
  137. # runner tests
  138. @slowTest
  139. def test_runner_token_not_authenticated(self):
  140. """
  141. Asserts that a TokenAuthenticationError is returned when the token can't authenticate.
  142. """
  143. mock_ret = {
  144. "error": {
  145. "name": "TokenAuthenticationError",
  146. "message": 'Authentication failure of type "token" occurred.',
  147. }
  148. }
  149. ret = self.clear_funcs.runner({"token": "asdfasdfasdfasdf"})
  150. self.assertDictEqual(mock_ret, ret)
  151. @slowTest
  152. def test_runner_token_authorization_error(self):
  153. """
  154. Asserts that a TokenAuthenticationError is returned when the token authenticates, but is
  155. not authorized.
  156. """
  157. token = "asdfasdfasdfasdf"
  158. clear_load = {"token": token, "fun": "test.arg"}
  159. mock_token = {"token": token, "eauth": "foo", "name": "test"}
  160. mock_ret = {
  161. "error": {
  162. "name": "TokenAuthenticationError",
  163. "message": 'Authentication failure of type "token" occurred '
  164. "for user test.",
  165. }
  166. }
  167. with patch(
  168. "salt.auth.LoadAuth.authenticate_token", MagicMock(return_value=mock_token)
  169. ), patch("salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[])):
  170. ret = self.clear_funcs.runner(clear_load)
  171. self.assertDictEqual(mock_ret, ret)
  172. @slowTest
  173. def test_runner_token_salt_invocation_error(self):
  174. """
  175. Asserts that a SaltInvocationError is returned when the token authenticates, but the
  176. command is malformed.
  177. """
  178. token = "asdfasdfasdfasdf"
  179. clear_load = {"token": token, "fun": "badtestarg"}
  180. mock_token = {"token": token, "eauth": "foo", "name": "test"}
  181. mock_ret = {
  182. "error": {
  183. "name": "SaltInvocationError",
  184. "message": "A command invocation error occurred: Check syntax.",
  185. }
  186. }
  187. with patch(
  188. "salt.auth.LoadAuth.authenticate_token", MagicMock(return_value=mock_token)
  189. ), patch(
  190. "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=["testing"])
  191. ):
  192. ret = self.clear_funcs.runner(clear_load)
  193. self.assertDictEqual(mock_ret, ret)
  194. @slowTest
  195. def test_runner_eauth_not_authenticated(self):
  196. """
  197. Asserts that an EauthAuthenticationError is returned when the user can't authenticate.
  198. """
  199. mock_ret = {
  200. "error": {
  201. "name": "EauthAuthenticationError",
  202. "message": 'Authentication failure of type "eauth" occurred for '
  203. "user UNKNOWN.",
  204. }
  205. }
  206. ret = self.clear_funcs.runner({"eauth": "foo"})
  207. self.assertDictEqual(mock_ret, ret)
  208. @slowTest
  209. def test_runner_eauth_authorization_error(self):
  210. """
  211. Asserts that an EauthAuthenticationError is returned when the user authenticates, but is
  212. not authorized.
  213. """
  214. clear_load = {"eauth": "foo", "username": "test", "fun": "test.arg"}
  215. mock_ret = {
  216. "error": {
  217. "name": "EauthAuthenticationError",
  218. "message": 'Authentication failure of type "eauth" occurred for '
  219. "user test.",
  220. }
  221. }
  222. with patch(
  223. "salt.auth.LoadAuth.authenticate_eauth", MagicMock(return_value=True)
  224. ), patch("salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[])):
  225. ret = self.clear_funcs.runner(clear_load)
  226. self.assertDictEqual(mock_ret, ret)
  227. @slowTest
  228. def test_runner_eauth_salt_invocation_error(self):
  229. """
  230. Asserts that an EauthAuthenticationError is returned when the user authenticates, but the
  231. command is malformed.
  232. """
  233. clear_load = {"eauth": "foo", "username": "test", "fun": "bad.test.arg.func"}
  234. mock_ret = {
  235. "error": {
  236. "name": "SaltInvocationError",
  237. "message": "A command invocation error occurred: Check syntax.",
  238. }
  239. }
  240. with patch(
  241. "salt.auth.LoadAuth.authenticate_eauth", MagicMock(return_value=True)
  242. ), patch(
  243. "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=["testing"])
  244. ):
  245. ret = self.clear_funcs.runner(clear_load)
  246. self.assertDictEqual(mock_ret, ret)
  247. @slowTest
  248. def test_runner_user_not_authenticated(self):
  249. """
  250. Asserts that an UserAuthenticationError is returned when the user can't authenticate.
  251. """
  252. mock_ret = {
  253. "error": {
  254. "name": "UserAuthenticationError",
  255. "message": 'Authentication failure of type "user" occurred',
  256. }
  257. }
  258. ret = self.clear_funcs.runner({})
  259. self.assertDictEqual(mock_ret, ret)
  260. # wheel tests
  261. @slowTest
  262. def test_wheel_token_not_authenticated(self):
  263. """
  264. Asserts that a TokenAuthenticationError is returned when the token can't authenticate.
  265. """
  266. mock_ret = {
  267. "error": {
  268. "name": "TokenAuthenticationError",
  269. "message": 'Authentication failure of type "token" occurred.',
  270. }
  271. }
  272. ret = self.clear_funcs.wheel({"token": "asdfasdfasdfasdf"})
  273. self.assertDictEqual(mock_ret, ret)
  274. @slowTest
  275. def test_wheel_token_authorization_error(self):
  276. """
  277. Asserts that a TokenAuthenticationError is returned when the token authenticates, but is
  278. not authorized.
  279. """
  280. token = "asdfasdfasdfasdf"
  281. clear_load = {"token": token, "fun": "test.arg"}
  282. mock_token = {"token": token, "eauth": "foo", "name": "test"}
  283. mock_ret = {
  284. "error": {
  285. "name": "TokenAuthenticationError",
  286. "message": 'Authentication failure of type "token" occurred '
  287. "for user test.",
  288. }
  289. }
  290. with patch(
  291. "salt.auth.LoadAuth.authenticate_token", MagicMock(return_value=mock_token)
  292. ), patch("salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[])):
  293. ret = self.clear_funcs.wheel(clear_load)
  294. self.assertDictEqual(mock_ret, ret)
  295. @slowTest
  296. def test_wheel_token_salt_invocation_error(self):
  297. """
  298. Asserts that a SaltInvocationError is returned when the token authenticates, but the
  299. command is malformed.
  300. """
  301. token = "asdfasdfasdfasdf"
  302. clear_load = {"token": token, "fun": "badtestarg"}
  303. mock_token = {"token": token, "eauth": "foo", "name": "test"}
  304. mock_ret = {
  305. "error": {
  306. "name": "SaltInvocationError",
  307. "message": "A command invocation error occurred: Check syntax.",
  308. }
  309. }
  310. with patch(
  311. "salt.auth.LoadAuth.authenticate_token", MagicMock(return_value=mock_token)
  312. ), patch(
  313. "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=["testing"])
  314. ):
  315. ret = self.clear_funcs.wheel(clear_load)
  316. self.assertDictEqual(mock_ret, ret)
  317. @slowTest
  318. def test_wheel_eauth_not_authenticated(self):
  319. """
  320. Asserts that an EauthAuthenticationError is returned when the user can't authenticate.
  321. """
  322. mock_ret = {
  323. "error": {
  324. "name": "EauthAuthenticationError",
  325. "message": 'Authentication failure of type "eauth" occurred for '
  326. "user UNKNOWN.",
  327. }
  328. }
  329. ret = self.clear_funcs.wheel({"eauth": "foo"})
  330. self.assertDictEqual(mock_ret, ret)
  331. @slowTest
  332. def test_wheel_eauth_authorization_error(self):
  333. """
  334. Asserts that an EauthAuthenticationError is returned when the user authenticates, but is
  335. not authorized.
  336. """
  337. clear_load = {"eauth": "foo", "username": "test", "fun": "test.arg"}
  338. mock_ret = {
  339. "error": {
  340. "name": "EauthAuthenticationError",
  341. "message": 'Authentication failure of type "eauth" occurred for '
  342. "user test.",
  343. }
  344. }
  345. with patch(
  346. "salt.auth.LoadAuth.authenticate_eauth", MagicMock(return_value=True)
  347. ), patch("salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[])):
  348. ret = self.clear_funcs.wheel(clear_load)
  349. self.assertDictEqual(mock_ret, ret)
  350. @slowTest
  351. def test_wheel_eauth_salt_invocation_error(self):
  352. """
  353. Asserts that an EauthAuthenticationError is returned when the user authenticates, but the
  354. command is malformed.
  355. """
  356. clear_load = {"eauth": "foo", "username": "test", "fun": "bad.test.arg.func"}
  357. mock_ret = {
  358. "error": {
  359. "name": "SaltInvocationError",
  360. "message": "A command invocation error occurred: Check syntax.",
  361. }
  362. }
  363. with patch(
  364. "salt.auth.LoadAuth.authenticate_eauth", MagicMock(return_value=True)
  365. ), patch(
  366. "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=["testing"])
  367. ):
  368. ret = self.clear_funcs.wheel(clear_load)
  369. self.assertDictEqual(mock_ret, ret)
  370. @slowTest
  371. def test_wheel_user_not_authenticated(self):
  372. """
  373. Asserts that an UserAuthenticationError is returned when the user can't authenticate.
  374. """
  375. mock_ret = {
  376. "error": {
  377. "name": "UserAuthenticationError",
  378. "message": 'Authentication failure of type "user" occurred',
  379. }
  380. }
  381. ret = self.clear_funcs.wheel({})
  382. self.assertDictEqual(mock_ret, ret)
  383. # publish tests
  384. @slowTest
  385. def test_publish_user_is_blacklisted(self):
  386. """
  387. Asserts that an AuthorizationError is returned when the user has been blacklisted.
  388. """
  389. mock_ret = {
  390. "error": {
  391. "name": "AuthorizationError",
  392. "message": "Authorization error occurred.",
  393. }
  394. }
  395. with patch(
  396. "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=True)
  397. ):
  398. self.assertEqual(
  399. mock_ret, self.clear_funcs.publish({"user": "foo", "fun": "test.arg"})
  400. )
  401. @slowTest
  402. def test_publish_cmd_blacklisted(self):
  403. """
  404. Asserts that an AuthorizationError is returned when the command has been blacklisted.
  405. """
  406. mock_ret = {
  407. "error": {
  408. "name": "AuthorizationError",
  409. "message": "Authorization error occurred.",
  410. }
  411. }
  412. with patch(
  413. "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False)
  414. ), patch(
  415. "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=True)
  416. ):
  417. self.assertEqual(
  418. mock_ret, self.clear_funcs.publish({"user": "foo", "fun": "test.arg"})
  419. )
  420. @slowTest
  421. def test_publish_token_not_authenticated(self):
  422. """
  423. Asserts that an AuthenticationError is returned when the token can't authenticate.
  424. """
  425. mock_ret = {
  426. "error": {
  427. "name": "AuthenticationError",
  428. "message": "Authentication error occurred.",
  429. }
  430. }
  431. load = {
  432. "user": "foo",
  433. "fun": "test.arg",
  434. "tgt": "test_minion",
  435. "kwargs": {"token": "asdfasdfasdfasdf"},
  436. }
  437. with patch(
  438. "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False)
  439. ), patch(
  440. "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False)
  441. ):
  442. self.assertEqual(mock_ret, self.clear_funcs.publish(load))
  443. @slowTest
  444. def test_publish_token_authorization_error(self):
  445. """
  446. Asserts that an AuthorizationError is returned when the token authenticates, but is not
  447. authorized.
  448. """
  449. token = "asdfasdfasdfasdf"
  450. load = {
  451. "user": "foo",
  452. "fun": "test.arg",
  453. "tgt": "test_minion",
  454. "arg": "bar",
  455. "kwargs": {"token": token},
  456. }
  457. mock_token = {"token": token, "eauth": "foo", "name": "test"}
  458. mock_ret = {
  459. "error": {
  460. "name": "AuthorizationError",
  461. "message": "Authorization error occurred.",
  462. }
  463. }
  464. with patch(
  465. "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False)
  466. ), patch(
  467. "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False)
  468. ), patch(
  469. "salt.auth.LoadAuth.authenticate_token", MagicMock(return_value=mock_token)
  470. ), patch(
  471. "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[])
  472. ):
  473. self.assertEqual(mock_ret, self.clear_funcs.publish(load))
  474. @slowTest
  475. def test_publish_eauth_not_authenticated(self):
  476. """
  477. Asserts that an AuthenticationError is returned when the user can't authenticate.
  478. """
  479. load = {
  480. "user": "test",
  481. "fun": "test.arg",
  482. "tgt": "test_minion",
  483. "kwargs": {"eauth": "foo"},
  484. }
  485. mock_ret = {
  486. "error": {
  487. "name": "AuthenticationError",
  488. "message": "Authentication error occurred.",
  489. }
  490. }
  491. with patch(
  492. "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False)
  493. ), patch(
  494. "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False)
  495. ):
  496. self.assertEqual(mock_ret, self.clear_funcs.publish(load))
  497. @slowTest
  498. def test_publish_eauth_authorization_error(self):
  499. """
  500. Asserts that an AuthorizationError is returned when the user authenticates, but is not
  501. authorized.
  502. """
  503. load = {
  504. "user": "test",
  505. "fun": "test.arg",
  506. "tgt": "test_minion",
  507. "kwargs": {"eauth": "foo"},
  508. "arg": "bar",
  509. }
  510. mock_ret = {
  511. "error": {
  512. "name": "AuthorizationError",
  513. "message": "Authorization error occurred.",
  514. }
  515. }
  516. with patch(
  517. "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False)
  518. ), patch(
  519. "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False)
  520. ), patch(
  521. "salt.auth.LoadAuth.authenticate_eauth", MagicMock(return_value=True)
  522. ), patch(
  523. "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[])
  524. ):
  525. self.assertEqual(mock_ret, self.clear_funcs.publish(load))
  526. @slowTest
  527. def test_publish_user_not_authenticated(self):
  528. """
  529. Asserts that an AuthenticationError is returned when the user can't authenticate.
  530. """
  531. load = {"user": "test", "fun": "test.arg", "tgt": "test_minion"}
  532. mock_ret = {
  533. "error": {
  534. "name": "AuthenticationError",
  535. "message": "Authentication error occurred.",
  536. }
  537. }
  538. with patch(
  539. "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False)
  540. ), patch(
  541. "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False)
  542. ):
  543. self.assertEqual(mock_ret, self.clear_funcs.publish(load))
  544. @slowTest
  545. def test_publish_user_authenticated_missing_auth_list(self):
  546. """
  547. Asserts that an AuthenticationError is returned when the user has an effective user id and is
  548. authenticated, but the auth_list is empty.
  549. """
  550. load = {
  551. "user": "test",
  552. "fun": "test.arg",
  553. "tgt": "test_minion",
  554. "kwargs": {"user": "test"},
  555. "arg": "foo",
  556. }
  557. mock_ret = {
  558. "error": {
  559. "name": "AuthenticationError",
  560. "message": "Authentication error occurred.",
  561. }
  562. }
  563. with patch(
  564. "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False)
  565. ), patch(
  566. "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False)
  567. ), patch(
  568. "salt.auth.LoadAuth.authenticate_key",
  569. MagicMock(return_value="fake-user-key"),
  570. ), patch(
  571. "salt.utils.master.get_values_of_matching_keys", MagicMock(return_value=[])
  572. ):
  573. self.assertEqual(mock_ret, self.clear_funcs.publish(load))
  574. @slowTest
  575. def test_publish_user_authorization_error(self):
  576. """
  577. Asserts that an AuthorizationError is returned when the user authenticates, but is not
  578. authorized.
  579. """
  580. load = {
  581. "user": "test",
  582. "fun": "test.arg",
  583. "tgt": "test_minion",
  584. "kwargs": {"user": "test"},
  585. "arg": "foo",
  586. }
  587. mock_ret = {
  588. "error": {
  589. "name": "AuthorizationError",
  590. "message": "Authorization error occurred.",
  591. }
  592. }
  593. with patch(
  594. "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False)
  595. ), patch(
  596. "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False)
  597. ), patch(
  598. "salt.auth.LoadAuth.authenticate_key",
  599. MagicMock(return_value="fake-user-key"),
  600. ), patch(
  601. "salt.utils.master.get_values_of_matching_keys",
  602. MagicMock(return_value=["test"]),
  603. ), patch(
  604. "salt.utils.minions.CkMinions.auth_check", MagicMock(return_value=False)
  605. ):
  606. self.assertEqual(mock_ret, self.clear_funcs.publish(load))