Merge pull request #273 from PR0M3TH3AN/codex/make-label-field-required-for-all-entries

Require label for all entries
This commit is contained in:
thePR0M3TH3AN
2025-07-05 10:02:08 -04:00
committed by GitHub
16 changed files with 123 additions and 68 deletions

View File

@@ -169,7 +169,7 @@ seedpass totp "email"
# The code is printed and copied to your clipboard # The code is printed and copied to your clipboard
# Sort or filter the list view # Sort or filter the list view
seedpass list --sort website seedpass list --sort label
seedpass list --filter totp seedpass list --filter totp
# Use the **Settings** menu to configure an extra backup directory # Use the **Settings** menu to configure an extra backup directory
@@ -185,7 +185,7 @@ The encrypted index file `seedpass_entries_db.json.enc` begins with `schema_vers
"schema_version": 2, "schema_version": 2,
"entries": { "entries": {
"0": { "0": {
"website": "example.com", "label": "example.com",
"length": 8, "length": 8,
"type": "password", "type": "password",
"notes": "" "notes": ""

View File

@@ -53,7 +53,7 @@ The following table provides a quick reference to all available advanced CLI com
| Retrieve a password entry | `retrieve` | `-R` | `--retrieve` | `seedpass retrieve --index 3` or `seedpass retrieve --title "GitHub"` | | Retrieve a password entry | `retrieve` | `-R` | `--retrieve` | `seedpass retrieve --index 3` or `seedpass retrieve --title "GitHub"` |
| Modify an existing entry | `modify` | `-M` | `--modify` | `seedpass modify --index 3 --title "GitHub Pro" --notes "Updated to pro account" --tags "work,development,pro" --length 22` | | Modify an existing entry | `modify` | `-M` | `--modify` | `seedpass modify --index 3 --title "GitHub Pro" --notes "Updated to pro account" --tags "work,development,pro" --length 22` |
| Delete an entry | `delete` | `-D` | `--delete` | `seedpass delete --index 3` | | Delete an entry | `delete` | `-D` | `--delete` | `seedpass delete --index 3` |
| List all entries | `list` | `-L` | `--list` | `seedpass list --sort website` | | List all entries | `list` | `-L` | `--list` | `seedpass list --sort label` |
| Search for a password entry | `search` | `-S` | `--search` | `seedpass search "GitHub"` | | Search for a password entry | `search` | `-S` | `--search` | `seedpass search "GitHub"` |
| Get password from query | `get` | | | `seedpass get "GitHub"` | Get password from query | `get` | | | `seedpass get "GitHub"`
| Display a TOTP code | `totp` | | | `seedpass totp "email"` | Display a TOTP code | `totp` | | | `seedpass totp "email"`
@@ -179,11 +179,11 @@ seedpass delete --index 3
**Long Flag:** `--list` **Long Flag:** `--list`
**Description:** **Description:**
Lists all password entries stored in the password manager. You can sort the output by index, website, or username and filter by entry type. Lists all password entries stored in the password manager. You can sort the output by index, label, or username and filter by entry type.
**Usage Example:** **Usage Example:**
```bash ```bash
seedpass list --sort website seedpass list --sort label
seedpass list --filter totp seedpass list --filter totp
``` ```

View File

@@ -42,7 +42,7 @@ All entries belonging to a seed profile are summarized in an encrypted file name
"schema_version": 2, "schema_version": 2,
"entries": { "entries": {
"0": { "0": {
"website": "example.com", "label": "example.com",
"length": 8, "length": 8,
"type": "password", "type": "password",
"notes": "" "notes": ""

View File

@@ -262,7 +262,7 @@ def print_matches(
print(colored(" Type: Nostr Key", "cyan")) print(colored(" Type: Nostr Key", "cyan"))
else: else:
if website: if website:
print(colored(f" Website: {website}", "cyan")) print(colored(f" Label: {website}", "cyan"))
if username: if username:
print(colored(f" Username: {username}", "cyan")) print(colored(f" Username: {username}", "cyan"))
if url: if url:

View File

@@ -65,6 +65,13 @@ class EntryManager:
if "kind" not in entry: if "kind" not in entry:
entry["kind"] = entry.get("type", EntryType.PASSWORD.value) entry["kind"] = entry.get("type", EntryType.PASSWORD.value)
entry.setdefault("type", entry["kind"]) entry.setdefault("type", entry["kind"])
if "label" not in entry and "website" in entry:
entry["label"] = entry["website"]
if (
"website" in entry
and entry.get("type") == EntryType.PASSWORD.value
):
entry.pop("website", None)
logger.debug("Index loaded successfully.") logger.debug("Index loaded successfully.")
return data return data
except Exception as e: except Exception as e:
@@ -106,7 +113,7 @@ class EntryManager:
def add_entry( def add_entry(
self, self,
website_name: str, label: str,
length: int, length: int,
username: Optional[str] = None, username: Optional[str] = None,
url: Optional[str] = None, url: Optional[str] = None,
@@ -117,7 +124,7 @@ class EntryManager:
""" """
Adds a new entry to the encrypted JSON index file. Adds a new entry to the encrypted JSON index file.
:param website_name: The name of the website. :param label: A label describing the entry (e.g. website name).
:param length: The desired length of the password. :param length: The desired length of the password.
:param username: (Optional) The username associated with the website. :param username: (Optional) The username associated with the website.
:param url: (Optional) The URL of the website. :param url: (Optional) The URL of the website.
@@ -131,7 +138,7 @@ class EntryManager:
data.setdefault("entries", {}) data.setdefault("entries", {})
data["entries"][str(index)] = { data["entries"][str(index)] = {
"website": website_name, "label": label,
"length": length, "length": length,
"username": username if username else "", "username": username if username else "",
"url": url if url else "", "url": url if url else "",
@@ -222,7 +229,11 @@ class EntryManager:
raise raise
def add_ssh_key( def add_ssh_key(
self, parent_seed: str, index: int | None = None, notes: str = "" self,
label: str,
parent_seed: str,
index: int | None = None,
notes: str = "",
) -> int: ) -> int:
"""Add a new SSH key pair entry. """Add a new SSH key pair entry.
@@ -240,6 +251,7 @@ class EntryManager:
"type": EntryType.SSH.value, "type": EntryType.SSH.value,
"kind": EntryType.SSH.value, "kind": EntryType.SSH.value,
"index": index, "index": index,
"label": label,
"notes": notes, "notes": notes,
} }
self._save_index(data) self._save_index(data)
@@ -263,6 +275,7 @@ class EntryManager:
def add_pgp_key( def add_pgp_key(
self, self,
label: str,
parent_seed: str, parent_seed: str,
index: int | None = None, index: int | None = None,
key_type: str = "ed25519", key_type: str = "ed25519",
@@ -280,6 +293,7 @@ class EntryManager:
"type": EntryType.PGP.value, "type": EntryType.PGP.value,
"kind": EntryType.PGP.value, "kind": EntryType.PGP.value,
"index": index, "index": index,
"label": label,
"key_type": key_type, "key_type": key_type,
"user_id": user_id, "user_id": user_id,
"notes": notes, "notes": notes,
@@ -362,6 +376,7 @@ class EntryManager:
def add_seed( def add_seed(
self, self,
label: str,
parent_seed: str, parent_seed: str,
index: int | None = None, index: int | None = None,
words_num: int = 24, words_num: int = 24,
@@ -378,6 +393,7 @@ class EntryManager:
"type": EntryType.SEED.value, "type": EntryType.SEED.value,
"kind": EntryType.SEED.value, "kind": EntryType.SEED.value,
"index": index, "index": index,
"label": label,
"words": words_num, "words": words_num,
"notes": notes, "notes": notes,
} }
@@ -596,11 +612,11 @@ class EntryManager:
idx_str, entry = item idx_str, entry = item
if sort_by == "index": if sort_by == "index":
return int(idx_str) return int(idx_str)
if sort_by == "website": if sort_by in {"website", "label"}:
return entry.get("website", "").lower() return entry.get("label", entry.get("website", "")).lower()
if sort_by == "username": if sort_by == "username":
return entry.get("username", "").lower() return entry.get("username", "").lower()
raise ValueError("sort_by must be 'index', 'website', or 'username'") raise ValueError("sort_by must be 'index', 'label', or 'username'")
sorted_items = sorted(entries_data.items(), key=sort_key) sorted_items = sorted(entries_data.items(), key=sort_key)
@@ -616,19 +632,20 @@ class EntryManager:
entries: List[Tuple[int, str, Optional[str], Optional[str], bool]] = [] entries: List[Tuple[int, str, Optional[str], Optional[str], bool]] = []
for idx, entry in filtered_items: for idx, entry in filtered_items:
label = entry.get("label", entry.get("website", ""))
etype = entry.get("type", entry.get("kind", EntryType.PASSWORD.value)) etype = entry.get("type", entry.get("kind", EntryType.PASSWORD.value))
if etype == EntryType.TOTP.value: if etype == EntryType.PASSWORD.value:
entries.append((idx, entry.get("label", ""), None, None, False))
else:
entries.append( entries.append(
( (
idx, idx,
entry.get("website", ""), label,
entry.get("username", ""), entry.get("username", ""),
entry.get("url", ""), entry.get("url", ""),
entry.get("blacklisted", False), entry.get("blacklisted", False),
) )
) )
else:
entries.append((idx, label, None, None, False))
logger.debug(f"Total entries found: {len(entries)}") logger.debug(f"Total entries found: {len(entries)}")
for idx, entry in filtered_items: for idx, entry in filtered_items:
@@ -644,8 +661,13 @@ class EntryManager:
"cyan", "cyan",
) )
) )
else: elif etype == EntryType.PASSWORD.value:
print(colored(f" Website: {entry.get('website', '')}", "cyan")) print(
colored(
f" Label: {entry.get('label', entry.get('website', ''))}",
"cyan",
)
)
print( print(
colored(f" Username: {entry.get('username') or 'N/A'}", "cyan") colored(f" Username: {entry.get('username') or 'N/A'}", "cyan")
) )
@@ -656,6 +678,13 @@ class EntryManager:
"cyan", "cyan",
) )
) )
else:
print(colored(f" Label: {entry.get('label', '')}", "cyan"))
print(
colored(
f" Derivation Index: {entry.get('index', index)}", "cyan"
)
)
print("-" * 40) print("-" * 40)
return entries return entries
@@ -680,16 +709,14 @@ class EntryManager:
for idx, entry in sorted(entries_data.items(), key=lambda x: int(x[0])): for idx, entry in sorted(entries_data.items(), key=lambda x: int(x[0])):
etype = entry.get("type", entry.get("kind", EntryType.PASSWORD.value)) etype = entry.get("type", entry.get("kind", EntryType.PASSWORD.value))
if etype == EntryType.TOTP.value: label = entry.get("label", entry.get("website", ""))
label = entry.get("label", "") notes = entry.get("notes", "")
notes = entry.get("notes", "") label_match = query_lower in label.lower()
if query_lower in label.lower() or query_lower in notes.lower(): notes_match = query_lower in notes.lower()
results.append((int(idx), label, None, None, False))
else: if etype == EntryType.PASSWORD.value:
website = entry.get("website", "")
username = entry.get("username", "") username = entry.get("username", "")
url = entry.get("url", "") url = entry.get("url", "")
notes = entry.get("notes", "")
custom_fields = entry.get("custom_fields", []) custom_fields = entry.get("custom_fields", [])
custom_match = any( custom_match = any(
query_lower in str(cf.get("label", "")).lower() query_lower in str(cf.get("label", "")).lower()
@@ -697,21 +724,24 @@ class EntryManager:
for cf in custom_fields for cf in custom_fields
) )
if ( if (
query_lower in website.lower() label_match
or query_lower in username.lower() or query_lower in username.lower()
or query_lower in url.lower() or query_lower in url.lower()
or query_lower in notes.lower() or notes_match
or custom_match or custom_match
): ):
results.append( results.append(
( (
int(idx), int(idx),
website, label,
username, username,
url, url,
entry.get("blacklisted", False), entry.get("blacklisted", False),
) )
) )
else:
if label_match or notes_match:
results.append((int(idx), label, None, None, False))
return results return results
@@ -829,7 +859,7 @@ class EntryManager:
for entry in entries: for entry in entries:
index, website, username, url, blacklisted = entry index, website, username, url, blacklisted = entry
print(colored(f"Index: {index}", "cyan")) print(colored(f"Index: {index}", "cyan"))
print(colored(f" Website: {website}", "cyan")) print(colored(f" Label: {website}", "cyan"))
print(colored(f" Username: {username or 'N/A'}", "cyan")) print(colored(f" Username: {username or 'N/A'}", "cyan"))
print(colored(f" URL: {url or 'N/A'}", "cyan")) print(colored(f" URL: {url or 'N/A'}", "cyan"))
print( print(
@@ -856,19 +886,9 @@ class EntryManager:
if filter_kind and etype != filter_kind: if filter_kind and etype != filter_kind:
continue continue
if etype == EntryType.PASSWORD.value: if etype == EntryType.PASSWORD.value:
label = entry.get("website", "") label = entry.get("label", entry.get("website", ""))
elif etype == EntryType.TOTP.value:
label = entry.get("label", "")
elif etype == EntryType.SSH.value:
label = "SSH Key"
elif etype == EntryType.SEED.value:
label = "Seed Phrase"
elif etype == EntryType.NOSTR.value:
label = entry.get("label", "Nostr Key")
elif etype == EntryType.PGP.value:
label = "PGP Key"
else: else:
label = etype label = entry.get("label", etype)
summaries.append((int(idx_str), label)) summaries.append((int(idx_str), label))
summaries.sort(key=lambda x: x[0]) summaries.sort(key=lambda x: x[0])

View File

@@ -859,9 +859,9 @@ class PasswordManager:
def handle_add_password(self) -> None: def handle_add_password(self) -> None:
try: try:
website_name = input("Enter the website name: ").strip() website_name = input("Enter the label or website name: ").strip()
if not website_name: if not website_name:
print(colored("Error: Website name cannot be empty.", "red")) print(colored("Error: Label cannot be empty.", "red"))
return return
username = input("Enter the username (optional): ").strip() username = input("Enter the username (optional): ").strip()
@@ -1040,8 +1040,12 @@ class PasswordManager:
def handle_add_ssh_key(self) -> None: def handle_add_ssh_key(self) -> None:
"""Add an SSH key pair entry and display the derived keys.""" """Add an SSH key pair entry and display the derived keys."""
try: try:
label = input("Label: ").strip()
if not label:
print(colored("Error: Label cannot be empty.", "red"))
return
notes = input("Notes (optional): ").strip() notes = input("Notes (optional): ").strip()
index = self.entry_manager.add_ssh_key(self.parent_seed, notes=notes) index = self.entry_manager.add_ssh_key(label, self.parent_seed, notes=notes)
priv_pem, pub_pem = self.entry_manager.get_ssh_key_pair( priv_pem, pub_pem = self.entry_manager.get_ssh_key_pair(
index, self.parent_seed index, self.parent_seed
) )
@@ -1075,6 +1079,10 @@ class PasswordManager:
def handle_add_seed(self) -> None: def handle_add_seed(self) -> None:
"""Add a derived BIP-39 seed phrase entry.""" """Add a derived BIP-39 seed phrase entry."""
try: try:
label = input("Label: ").strip()
if not label:
print(colored("Error: Label cannot be empty.", "red"))
return
words_input = input("Word count (12 or 24, default 24): ").strip() words_input = input("Word count (12 or 24, default 24): ").strip()
notes = input("Notes (optional): ").strip() notes = input("Notes (optional): ").strip()
if words_input and words_input not in {"12", "24"}: if words_input and words_input not in {"12", "24"}:
@@ -1082,7 +1090,7 @@ class PasswordManager:
return return
words = int(words_input) if words_input else 24 words = int(words_input) if words_input else 24
index = self.entry_manager.add_seed( index = self.entry_manager.add_seed(
self.parent_seed, words_num=words, notes=notes label, self.parent_seed, words_num=words, notes=notes
) )
phrase = self.entry_manager.get_seed_phrase(index, self.parent_seed) phrase = self.entry_manager.get_seed_phrase(index, self.parent_seed)
self.is_dirty = True self.is_dirty = True
@@ -1117,6 +1125,10 @@ class PasswordManager:
def handle_add_pgp(self) -> None: def handle_add_pgp(self) -> None:
"""Add a PGP key entry and display the generated key.""" """Add a PGP key entry and display the generated key."""
try: try:
label = input("Label: ").strip()
if not label:
print(colored("Error: Label cannot be empty.", "red"))
return
key_type = ( key_type = (
input("Key type (ed25519 or rsa, default ed25519): ").strip().lower() input("Key type (ed25519 or rsa, default ed25519): ").strip().lower()
or "ed25519" or "ed25519"
@@ -1124,6 +1136,7 @@ class PasswordManager:
user_id = input("User ID (optional): ").strip() user_id = input("User ID (optional): ").strip()
notes = input("Notes (optional): ").strip() notes = input("Notes (optional): ").strip()
index = self.entry_manager.add_pgp_key( index = self.entry_manager.add_pgp_key(
label,
self.parent_seed, self.parent_seed,
key_type=key_type, key_type=key_type,
user_id=user_id, user_id=user_id,
@@ -1162,7 +1175,10 @@ class PasswordManager:
def handle_add_nostr_key(self) -> None: def handle_add_nostr_key(self) -> None:
"""Add a Nostr key entry and display the derived keys.""" """Add a Nostr key entry and display the derived keys."""
try: try:
label = input("Label (optional): ").strip() label = input("Label: ").strip()
if not label:
print(colored("Error: Label cannot be empty.", "red"))
return
notes = input("Notes (optional): ").strip() notes = input("Notes (optional): ").strip()
index = self.entry_manager.add_nostr_key(label, notes=notes) index = self.entry_manager.add_nostr_key(label, notes=notes)
npub, nsec = self.entry_manager.get_nostr_key_pair(index, self.parent_seed) npub, nsec = self.entry_manager.get_nostr_key_pair(index, self.parent_seed)
@@ -1779,6 +1795,7 @@ class PasswordManager:
print(colored(f" Notes: {notes}", "cyan")) print(colored(f" Notes: {notes}", "cyan"))
elif etype == EntryType.SEED.value: elif etype == EntryType.SEED.value:
print(colored(" Type: Seed Phrase", "cyan")) print(colored(" Type: Seed Phrase", "cyan"))
print(colored(f" Label: {entry.get('label', '')}", "cyan"))
print(colored(f" Words: {entry.get('words', 24)}", "cyan")) print(colored(f" Words: {entry.get('words', 24)}", "cyan"))
print(colored(f" Derivation Index: {entry.get('index', index)}", "cyan")) print(colored(f" Derivation Index: {entry.get('index', index)}", "cyan"))
notes = entry.get("notes", "") notes = entry.get("notes", "")
@@ -1786,12 +1803,14 @@ class PasswordManager:
print(colored(f" Notes: {notes}", "cyan")) print(colored(f" Notes: {notes}", "cyan"))
elif etype == EntryType.SSH.value: elif etype == EntryType.SSH.value:
print(colored(" Type: SSH Key", "cyan")) print(colored(" Type: SSH Key", "cyan"))
print(colored(f" Label: {entry.get('label', '')}", "cyan"))
print(colored(f" Derivation Index: {entry.get('index', index)}", "cyan")) print(colored(f" Derivation Index: {entry.get('index', index)}", "cyan"))
notes = entry.get("notes", "") notes = entry.get("notes", "")
if notes: if notes:
print(colored(f" Notes: {notes}", "cyan")) print(colored(f" Notes: {notes}", "cyan"))
elif etype == EntryType.PGP.value: elif etype == EntryType.PGP.value:
print(colored(" Type: PGP Key", "cyan")) print(colored(" Type: PGP Key", "cyan"))
print(colored(f" Label: {entry.get('label', '')}", "cyan"))
print(colored(f" Key Type: {entry.get('key_type', 'ed25519')}", "cyan")) print(colored(f" Key Type: {entry.get('key_type', 'ed25519')}", "cyan"))
uid = entry.get("user_id", "") uid = entry.get("user_id", "")
if uid: if uid:
@@ -1808,11 +1827,11 @@ class PasswordManager:
if notes: if notes:
print(colored(f" Notes: {notes}", "cyan")) print(colored(f" Notes: {notes}", "cyan"))
else: else:
website = entry.get("website", "") website = entry.get("label", entry.get("website", ""))
username = entry.get("username", "") username = entry.get("username", "")
url = entry.get("url", "") url = entry.get("url", "")
blacklisted = entry.get("blacklisted", False) blacklisted = entry.get("blacklisted", False)
print(colored(f" Website: {website}", "cyan")) print(colored(f" Label: {website}", "cyan"))
print(colored(f" Username: {username or 'N/A'}", "cyan")) print(colored(f" Username: {username or 'N/A'}", "cyan"))
print(colored(f" URL: {url or 'N/A'}", "cyan")) print(colored(f" URL: {url or 'N/A'}", "cyan"))
print(colored(f" Blacklisted: {'Yes' if blacklisted else 'No'}", "cyan")) print(colored(f" Blacklisted: {'Yes' if blacklisted else 'No'}", "cyan"))

View File

@@ -33,6 +33,10 @@ def _v1_to_v2(data: dict) -> dict:
for k, v in passwords.items(): for k, v in passwords.items():
v.setdefault("type", "password") v.setdefault("type", "password")
v.setdefault("notes", "") v.setdefault("notes", "")
if "label" not in v and "website" in v:
v["label"] = v["website"]
if v.get("type") == "password" and "website" in v:
v.pop("website", None)
entries[k] = v entries[k] = v
data["entries"] = entries data["entries"] = entries
data["schema_version"] = 2 data["schema_version"] = 2
@@ -46,6 +50,10 @@ def _v2_to_v3(data: dict) -> dict:
for entry in entries.values(): for entry in entries.values():
entry.setdefault("custom_fields", []) entry.setdefault("custom_fields", [])
entry.setdefault("origin", "") entry.setdefault("origin", "")
if entry.get("type", "password") == "password":
if "label" not in entry and "website" in entry:
entry["label"] = entry["website"]
entry.pop("website", None)
data["schema_version"] = 3 data["schema_version"] = 3
return data return data

View File

@@ -25,7 +25,7 @@ def test_backup_restore_workflow(monkeypatch):
"schema_version": 3, "schema_version": 3,
"entries": { "entries": {
"0": { "0": {
"website": "a", "label": "a",
"length": 10, "length": 10,
"type": "password", "type": "password",
"kind": "password", "kind": "password",
@@ -48,7 +48,7 @@ def test_backup_restore_workflow(monkeypatch):
"schema_version": 3, "schema_version": 3,
"entries": { "entries": {
"0": { "0": {
"website": "b", "label": "b",
"length": 12, "length": 12,
"type": "password", "type": "password",
"kind": "password", "kind": "password",

View File

@@ -30,7 +30,7 @@ def test_add_and_retrieve_entry():
entry = entry_mgr.retrieve_entry(index) entry = entry_mgr.retrieve_entry(index)
assert entry == { assert entry == {
"website": "example.com", "label": "example.com",
"length": 12, "length": 12,
"username": "user", "username": "user",
"url": "", "url": "",
@@ -69,9 +69,9 @@ def test_round_trip_entry_types(method, expected_type):
index = 0 index = 0
else: else:
if method == "add_ssh_key": if method == "add_ssh_key":
index = entry_mgr.add_ssh_key(TEST_SEED) index = entry_mgr.add_ssh_key("ssh", TEST_SEED)
elif method == "add_seed": elif method == "add_seed":
index = entry_mgr.add_seed(TEST_SEED) index = entry_mgr.add_seed("seed", TEST_SEED)
else: else:
index = getattr(entry_mgr, method)() index = getattr(entry_mgr, method)()

View File

@@ -34,7 +34,7 @@ def test_index_export_import_round_trip():
"schema_version": 3, "schema_version": 3,
"entries": { "entries": {
"0": { "0": {
"website": "example", "label": "example",
"type": "password", "type": "password",
"notes": "", "notes": "",
"custom_fields": [], "custom_fields": [],
@@ -52,7 +52,7 @@ def test_index_export_import_round_trip():
"schema_version": 3, "schema_version": 3,
"entries": { "entries": {
"0": { "0": {
"website": "changed", "label": "changed",
"type": "password", "type": "password",
"notes": "", "notes": "",
"custom_fields": [], "custom_fields": [],

View File

@@ -20,7 +20,7 @@ def test_migrate_v0_to_v3(tmp_path: Path):
data = vault.load_index() data = vault.load_index()
assert data["schema_version"] == LATEST_VERSION assert data["schema_version"] == LATEST_VERSION
expected_entry = { expected_entry = {
"website": "a", "label": "a",
"length": 8, "length": 8,
"type": "password", "type": "password",
"notes": "", "notes": "",
@@ -37,7 +37,7 @@ def test_migrate_v1_to_v3(tmp_path: Path):
data = vault.load_index() data = vault.load_index()
assert data["schema_version"] == LATEST_VERSION assert data["schema_version"] == LATEST_VERSION
expected_entry = { expected_entry = {
"website": "b", "label": "b",
"length": 10, "length": 10,
"type": "password", "type": "password",
"notes": "", "notes": "",
@@ -59,7 +59,7 @@ def test_migrate_v2_to_v3(tmp_path: Path):
data = vault.load_index() data = vault.load_index()
assert data["schema_version"] == LATEST_VERSION assert data["schema_version"] == LATEST_VERSION
expected_entry = { expected_entry = {
"website": "c", "label": "c",
"length": 5, "length": 5,
"type": "password", "type": "password",
"notes": "", "notes": "",

View File

@@ -58,7 +58,7 @@ def test_nostr_index_size_limits(pytestconfig: pytest.Config):
if max_entries is not None and entry_count >= max_entries: if max_entries is not None and entry_count >= max_entries:
break break
entry_mgr.add_entry( entry_mgr.add_entry(
website_name=f"site-{entry_count + 1}", label=f"site-{entry_count + 1}",
length=12, length=12,
username="u" * size, username="u" * size,
url="https://example.com/" + "a" * size, url="https://example.com/" + "a" * size,

View File

@@ -19,7 +19,9 @@ def test_pgp_key_determinism():
backup_mgr = BackupManager(tmp_path, cfg_mgr) backup_mgr = BackupManager(tmp_path, cfg_mgr)
entry_mgr = EntryManager(vault, backup_mgr) entry_mgr = EntryManager(vault, backup_mgr)
idx = entry_mgr.add_pgp_key(TEST_SEED, key_type="ed25519", user_id="Test") idx = entry_mgr.add_pgp_key(
"pgp", TEST_SEED, key_type="ed25519", user_id="Test"
)
key1, fp1 = entry_mgr.get_pgp_key(idx, TEST_SEED) key1, fp1 = entry_mgr.get_pgp_key(idx, TEST_SEED)
key2, fp2 = entry_mgr.get_pgp_key(idx, TEST_SEED) key2, fp2 = entry_mgr.get_pgp_key(idx, TEST_SEED)

View File

@@ -23,8 +23,8 @@ def test_seed_phrase_determinism():
backup_mgr = BackupManager(tmp_path, cfg_mgr) backup_mgr = BackupManager(tmp_path, cfg_mgr)
entry_mgr = EntryManager(vault, backup_mgr) entry_mgr = EntryManager(vault, backup_mgr)
idx_12 = entry_mgr.add_seed(TEST_SEED, words_num=12) idx_12 = entry_mgr.add_seed("seed12", TEST_SEED, words_num=12)
idx_24 = entry_mgr.add_seed(TEST_SEED, words_num=24) idx_24 = entry_mgr.add_seed("seed24", TEST_SEED, words_num=24)
phrase12_a = entry_mgr.get_seed_phrase(idx_12, TEST_SEED) phrase12_a = entry_mgr.get_seed_phrase(idx_12, TEST_SEED)
phrase12_b = entry_mgr.get_seed_phrase(idx_12, TEST_SEED) phrase12_b = entry_mgr.get_seed_phrase(idx_12, TEST_SEED)

View File

@@ -20,9 +20,15 @@ def test_add_and_retrieve_ssh_key_pair():
backup_mgr = BackupManager(tmp_path, cfg_mgr) backup_mgr = BackupManager(tmp_path, cfg_mgr)
entry_mgr = EntryManager(vault, backup_mgr) entry_mgr = EntryManager(vault, backup_mgr)
index = entry_mgr.add_ssh_key(TEST_SEED) index = entry_mgr.add_ssh_key("ssh", TEST_SEED)
entry = entry_mgr.retrieve_entry(index) entry = entry_mgr.retrieve_entry(index)
assert entry == {"type": "ssh", "kind": "ssh", "index": index, "notes": ""} assert entry == {
"type": "ssh",
"kind": "ssh",
"index": index,
"label": "ssh",
"notes": "",
}
priv1, pub1 = entry_mgr.get_ssh_key_pair(index, TEST_SEED) priv1, pub1 = entry_mgr.get_ssh_key_pair(index, TEST_SEED)
priv2, pub2 = entry_mgr.get_ssh_key_pair(index, TEST_SEED) priv2, pub2 = entry_mgr.get_ssh_key_pair(index, TEST_SEED)

View File

@@ -21,7 +21,7 @@ def test_ssh_private_key_corresponds_to_public():
backup_mgr = BackupManager(tmp_path, cfg_mgr) backup_mgr = BackupManager(tmp_path, cfg_mgr)
entry_mgr = EntryManager(vault, backup_mgr) entry_mgr = EntryManager(vault, backup_mgr)
idx = entry_mgr.add_ssh_key(TEST_SEED) idx = entry_mgr.add_ssh_key("ssh", TEST_SEED)
priv_pem, pub_pem = entry_mgr.get_ssh_key_pair(idx, TEST_SEED) priv_pem, pub_pem = entry_mgr.get_ssh_key_pair(idx, TEST_SEED)
priv_key = serialization.load_pem_private_key( priv_key = serialization.load_pem_private_key(