test_app.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. # coding: utf-8
  2. # Import python libs
  3. from __future__ import absolute_import
  4. import os
  5. # Import salt libs
  6. import salt.utils.json
  7. import salt.utils.stringutils
  8. # Import test support libs
  9. import tests.support.cherrypy_testclasses as cptc
  10. from tests.support.helpers import flaky
  11. # Import 3rd-party libs
  12. from salt.ext.six.moves.urllib.parse import urlencode # pylint: disable=no-name-in-module,import-error
  13. class TestAuth(cptc.BaseRestCherryPyTest):
  14. def test_get_root_noauth(self):
  15. '''
  16. GET requests to the root URL should not require auth
  17. '''
  18. request, response = self.request('/')
  19. self.assertEqual(response.status, '200 OK')
  20. def test_post_root_auth(self):
  21. '''
  22. POST requests to the root URL redirect to login
  23. '''
  24. request, response = self.request('/', method='POST', data={})
  25. self.assertEqual(response.status, '401 Unauthorized')
  26. def test_login_noauth(self):
  27. '''
  28. GET requests to the login URL should not require auth
  29. '''
  30. request, response = self.request('/login')
  31. self.assertEqual(response.status, '200 OK')
  32. def test_webhook_auth(self):
  33. '''
  34. Requests to the webhook URL require auth by default
  35. '''
  36. request, response = self.request('/hook', method='POST', data={})
  37. self.assertEqual(response.status, '401 Unauthorized')
  38. class TestLogin(cptc.BaseRestCherryPyTest):
  39. auth_creds = (
  40. ('username', 'saltdev'),
  41. ('password', 'saltdev'),
  42. ('eauth', 'auto'))
  43. def test_good_login(self):
  44. '''
  45. Test logging in
  46. '''
  47. body = urlencode(self.auth_creds)
  48. request, response = self.request('/login', method='POST', body=body,
  49. headers={
  50. 'content-type': 'application/x-www-form-urlencoded'
  51. })
  52. self.assertEqual(response.status, '200 OK')
  53. return response
  54. def test_bad_login(self):
  55. '''
  56. Test logging in
  57. '''
  58. body = urlencode({'totally': 'invalid_creds'})
  59. request, response = self.request('/login', method='POST', body=body,
  60. headers={
  61. 'content-type': 'application/x-www-form-urlencoded'
  62. })
  63. self.assertEqual(response.status, '401 Unauthorized')
  64. def test_logout(self):
  65. ret = self.test_good_login()
  66. token = ret.headers['X-Auth-Token']
  67. body = urlencode({})
  68. request, response = self.request('/logout', method='POST', body=body,
  69. headers={
  70. 'content-type': 'application/x-www-form-urlencoded',
  71. 'X-Auth-Token': token,
  72. })
  73. self.assertEqual(response.status, '200 OK')
  74. class TestRun(cptc.BaseRestCherryPyTest):
  75. auth_creds = (
  76. ('username', 'saltdev_auto'),
  77. ('password', 'saltdev'),
  78. ('eauth', 'auto'))
  79. low = (
  80. ('client', 'local'),
  81. ('tgt', '*'),
  82. ('fun', 'test.ping'),
  83. )
  84. def test_run_good_login(self):
  85. '''
  86. Test the run URL with good auth credentials
  87. '''
  88. cmd = dict(self.low, **dict(self.auth_creds))
  89. body = urlencode(cmd)
  90. request, response = self.request('/run', method='POST', body=body,
  91. headers={
  92. 'content-type': 'application/x-www-form-urlencoded'
  93. })
  94. self.assertEqual(response.status, '200 OK')
  95. def test_run_bad_login(self):
  96. '''
  97. Test the run URL with bad auth credentials
  98. '''
  99. cmd = dict(self.low, **{'totally': 'invalid_creds'})
  100. body = urlencode(cmd)
  101. request, response = self.request('/run', method='POST', body=body,
  102. headers={
  103. 'content-type': 'application/x-www-form-urlencoded'
  104. })
  105. self.assertEqual(response.status, '401 Unauthorized')
  106. def test_run_empty_token(self):
  107. '''
  108. Test the run URL with empty token
  109. '''
  110. cmd = dict(self.low, **{'token': ''})
  111. body = urlencode(cmd)
  112. request, response = self.request('/run', method='POST', body=body,
  113. headers={
  114. 'content-type': 'application/x-www-form-urlencoded'
  115. })
  116. assert response.status == '401 Unauthorized'
  117. def test_run_empty_token_upercase(self):
  118. '''
  119. Test the run URL with empty token with upercase characters
  120. '''
  121. cmd = dict(self.low, **{'ToKen': ''})
  122. body = urlencode(cmd)
  123. request, response = self.request('/run', method='POST', body=body,
  124. headers={
  125. 'content-type': 'application/x-www-form-urlencoded'
  126. })
  127. assert response.status == '401 Unauthorized'
  128. def test_run_wrong_token(self):
  129. '''
  130. Test the run URL with incorrect token
  131. '''
  132. cmd = dict(self.low, **{'token': 'bad'})
  133. body = urlencode(cmd)
  134. request, response = self.request('/run', method='POST', body=body,
  135. headers={
  136. 'content-type': 'application/x-www-form-urlencoded'
  137. })
  138. assert response.status == '401 Unauthorized'
  139. def test_run_pathname_token(self):
  140. '''
  141. Test the run URL with path that exists in token
  142. '''
  143. cmd = dict(self.low, **{'token': os.path.join('etc', 'passwd')})
  144. body = urlencode(cmd)
  145. request, response = self.request('/run', method='POST', body=body,
  146. headers={
  147. 'content-type': 'application/x-www-form-urlencoded'
  148. })
  149. assert response.status == '401 Unauthorized'
  150. def test_run_pathname_not_exists_token(self):
  151. '''
  152. Test the run URL with path that does not exist in token
  153. '''
  154. cmd = dict(self.low, **{'token': os.path.join('tmp', 'doesnotexist')})
  155. body = urlencode(cmd)
  156. request, response = self.request('/run', method='POST', body=body,
  157. headers={
  158. 'content-type': 'application/x-www-form-urlencoded'
  159. })
  160. assert response.status == '401 Unauthorized'
  161. def test_run_extra_parameters(self):
  162. '''
  163. Test the run URL with good auth credentials
  164. '''
  165. cmd = dict(self.low, **dict(self.auth_creds))
  166. cmd['id_'] = 'someminionname'
  167. body = urlencode(cmd)
  168. request, response = self.request('/run', method='POST', body=body,
  169. headers={
  170. 'content-type': 'application/x-www-form-urlencoded'
  171. })
  172. self.assertEqual(response.status, '200 OK')
  173. class TestWebhookDisableAuth(cptc.BaseRestCherryPyTest):
  174. def __get_opts__(self):
  175. return {
  176. 'rest_cherrypy': {
  177. 'port': 8000,
  178. 'debug': True,
  179. 'webhook_disable_auth': True,
  180. },
  181. }
  182. def test_webhook_noauth(self):
  183. '''
  184. Auth can be disabled for requests to the webhook URL
  185. '''
  186. body = urlencode({'foo': 'Foo!'})
  187. request, response = self.request('/hook', method='POST', body=body,
  188. headers={
  189. 'content-type': 'application/x-www-form-urlencoded'
  190. })
  191. self.assertEqual(response.status, '200 OK')
  192. class TestArgKwarg(cptc.BaseRestCherryPyTest):
  193. auth_creds = (
  194. ('username', 'saltdev'),
  195. ('password', 'saltdev'),
  196. ('eauth', 'auto'))
  197. low = (
  198. ('client', 'runner'),
  199. ('fun', 'test.arg'),
  200. # use singular form for arg and kwarg
  201. ('arg', [1234]),
  202. ('kwarg', {'ext_source': 'redis'}),
  203. )
  204. def _token(self):
  205. '''
  206. Return the token
  207. '''
  208. body = urlencode(self.auth_creds)
  209. request, response = self.request(
  210. '/login',
  211. method='POST',
  212. body=body,
  213. headers={
  214. 'content-type': 'application/x-www-form-urlencoded'
  215. }
  216. )
  217. return response.headers['X-Auth-Token']
  218. def test_accepts_arg_kwarg_keys(self):
  219. '''
  220. Ensure that (singular) arg and kwarg keys (for passing parameters)
  221. are supported by runners.
  222. '''
  223. cmd = dict(self.low)
  224. body = salt.utils.json.dumps(cmd)
  225. request, response = self.request(
  226. '/',
  227. method='POST',
  228. body=body,
  229. headers={
  230. 'content-type': 'application/json',
  231. 'X-Auth-Token': self._token(),
  232. 'Accept': 'application/json',
  233. }
  234. )
  235. resp = salt.utils.json.loads(salt.utils.stringutils.to_str(response.body[0]))
  236. self.assertEqual(resp['return'][0]['args'], [1234])
  237. self.assertEqual(resp['return'][0]['kwargs'],
  238. {'ext_source': 'redis'})
  239. class TestJobs(cptc.BaseRestCherryPyTest):
  240. auth_creds = (
  241. ('username', 'saltdev_auto'),
  242. ('password', 'saltdev'),
  243. ('eauth', 'auto'))
  244. low = (
  245. ('client', 'local'),
  246. ('tgt', '*'),
  247. ('fun', 'test.ping'),
  248. )
  249. def _token(self):
  250. '''
  251. Return the token
  252. '''
  253. body = urlencode(self.auth_creds)
  254. request, response = self.request(
  255. '/login',
  256. method='POST',
  257. body=body,
  258. headers={
  259. 'content-type': 'application/x-www-form-urlencoded'
  260. }
  261. )
  262. return response.headers['X-Auth-Token']
  263. def _add_job(self):
  264. '''
  265. Helper function to add a job to the job cache
  266. '''
  267. cmd = dict(self.low, **dict(self.auth_creds))
  268. body = urlencode(cmd)
  269. request, response = self.request('/run', method='POST', body=body,
  270. headers={
  271. 'content-type': 'application/x-www-form-urlencoded'
  272. })
  273. self.assertEqual(response.status, '200 OK')
  274. @flaky
  275. def test_all_jobs(self):
  276. '''
  277. test query to /jobs returns job data
  278. '''
  279. self._add_job()
  280. request, response = self.request('/jobs', method='GET',
  281. headers={
  282. 'Accept': 'application/json',
  283. 'X-Auth-Token': self._token(),
  284. })
  285. resp = salt.utils.json.loads(salt.utils.stringutils.to_str(response.body[0]))
  286. self.assertIn('test.ping', str(resp['return']))
  287. self.assertEqual(response.status, '200 OK')