poetry icon indicating copy to clipboard operation
poetry copied to clipboard

Crash when configuring secrets from stdin

Open lubo opened this issue 2 years ago • 3 comments

  • Poetry version: 1.3.2
  • Python version: 3.10.9
  • OS version and name: Arch Linux
  • pyproject.toml:
Click to expand
[tool.poetry]
name = "<name>"
version = "0.1.0"
description = ""
authors = ["Your Name <[email protected]>"]
packages = [
    { include = "service" }
]

[tool.poetry.dependencies]
python = ">=3.9,<3.11"
uvicorn = "0.17.0"
requests = "^2.27.1"
fastapi = "0.73.0"
cryptography = "^39.0.0"
pydantic = "^1.10.4"
pyjwt = "^2.6.0"

[tool.poetry.dev-dependencies]

[tool.poetry.group.dev.dependencies]
pytest = "^7.2.1"

[[tool.poetry.source]]
name = "<name>"
url = "<url>"
default = true

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
  • [x] I am on the latest stable Poetry version, installed using a recommended method.
  • [x] I have searched the issues of this repo and believe that this is not a duplicate.
  • [x] I have consulted the FAQ and blog for any relevant entries or release notes.
  • [x] If an exception occurs when executing a command, I executed it again in debug mode (-vvv option) and have included the output below.

Issue

A crash occurs when trying to configure HTTP Basic login crendetials while supplying the password on stdin. See the output:

Click to expand
$ echo <password> | poetry config -vvv http-basic.<source> <username>
Loading configuration file /home/user/.config/pypoetry/config.toml
Loading configuration file /home/user/.config/pypoetry/auth.toml
Adding repository <name> (<url>) and setting it as the default one
Deactivating the PyPI repository
[keyring.backend] Loading KWallet
[keyring.backend] Loading SecretService
[keyring.backend] Loading Windows
[keyring.backend] Loading chainer
[keyring.backend] Loading libsecret
[keyring.backend] Loading macOS

  Stack trace:

  14  /usr/lib/python3.10/site-packages/cleo/application.py:327 in run
       325│ 
       326│             try:
     → 327│                 exit_code = self._run(io)
       328│             except BrokenPipeError:
       329│                 # If we are piped to another process, it may close early and send a

  13  /usr/lib/python3.10/site-packages/poetry/console/application.py:190 in _run
       188│         self._load_plugins(io)
       189│ 
     → 190│         exit_code: int = super()._run(io)
       191│         return exit_code
       192│ 

  12  /usr/lib/python3.10/site-packages/cleo/application.py:431 in _run
       429│             io.input.interactive(interactive)
       430│ 
     → 431│         exit_code = self._run_command(command, io)
       432│         self._running_command = None
       433│ 

  11  /usr/lib/python3.10/site-packages/cleo/application.py:473 in _run_command
       471│ 
       472│         if error is not None:
     → 473│             raise error
       474│ 
       475│         return terminate_event.exit_code

  10  /usr/lib/python3.10/site-packages/cleo/application.py:457 in _run_command
       455│ 
       456│             if command_event.command_should_run():
     → 457│                 exit_code = command.run(io)
       458│             else:
       459│                 exit_code = ConsoleCommandEvent.RETURN_CODE_DISABLED

   9  /usr/lib/python3.10/site-packages/cleo/commands/base_command.py:119 in run
       117│         io.input.validate()
       118│ 
     → 119│         status_code = self.execute(io)
       120│ 
       121│         if status_code is None:

   8  /usr/lib/python3.10/site-packages/cleo/commands/command.py:62 in execute
        60│ 
        61│         try:
     →  62│             return self.handle()
        63│         except KeyboardInterrupt:
        64│             return 1

   7  /usr/lib/python3.10/site-packages/poetry/console/commands/config.py:267 in handle
       265│                     password = values[1]
       266│ 
     → 267│                 password_manager.set_http_password(m.group(2), username, password)
       268│             elif m.group(1) == "pypi-token":
       269│                 if len(values) != 1:

   6  /usr/lib/python3.10/site-packages/poetry/utils/password_manager.py:219 in set_http_password
       217│             auth["password"] = password
       218│         else:
     → 219│             self.keyring.set_password(name, username, password)
       220│ 
       221│         self._config.auth_config_source.add_property(f"http-basic.{name}", auth)

   5  /usr/lib/python3.10/site-packages/poetry/utils/password_manager.py:85 in set_password
        83│ 
        84│         try:
     →  85│             keyring.set_password(name, username, password)
        86│         except (RuntimeError, keyring.errors.KeyringError) as e:
        87│             raise PoetryKeyringError(

   4  /usr/lib/python3.10/site-packages/keyring/core.py:60 in set_password
        58│ def set_password(service_name: str, username: str, password: str) -> None:
        59│     """Set password for the user in the specified service."""
     →  60│     get_keyring().set_password(service_name, username, password)
        61│ 
        62│ 

   3  /usr/lib/python3.10/site-packages/keyring/backends/chainer.py:56 in set_password
        54│         for keyring in self.backends:
        55│             try:
     →  56│                 return keyring.set_password(service, username, password)
        57│             except NotImplementedError:
        58│                 pass

   2  /usr/lib/python3.10/site-packages/keyring/backends/SecretService.py:91 in set_password
        89│         label = "Password for '{}' on '{}'".format(username, service)
        90│         with closing(collection.connection):
     →  91│             collection.create_item(label, attributes, password, replace=True)
        92│ 
        93│     def delete_password(self, service, username):

   1  /usr/lib/python3.10/site-packages/secretstorage/collection.py:119 in create_item
       117│         if not self.session:
       118│             self.session = open_session(self.connection)
     → 119│         _secret = format_secret(self.session, secret, content_type)
       120│         properties = {
       121│             SS_PREFIX + 'Item.Label': ('s', label),

  TypeError

  secret must be bytes

  at /usr/lib/python3.10/site-packages/secretstorage/util.py:108 in format_secret
      104│     Secret Service API."""
      105│     if isinstance(secret, str):
      106│         secret = secret.encode('utf-8')
      107│     elif not isinstance(secret, bytes):
    → 108│         raise TypeError('secret must be bytes')
      109│     assert session.object_path is not None
      110│     if not session.encrypted:
      111│         return (session.object_path, b'', secret, content_type)
      112│     assert session.aes_key is not None

lubo avatar Jan 27 '23 12:01 lubo

this needs to be reported / fixed at https://github.com/python-poetry/cleo. It's cleo that is deciding that the IO for the poetry command should be "non-interactive", therefore poetry doesn't get to prompt for a password and the echo'd value goes nowhere.

I think this demonstrates that cleo is simply wrong to make this test https://github.com/python-poetry/cleo/blob/675ec7e1d89aa65ce6dbca0938a89b4788db1045/src/cleo/application.py#L512-L513

Edit: introduced at https://github.com/python-poetry/cleo/pull/245

dimbleby avatar Jan 28 '23 13:01 dimbleby

Forgot to mention, but -n and --no-interaction don't work as well, same error.

lubo avatar Jan 28 '23 14:01 lubo

Still happening on 1.7.1

dorinclisu avatar Apr 04 '24 11:04 dorinclisu