Merge pull request #5968 from cjerdonek/dry-up-parse-credentials

Use split_auth_from_netloc() inside MultiDomainBasicAuth
This commit is contained in:
Pradyun Gedam 2018-10-30 18:45:35 +00:00 committed by GitHub
commit 2ed581c79f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 28 additions and 32 deletions

1
news/5968.bugfix Normal file
View File

@ -0,0 +1 @@
Percent-decode special characters in SVN URL credentials.

View File

@ -26,7 +26,6 @@ from pip._vendor.requests.utils import get_netrc_auth
from pip._vendor.six.moves import xmlrpc_client # type: ignore from pip._vendor.six.moves import xmlrpc_client # type: ignore
from pip._vendor.six.moves.urllib import parse as urllib_parse from pip._vendor.six.moves.urllib import parse as urllib_parse
from pip._vendor.six.moves.urllib import request as urllib_request from pip._vendor.six.moves.urllib import request as urllib_request
from pip._vendor.six.moves.urllib.parse import unquote as urllib_unquote
from pip._vendor.urllib3.util import IS_PYOPENSSL from pip._vendor.urllib3.util import IS_PYOPENSSL
import pip import pip
@ -39,8 +38,8 @@ from pip._internal.utils.glibc import libc_ver
from pip._internal.utils.logging import indent_log from pip._internal.utils.logging import indent_log
from pip._internal.utils.misc import ( from pip._internal.utils.misc import (
ARCHIVE_EXTENSIONS, ask_path_exists, backup_dir, call_subprocess, consume, ARCHIVE_EXTENSIONS, ask_path_exists, backup_dir, call_subprocess, consume,
display_path, format_size, get_installed_version, rmtree, splitext, display_path, format_size, get_installed_version, rmtree,
unpack_file, split_auth_from_netloc, splitext, unpack_file,
) )
from pip._internal.utils.setuptools_build import SETUPTOOLS_SHIM from pip._internal.utils.setuptools_build import SETUPTOOLS_SHIM
from pip._internal.utils.temp_dir import TempDirectory from pip._internal.utils.temp_dir import TempDirectory
@ -142,8 +141,8 @@ class MultiDomainBasicAuth(AuthBase):
def __call__(self, req): def __call__(self, req):
parsed = urllib_parse.urlparse(req.url) parsed = urllib_parse.urlparse(req.url)
# Get the netloc without any embedded credentials # Split the credentials from the netloc.
netloc = parsed.netloc.rsplit("@", 1)[-1] netloc, url_user_password = split_auth_from_netloc(parsed.netloc)
# Set the url of the request to the url without any credentials # Set the url of the request to the url without any credentials
req.url = urllib_parse.urlunparse(parsed[:1] + (netloc,) + parsed[2:]) req.url = urllib_parse.urlunparse(parsed[:1] + (netloc,) + parsed[2:])
@ -151,9 +150,9 @@ class MultiDomainBasicAuth(AuthBase):
# Use any stored credentials that we have for this netloc # Use any stored credentials that we have for this netloc
username, password = self.passwords.get(netloc, (None, None)) username, password = self.passwords.get(netloc, (None, None))
# Extract credentials embedded in the url if we have none stored # Use the credentials embedded in the url if we have none stored
if username is None: if username is None:
username, password = self.parse_credentials(parsed.netloc) username, password = url_user_password
# Get creds from netrc if we still don't have them # Get creds from netrc if we still don't have them
if username is None and password is None: if username is None and password is None:
@ -213,15 +212,6 @@ class MultiDomainBasicAuth(AuthBase):
logger.warning('401 Error, Credentials not correct for %s', logger.warning('401 Error, Credentials not correct for %s',
resp.request.url) resp.request.url)
def parse_credentials(self, netloc):
if "@" in netloc:
userinfo = netloc.rsplit("@", 1)[0]
if ":" in userinfo:
user, pwd = userinfo.split(":", 1)
return (urllib_unquote(user), urllib_unquote(pwd))
return urllib_unquote(userinfo), None
return None, None
class LocalFSAdapter(BaseAdapter): class LocalFSAdapter(BaseAdapter):

View File

@ -25,6 +25,7 @@ from pip._vendor.retrying import retry # type: ignore
from pip._vendor.six import PY2 from pip._vendor.six import PY2
from pip._vendor.six.moves import input from pip._vendor.six.moves import input
from pip._vendor.six.moves.urllib import parse as urllib_parse from pip._vendor.six.moves.urllib import parse as urllib_parse
from pip._vendor.six.moves.urllib.parse import unquote as urllib_unquote
from pip._internal.exceptions import CommandError, InstallationError from pip._internal.exceptions import CommandError, InstallationError
from pip._internal.locations import ( from pip._internal.locations import (
@ -880,10 +881,14 @@ def split_auth_from_netloc(netloc):
# Split from the left because that's how urllib.parse.urlsplit() # Split from the left because that's how urllib.parse.urlsplit()
# behaves if more than one : is present (which again can be checked # behaves if more than one : is present (which again can be checked
# using the password attribute of the return value) # using the password attribute of the return value)
user_pass = tuple(auth.split(':', 1)) user_pass = auth.split(':', 1)
else: else:
user_pass = auth, None user_pass = auth, None
user_pass = tuple(
None if x is None else urllib_unquote(x) for x in user_pass
)
return netloc, user_pass return netloc, user_pass
@ -897,7 +902,7 @@ def redact_netloc(netloc):
if user is None: if user is None:
return netloc return netloc
password = '' if password is None else ':****' password = '' if password is None else ':****'
return '{user}{password}@{netloc}'.format(user=user, return '{user}{password}@{netloc}'.format(user=urllib_parse.quote(user),
password=password, password=password,
netloc=netloc) netloc=netloc)

View File

@ -10,8 +10,8 @@ from pip._vendor.six.moves.urllib import request as urllib_request
import pip import pip
from pip._internal.download import ( from pip._internal.download import (
MultiDomainBasicAuth, PipSession, SafeFileCache, path_to_url, PipSession, SafeFileCache, path_to_url, unpack_file_url, unpack_http_url,
unpack_file_url, unpack_http_url, url_to_path, url_to_path,
) )
from pip._internal.exceptions import HashMismatch from pip._internal.exceptions import HashMismatch
from pip._internal.models.link import Link from pip._internal.models.link import Link
@ -328,14 +328,3 @@ class TestPipSession:
) )
assert not hasattr(session.adapters["https://example.com/"], "cache") assert not hasattr(session.adapters["https://example.com/"], "cache")
def test_parse_credentials():
auth = MultiDomainBasicAuth()
assert auth.parse_credentials("foo:bar@example.com") == ('foo', 'bar')
assert auth.parse_credentials("foo@example.com") == ('foo', None)
assert auth.parse_credentials("example.com") == (None, None)
# URL-encoded reserved characters:
assert auth.parse_credentials("user%3Aname:%23%40%5E@example.com") \
== ("user:name", "#@^")

View File

@ -665,6 +665,9 @@ def test_make_vcs_requirement_url(args, expected):
('user:pass@word@example.com', ('example.com', ('user', 'pass@word'))), ('user:pass@word@example.com', ('example.com', ('user', 'pass@word'))),
# Test the password containing a : symbol. # Test the password containing a : symbol.
('user:pass:word@example.com', ('example.com', ('user', 'pass:word'))), ('user:pass:word@example.com', ('example.com', ('user', 'pass:word'))),
# Test URL-encoded reserved characters.
('user%3Aname:%23%40%5E@example.com',
('example.com', ('user:name', '#@^'))),
]) ])
def test_split_auth_from_netloc(netloc, expected): def test_split_auth_from_netloc(netloc, expected):
actual = split_auth_from_netloc(netloc) actual = split_auth_from_netloc(netloc)
@ -684,6 +687,8 @@ def test_split_auth_from_netloc(netloc, expected):
('user:pass@word@example.com', 'user:****@example.com'), ('user:pass@word@example.com', 'user:****@example.com'),
# Test the password containing a : symbol. # Test the password containing a : symbol.
('user:pass:word@example.com', 'user:****@example.com'), ('user:pass:word@example.com', 'user:****@example.com'),
# Test URL-encoded reserved characters.
('user%3Aname:%23%40%5E@example.com', 'user%3Aname:****@example.com'),
]) ])
def test_redact_netloc(netloc, expected): def test_redact_netloc(netloc, expected):
actual = redact_netloc(netloc) actual = redact_netloc(netloc)
@ -715,7 +720,10 @@ def test_remove_auth_from_url(auth_url, expected_url):
('https://user@example.com/abc', 'https://user@example.com/abc'), ('https://user@example.com/abc', 'https://user@example.com/abc'),
('https://user:password@example.com', 'https://user:****@example.com'), ('https://user:password@example.com', 'https://user:****@example.com'),
('https://user:@example.com', 'https://user:****@example.com'), ('https://user:@example.com', 'https://user:****@example.com'),
('https://example.com', 'https://example.com') ('https://example.com', 'https://example.com'),
# Test URL-encoded reserved characters.
('https://user%3Aname:%23%40%5E@example.com',
'https://user%3Aname:****@example.com'),
]) ])
def test_redact_password_from_url(auth_url, expected_url): def test_redact_password_from_url(auth_url, expected_url):
url = redact_password_from_url(auth_url) url = redact_password_from_url(auth_url)

View File

@ -192,6 +192,9 @@ def test_git__get_netloc_and_auth(args, expected):
(('user@example.com', 'https'), ('example.com', ('user', None))), (('user@example.com', 'https'), ('example.com', ('user', None))),
# Test https with username and password. # Test https with username and password.
(('user:pass@example.com', 'https'), ('example.com', ('user', 'pass'))), (('user:pass@example.com', 'https'), ('example.com', ('user', 'pass'))),
# Test https with URL-encoded reserved characters.
(('user%3Aname:%23%40%5E@example.com', 'https'),
('example.com', ('user:name', '#@^'))),
# Test ssh with username and password. # Test ssh with username and password.
(('user:pass@example.com', 'ssh'), (('user:pass@example.com', 'ssh'),
('user:pass@example.com', (None, None))), ('user:pass@example.com', (None, None))),