dev: 支持加密数据

This commit is contained in:
Julian Freeman
2024-07-20 16:58:31 -04:00
parent b476da4697
commit 4c6ee85f5a
14 changed files with 257 additions and 173 deletions

View File

@@ -71,12 +71,11 @@ class DaEntryInfo(QtWidgets.QDialog):
def __init__(
self,
entry_id: int,
config: dict,
sqh: Sqlite3Worker,
parent: QtWidgets.QWidget = None
):
super().__init__(parent)
_, results = sqh.select(config["table_name"], all_columns,
_, results = sqh.select("entries", all_columns,
where=Operand(entry_id_col).equal_to(entry_id))
entry = results[0]
@@ -88,5 +87,3 @@ class DaEntryInfo(QtWidgets.QDialog):
def sizeHint(self):
return QtCore.QSize(640, 360)

View File

@@ -1,13 +1,14 @@
# coding: utf8
from PySide6 import QtWidgets, QtCore, QtGui
from pykeepass import PyKeePass
from pykeepass.exceptions import HeaderChecksumError, CredentialsError
class DaTargetLogin(QtWidgets.QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("目标文件")
class UiDaTargetLogin(object):
def __init__(self, window: QtWidgets.QWidget):
window.setWindowTitle("目标文件")
self.vly_m = QtWidgets.QVBoxLayout()
self.setLayout(self.vly_m)
window.setLayout(self.vly_m)
icon_ellipsis = QtGui.QIcon(":/asset/img/ellipsis.svg")
self.icon_eye = QtGui.QIcon(":/asset/img/eye.svg")
@@ -15,61 +16,77 @@ class DaTargetLogin(QtWidgets.QDialog):
self.hly_path = QtWidgets.QHBoxLayout()
self.vly_m.addLayout(self.hly_path)
self.lb_path = QtWidgets.QLabel("路径:", self)
self.lne_path = QtWidgets.QLineEdit(self)
self.pbn_browse = QtWidgets.QPushButton(icon=icon_ellipsis, parent=self)
self.lb_path = QtWidgets.QLabel("路径:", window)
self.lne_path = QtWidgets.QLineEdit(window)
self.pbn_browse = QtWidgets.QPushButton(icon=icon_ellipsis, parent=window)
self.hly_path.addWidget(self.lb_path)
self.hly_path.addWidget(self.lne_path)
self.hly_path.addWidget(self.pbn_browse)
self.hly_password = QtWidgets.QHBoxLayout()
self.vly_m.addLayout(self.hly_password)
self.lb_password = QtWidgets.QLabel("密码:", self)
self.lne_password = QtWidgets.QLineEdit(self)
self.lb_password = QtWidgets.QLabel("密码:", window)
self.lne_password = QtWidgets.QLineEdit(window)
self.lne_password.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password)
self.pbn_eye = QtWidgets.QPushButton(icon=self.icon_eye_off, parent=self)
self.pbn_eye = QtWidgets.QPushButton(icon=self.icon_eye_off, parent=window)
self.hly_password.addWidget(self.lb_password)
self.hly_password.addWidget(self.lne_password)
self.hly_password.addWidget(self.pbn_eye)
self.hly_bottom = QtWidgets.QHBoxLayout()
self.vly_m.addLayout(self.hly_bottom)
self.pbn_ok = QtWidgets.QPushButton("确定", self)
self.pbn_cancel = QtWidgets.QPushButton("取消", self)
self.pbn_ok = QtWidgets.QPushButton("确定", window)
self.pbn_cancel = QtWidgets.QPushButton("取消", window)
self.hly_bottom.addStretch(1)
self.hly_bottom.addWidget(self.pbn_ok)
self.hly_bottom.addWidget(self.pbn_cancel)
self.vly_m.addStretch(1)
self.pbn_browse.clicked.connect(self.on_pbn_browse_clicked)
self.pbn_eye.clicked.connect(self.on_pbn_eye_clicked)
self.pbn_ok.clicked.connect(self.on_pbn_ok_clicked)
self.pbn_cancel.clicked.connect(self.on_pbn_cancel_clicked)
self.pbn_ok.setFocus()
class DaTargetLogin(QtWidgets.QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = UiDaTargetLogin(self)
self.tar_kp: PyKeePass | None = None
self.ui.pbn_browse.clicked.connect(self.on_pbn_browse_clicked)
self.ui.pbn_eye.clicked.connect(self.on_pbn_eye_clicked)
self.ui.pbn_ok.clicked.connect(self.on_pbn_ok_clicked)
self.ui.pbn_cancel.clicked.connect(self.on_pbn_cancel_clicked)
def on_pbn_browse_clicked(self):
filename, _ = QtWidgets.QFileDialog.getOpenFileName(self, "选择", "../",
filter="KeePass 2 数据库 (*.kdbx);;所有文件 (*)")
if len(filename) == 0:
return
self.lne_path.setText(filename)
self.ui.lne_path.setText(filename)
def on_pbn_eye_clicked(self):
if self.lne_password.echoMode() == QtWidgets.QLineEdit.EchoMode.Password:
self.lne_password.setEchoMode(QtWidgets.QLineEdit.EchoMode.Normal)
self.pbn_eye.setIcon(self.icon_eye)
if self.ui.lne_password.echoMode() == QtWidgets.QLineEdit.EchoMode.Password:
self.ui.lne_password.setEchoMode(QtWidgets.QLineEdit.EchoMode.Normal)
self.ui.pbn_eye.setIcon(self.ui.icon_eye)
else:
self.lne_password.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password)
self.pbn_eye.setIcon(self.icon_eye_off)
self.ui.lne_password.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password)
self.ui.pbn_eye.setIcon(self.ui.icon_eye_off)
def sizeHint(self):
return QtCore.QSize(540, 40)
def on_pbn_ok_clicked(self):
try:
self.tar_kp = PyKeePass(self.ui.lne_path.text(), self.ui.lne_password.text())
except CredentialsError:
QtWidgets.QMessageBox.critical(self, "错误", "keepass 密码错误")
return
except (FileNotFoundError, HeaderChecksumError):
QtWidgets.QMessageBox.critical(self, "错误", "文件不存在或不是 keepass 文件")
return
self.accept()
def on_pbn_cancel_clicked(self):
self.tar_kp = None
self.reject()

View File

@@ -90,7 +90,7 @@ class GbxKpsLogin(QtWidgets.QGroupBox):
kp = read_kps_to_db(
kps_file=self.lne_path.text(),
password=self.lne_password.text(),
table_name=self.config["table_name"],
table_name="entries",
sqh=self.sqh
)
except CredentialsError:

View File

@@ -10,7 +10,7 @@ from .cmbx_styles import StyleComboBox
from lib.Sqlite3Helper import Sqlite3Worker
from lib.db_columns_def import all_columns
from lib.sec_db_columns_def import sec_all_columns
from lib.config_utils import write_config
from lib.config_utils import write_config, get_or_generate_key
class UiKpsUnifier(object):
@@ -78,10 +78,15 @@ class KpsUnifier(QtWidgets.QMainWindow):
db_path: str,
secrets_path: str,
config: dict,
org_name: str,
app_name: str,
version: str,
parent: QtWidgets.QWidget = None,
):
super().__init__(parent)
self.org_name = org_name
self.app_name = app_name
self.db_path = db_path
self.secrets_path = secrets_path
self.config = config
@@ -103,18 +108,23 @@ class KpsUnifier(QtWidgets.QMainWindow):
def __del__(self):
self.config["last_db_path"] = self.db_path
write_config(self.config,
QtWidgets.QApplication.organizationName(),
QtWidgets.QApplication.applicationName())
write_config(self.config, self.org_name, self.app_name)
def sizeHint(self):
return QtCore.QSize(860, 640)
def init_db(self) -> Sqlite3Worker:
sqh = Sqlite3Worker(self.db_path)
sqh.create_table(self.config["table_name"], all_columns, if_not_exists=True)
key = get_or_generate_key(self.db_path, self.org_name, self.app_name)
sqh = Sqlite3Worker(self.db_path, key)
sqh.create_table("entries", all_columns, if_not_exists=True)
return sqh
def init_secrets_db(self) -> Sqlite3Worker:
key = get_or_generate_key(self.secrets_path, self.org_name, self.app_name)
sec_sqh = Sqlite3Worker(self.secrets_path, key)
sec_sqh.create_table("secrets", sec_all_columns, if_not_exists=True)
return sec_sqh
def update_db(self, filename: str):
self.db_path = filename
self.sqh = self.init_db()
@@ -153,8 +163,3 @@ class KpsUnifier(QtWidgets.QMainWindow):
def on_act_about_qt_triggered(self):
QtWidgets.QMessageBox.aboutQt(self, "关于 Qt")
def init_secrets_db(self) -> Sqlite3Worker:
sec_sqh = Sqlite3Worker(self.secrets_path)
sec_sqh.create_table("secrets", sec_all_columns, if_not_exists=True)
return sec_sqh

View File

@@ -5,7 +5,8 @@ from PySide6 import QtWidgets
from pykeepass import PyKeePass
from .gbx_kps_login import GbxKpsLogin
from .utils import accept_warning
from .da_target_login import DaTargetLogin
from .utils import accept_warning, HorizontalLine
from lib.Sqlite3Helper import Sqlite3Worker
@@ -81,6 +82,15 @@ class PageLoad(QtWidgets.QWidget):
self.vly_left = QtWidgets.QVBoxLayout()
self.hly_m.addLayout(self.vly_left)
self.pbn_set_target = QtWidgets.QPushButton("设置目标文件", self)
# 暂时没用
self.pbn_set_target.setDisabled(True)
self.pbn_set_target.setMinimumWidth(config["button_min_width"])
self.vly_left.addWidget(self.pbn_set_target)
self.hln_1 = HorizontalLine(self)
self.vly_left.addWidget(self.hln_1)
self.pbn_add_kps = QtWidgets.QPushButton("添加 KPS", self)
self.pbn_add_kps.setMinimumWidth(config["button_min_width"])
self.vly_left.addWidget(self.pbn_add_kps)
@@ -101,6 +111,8 @@ class PageLoad(QtWidgets.QWidget):
self.wg_sa = WgLoadKps(config, file_kp, sqh, sec_sqh, self.sa_m)
self.sa_m.setWidget(self.wg_sa)
self.pbn_set_target.clicked.connect(self.on_pbn_set_target_clicked)
self.pbn_add_kps.clicked.connect(self.on_pbn_add_kps_clicked)
self.pbn_clear_db.clicked.connect(self.on_pbn_clear_db_clicked)
self.pbn_clear_loaded_mem.clicked.connect(self.on_pbn_clear_loaded_mem_clicked)
@@ -121,7 +133,7 @@ class PageLoad(QtWidgets.QWidget):
return
try:
self.sqh.delete_from(self.config["table_name"])
self.sqh.delete_from("entries")
except sqlite3.OperationalError as e:
QtWidgets.QMessageBox.critical(self, "错误", f"清空数据库失败:\n{e}")
else:
@@ -148,3 +160,7 @@ class PageLoad(QtWidgets.QWidget):
# 更新kps加载状态
for wg in self.wg_sa.kps_wgs:
self.wg_sa.update_load_status(wg)
def on_pbn_set_target_clicked(self):
da_target_login = DaTargetLogin(self)
da_target_login.exec()

View File

@@ -5,7 +5,6 @@ from PySide6 import QtWidgets, QtCore, QtGui
from pykeepass import PyKeePass
from .da_entry_info import DaEntryInfo
from .da_target_login import DaTargetLogin
from .utils import HorizontalLine, get_filepath_uuids_map, accept_warning
from lib.Sqlite3Helper import Sqlite3Worker, Expression, Operand
from lib.db_columns_def import (
@@ -18,7 +17,7 @@ from lib.kps_operations import blob_fy
class QueryTableModel(QtCore.QAbstractTableModel):
def __init__(self, query_results: list[tuple], parent=None):
def __init__(self, query_results: list[list], parent=None):
super().__init__(parent)
self.query_results = query_results
self.headers = ["序号", "标题", "用户名", "URL"]
@@ -98,10 +97,6 @@ class UiPageQuery(object):
self.pbn_execute.setMinimumWidth(config["button_min_width"])
self.vly_sa_wg.addWidget(self.pbn_execute)
self.pbn_set_target = QtWidgets.QPushButton("目标文件", window)
self.pbn_set_target.setMinimumWidth(config["button_min_width"])
self.vly_sa_wg.addWidget(self.pbn_set_target)
self.hln_1 = HorizontalLine(window)
self.vly_sa_wg.addWidget(self.hln_1)
@@ -147,7 +142,6 @@ class PageQuery(QtWidgets.QWidget):
self.ui.act_delete.triggered_with_str.connect(self.on_act_mark_triggered_with_str)
self.ui.pbn_execute.clicked.connect(self.on_pbn_execute_clicked)
self.ui.pbn_set_target.clicked.connect(self.on_pbn_set_target_clicked)
self.ui.pbn_all.clicked.connect(self.on_pbn_all_clicked)
self.ui.pbn_deleted.clicked.connect(self.on_pbn_deleted_clicked)
@@ -169,7 +163,7 @@ class PageQuery(QtWidgets.QWidget):
},
{
"name": "谷歌文档",
"where": "url LIKE 'https://docs.google.com/%' or url LIKE 'https://drive.google.com/%'"
"where": "(url LIKE 'https://docs.google.com/%' OR url LIKE 'https://drive.google.com/%')"
},
]
for fil in default_filters:
@@ -193,7 +187,7 @@ class PageQuery(QtWidgets.QWidget):
self.set_filter_button(fil)
def on_custom_filters_clicked_with_data(self, data: dict):
_, results = self.sqh.select(self.config["table_name"], query_columns,
_, results = self.sqh.select("entries", query_columns,
where=Expression(data["where"]).and_(Operand(deleted_col).equal_to(0)))
model = QueryTableModel(results, self)
self.ui.trv_m.setModel(model)
@@ -202,20 +196,20 @@ class PageQuery(QtWidgets.QWidget):
self.sqh = sqh
def on_pbn_all_clicked(self):
_, results = self.sqh.select(self.config["table_name"], query_columns,
_, results = self.sqh.select("entries", query_columns,
where=Operand(deleted_col).equal_to(0))
model = QueryTableModel(results, self)
self.ui.trv_m.setModel(model)
def on_pbn_deleted_clicked(self):
_, results = self.sqh.select(self.config["table_name"], query_columns,
_, results = self.sqh.select("entries", query_columns,
where=Operand(deleted_col).equal_to(1))
model = QueryTableModel(results, self)
self.ui.trv_m.setModel(model)
def on_trv_m_double_clicked(self, index: QtCore.QModelIndex):
entry_id = index.siblingAtColumn(0).data(QtCore.Qt.ItemDataRole.DisplayRole)
da_entry_info = DaEntryInfo(entry_id, self.config, self.sqh, self)
da_entry_info = DaEntryInfo(entry_id, self.sqh, self)
da_entry_info.exec()
def on_trv_m_custom_context_menu_requested(self, pos: QtCore.QPoint):
@@ -228,7 +222,7 @@ class PageQuery(QtWidgets.QWidget):
for index in indexes if index.column() == 0
]
self.sqh.update(self.config["table_name"], [(status_col, info)],
self.sqh.update("entries", [(status_col, info)],
where=Operand(entry_id_col).in_(entry_ids))
def on_pbn_execute_clicked(self):
@@ -236,7 +230,7 @@ class PageQuery(QtWidgets.QWidget):
return
# 删除功能
_, results = self.sqh.select(self.config["table_name"], sim_columns,
_, results = self.sqh.select("entries", sim_columns,
where=Operand(status_col).equal_to("delete"))
file_uuids = get_filepath_uuids_map(results)
@@ -252,7 +246,7 @@ class PageQuery(QtWidgets.QWidget):
for u in file_uuids[file]:
total += 1
self.sqh.update(self.config["table_name"], [(deleted_col, 1)],
self.sqh.update("entries", [(deleted_col, 1)],
where=Operand(uuid_col).equal_to(u).and_(
Operand(filepath_col).equal_to(blob_fy(file))))
@@ -269,10 +263,6 @@ class PageQuery(QtWidgets.QWidget):
QtWidgets.QMessageBox.information(self, "提示",
f"{total} 条标记的条目,已删除 {success} 条,无效 {invalid} 条。")
def on_pbn_set_target_clicked(self):
da_target_login = DaTargetLogin(self)
da_target_login.exec()
class PushButtonWithData(QtWidgets.QPushButton):

View File

@@ -1,6 +1,5 @@
# coding: utf8
from itertools import combinations
from uuid import UUID
from PySide6 import QtWidgets, QtCore
from PySide6.QtCore import QAbstractTableModel
@@ -70,7 +69,7 @@ class PageSimilar(QtWidgets.QWidget):
self.pbn_delete_invalid_data.clicked.connect(self.on_pbn_delete_invalid_data_clicked)
def on_pbn_read_db_clicked(self):
_, results = self.sqh.select(self.config["table_name"], sim_columns)
_, results = self.sqh.select("entries", sim_columns)
file_uuids = get_filepath_uuids_map(results)
files = file_uuids.keys()
@@ -98,11 +97,11 @@ class PageSimilar(QtWidgets.QWidget):
if accept_warning(self, True, "警告", "你确定要从数据库删除无效文件的记录吗?"):
return
_, filepaths = self.sqh.select(self.config["table_name"], [filepath_col,])
_, filepaths = self.sqh.select("entries", [filepath_col,])
unique_filepaths = set([p[0].decode("utf8") for p in filepaths])
invalid_filepaths = [p for p in unique_filepaths if path_not_exist(p)]
for path in invalid_filepaths:
self.sqh.delete_from(self.config["table_name"],
self.sqh.delete_from("entries",
where=Operand(filepath_col).equal_to(blob_fy(path)),
commit=False)
self.sqh.commit()

View File

@@ -19,7 +19,7 @@ class HorizontalLine(QtWidgets.QFrame):
self.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
def get_filepath_uuids_map(query_results: list[tuple]) -> dict[str, list[str]]:
def get_filepath_uuids_map(query_results: list[list]) -> dict[str, list[str]]:
file_uuids: dict[str, list[str]] = {}
for u, filepath in query_results:
filepath = filepath.decode("utf8")