diff --git a/sssd_test_framework/utils/authentication.py b/sssd_test_framework/utils/authentication.py index b12bfc8..aaa0b68 100644 --- a/sssd_test_framework/utils/authentication.py +++ b/sssd_test_framework/utils/authentication.py @@ -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 \ @@ -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: """