test_app.py 9.6 KB

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