243 lines
8.4 KiB
Python
243 lines
8.4 KiB
Python
"""Unit tests for the password context class.
|
|
|
|
We are primarily testing whether the actions of the PasswordContext matches
|
|
those in the low level API.
|
|
|
|
"""
|
|
|
|
import random
|
|
import string
|
|
from pathlib import Path
|
|
from typing import cast
|
|
import pytest
|
|
import pykeepass
|
|
|
|
from sshecret_admin.services.keepass import PasswordContext
|
|
|
|
|
|
def random_string(length: int = 5) -> str:
|
|
"""Generate random string."""
|
|
chars = string.ascii_lowercase
|
|
return "".join(random.choice(chars) for _ in range(length))
|
|
|
|
|
|
def create_random_entries(
|
|
password_database: pykeepass.PyKeePass,
|
|
amount: int = 10,
|
|
prefix: str = "secret",
|
|
group: pykeepass.group.Group | None = None,
|
|
) -> None:
|
|
"""Create some random entries."""
|
|
if not group:
|
|
group = cast(pykeepass.group.Group, password_database.root_group)
|
|
for n in range(amount):
|
|
name = f"{prefix}-{n}"
|
|
username = "NONE"
|
|
password = random_string(12)
|
|
password_database.add_entry(
|
|
destination_group=group,
|
|
title=name,
|
|
username=username,
|
|
password=password,
|
|
)
|
|
password_database.save()
|
|
|
|
|
|
@pytest.fixture(name="password_database")
|
|
def password_db_fixture(tmp_path: Path):
|
|
"""Create a password database."""
|
|
filename = tmp_path / "kpdb.kdbx"
|
|
yield pykeepass.create_database(str(filename), password="test")
|
|
|
|
|
|
def test_add_entry(password_database: pykeepass.PyKeePass) -> None:
|
|
"""Test add entry."""
|
|
context = PasswordContext(password_database)
|
|
context.add_entry("testentry", "testsecret")
|
|
|
|
entry = password_database.find_entries(title="testentry", first=True)
|
|
assert entry is not None
|
|
assert isinstance(entry, pykeepass.entry.Entry)
|
|
assert entry.password == "testsecret"
|
|
|
|
|
|
def test_get_secret(password_database: pykeepass.PyKeePass) -> None:
|
|
"""Test get secret."""
|
|
|
|
context = PasswordContext(password_database)
|
|
context.add_entry("testentry", "testsecret")
|
|
|
|
secret = context.get_secret("testentry")
|
|
assert secret is not None
|
|
assert secret == "testsecret"
|
|
|
|
|
|
def test_get_available_secrets(password_database: pykeepass.PyKeePass) -> None:
|
|
"""Test get_available_secrets."""
|
|
create_random_entries(password_database, 10)
|
|
context = PasswordContext(password_database)
|
|
available_secrets = context.get_available_secrets()
|
|
assert len(available_secrets) == 10
|
|
for n in range(10):
|
|
assert f"secret-{n}" in available_secrets
|
|
|
|
|
|
def test_delete_entry(password_database: pykeepass.PyKeePass) -> None:
|
|
"""Test deletion of entry."""
|
|
create_random_entries(password_database, 3)
|
|
context = PasswordContext(password_database)
|
|
available_secrets = context.get_available_secrets()
|
|
assert len(available_secrets) == 3
|
|
context.delete_entry("secret-2")
|
|
entry = password_database.find_entries(title="secret-2", first=True)
|
|
assert entry is None
|
|
available_secrets = context.get_available_secrets()
|
|
assert len(available_secrets) == 2
|
|
|
|
|
|
def test_get_secret_groups(password_database: pykeepass.PyKeePass) -> None:
|
|
"""Test get secret groups."""
|
|
# We create a hierarchy of groups to test how that parses.
|
|
root_group = password_database.root_group
|
|
first_group = password_database.add_group(
|
|
root_group, "level_one", notes="A group in the root"
|
|
)
|
|
password_database.add_group(
|
|
first_group, "level_two", notes="A group one level down"
|
|
)
|
|
# Another group at the root, without a note
|
|
password_database.add_group(root_group, "free_group")
|
|
password_database.save()
|
|
|
|
context = PasswordContext(password_database)
|
|
groups = context.get_secret_groups()
|
|
assert len(groups) == 3
|
|
|
|
|
|
def test_get_secret_groups_regex(password_database: pykeepass.PyKeePass) -> None:
|
|
"""Get secret groups matching a regex."""
|
|
# create some groups matching a pattern
|
|
for n in range(4):
|
|
password_database.add_group(password_database.root_group, f"foo-{n}")
|
|
|
|
for n in range(3):
|
|
parent_group = password_database.find_groups(name="foo-1", first=True)
|
|
password_database.add_group(parent_group, f"bar-{n}")
|
|
|
|
password_database.save()
|
|
|
|
context = PasswordContext(password_database)
|
|
foo_groups = context.get_secret_groups("foo-.*")
|
|
assert len(foo_groups) == 4
|
|
bar_groups = context.get_secret_groups("bar-.*")
|
|
assert len(bar_groups) == 3
|
|
|
|
|
|
def test_add_group(password_database: pykeepass.PyKeePass) -> None:
|
|
"""Test add_group."""
|
|
context = PasswordContext(password_database)
|
|
context.add_group("test_group", "Test Group")
|
|
assert password_database.find_groups(name="test_group", first=True) is not None
|
|
|
|
# add a nested group below the first one
|
|
context.add_group("nested_group", "Nested test group", "test_group")
|
|
group = password_database.find_groups(name="nested_group", first=True)
|
|
assert group is not None
|
|
assert isinstance(group, pykeepass.group.Group)
|
|
parent_group = group.parentgroup
|
|
assert parent_group.name == "test_group"
|
|
|
|
|
|
def test_set_group_description(password_database: pykeepass.PyKeePass) -> None:
|
|
"""Test setting the group description."""
|
|
context = PasswordContext(password_database)
|
|
context.add_group("test_group", "Test Group")
|
|
|
|
kp_group = password_database.find_groups(name="test_group", first=True)
|
|
assert isinstance(kp_group, pykeepass.group.Group)
|
|
assert kp_group.notes == "Test Group"
|
|
|
|
context.set_group_description("test_group", "New Description")
|
|
|
|
kp_group = password_database.find_groups(name="test_group", first=True)
|
|
assert isinstance(kp_group, pykeepass.group.Group)
|
|
assert kp_group.notes == "New Description"
|
|
|
|
|
|
def test_add_entry_with_group(password_database: pykeepass.PyKeePass) -> None:
|
|
"""Test adding an entry with a group."""
|
|
context = PasswordContext(password_database)
|
|
context.add_group("test_group", "A test group")
|
|
context.add_entry("test_entry", "test_secret", group_name="test_group")
|
|
|
|
entry = password_database.find_entries(title="test_entry", first=True)
|
|
assert entry is not None
|
|
|
|
assert isinstance(entry, pykeepass.entry.Entry)
|
|
assert entry.group.name == "test_group"
|
|
|
|
|
|
def test_move_entry(password_database: pykeepass.PyKeePass) -> None:
|
|
"""Test moving entries between groups."""
|
|
context = PasswordContext(password_database)
|
|
|
|
context.add_group("test_group", "A test group")
|
|
context.add_group("test_group_2", "Another test group")
|
|
|
|
context.add_entry("test_entry", "test_secret")
|
|
entry = password_database.find_entries(title="test_entry", first=True)
|
|
assert isinstance(entry, pykeepass.entry.Entry)
|
|
assert entry.group.is_root_group is True
|
|
|
|
context.set_secret_group("test_entry", "test_group")
|
|
|
|
entry = password_database.find_entries(title="test_entry", first=True)
|
|
assert isinstance(entry, pykeepass.entry.Entry)
|
|
|
|
assert entry.group.is_root_group is False
|
|
assert entry.group.name == "test_group"
|
|
|
|
context.set_secret_group("test_entry", "test_group_2")
|
|
|
|
entry = password_database.find_entries(title="test_entry", first=True)
|
|
assert isinstance(entry, pykeepass.entry.Entry)
|
|
|
|
assert entry.group.is_root_group is False
|
|
assert entry.group.name == "test_group_2"
|
|
|
|
context.set_secret_group("test_entry", None)
|
|
|
|
entry = password_database.find_entries(title="test_entry", first=True)
|
|
assert isinstance(entry, pykeepass.entry.Entry)
|
|
assert entry.group.is_root_group is True
|
|
|
|
|
|
def test_move_group(password_database: pykeepass.PyKeePass) -> None:
|
|
"""Test moving a group."""
|
|
context = PasswordContext(password_database)
|
|
context.add_group("test_group", "A test group")
|
|
context.add_group("parent_group", "A parent group")
|
|
context.move_group("test_group", "parent_group")
|
|
|
|
kp_group = password_database.find_groups(name="test_group", first=True)
|
|
assert isinstance(kp_group, pykeepass.group.Group)
|
|
assert kp_group.parentgroup.name == "parent_group"
|
|
|
|
|
|
def test_delete_group(password_database: pykeepass.PyKeePass) -> None:
|
|
"""Test group deletion."""
|
|
context = PasswordContext(password_database)
|
|
context.add_group("test_group", "A test group")
|
|
# Add some entries to this group.
|
|
kp_group = password_database.find_groups(name="test_group", first=True)
|
|
assert isinstance(kp_group, pykeepass.group.Group)
|
|
create_random_entries(password_database, amount=10, group=kp_group)
|
|
|
|
context.delete_group("test_group")
|
|
kp_group = password_database.find_groups(name="test_group", first=True)
|
|
assert kp_group is None
|
|
|
|
# Check if the secrets are still there.
|
|
secrets = context.get_available_secrets()
|
|
assert len(secrets) == 10
|