mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 04:20:45 +08:00
Fix Huawei stelnet hop auth hanging on host-key [Y/N] prompts.
Answer Continue/Save-key prompts during CLI-hop secondary auth so WebCRT terminal login no longer times out after connectivity passes.
This commit is contained in:
parent
91a2d1ad6b
commit
6901a7c019
4 changed files with 240 additions and 16 deletions
113
tests/test_cli_hop_target_auth.py
Normal file
113
tests/test_cli_hop_target_auth.py
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
"""Unit tests for CLI-hop secondary auth (Huawei stelnet host-key + login)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from netx_api.ne_session_connect import (
|
||||
_interactive_target_auth,
|
||||
_looks_like_target_cli_prompt,
|
||||
_prompt_needs_auth,
|
||||
_prompt_needs_host_key_confirm,
|
||||
)
|
||||
|
||||
|
||||
class PromptDetectTests(unittest.TestCase):
|
||||
def test_huawei_username_and_password(self) -> None:
|
||||
self.assertEqual(
|
||||
_prompt_needs_auth("Please input the username:"),
|
||||
(True, False),
|
||||
)
|
||||
self.assertEqual(_prompt_needs_auth("Enter password:"), (False, True))
|
||||
self.assertEqual(_prompt_needs_auth("Password:"), (False, True))
|
||||
|
||||
def test_host_key_prompts(self) -> None:
|
||||
cont, save = _prompt_needs_host_key_confirm(
|
||||
"The server is not authenticated. Continue to access it? [Y/N]:"
|
||||
)
|
||||
self.assertTrue(cont)
|
||||
self.assertFalse(save)
|
||||
cont, save = _prompt_needs_host_key_confirm("Save the server's public key? [Y/N]:")
|
||||
self.assertFalse(cont)
|
||||
self.assertTrue(save)
|
||||
|
||||
def test_password_change_not_host_key(self) -> None:
|
||||
cont, save = _prompt_needs_host_key_confirm("Change now? [Y/N]:")
|
||||
self.assertFalse(cont)
|
||||
self.assertFalse(save)
|
||||
|
||||
def test_cli_prompt_rejects_yn(self) -> None:
|
||||
self.assertFalse(_looks_like_target_cli_prompt("Continue? [Y/N]:"))
|
||||
self.assertTrue(_looks_like_target_cli_prompt("<HW-TARGET>"))
|
||||
self.assertTrue(_looks_like_target_cli_prompt("[HW-VM-AR1000V-1]"))
|
||||
|
||||
|
||||
class HuaweiStelnetAuthTests(unittest.TestCase):
|
||||
@patch("netx_api.ne_session_connect._read_channel")
|
||||
@patch("netx_api.ne_session_connect._send_line")
|
||||
def test_answers_host_key_then_login(
|
||||
self,
|
||||
mock_send: MagicMock,
|
||||
mock_read: MagicMock,
|
||||
) -> None:
|
||||
"""Transcript from Huawei AR stelnet first connect to untrusted target."""
|
||||
mock_read.side_effect = [
|
||||
"Please input the username:",
|
||||
(
|
||||
"Trying 1.1.1.2 ...\n"
|
||||
"Press CTRL+K to abort\n"
|
||||
"Connected to 1.1.1.2 ...\n"
|
||||
"The server is not authenticated. Continue to access it? [Y/N]:"
|
||||
),
|
||||
"Save the server's public key? [Y/N]:",
|
||||
(
|
||||
"Jan 1 2017 01:16:39+00:00 HW-VM-AR1000V-1 "
|
||||
"%%01SSH/4/SAVE_PUBLICKEY(l)[3]:When deciding whether to save "
|
||||
"the server's public key 1.1.1.2, the user chose N.\n"
|
||||
"[HW-VM-AR1000V-1]\n"
|
||||
"Enter password:"
|
||||
),
|
||||
(
|
||||
" -----------------------------------------------------------------------------\n"
|
||||
" User last login information:\n"
|
||||
" -----------------------------------------------------------------------------\n"
|
||||
" Access Type: SSH\n"
|
||||
" IP-Address : 172.16.0.6\n"
|
||||
" Time : 2017-01-05 00:11:36+00:00\n"
|
||||
" ----------------\n"
|
||||
"<HW-TARGET>"
|
||||
),
|
||||
]
|
||||
conn = MagicMock()
|
||||
_interactive_target_auth(conn, "ipran", "secret")
|
||||
self.assertEqual(
|
||||
[c.args[1] for c in mock_send.call_args_list],
|
||||
["ipran", "Y", "N", "secret"],
|
||||
)
|
||||
|
||||
@patch("netx_api.ne_session_connect._read_channel")
|
||||
@patch("netx_api.ne_session_connect._send_line")
|
||||
def test_hop_prompt_before_password_not_success(
|
||||
self,
|
||||
mock_send: MagicMock,
|
||||
mock_read: MagicMock,
|
||||
) -> None:
|
||||
"""``[hop]`` between host-key and password must not end auth early."""
|
||||
mock_read.side_effect = [
|
||||
"Please input the username:",
|
||||
"The server is not authenticated. Continue to access it? [Y/N]:",
|
||||
"Save the server's public key? [Y/N]:",
|
||||
"[HW-VM-AR1000V-1]\n", # hop reprint — must keep waiting
|
||||
"Enter password:",
|
||||
"<TARGET>\n",
|
||||
"",
|
||||
]
|
||||
conn = MagicMock()
|
||||
_interactive_target_auth(conn, "ipran", "secret")
|
||||
sent = [c.args[1] for c in mock_send.call_args_list]
|
||||
self.assertEqual(sent, ["ipran", "Y", "N", "secret"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
@ -111,6 +111,13 @@ class WebcrtServiceTests(unittest.TestCase):
|
|||
self.assertEqual(svc.prepare_bootstrap_output("banner\n<r1>:"), "banner\n<r1>")
|
||||
self.assertTrue(svc._looks_like_password_change_prompt("Change now? [Y/N]:"))
|
||||
self.assertFalse(svc._looks_like_password_change_prompt("Change now? [Y/N]:N"))
|
||||
# Bare / stelnet host-key [Y/N] must not be treated as password-change.
|
||||
self.assertFalse(svc._looks_like_password_change_prompt("[Y/N]:"))
|
||||
self.assertFalse(
|
||||
svc._looks_like_password_change_prompt(
|
||||
"The server is not authenticated. Continue to access it? [Y/N]:"
|
||||
)
|
||||
)
|
||||
# WS attach must not send Enter when bootstrap is a login prompt.
|
||||
self.assertFalse(
|
||||
(not svc._looks_like_cli_prompt("Username:") and not svc._looks_like_login_prompt("Username:"))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue