This allows using python linters and tools directly and makes it much easier to create our own convenience methods. If we can avoid the trap of excess complexity and configurability, I think this could facilitate addition of much broader integration tests, especially for the CLI.
130 lines
4.4 KiB
Python
130 lines
4.4 KiB
Python
"""Provide a class and helper methods for agenix integration tests."""
|
|
|
|
|
|
import typing as t
|
|
|
|
T = t.TypeVar("T", str, list[str])
|
|
|
|
|
|
class AgenixTester:
|
|
"""Provide a class to help reduce repetition in setup."""
|
|
|
|
def __init__(self, system, user, password) -> None:
|
|
"""Necessary setup can be put here."""
|
|
self.system = system
|
|
self.user = user
|
|
self.password = password
|
|
self.setup()
|
|
|
|
def login(self) -> None:
|
|
self.system.wait_for_unit("multi-user.target")
|
|
self.system.wait_until_succeeds("pgrep -f 'agetty.*tty1'")
|
|
self.system.sleep(2)
|
|
self.system.send_key("alt-f2")
|
|
self.system.wait_until_succeeds("[ $(fgconsole) = 2 ]")
|
|
self.system.wait_for_unit("getty@tty2.service")
|
|
self.system.wait_until_succeeds("pgrep -f 'agetty.*tty2'")
|
|
self.system.wait_until_tty_matches("2", "login: ")
|
|
self.system.send_chars(f"{self.user}\n")
|
|
self.system.wait_until_tty_matches("2", f"login: {self.user}")
|
|
self.system.wait_until_succeeds("pgrep login")
|
|
self.system.sleep(2)
|
|
self.system.send_chars(f"{self.password}\n")
|
|
|
|
def setup(self) -> None:
|
|
"""Run common setup code."""
|
|
self.login()
|
|
|
|
def user_succeed(
|
|
self,
|
|
cmds: T,
|
|
directory: str | None = None,
|
|
debug: bool = False,
|
|
) -> T:
|
|
"""Run cmds as `self.user`, optionally in a specified directory.
|
|
|
|
For convenience, if cmds is a sequence, returns output as a list of
|
|
outputs corresponding with each line in cmds. if cmds is a string,
|
|
returns output as a string.
|
|
"""
|
|
|
|
context: list[str] = [
|
|
"set -Eeu -o pipefail",
|
|
"shopt -s inherit_errexit",
|
|
]
|
|
if debug:
|
|
context.append("set -x")
|
|
|
|
if directory:
|
|
context.append(f"cd {directory}")
|
|
|
|
if isinstance(cmds, str):
|
|
commands_str = "\n".join([*context, cmds])
|
|
final_command = f"sudo -u {self.user} -- bash -c '{commands_str}'"
|
|
return self.system.succeed(final_command)
|
|
|
|
results: list[str] = []
|
|
for cmd in cmds:
|
|
commands_str = "\n".join([*context, cmd])
|
|
final_command = f"sudo -u {self.user} -- bash -c '{commands_str}'"
|
|
result = self.system.succeed(final_command)
|
|
results.append(result.strip())
|
|
return t.cast(T, results)
|
|
|
|
def run_all(self) -> None:
|
|
self.test_rekeying()
|
|
self.test_user_edit()
|
|
|
|
def test_rekeying(self) -> None:
|
|
"""Ensure we can rekey a file and its hash changes."""
|
|
|
|
before_hash, _, after_hash = self.user_succeed(
|
|
[
|
|
"sha256sum passwordfile-user1.age",
|
|
f"agenix -r -i /home/{self.user}/.ssh/id_ed25519",
|
|
"sha256sum passwordfile-user1.age",
|
|
],
|
|
directory="/tmp/secrets",
|
|
)
|
|
|
|
# Ensure we actually have hashes
|
|
for line in [before_hash, after_hash]:
|
|
h = line.split()
|
|
assert len(h) == 2, f"hash should be [hash, filename], got {h}"
|
|
assert h[1] == "passwordfile-user1.age", "filename is incorrect"
|
|
assert len(h[0].strip()) == 64, "hash length is incorrect"
|
|
assert (
|
|
before_hash[0] != after_hash[0]
|
|
), "hash did not change with rekeying"
|
|
|
|
def test_user_edit(self):
|
|
"""Ensure user1 can edit passwordfile-user1.age."""
|
|
self.user_succeed(
|
|
"EDITOR=cat agenix -e passwordfile-user1.age",
|
|
directory="/tmp/secrets",
|
|
)
|
|
|
|
self.user_succeed("echo bogus > ~/.ssh/id_rsa")
|
|
|
|
# Cannot edit with bogus default id_rsa
|
|
self.system.fail(
|
|
f"sudo -u {self.user} -- bash -c '"
|
|
"cd /tmp/secrets; "
|
|
"EDITOR=cat agenix -e /tmp/secrets/passwordfile-user1.age; "
|
|
"'"
|
|
)
|
|
|
|
# user1 can still edit if good identity specified
|
|
*_, pw = self.user_succeed(
|
|
[
|
|
(
|
|
"EDITOR=cat agenix -e passwordfile-user1.age "
|
|
"-i /home/user1/.ssh/id_ed25519"
|
|
),
|
|
"rm ~/.ssh/id_rsa",
|
|
"echo 'secret1234' | agenix -e passwordfile-user1.age",
|
|
"EDITOR=cat agenix -e passwordfile-user1.age",
|
|
],
|
|
directory="/tmp/secrets",
|
|
)
|
|
assert pw == "secret1234", f"password didn't match, got '{pw}'"
|