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