Skip to content

Commit

Permalink
authentication: added password_expired_with_output to su and ssh objects
Browse files Browse the repository at this point in the history
  • Loading branch information
Dan Lavu committed Nov 30, 2024
1 parent a0156fe commit 697c845
Showing 1 changed file with 138 additions and 57 deletions.
195 changes: 138 additions & 57 deletions sssd_test_framework/utils/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,75 +284,122 @@ def password(self, username: str, password: str) -> bool:
rc, _, _, _ = self.password_with_output(username, password)
return rc == 0

def password_expired(self, username: str, password: str, new_password: str) -> bool:
def password_expired_with_output(
self, username: str, password: str, new_password: str
) -> tuple[int, int, str, str]:
"""
Call ``su - $username`` and authenticate the user with password, expect
that the password is expired and change it to the new password.
Call ``su - $username`` and authenticate the user with password, expect that the password
is expired and change it to the new password and captures standard output and error.
:param username: Username.
:type username: str
:param password: Old, expired user password.
:type password: str
:param new_password: New user password.
:type new_password: str
:return: True if authentication and password change was successful, False otherwise.
:rtype: bool
:return: Tuple containing [return code, command code, stdout, stderr].
:rtype: Tuple[int, int, str, str]
"""
result = self.host.conn.expect_nobody(
rf"""
# Disable debug output
# exp_internal 0
proc exitmsg {{ msg code }} {{
# Close spawned program, if we are in the prompt
catch close
# 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 su - "{username}"
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 {{
"Password expired. Change your password now." {{ }}
-re $prompt {{puts "expect result: Authentication succeeded without password change"; exit 2}}
"Authentication failure" {{puts "expect result: Authentication failure"; exit 1}}
timeout {{puts "expect result: Unexpected output"; exit 201}}
eof {{puts "expect result: Unexpected end of file"; exit 202}}
-re $prompt {{exitmsg "Authentication succeeded without password change" 2}}
"Authentication failure" {{exitmsg "Authentication failure" 1}}
timeout {{exitmsg "Unexpected output" 201}}
eof {{exitmsg "Unexpected end of file" 202}}
}}
expect {{
"Current 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 {{
"New password:" {{send "{new_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 {{
"Retype new password:" {{send "{new_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 change was successful"; exit 0}}
timeout {{puts "expect result: Unexpected output"; exit 201}}
eof {{puts "expect result: Unexpected end of file"; exit 202}}
-re $prompt {{exitmsg "Password change was successful" 0}}
"Please make sure the password meets the complexity constraints." {{exitmsg "Complexity failure" 1}}
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_expired(self, username: str, password: str, new_password: str) -> bool:
"""
Call ``su - $username`` and authenticate the user with password, expect
that the password is expired and change it to the new password.
:param username: Username.
:type username: str
:param password: Old, expired user password.
:type password: str
:param new_password: New user password.
:type new_password: str
:return: True if password change is successful.
:rtype: bool
"""
rc, _, _, _ = self.password_expired_with_output(username, password, new_password)
return rc == 0

def passkey_with_output(
self,
Expand Down Expand Up @@ -688,25 +735,46 @@ def password(self, username: str, password: str) -> bool:
rc, _, _, _ = self.password_with_output(username, password)
return rc == 0

def password_expired(self, username: str, password: str, new_password: str) -> bool:
def password_expired_with_output(
self, username: str, password: str, new_password: str
) -> tuple[int, int, str, str]:
"""
SSH to the remote host and authenticate the user with password, expect
that the password is expired and change it to the new password.
SSH to the remote host and authenticate the user with password, expect that the password
is expired and change it to the new password and captures standard output and error.
:param username: Username.
:type username: str
:param password: Old, expired user password.
:type password: str
:param new_password: New user password.
:type new_password: str
:return: True if authentication and password change 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.conn.expect_nobody(
rf"""
# Disable debug output
exp_internal 0
proc exitmsg {{ msg code }} {{
# Close spawned program, if we are in the prompt
catch close
# 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 @@ -715,56 +783,69 @@ def password_expired(self, username: str, password: str, new_password: str) -> b
expect {{
"password:" {{send "{password}\n"}}
timeout {{puts "expect result: Unexpected output"; exit 201}}
eof {{puts "expect result: Unexpected end of file"; exit 202}}
}}
expect {{
"Password expired. Change your password now." {{ }}
-re $prompt {{puts "expect result: Authentication succeeded without password change"; exit 2}}
"{username}@localhost: Permission denied" {{puts "expect result: Authentication failure"; exit 1}}
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 {{
"Current 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 {{
"New password:" {{send "{new_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 {{
"Retype new password:" {{send "{new_password}\n"}}
timeout {{puts "expect result: Unexpected output"; exit 201}}
eof {{puts "expect result: Unexpected end of file"; exit 202}}
}}
expect {{
-re "passwd: .+ updated successfully." {{ }}
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 {{
timeout {{puts "expect result: Unexpected output"; exit 201}}
eof {{puts "expect result: Password change was successful"; exit 0}}
"password updated successfully" {{exitmsg "Password change was successful" 0}}
"Please make sure the password meets the complexity constraints." {{exitmsg "Complexity failure" 1}}
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_expired(self, username: str, password: str, new_password: str) -> bool:
"""
SSH to the remote host and authenticate the user with password, expect
that the password is expired and change it to the new password.
:param username: Username.
:type username: str
:param password: Old, expired user password.
:type password: str
:param new_password: New user password.
:type new_password: str
:return: True if authentication and password change was successful, False otherwise.
:rtype: bool
"""
rc, _, _, _ = self.password_expired_with_output(username, password, new_password)
return rc == 0


class SudoAuthenticationUtils(MultihostUtility[MultihostHost]):
Expand Down

0 comments on commit 697c845

Please sign in to comment.