Unquote user:password in hosts (#569)

Before turning them into an authentication header, the user or password
supplied in hosts must be URL decoded.

The RFC https://tools.ietf.org/html/rfc3986 describes userinfo like:

  userinfo    = *( unreserved / pct-encoded / sub-delims / ":" )

and python documentation about urlparse says:

  The components are not broken up in smaller parts (for example, the
  network location is a single string), and % escapes are not expanded.

It is the caller job to unquote them before using them.

For instance, a character like "]" cannot be supplied in a host or
urllib will incorrectly interpret it using ipv6 syntax.

Fix #568
This commit is contained in:
Patrick Mézard
2017-04-13 15:10:40 +02:00
committed by Honza Král
parent fe897ebe0d
commit 7f6d38273e
3 changed files with 7 additions and 6 deletions
+3 -2
View File
@@ -3,7 +3,7 @@ import logging
from ..transport import Transport
from ..exceptions import TransportError
from ..compat import string_types, urlparse
from ..compat import string_types, urlparse, unquote
from .indices import IndicesClient
from .ingest import IngestClient
from .cluster import ClusterClient
@@ -49,7 +49,8 @@ def _normalize_hosts(hosts):
h['scheme'] = parsed_url.scheme
if parsed_url.username or parsed_url.password:
h['http_auth'] = '%s:%s' % (parsed_url.username, parsed_url.password)
h['http_auth'] = '%s:%s' % (unquote(parsed_url.username),
unquote(parsed_url.password))
if parsed_url.path and parsed_url.path != '/':
h['url_prefix'] = parsed_url.path
+2 -2
View File
@@ -4,10 +4,10 @@ PY2 = sys.version_info[0] == 2
if PY2:
string_types = basestring,
from urllib import quote_plus, urlencode
from urllib import quote_plus, urlencode, unquote
from urlparse import urlparse
from itertools import imap as map
else:
string_types = str, bytes
from urllib.parse import quote_plus, urlencode, urlparse
from urllib.parse import quote_plus, urlencode, urlparse, unquote
map = map
+2 -2
View File
@@ -13,8 +13,8 @@ class TestNormalizeHosts(TestCase):
def test_strings_are_parsed_for_port_and_user(self):
self.assertEquals(
[{"host": "elastic.co", "port": 42}, {"host": "elastic.co", "http_auth": "user:secret"}],
_normalize_hosts(["elastic.co:42", "user:secret@elastic.co"])
[{"host": "elastic.co", "port": 42}, {"host": "elastic.co", "http_auth": "user:secre]"}],
_normalize_hosts(["elastic.co:42", "user:secre%5D@elastic.co"])
)
def test_strings_are_parsed_for_scheme(self):