Commit 6c5be4b8 by Sébastien Eustace

Fix handling of authentication for legacy repositories

parent 94304e9d
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
- Fixed `run` not executing scripts. - Fixed `run` not executing scripts.
- Fixed environment detection. - Fixed environment detection.
- Fixed handling of authentication for legacy repositories.
## [0.12.9] - 2018-11-19 ## [0.12.9] - 2018-11-19
......
...@@ -8,16 +8,18 @@ from typing import List ...@@ -8,16 +8,18 @@ from typing import List
from .__version__ import __version__ from .__version__ import __version__
from .config import Config from .config import Config
from .exceptions import InvalidProjectFile
from .json import validate_object from .json import validate_object
from .packages import Dependency from .packages import Dependency
from .packages import Locker from .packages import Locker
from .packages import Package from .packages import Package
from .packages import ProjectPackage from .packages import ProjectPackage
from .repositories import Pool from .repositories import Pool
from .repositories.auth import Auth
from .repositories.legacy_repository import LegacyRepository
from .repositories.pypi_repository import PyPiRepository from .repositories.pypi_repository import PyPiRepository
from .spdx import license_by_id from .spdx import license_by_id
from .utils._compat import Path from .utils._compat import Path
from .utils.helpers import get_http_basic_auth
from .utils.toml_file import TomlFile from .utils.toml_file import TomlFile
...@@ -42,9 +44,9 @@ class Poetry: ...@@ -42,9 +44,9 @@ class Poetry:
# Configure sources # Configure sources
self._pool = Pool() self._pool = Pool()
for source in self._local_config.get("source", []): 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()) self._pool.add_repository(PyPiRepository())
@property @property
...@@ -194,6 +196,26 @@ class Poetry: ...@@ -194,6 +196,26 @@ class Poetry:
return cls(poetry_file, local_config, package, locker) 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 @classmethod
def check(cls, config, strict=False): # type: (dict, bool) -> Dict[str, List[str]] 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: ...@@ -17,6 +17,7 @@ except ImportError:
unescape = HTMLParser().unescape unescape = HTMLParser().unescape
from typing import Generator from typing import Generator
from typing import Optional
from typing import Union from typing import Union
import html5lib import html5lib
...@@ -28,7 +29,6 @@ from cachy import CacheManager ...@@ -28,7 +29,6 @@ from cachy import CacheManager
import poetry.packages import poetry.packages
from poetry.config import Config
from poetry.locations import CACHE_DIR from poetry.locations import CACHE_DIR
from poetry.masonry.publishing.uploader import wheel_file_re from poetry.masonry.publishing.uploader import wheel_file_re
from poetry.packages import Package from poetry.packages import Package
...@@ -39,9 +39,10 @@ from poetry.semver import Version ...@@ -39,9 +39,10 @@ from poetry.semver import Version
from poetry.semver import VersionConstraint from poetry.semver import VersionConstraint
from poetry.semver import VersionRange from poetry.semver import VersionRange
from poetry.utils._compat import Path 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 poetry.version.markers import InvalidMarker
from .auth import Auth
from .exceptions import PackageNotFound from .exceptions import PackageNotFound
from .pypi_repository import PyPiRepository from .pypi_repository import PyPiRepository
...@@ -146,7 +147,9 @@ class Page: ...@@ -146,7 +147,9 @@ class Page:
class LegacyRepository(PyPiRepository): 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": if name == "pypi":
raise ValueError("The name [pypi] is reserved for repositories") raise ValueError("The name [pypi] is reserved for repositories")
...@@ -172,10 +175,8 @@ class LegacyRepository(PyPiRepository): ...@@ -172,10 +175,8 @@ class LegacyRepository(PyPiRepository):
) )
url_parts = urlparse.urlparse(self._url) url_parts = urlparse.urlparse(self._url)
if not url_parts.username: if not url_parts.username and auth:
self._session.auth = get_http_basic_auth( self._session.auth = auth
Config.create("auth.toml"), self.name
)
self._disable_cache = disable_cache self._disable_cache = disable_cache
......
...@@ -40,24 +40,6 @@ class Pool(BaseRepository): ...@@ -40,24 +40,6 @@ class Pool(BaseRepository):
return self 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): def has_package(self, package):
raise NotImplementedError() raise NotImplementedError()
......
...@@ -391,13 +391,18 @@ class PyPiRepository(Repository): ...@@ -391,13 +391,18 @@ class PyPiRepository(Repository):
): # type: (Dict[str, str]) -> Dict[str, Union[str, List, None]] ): # type: (Dict[str, str]) -> Dict[str, Union[str, List, None]]
if "bdist_wheel" in urls: if "bdist_wheel" in urls:
self._log( self._log(
"Downloading wheel: {}".format(urls["bdist_wheel"].split("/")[-1]), "Downloading wheel: {}".format(
urlparse.urlparse(urls["bdist_wheel"]).path.rsplit("/")[-1]
),
level="debug", level="debug",
) )
return self._get_info_from_wheel(urls["bdist_wheel"]) return self._get_info_from_wheel(urls["bdist_wheel"])
self._log( 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"]) return self._get_info_from_sdist(urls["sdist"])
...@@ -406,7 +411,7 @@ class PyPiRepository(Repository): ...@@ -406,7 +411,7 @@ class PyPiRepository(Repository):
): # type: (str) -> Dict[str, Union[str, List, None]] ): # type: (str) -> Dict[str, Union[str, List, None]]
info = {"summary": "", "requires_python": None, "requires_dist": 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: with temporary_directory() as temp_dir:
filepath = os.path.join(temp_dir, filename) filepath = os.path.join(temp_dir, filename)
...@@ -551,6 +556,8 @@ class PyPiRepository(Repository): ...@@ -551,6 +556,8 @@ class PyPiRepository(Repository):
def _download(self, url, dest): # type: (str, str) -> None def _download(self, url, dest): # type: (str, str) -> None
r = get(url, stream=True) r = get(url, stream=True)
r.raise_for_status()
with open(dest, "wb") as f: with open(dest, "wb") as f:
for chunk in r.iter_content(chunk_size=1024): for chunk in r.iter_content(chunk_size=1024):
if chunk: if chunk:
......
...@@ -6,6 +6,11 @@ try: ...@@ -6,6 +6,11 @@ try:
except ImportError: except ImportError:
from functools import lru_cache from functools import lru_cache
try:
import urllib.parse as urlparse
except ImportError:
import urlparse
try: # Python 2 try: # Python 2
long = long long = long
unicode = unicode 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