Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions KeePassDiff/components/entry_modal.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from typing import Dict, Optional

import streamlit as st

from KeePassDiff.utils.database import EntryDetails


def show_entry_details(entry_details: Optional[Dict] = None, key: str = None):
def show_entry_details(
entry_details: EntryDetails | None = None,
key: str | None = None,
):
if entry_details:
st.markdown("### Entry Details")
st.markdown(f"**Title:** {entry_details['title']}")
Expand All @@ -12,11 +15,12 @@ def show_entry_details(entry_details: Optional[Dict] = None, key: str = None):
st.markdown(f"**URL:** {entry_details['url']}")
st.markdown("**Notes:**")
st.text_area(
"",
"notes",
label_visibility="collapsed",
value=entry_details["notes"],
height=100,
disabled=True,
key=f"notes_{key}" if key else None,
key=f"notes_{key}",
)
st.markdown(f"**Created:** {entry_details['created']}")
st.markdown(f"**Modified:** {entry_details['modified']}")
Expand Down
4 changes: 2 additions & 2 deletions KeePassDiff/utils/comparison.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from typing import Dict, Set
from typing import Dict, Set, List


def compare_databases(
db1_data: Dict[str, Set[str]], db2_data: Dict[str, Set[str]]
) -> Dict:
) -> Dict[str, List[str]]:
return {
"entries_only_in_db1": sorted(db1_data["entries"] - db2_data["entries"]),
"entries_only_in_db2": sorted(db2_data["entries"] - db1_data["entries"]),
Expand Down
92 changes: 57 additions & 35 deletions KeePassDiff/utils/database.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
import datetime
import tempfile
from typing import Dict, Optional, Set, Tuple
from typing import TypedDict, Dict, Optional, Set, Tuple

from pykeepass import PyKeePass
from pykeepass import Group, PyKeePass


class EntryDetails(TypedDict):
title: str | None
username: str | None
password: str | None
url: str | None
notes: str | None
created: datetime.datetime | None
modified: datetime.datetime | None
path: str


def save_temp_database(db_file, keyfile=None) -> Tuple[str, Optional[str]]:
Expand Down Expand Up @@ -42,42 +54,45 @@ def get_entries_set(kp: PyKeePass) -> Dict[str, Set[str]]:
entries = set()
groups = set()

for entry in kp.entries:
# Ensure all elements are strings and not None
entry_path = "/".join(
[str(g) for g in entry.path[:-1] if g is not None]
+ [str(entry.title) if entry.title is not None else ""]
)
entries.add(entry_path)

for group in kp.groups:
if group != "Root":
group_path = "/".join([str(g) for g in group.path if g is not None])
groups.add(group_path)
if kp.entries:
for entry in kp.entries:
# Ensure all elements are strings and not None
entry_path = "/".join(
[str(g) for g in entry.path[:-1] if g is not None]
+ [str(entry.title) if entry.title is not None else ""]
)
entries.add(entry_path)

if kp.groups:
for group in kp.groups:
if group != "Root":
group_path = "/".join([str(g) for g in group.path if g is not None])
groups.add(group_path)

return {"entries": entries, "groups": groups}


def get_entry_details(kp: PyKeePass, entry_path: str) -> Dict:
def get_entry_details(kp: PyKeePass, entry_path: str) -> EntryDetails | None:
path_parts = entry_path.split("/")
title = path_parts[-1]
group_path = path_parts[:-1]

for entry in kp.entries:
if (
entry.title == title
and [str(g) for g in entry.path[:-1] if g is not None] == group_path
):
return {
"title": entry.title,
"username": entry.username,
"password": entry.password,
"url": entry.url,
"notes": entry.notes,
"created": entry.ctime,
"modified": entry.mtime,
"path": "/".join(group_path),
}
if kp.entries:
for entry in kp.entries:
if (
entry.title == title
and [str(g) for g in entry.path[:-1] if g is not None] == group_path
):
return {
"title": entry.title,
"username": entry.username,
"password": entry.password,
"url": entry.url,
"notes": entry.notes,
"created": entry.ctime,
"modified": entry.mtime,
"path": "/".join(group_path),
}
return None


Expand All @@ -87,17 +102,24 @@ def merge_entry(source_kp: PyKeePass, target_kp: PyKeePass, entry_path: str) ->
group_path = path_parts[:-1]

source_entry = None
for entry in source_kp.entries:
if entry.title == title and [g for g in entry.path[:-1]] == group_path:
source_entry = entry
break
if source_kp.entries:
for entry in source_kp.entries:
if entry.title == title and [g for g in entry.path[:-1]] == group_path:
source_entry = entry
break

if not source_entry:
return False

current_group = target_kp.root_group

if not isinstance(current_group, Group):
return False

for group_name in group_path:
next_group = next((g for g in current_group.subgroups if g == group_name), None)
next_group = next(
(g for g in current_group.subgroups if g.name == group_name), None
)
if not next_group:
next_group = target_kp.add_group(current_group, group_name)
current_group = next_group
Expand Down