ssh-handler 1.0.2__tar.gz → 1.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. {ssh_handler-1.0.2/ssh_handler.egg-info → ssh_handler-1.0.3}/PKG-INFO +1 -1
  2. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/pyproject.toml +1 -1
  3. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/ssh_handler/__init__.py +1 -1
  4. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/ssh_handler/core.py +26 -2
  5. {ssh_handler-1.0.2 → ssh_handler-1.0.3/ssh_handler.egg-info}/PKG-INFO +1 -1
  6. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/tests/test_offline.py +11 -0
  7. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/LICENSE +0 -0
  8. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/README.md +0 -0
  9. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/setup.cfg +0 -0
  10. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/ssh_handler/__main__.py +0 -0
  11. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/ssh_handler/cli.py +0 -0
  12. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/ssh_handler/config.py +0 -0
  13. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/ssh_handler/credentials.py +0 -0
  14. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/ssh_handler/exceptions.py +0 -0
  15. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/ssh_handler/ftp.py +0 -0
  16. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/ssh_handler/pool.py +0 -0
  17. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/ssh_handler/pyqt_worker.py +0 -0
  18. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/ssh_handler/results.py +0 -0
  19. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/ssh_handler/winrm_bootstrap.py +0 -0
  20. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/ssh_handler.egg-info/SOURCES.txt +0 -0
  21. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/ssh_handler.egg-info/dependency_links.txt +0 -0
  22. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/ssh_handler.egg-info/entry_points.txt +0 -0
  23. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/ssh_handler.egg-info/requires.txt +0 -0
  24. {ssh_handler-1.0.2 → ssh_handler-1.0.3}/ssh_handler.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ssh-handler
3
- Version: 1.0.2
3
+ Version: 1.0.3
4
4
  Summary: Extensive SSH/SFTP/SCP/FTP handler built on Paramiko, for test automation, CLIs and PyQt5 tools.
5
5
  Author: ssh-handler contributors
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ssh-handler"
7
- version = "1.0.2"
7
+ version = "1.0.3"
8
8
  description = "Extensive SSH/SFTP/SCP/FTP handler built on Paramiko, for test automation, CLIs and PyQt5 tools."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -22,7 +22,7 @@ dependencies (PyQt5 / scp) don't break the core package.
22
22
 
23
23
  from __future__ import annotations
24
24
 
25
- __version__ = "1.0.2"
25
+ __version__ = "1.0.3"
26
26
 
27
27
  from .config import SSHConfig, FTPConfig
28
28
  from .core import SSHHandler, ShellSession
@@ -269,7 +269,12 @@ class SSHHandler:
269
269
  if cfg.passwordless:
270
270
  kwargs["allow_agent"] = True
271
271
  kwargs["look_for_keys"] = True
272
- elif empty_password:
272
+ elif empty_password or pw == "":
273
+ # Explicit empty-password authentication. An empty string means
274
+ # "log in with a blank password" (PermitEmptyPasswords hosts) - send
275
+ # it directly and don't waste the first attempt probing keys/agent,
276
+ # which would otherwise fail with "No authentication methods
277
+ # available" before the empty password is ever tried.
273
278
  kwargs["password"] = ""
274
279
  kwargs["look_for_keys"] = False
275
280
  kwargs["allow_agent"] = False
@@ -308,9 +313,28 @@ class SSHHandler:
308
313
  last_auth = exc
309
314
  client.close()
310
315
  self._emit(logging.DEBUG, f"Auth strategy '{label}' rejected.")
311
- except (paramiko.SSHException, OSError) as exc:
316
+ except paramiko.SSHException as exc:
312
317
  client.close()
318
+ # "No authentication methods available" means paramiko had no
319
+ # credential the server would accept (no password/key/agent).
320
+ # That's an auth problem, not a transport one - try the next
321
+ # strategy, and if none are left raise a clear, guided error.
322
+ if "no authentication methods" in str(exc).lower():
323
+ last_auth = exc
324
+ self._emit(logging.DEBUG,
325
+ f"Auth strategy '{label}': {exc}")
326
+ continue
313
327
  raise exc
328
+ except OSError as exc:
329
+ client.close()
330
+ raise exc
331
+ if last_auth and "no authentication methods" in str(last_auth).lower():
332
+ raise SSHAuthenticationError(
333
+ f"No usable credentials for {cfg.auth_username}@{cfg.host}. "
334
+ f"Provide one of: password=..., key_filename=..., "
335
+ f"passwordless=True (use your SSH key/agent), or password='' for a "
336
+ f"blank-password account (the server must allow PermitEmptyPasswords)."
337
+ )
314
338
  raise SSHAuthenticationError(
315
339
  f"All authentication strategies failed for "
316
340
  f"{cfg.auth_username}@{cfg.host}: {last_auth}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ssh-handler
3
- Version: 1.0.2
3
+ Version: 1.0.3
4
4
  Summary: Extensive SSH/SFTP/SCP/FTP handler built on Paramiko, for test automation, CLIs and PyQt5 tools.
5
5
  Author: ssh-handler contributors
6
6
  License-Expression: MIT
@@ -38,6 +38,17 @@ kw2 = h._build_connect_kwargs(empty_password=True, sock=None)
38
38
  assert kw2["password"] == "" and kw2["allow_agent"] is False
39
39
  print("empty-password strategy OK")
40
40
 
41
+ # explicit password="" must send a blank password on the PRIMARY attempt
42
+ # (regression guard: previously the primary probed keys and failed with
43
+ # "No authentication methods available" before the empty password was tried)
44
+ hp = SSHHandler.__new__(SSHHandler)
45
+ hp.config = SSHConfig(host="t", username="u", password="")
46
+ hp._jump = None; hp.log = logging.getLogger("x2"); hp._log_callback = None
47
+ kw3 = hp._build_connect_kwargs(empty_password=False, sock=None)
48
+ assert kw3["password"] == "" and kw3["allow_agent"] is False \
49
+ and kw3["look_for_keys"] is False
50
+ print("explicit empty-password (primary) OK")
51
+
41
52
  # TransferResult math
42
53
  tr = TransferResult("a", "b", "push", "sftp", 1048576, 2.0, 1)
43
54
  assert abs(tr.speed_bps - 524288) < 1 and tr.human_size == "1.0MB"
File without changes
File without changes
File without changes