Skip to content

Commit

Permalink
Add support for password argument being a callable or coroutine
Browse files Browse the repository at this point in the history
This commit allows a callable or coroutine to be passed in as the
password argument in SSHClientConnectionOptions. If password or
keyboard-interactive auth is performed, the callable or coroutine
will be invoked to get the password to use. Thanks go to GitHub
user goblin for suggesting this enhancement.
  • Loading branch information
ronf committed Mar 23, 2024
1 parent f7b2992 commit f4df7f4
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 3 deletions.
16 changes: 13 additions & 3 deletions asyncssh/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -3637,6 +3637,15 @@ async def password_auth_requested(self) -> Optional[str]:

if self._password is not None:
password: Optional[str] = self._password

if callable(password):
password = cast(Callable[[], Optional[str]], password)()

if inspect.isawaitable(password):
password = await cast(Awaitable[Optional[str]], password)
else:
password = cast(Optional[str], password)

self._password = None
else:
result = self._owner.password_auth_requested()
Expand Down Expand Up @@ -7195,9 +7204,10 @@ class SSHClientConnectionOptions(SSHConnectionOptions):
the currently logged in user on the local machine will be used.
:param password: (optional)
The password to use for client password authentication or
keyboard-interactive authentication which prompts for a password.
If this is not specified, client password authentication will
not be performed.
keyboard-interactive authentication which prompts for a password,
or a `callable` or coroutine which returns the password to use.
If this is not specified or set to `None`, client password
authentication will not be performed.
:param client_host_keysign: (optional)
Whether or not to use `ssh-keysign` to sign host-based
authentication requests. If set to `True`, an attempt will be
Expand Down
30 changes: 30 additions & 0 deletions tests/test_connection_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -1795,6 +1795,36 @@ async def test_password_auth(self):
async with self.connect(username='pw', password='pw', client_keys=None):
pass

@asynctest
async def test_password_auth_callable(self):
"""Test connecting with a callable for password authentication"""

async with self.connect(username='pw', password=lambda: 'pw',
client_keys=None):
pass

@asynctest
async def test_password_auth_async_callable(self):
"""Test connecting with an async callable for password authentication"""

async def get_password():
return 'pw'

async with self.connect(username='pw', password=get_password,
client_keys=None):
pass

@asynctest
async def test_password_auth_awaitable(self):
"""Test connecting with an awaitable for password authentication"""

async def get_password():
return 'pw'

async with self.connect(username='pw', password=get_password(),
client_keys=None):
pass

@asynctest
async def test_password_auth_disabled(self):
"""Test connecting with password authentication disabled"""
Expand Down

0 comments on commit f4df7f4

Please sign in to comment.