Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Added option to disable SSL verify #3676

Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ed1819c
feat: add trusted option to repository on develop branch, but require…
Celeborn2BeAlive May 29, 2020
7c056a7
removed redundant double check of http scheme
maayanbar13 Sep 12, 2020
af7b1e7
Added trusted source tests and fixture to test_factory
maayanbar13 Sep 12, 2020
c3dccac
Added trusted source tests to pip_installer
maayanbar13 Sep 12, 2020
92dad18
Merge branch 'master' of https://github.com/python-poetry/poetry into…
maayanbar13 Feb 6, 2021
2304072
Implemented saving trusted state in cert configuration
maayanbar13 Feb 7, 2021
e0711ca
Fixed handling trusted as str and being overriden by default certific…
maayanbar13 Feb 8, 2021
f4c52a7
doc: Added docs about configurating trusted repositories in poetry co…
maayanbar13 Feb 10, 2021
9e40186
fix: Fixed bug where warnings are not reset to default when the reque…
maayanbar13 Feb 10, 2021
423236a
Apply suggestions from code review
maayanbar13 Aug 19, 2021
624c7ce
Fixed conflicts with latest upstream
maayanbar13 Aug 19, 2021
0270182
Fixed typo in get_truested
maayanbar13 Sep 25, 2021
514a531
Merge branch 'option-to-disable-ssl-verify-develop' of https://github…
maayanbar13 Sep 25, 2021
1be75fa
Fixed merge conflicts with latest changes
maayanbar13 Mar 28, 2022
20b31e6
Fixed lint issues
maayanbar13 Apr 7, 2022
28f5087
Fixed merge conflicts
maayanbar13 Apr 21, 2022
b90c3db
Fixed fstring formatting according to pyupgrade
maayanbar13 Apr 21, 2022
54656d3
Merged latest changes
maayanbar13 May 27, 2022
be3e319
Added trusted host verification for authenticator and legacy reposito…
maayanbar13 May 27, 2022
986fb2b
Updated tests according to authenticator changes
maayanbar13 May 27, 2022
270ceb1
Reverted small changes relative to dev
maayanbar13 May 27, 2022
61a65a7
Moved trusted option to seperate property then certs
maayanbar13 May 27, 2022
cbac250
Fixed test monkeypatch
maayanbar13 May 28, 2022
8b84ca6
Fixed acciderntaly deleted files in conflict merge and moved the trus…
maayanbar13 May 28, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/docs/repositories.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,19 @@ default = true
```

A default source will also be the fallback source if you add other sources.

### Trusting a repository

You can bypass SSL verification for a repository if you know it can be trusted (useful for corporate private repositories):

```toml
[[tool.poetry.source]]
name = "foo"
url = "https://foo.bar/simple/"
trusted = true
```

You can also change it in your local configuration using the `config` command:
```bash
poetry config certificates.foo.trusted true
```
26 changes: 19 additions & 7 deletions poetry/console/commands/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,20 +247,32 @@ def handle(self) -> Optional[int]:

# handle certs
m = re.match(
r"(?:certificates)\.([^.]+)\.(cert|client-cert)", self.argument("key")
r"(?:certificates)\.([^.]+)\.(cert|client-cert|trusted)",
self.argument("key"),
)
if m:
key = "certificates.{}.{}".format(m.group(1), m.group(2))
if self.option("unset"):
config.auth_config_source.remove_property(
"certificates.{}.{}".format(m.group(1), m.group(2))
)
config.auth_config_source.remove_property(key)

return 0

if len(values) == 1:
config.auth_config_source.add_property(
"certificates.{}.{}".format(m.group(1), m.group(2)), values[0]
if m.group(2) == "trusted":
from poetry.config.config import boolean_normalizer
from poetry.config.config import boolean_validator

self._handle_single_value(
config.auth_config_source,
key,
(
boolean_validator,
boolean_normalizer,
False,
),
values,
)
elif len(values) == 1:
config.auth_config_source.add_property(key, values[0])
else:
raise ValueError("You must pass exactly 1 value")

Expand Down
2 changes: 2 additions & 0 deletions poetry/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ def create_legacy_repository(
from .repositories.legacy_repository import LegacyRepository
from .utils.helpers import get_cert
from .utils.helpers import get_client_cert
from .utils.helpers import get_truested
maayanbar13 marked this conversation as resolved.
Show resolved Hide resolved

if "url" in source:
# PyPI-like repository
Expand All @@ -163,4 +164,5 @@ def create_legacy_repository(
config=auth_config,
cert=get_cert(auth_config, name),
client_cert=get_client_cert(auth_config, name),
trusted=source.get("trusted", False) or get_truested(auth_config, name),
maayanbar13 marked this conversation as resolved.
Show resolved Hide resolved
)
2 changes: 2 additions & 0 deletions poetry/installation/pip_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ def install(self, package: "Package", update: bool = False) -> None:
)
)
args += ["--trusted-host", parsed.hostname]
elif repository.trusted:
args += ["--trusted-host", parsed.hostname]

if repository.cert:
args += ["--cert", str(repository.cert)]
Expand Down
4 changes: 4 additions & 0 deletions poetry/json/schemas/poetry-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,10 @@
"secondary": {
"type": "boolean",
"description": "Declare this repository as secondary, i.e. it will only be looked up last for packages."
},
"trusted": {
"type": "boolean",
"description": "Declare this repository as trusted, i.e. option --trusted-host will be used when installing packages with pip."
}
}
}
Expand Down
19 changes: 19 additions & 0 deletions poetry/repositories/legacy_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ def __init__(
disable_cache: bool = False,
cert: Optional[Path] = None,
client_cert: Optional[Path] = None,
trusted: Optional[bool] = False,
) -> None:
if name == "pypi":
raise ValueError("The name [pypi] is reserved for repositories")
Expand All @@ -179,6 +180,7 @@ def __init__(
self._url = url.rstrip("/")
self._client_cert = client_cert
self._cert = cert
self._trusted = trusted
self._cache_dir = REPOSITORY_CACHE_DIR / name
self._cache = CacheManager(
{
Expand Down Expand Up @@ -222,6 +224,10 @@ def cert(self) -> Optional[Path]:
def client_cert(self) -> Optional[Path]:
return self._client_cert

@property
def trusted(self) -> bool:
return self._trusted

@property
def authenticated_url(self) -> str:
if not self._session.auth:
Expand Down Expand Up @@ -392,12 +398,25 @@ def _get_release_info(self, name: str, version: str) -> dict:
def _get(self, endpoint: str) -> Optional[Page]:
url = self._url + endpoint
try:
if self._trusted:
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

response = self.session.get(url)

if response.status_code == 404:
return
response.raise_for_status()
except requests.HTTPError as e:
raise RepositoryError(e)
finally:
if self._trusted:
import urllib3

urllib3.warnings.simplefilter(
"default", urllib3.exceptions.InsecureRequestWarning
)

if response.status_code in (401, 403):
self._log(
Expand Down
8 changes: 8 additions & 0 deletions poetry/utils/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ def get_client_cert(config: Config, repository_name: str) -> Optional[Path]:
return None


def get_truested(config: Config, repository_name: str) -> Optional[bool]:
maayanbar13 marked this conversation as resolved.
Show resolved Hide resolved
trusted = config.get("certificates.{}.trusted".format(repository_name))
if trusted:
return bool(trusted)
else:
return None


def _on_rm_error(func: Callable, path: str, exc_info: Exception) -> None:
if not os.path.exists(path):
return
Expand Down
8 changes: 8 additions & 0 deletions tests/console/commands/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,14 @@ def test_set_cert(tester, auth_config_source, mocker):
assert "path/to/ca.pem" == auth_config_source.config["certificates"]["foo"]["cert"]


def test_set_trusted(tester, auth_config_source, mocker):
mocker.spy(ConfigSource, "__init__")

tester.execute("certificates.foo.trusted true")

assert "true" == auth_config_source.config["certificates"]["foo"]["trusted"]


def test_config_installer_parallel(tester, command_tester_factory):
tester.execute("--local installer.parallel")
assert tester.io.fetch_output().strip() == "true"
Expand Down
2 changes: 2 additions & 0 deletions tests/fixtures/with_trusted_source/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
My Package
==========
62 changes: 62 additions & 0 deletions tests/fixtures/with_trusted_source/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
[tool.poetry]
name = "my-package"
version = "1.2.3"
description = "Some description."
authors = [
"Sébastien Eustace <sebastien@eustace.io>"
]
license = "MIT"

readme = "README.rst"

homepage = "https://python-poetry.org"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"

keywords = ["packaging", "dependency", "poetry"]

classifiers = [
"Topic :: Software Development :: Build Tools",
"Topic :: Software Development :: Libraries :: Python Modules"
]

# Requirements
[tool.poetry.dependencies]
python = "~2.7 || ^3.6"
cleo = "^0.6"
pendulum = { git = "https://github.com/sdispater/pendulum.git", branch = "2.0" }
requests = { version = "^2.18", optional = true, extras=[ "security" ] }
pathlib2 = { version = "^2.2", python = "~2.7" }

orator = { version = "^0.9", optional = true }

# File dependency
demo = { path = "../distributions/demo-0.1.0-py2.py3-none-any.whl" }

# Dir dependency with setup.py
my-package = { path = "../project_with_setup/" }

# Dir dependency with pyproject.toml
simple-project = { path = "../simple_project/" }


[tool.poetry.extras]
db = [ "orator" ]

[tool.poetry.dev-dependencies]
pytest = "~3.4"


[tool.poetry.scripts]
my-script = "my_package:main"


[tool.poetry.plugins."blogtool.parsers"]
".rst" = "some_module::SomeClass"


[[tool.poetry.source]]
name = "foo"
url = "https://foo.bar/simple/"
default = true
trusted = true
30 changes: 30 additions & 0 deletions tests/installation/test_pip_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,36 @@ def test_requirement_git_develop_true(installer, package_git):
assert expected == result


def test_install_with_trusted_host():
pool = Pool()
host = "foo.bar"

default = LegacyRepository("default", f"https://{host}", trusted=True)

pool.add_repository(default, default=True)

null_env = NullEnv()

installer = PipInstaller(null_env, NullIO(), pool)

foo = Package(
"foo",
"0.0.0",
source_type="legacy",
source_reference=default._name,
source_url=default._url,
)

installer.install(foo)

assert len(null_env.executed) == 1
cmd = null_env.executed[0]
assert "--trusted-host" in cmd
trusted_host_index = cmd.index("--trusted-host")

assert cmd[trusted_host_index + 1] == host


def test_uninstall_git_package_nspkg_pth_cleanup(mocker, tmp_venv, pool):
# this test scenario requires a real installation using the pip installer
installer = PipInstaller(tmp_venv, NullIO(), pool)
Expand Down
6 changes: 6 additions & 0 deletions tests/test_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ def test_poetry_with_default_source():
assert 1 == len(poetry.pool.repositories)


def test_poetry_with_trusted_source():
poetry = Factory().create_poetry(fixtures_dir / "with_trusted_source")

assert 1 == len(poetry.pool.repositories)


def test_poetry_with_non_default_source():
poetry = Factory().create_poetry(fixtures_dir / "with_non_default_source")

Expand Down
8 changes: 8 additions & 0 deletions tests/utils/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from poetry.core.utils.helpers import parse_requires
from poetry.utils.helpers import get_cert
from poetry.utils.helpers import get_client_cert
from poetry.utils.helpers import get_truested
maayanbar13 marked this conversation as resolved.
Show resolved Hide resolved


def test_parse_requires():
Expand Down Expand Up @@ -70,3 +71,10 @@ def test_get_client_cert(config):
config.merge({"certificates": {"foo": {"client-cert": client_cert}}})

assert get_client_cert(config, "foo") == Path(client_cert)


def test_get_trusted(config):
trusted = "true"
config.merge({"certificates": {"foo": {"trusted": trusted}}})

assert get_truested(config, "foo")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there seems to be a typo here as well: get_trusted

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed :)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! However it seems more must be done as part of this change:
RuntimeError: The Poetry configuration is invalid: E - [source.0] Additional properties are not allowed ('trusted' was unexpected)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is expected, as written in the original post, in order for tests to pass you must first complete the mentioned PR in poetry-core as it contains the required changes to the poetry configuration schema.