Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

utils: added password_with_output function to capture ssh output #72

Merged
merged 1 commit into from
Feb 19, 2024
Merged
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
71 changes: 56 additions & 15 deletions sssd_test_framework/utils/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,23 +413,42 @@ def __init__(self, host: MultihostHost) -> None:
self.opts = "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
"""SSH CLI options."""

def password(self, username: str, password: str) -> bool:
def password_with_output(self, username: str, password: str) -> tuple[int, int, str, str]:
"""
SSH to the remote host and authenticate the user with password.
SSH to the remote host and authenticate the user with password and captures standard output and error.

:param username: Username.
:type username: str
:param password: User password.
:type password: str
:return: True if authentication was successful, False otherwise.
:rtype: bool
:return: Tuple containing [except return code, command exit code, stdout, stderr].
:rtype: Tuple[int, int, str, str]
"""

result = self.host.ssh.expect_nobody(
rf"""
# Disable debug output
exp_internal 0

proc exitmsg {{ msg code }} {{
# Send EOF, if we are in the prompt
send \x04;

# Wait for the exit code
lassign [wait] pid spawnid os_error_flag rc

puts ""
puts "expect result: $msg"
puts "expect exit code: $code"
puts "expect spawn exit code: $rc"
exit $code
}}

# It takes some time to get authentication failure
set timeout {DEFAULT_AUTHENTICATION_TIMEOUT}
set prompt "\n.*\[#\$>\] $"
log_user 1
log_file /tmp/expect.log

spawn ssh {self.opts} \
-o PreferredAuthentications=password \
Expand All @@ -438,27 +457,49 @@ def password(self, username: str, password: str) -> bool:

expect {{
"password:" {{send "{password}\n"}}
timeout {{puts "expect result: Unexpected output"; exit 201}}
eof {{puts "expect result: Unexpected end of file"; exit 202}}
timeout {{exitmsg "Unexpected output" 201}}
eof {{exitmsg "Unexpected end of file" 202}}
}}

expect {{
-re $prompt {{puts "expect result: Password authentication successful"; exit 0}}
"{username}@localhost: Permission denied" {{puts "expect result: Authentication failure"; exit 1}}
"Connection closed by UNKNOWN port 65535" {{puts "expect result: Connection closed"; exit 2}}
timeout {{puts "expect result: Unexpected output"; exit 201}}
eof {{puts "expect result: Unexpected end of file"; exit 202}}
-re $prompt {{exitmsg "Password authentication successful" 0}}
"{username}@localhost: Permission denied" {{exitmsg "Authentication failure" 1}}
"Connection closed by UNKNOWN port 65535" {{exitmsg "Connection closed" 2}}
timeout {{exitmsg "Unexpected output" 201}}
eof {{exitmsg "Unexpected end of file" 202}}
}}

puts "expect result: Unexpected code path"
exit 203
"""
exitmsg "Unexpected code path" 203
""",
verbose=False,
)

if result.rc > 200:
raise ExpectScriptError(result.rc)

return result.rc == 0
expect_data = result.stdout_lines[-3:]

# Get command exit code.
cmdrc = int(expect_data[2].split(":")[1].strip())

# Alter stdout, first line is spawned command, the last three are our expect output.
stdout = "\n".join(result.stdout_lines[1:-3])

return result.rc, cmdrc, stdout, result.stderr

def password(self, username: str, password: str) -> bool:
"""
SSH to the remote host and authenticate the user with password.

:param username: Username.
:type username: str
:param password: User password.
:type password: str
:return: True if authentication was successful, False otherwise.
:rtype: bool
"""
rc, _, _, _ = self.password_with_output(username, password)
return rc == 0

def password_expired(self, username: str, password: str, new_password: str) -> bool:
"""
Expand Down
Loading