Commit 6c5be4b8 by Sébastien Eustace

Fix handling of authentication for legacy repositories

parent 94304e9d
......@@ -6,6 +6,7 @@
- Fixed `run` not executing scripts.
- Fixed environment detection.
- Fixed handling of authentication for legacy repositories.
## [0.12.9] - 2018-11-19
......
......@@ -8,16 +8,18 @@ from typing import List
from .__version__ import __version__
from .config import Config
from .exceptions import InvalidProjectFile
from .json import validate_object
from .packages import Dependency
from .packages import Locker
from .packages import Package
from .packages import ProjectPackage
from .repositories import Pool
from .repositories.auth import Auth
from .repositories.legacy_repository import LegacyRepository
from .repositories.pypi_repository import PyPiRepository
from .spdx import license_by_id
from .utils._compat import Path
from .utils.helpers import get_http_basic_auth
from .utils.toml_file import TomlFile
......@@ -42,9 +44,9 @@ class Poetry:
# Configure sources
self._pool = Pool()
for source in self._local_config.get("source", []):
self._pool.configure(source)
self._pool.add_repository(self.create_legacy_repository(source))
# Always put PyPI last to prefere private repositories
# Always put PyPI last to prefer private repositories
self._pool.add_repository(PyPiRepository())
@property
......@@ -194,6 +196,26 @@ class Poetry:
return cls(poetry_file, local_config, package, locker)
def create_legacy_repository(
self, source
): # type: (Dict[str, str]) -> LegacyRepository
if "url" in source:
# PyPI-like repository
if "name" not in source:
raise RuntimeError("Missing [name] in source.")
else:
raise RuntimeError("Unsupported source specified")
name = source["name"]
url = source["url"]
credentials = get_http_basic_auth(self._auth_config, name)
if not credentials:
return LegacyRepository(name, url)
auth = Auth(url, credentials[0], credentials[1])
return LegacyRepository(name, url, auth=auth)
@classmethod
def check(cls, config, strict=False): # type: (dict, bool) -> Dict[str, List[str]]
"""
......
from requests import Request
from requests.auth import AuthBase
from requests.auth import HTTPBasicAuth
from poetry.utils._compat import urlparse
class Auth(AuthBase):
def __init__(self, url, username, password): # type: (str, str, str) -> None
self._netloc = urlparse.urlparse(url).netloc
self._auth = HTTPBasicAuth(username, password)
def __call__(self, r): # type: (Request) -> Request
if urlparse.urlparse(r.url).netloc != self._netloc:
return r
self._auth(r)
return r
......@@ -17,6 +17,7 @@ except ImportError:
unescape = HTMLParser().unescape
from typing import Generator
from typing import Optional
from typing import Union
import html5lib
......@@ -28,7 +29,6 @@ from cachy import CacheManager
import poetry.packages
from poetry.config import Config
from poetry.locations import CACHE_DIR
from poetry.masonry.publishing.uploader import wheel_file_re
from poetry.packages import Package
......@@ -39,9 +39,10 @@ from poetry.semver import Version
from poetry.semver import VersionConstraint
from poetry.semver import VersionRange
from poetry.utils._compat import Path
from poetry.utils.helpers import canonicalize_name, get_http_basic_auth
from poetry.utils.helpers import canonicalize_name
from poetry.version.markers import InvalidMarker
from .auth import Auth
from .exceptions import PackageNotFound
from .pypi_repository import PyPiRepository
......@@ -146,7 +147,9 @@ class Page:
class LegacyRepository(PyPiRepository):
def __init__(self, name, url, disable_cache=False):
def __init__(
self, name, url, auth=None, disable_cache=False
): # type: (str, str, Optional[Auth], bool) -> None
if name == "pypi":
raise ValueError("The name [pypi] is reserved for repositories")
......@@ -172,10 +175,8 @@ class LegacyRepository(PyPiRepository):
)
url_parts = urlparse.urlparse(self._url)
if not url_parts.username:
self._session.auth = get_http_basic_auth(
Config.create("auth.toml"), self.name
)
if not url_parts.username and auth:
self._session.auth = auth
self._disable_cache = disable_cache
......
......@@ -40,24 +40,6 @@ class Pool(BaseRepository):
return self
def configure(self, source): # type: (dict) -> Pool
"""
Configures a repository based on a source
specification and add it to the pool.
"""
from .legacy_repository import LegacyRepository
if "url" in source:
# PyPI-like repository
if "name" not in source:
raise RuntimeError("Missing [name] in source.")
repository = LegacyRepository(source["name"], source["url"])
else:
raise RuntimeError("Unsupported source specified")
return self.add_repository(repository)
def has_package(self, package):
raise NotImplementedError()
......
......@@ -391,13 +391,18 @@ class PyPiRepository(Repository):
): # type: (Dict[str, str]) -> Dict[str, Union[str, List, None]]
if "bdist_wheel" in urls:
self._log(
"Downloading wheel: {}".format(urls["bdist_wheel"].split("/")[-1]),
"Downloading wheel: {}".format(
urlparse.urlparse(urls["bdist_wheel"]).path.rsplit("/")[-1]
),
level="debug",
)
return self._get_info_from_wheel(urls["bdist_wheel"])
self._log(
"Downloading sdist: {}".format(urls["sdist"].split("/")[-1]), level="debug"
"Downloading sdist: {}".format(
urlparse.urlparse(urls["sdist"]).path.rsplit("/")[-1]
),
level="debug",
)
return self._get_info_from_sdist(urls["sdist"])
......@@ -406,7 +411,7 @@ class PyPiRepository(Repository):
): # type: (str) -> Dict[str, Union[str, List, None]]
info = {"summary": "", "requires_python": None, "requires_dist": None}
filename = os.path.basename(urlparse.urlparse(url).path)
filename = os.path.basename(urlparse.urlparse(url).path.rsplit("/")[-1])
with temporary_directory() as temp_dir:
filepath = os.path.join(temp_dir, filename)
......@@ -551,6 +556,8 @@ class PyPiRepository(Repository):
def _download(self, url, dest): # type: (str, str) -> None
r = get(url, stream=True)
r.raise_for_status()
with open(dest, "wb") as f:
for chunk in r.iter_content(chunk_size=1024):
if chunk:
......
......@@ -6,6 +6,11 @@ try:
except ImportError:
from functools import lru_cache
try:
import urllib.parse as urlparse
except ImportError:
import urlparse
try: # Python 2
long = long
unicode = unicode
......
import base64
from requests import Request
from poetry.repositories.auth import Auth
from poetry.utils._compat import decode
from poetry.utils._compat import encode
def test_auth_with_request_on_the_same_host():
auth = Auth("https://poetry.eustace.io", "foo", "bar")
request = Request("GET", "https://poetry.eustace.io/docs/")
assert "Authorization" not in request.headers
request = auth(request)
assert "Authorization" in request.headers
assert request.headers["Authorization"] == "Basic {}".format(
decode(base64.b64encode(encode(":".join(("foo", "bar")))))
)
def test_auth_with_request_on_different_hosts():
auth = Auth("https://poetry.eustace.io", "foo", "bar")
request = Request("GET", "https://pendulum.eustace.io/docs/")
assert "Authorization" not in request.headers
request = auth(request)
assert "Authorization" not in request.headers
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment