test_url.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. # -*- coding: utf-8 -*-
  2. # Import python libs
  3. from __future__ import absolute_import, print_function, unicode_literals
  4. # Import Salt Libs
  5. import salt.utils.platform
  6. import salt.utils.url
  7. # Import Salt Testing Libs
  8. from tests.support.unit import TestCase, skipIf
  9. from tests.support.mock import (
  10. MagicMock,
  11. patch,
  12. NO_MOCK,
  13. NO_MOCK_REASON
  14. )
  15. @skipIf(NO_MOCK, NO_MOCK_REASON)
  16. class UrlTestCase(TestCase):
  17. '''
  18. TestCase for salt.utils.url module
  19. '''
  20. # parse tests
  21. def test_parse_path(self):
  22. '''
  23. Test parsing an ordinary path
  24. '''
  25. path = 'interesting?/path&.conf:and other things'
  26. self.assertEqual(salt.utils.url.parse(path), (path, None))
  27. def test_parse_salt_url(self):
  28. '''
  29. Test parsing a 'salt://' URL
  30. '''
  31. path = '?funny/path with {interesting|chars}'
  32. url = 'salt://' + path
  33. if salt.utils.platform.is_windows():
  34. path = '_funny/path with {interesting_chars}'
  35. self.assertEqual(salt.utils.url.parse(url), (path, None))
  36. def test_parse_salt_saltenv(self):
  37. '''
  38. Test parsing a 'salt://' URL with a '?saltenv=' query
  39. '''
  40. saltenv = 'ambience'
  41. path = '?funny/path&with {interesting|chars}'
  42. url = 'salt://' + path + '?saltenv=' + saltenv
  43. if salt.utils.platform.is_windows():
  44. path = '_funny/path&with {interesting_chars}'
  45. self.assertEqual(salt.utils.url.parse(url), (path, saltenv))
  46. # create tests
  47. def test_create_url(self):
  48. '''
  49. Test creating a 'salt://' URL
  50. '''
  51. path = '? interesting/&path.filetype'
  52. url = 'salt://' + path
  53. if salt.utils.platform.is_windows():
  54. url = 'salt://_ interesting/&path.filetype'
  55. self.assertEqual(salt.utils.url.create(path), url)
  56. def test_create_url_saltenv(self):
  57. '''
  58. Test creating a 'salt://' URL with a saltenv
  59. '''
  60. saltenv = 'raumklang'
  61. path = '? interesting/&path.filetype'
  62. if salt.utils.platform.is_windows():
  63. path = '_ interesting/&path.filetype'
  64. url = 'salt://' + path + '?saltenv=' + saltenv
  65. self.assertEqual(salt.utils.url.create(path, saltenv), url)
  66. # is_escaped tests
  67. def test_is_escaped_windows(self):
  68. '''
  69. Test not testing a 'salt://' URL on windows
  70. '''
  71. url = 'salt://dir/file.ini'
  72. with patch('salt.utils.platform.is_windows', MagicMock(return_value=True)):
  73. self.assertFalse(salt.utils.url.is_escaped(url))
  74. def test_is_escaped_escaped_path(self):
  75. '''
  76. Test testing an escaped path
  77. '''
  78. path = '|dir/file.conf?saltenv=basic'
  79. self.assertTrue(salt.utils.url.is_escaped(path))
  80. def test_is_escaped_unescaped_path(self):
  81. '''
  82. Test testing an unescaped path
  83. '''
  84. path = 'dir/file.conf'
  85. self.assertFalse(salt.utils.url.is_escaped(path))
  86. def test_is_escaped_escaped_url(self):
  87. '''
  88. Test testing an escaped 'salt://' URL
  89. '''
  90. url = 'salt://|dir/file.conf?saltenv=basic'
  91. self.assertTrue(salt.utils.url.is_escaped(url))
  92. def test_is_escaped_unescaped_url(self):
  93. '''
  94. Test testing an unescaped 'salt://' URL
  95. '''
  96. url = 'salt://dir/file.conf'
  97. self.assertFalse(salt.utils.url.is_escaped(url))
  98. def test_is_escaped_generic_url(self):
  99. '''
  100. Test testing an unescaped 'salt://' URL
  101. '''
  102. url = 'https://gentoo.org/'
  103. self.assertFalse(salt.utils.url.is_escaped(url))
  104. # escape tests
  105. def test_escape_windows(self):
  106. '''
  107. Test not escaping a 'salt://' URL on windows
  108. '''
  109. url = 'salt://dir/file.ini'
  110. with patch('salt.utils.platform.is_windows', MagicMock(return_value=True)):
  111. self.assertEqual(salt.utils.url.escape(url), url)
  112. def test_escape_escaped_path(self):
  113. '''
  114. Test escaping an escaped path
  115. '''
  116. resource = '|dir/file.conf?saltenv=basic'
  117. self.assertEqual(salt.utils.url.escape(resource), resource)
  118. def test_escape_unescaped_path(self):
  119. '''
  120. Test escaping an unescaped path
  121. '''
  122. path = 'dir/file.conf'
  123. escaped_path = '|' + path
  124. if salt.utils.platform.is_windows():
  125. escaped_path = path
  126. self.assertEqual(salt.utils.url.escape(path), escaped_path)
  127. def test_escape_escaped_url(self):
  128. '''
  129. Test testing an escaped 'salt://' URL
  130. '''
  131. url = 'salt://|dir/file.conf?saltenv=basic'
  132. self.assertEqual(salt.utils.url.escape(url), url)
  133. def test_escape_unescaped_url(self):
  134. '''
  135. Test testing an unescaped 'salt://' URL
  136. '''
  137. path = 'dir/file.conf'
  138. url = 'salt://' + path
  139. escaped_url = 'salt://|' + path
  140. if salt.utils.platform.is_windows():
  141. escaped_url = url
  142. self.assertEqual(salt.utils.url.escape(url), escaped_url)
  143. def test_escape_generic_url(self):
  144. '''
  145. Test testing an unescaped 'salt://' URL
  146. '''
  147. url = 'https://gentoo.org/'
  148. self.assertEqual(salt.utils.url.escape(url), url)
  149. # unescape tests
  150. def test_unescape_windows(self):
  151. '''
  152. Test not escaping a 'salt://' URL on windows
  153. '''
  154. url = 'salt://dir/file.ini'
  155. with patch('salt.utils.platform.is_windows', MagicMock(return_value=True)):
  156. self.assertEqual(salt.utils.url.unescape(url), url)
  157. def test_unescape_escaped_path(self):
  158. '''
  159. Test escaping an escaped path
  160. '''
  161. resource = 'dir/file.conf?saltenv=basic'
  162. escaped_path = '|' + resource
  163. self.assertEqual(salt.utils.url.unescape(escaped_path), resource)
  164. def test_unescape_unescaped_path(self):
  165. '''
  166. Test escaping an unescaped path
  167. '''
  168. path = 'dir/file.conf'
  169. self.assertEqual(salt.utils.url.unescape(path), path)
  170. def test_unescape_escaped_url(self):
  171. '''
  172. Test testing an escaped 'salt://' URL
  173. '''
  174. resource = 'dir/file.conf?saltenv=basic'
  175. url = 'salt://' + resource
  176. escaped_url = 'salt://|' + resource
  177. self.assertEqual(salt.utils.url.unescape(escaped_url), url)
  178. def test_unescape_unescaped_url(self):
  179. '''
  180. Test testing an unescaped 'salt://' URL
  181. '''
  182. url = 'salt://dir/file.conf'
  183. self.assertEqual(salt.utils.url.unescape(url), url)
  184. def test_unescape_generic_url(self):
  185. '''
  186. Test testing an unescaped 'salt://' URL
  187. '''
  188. url = 'https://gentoo.org/'
  189. self.assertEqual(salt.utils.url.unescape(url), url)
  190. # add_env tests
  191. def test_add_env_not_salt(self):
  192. '''
  193. Test not adding a saltenv to a non 'salt://' URL
  194. '''
  195. saltenv = 'higgs'
  196. url = 'https://pdg.lbl.gov/'
  197. self.assertEqual(salt.utils.url.add_env(url, saltenv), url)
  198. def test_add_env(self):
  199. '''
  200. Test adding a saltenv to a 'salt://' URL
  201. '''
  202. saltenv = 'erstwhile'
  203. url = 'salt://salted/file.conf'
  204. url_env = url + '?saltenv=' + saltenv
  205. self.assertEqual(salt.utils.url.add_env(url, saltenv), url_env)
  206. # split_env tests
  207. def test_split_env_non_salt(self):
  208. '''
  209. Test not splitting a saltenv from a non 'salt://' URL
  210. '''
  211. saltenv = 'gravitodynamics'
  212. url = 'https://arxiv.org/find/all/?' + saltenv
  213. self.assertEqual(salt.utils.url.split_env(url), (url, None))
  214. def test_split_env(self):
  215. '''
  216. Test splitting a 'salt://' URL
  217. '''
  218. saltenv = 'elsewhere'
  219. url = 'salt://salted/file.conf'
  220. url_env = url + '?saltenv=' + saltenv
  221. self.assertEqual(salt.utils.url.split_env(url_env), (url, saltenv))
  222. # validate tests
  223. def test_validate_valid(self):
  224. '''
  225. Test URL valid validation
  226. '''
  227. url = 'salt://config/file.name?saltenv=vapid'
  228. protos = ['salt', 'pepper', 'cinnamon', 'melange']
  229. self.assertTrue(salt.utils.url.validate(url, protos))
  230. def test_validate_invalid(self):
  231. '''
  232. Test URL invalid validation
  233. '''
  234. url = 'cumin://config/file.name?saltenv=vapid'
  235. protos = ['salt', 'pepper', 'cinnamon', 'melange']
  236. self.assertFalse(salt.utils.url.validate(url, protos))
  237. # strip tests
  238. def test_strip_url_with_scheme(self):
  239. '''
  240. Test stripping of URL scheme
  241. '''
  242. scheme = 'git+salt+rsync+AYB://'
  243. resource = 'all/the/things.stuff;parameter?query=I guess'
  244. url = scheme + resource
  245. self.assertEqual(salt.utils.url.strip_proto(url), resource)
  246. def test_strip_url_without_scheme(self):
  247. '''
  248. Test stripping of a URL without a scheme
  249. '''
  250. resource = 'all/the/things.stuff;parameter?query=I guess'
  251. self.assertEqual(salt.utils.url.strip_proto(resource), resource)
  252. def test_http_basic_auth(self):
  253. '''
  254. Tests that adding basic auth to a URL works as expected
  255. '''
  256. # ((user, password), expected) tuples
  257. test_inputs = (
  258. ((None, None), 'http://example.com'),
  259. (('user', None), 'http://user@example.com'),
  260. (('user', 'pass'), 'http://user:pass@example.com'),
  261. )
  262. for (user, password), expected in test_inputs:
  263. kwargs = {
  264. 'url': 'http://example.com',
  265. 'user': user,
  266. 'password': password,
  267. }
  268. # Test http
  269. result = salt.utils.url.add_http_basic_auth(**kwargs)
  270. self.assertEqual(result, expected)
  271. # Test https
  272. kwargs['url'] = kwargs['url'].replace('http://', 'https://', 1)
  273. expected = expected.replace('http://', 'https://', 1)
  274. result = salt.utils.url.add_http_basic_auth(**kwargs)
  275. self.assertEqual(result, expected)
  276. def test_http_basic_auth_https_only(self):
  277. '''
  278. Tests that passing a non-https URL with https_only=True will raise a
  279. ValueError.
  280. '''
  281. kwargs = {
  282. 'url': 'http://example.com',
  283. 'user': 'foo',
  284. 'password': 'bar',
  285. 'https_only': True,
  286. }
  287. self.assertRaises(
  288. ValueError,
  289. salt.utils.url.add_http_basic_auth,
  290. **kwargs
  291. )
  292. def test_redact_http_basic_auth(self):
  293. sensitive_outputs = (
  294. 'https://deadbeaf@example.com',
  295. 'https://user:pw@example.com',
  296. )
  297. sanitized = 'https://<redacted>@example.com'
  298. for sensitive_output in sensitive_outputs:
  299. result = salt.utils.url.redact_http_basic_auth(sensitive_output)
  300. self.assertEqual(result, sanitized)
  301. def test_redact_non_auth_output(self):
  302. non_auth_output = 'This is just normal output'
  303. self.assertEqual(
  304. non_auth_output,
  305. salt.utils.url.redact_http_basic_auth(non_auth_output)
  306. )