Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
Upcoming (TBD)
==============

Features
---------
* Add `auto` option to ignore keyring when inside an SSH session.


2.0.0 (2026/07/03)
==============

Expand Down
2 changes: 1 addition & 1 deletion mycli/TIPS
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ the --character-set option sets the character set for a single session!

the --unbuffered flag can save memory when in batch mode!

--use-keyring=true lets you access the system keyring for passwords!
--use-keyring=auto lets you access the system keyring for passwords!

--use-keyring=reset resets a password saved to the system keyring!

Expand Down
19 changes: 17 additions & 2 deletions mycli/cli_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,9 +304,24 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
if cli_args.use_keyring is not None and cli_args.use_keyring.lower() == 'reset':
use_keyring = True
reset_keyring = True
elif cli_args.use_keyring is not None and cli_args.use_keyring.lower() == 'auto':
if os.environ.get('SSH_CONNECTION'):
use_keyring = False
reset_keyring = False
else:
use_keyring = True
reset_keyring = False
elif cli_args.use_keyring is None:
use_keyring = str_to_bool(mycli.config['main'].get('use_keyring', 'False'))
reset_keyring = False
if mycli.config['main'].get('use_keyring', 'False').lower() == 'auto':
if os.environ.get('SSH_CONNECTION'):
use_keyring = False
reset_keyring = False
else:
use_keyring = True
reset_keyring = False
else:
use_keyring = str_to_bool(mycli.config['main'].get('use_keyring', 'False'))
reset_keyring = False
else:
use_keyring = str_to_bool(cli_args.use_keyring)
reset_keyring = False
Expand Down
4 changes: 2 additions & 2 deletions mycli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,9 +263,9 @@ class CliArgs:
help='Show progress on the standard error with --batch.',
)
use_keyring: str | None = clickdc.option(
type=click.Choice(['true', 'false', 'reset']),
type=click.Choice(['auto', 'true', 'false', 'reset']),
default=None,
help='Store and retrieve passwords from the system keyring: true/false/reset.',
help='Store and retrieve passwords from the system keyring. auto means true, unless within an SSH connection.',
)
keepalive_ticks: int | None = clickdc.option(
type=int,
Expand Down
4 changes: 4 additions & 0 deletions mycli/myclirc
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,14 @@ enable_pager = True
pager = 'less'

# Whether to store and retrieve passwords from the system keyring.
# * False - never use keyring
# * True - always use keyring
# * auto - use keyring unless currently in an SSH connection
# See the documentation for https://pypi.org/project/keyring/ for your OS.
# Note that the hostname is considered to be different if short or qualified.
# This can be overridden with --use-keyring= at the CLI.
# A password can be reset with --use-keyring=reset at the CLI.
# Recommanded: auto
use_keyring = False

[search]
Expand Down
4 changes: 4 additions & 0 deletions test/myclirc
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,14 @@ enable_pager = True
pager = python test/features/wrappager.py ---boundary---

# Whether to store and retrieve passwords from the system keyring.
# * False - never use keyring
# * True - always use keyring
# * auto - use keyring unless currently in an SSH connection
# See the documentation for https://pypi.org/project/keyring/ for your OS.
# Note that the hostname is considered to be different if short or qualified.
# This can be overridden with --use-keyring= at the CLI.
# A password can be reset with --use-keyring=reset at the CLI.
# Recommended: auto
use_keyring = False

[search]
Expand Down
53 changes: 53 additions & 0 deletions test/pytests/test_cli_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,3 +418,56 @@ def test_run_from_cli_args_uses_explicit_keyring_flag(monkeypatch: pytest.Monkey

assert client.connect_calls[-1]['use_keyring'] is True
assert client.connect_calls[-1]['reset_keyring'] is False


@pytest.mark.parametrize(
('ssh_connection', 'expected_use_keyring'),
(
(None, True),
('client-ip client-port server-ip server-port', False),
),
)
def test_run_from_cli_args_uses_auto_keyring_flag(
monkeypatch: pytest.MonkeyPatch,
ssh_connection: str | None,
expected_use_keyring: bool,
) -> None:
cli_args = make_cli_args()
cli_args.use_keyring = 'auto'
client = DummyMyCli()
if ssh_connection is None:
monkeypatch.delenv('SSH_CONNECTION', raising=False)
else:
monkeypatch.setenv('SSH_CONNECTION', ssh_connection)

run_with_client(monkeypatch, cli_args, client)

assert client.connect_calls[-1]['use_keyring'] is expected_use_keyring
assert client.connect_calls[-1]['reset_keyring'] is False


@pytest.mark.parametrize(
('ssh_connection', 'expected_use_keyring'),
(
(None, True),
('client-ip client-port server-ip server-port', False),
),
)
def test_run_from_cli_args_uses_auto_keyring_config(
monkeypatch: pytest.MonkeyPatch,
ssh_connection: str | None,
expected_use_keyring: bool,
) -> None:
cli_args = make_cli_args()
config = default_config()
config['main'] = {**config['main'], 'use_keyring': 'auto'}
client = DummyMyCli(config=config)
if ssh_connection is None:
monkeypatch.delenv('SSH_CONNECTION', raising=False)
else:
monkeypatch.setenv('SSH_CONNECTION', ssh_connection)

run_with_client(monkeypatch, cli_args, client)

assert client.connect_calls[-1]['use_keyring'] is expected_use_keyring
assert client.connect_calls[-1]['reset_keyring'] is False
Loading