keepassxc
305 строк · 11.1 Кб
1/*
2* Copyright (C) 2022 KeePassXC Team <team@keepassxc.org>
3* Copyright (C) 2018 Sami Vänttinen <sami.vanttinen@protonmail.com>
4*
5* This program is free software: you can redistribute it and/or modify
6* it under the terms of the GNU General Public License as published by
7* the Free Software Foundation, either version 2 or (at your option)
8* version 3 of the License.
9*
10* This program is distributed in the hope that it will be useful,
11* but WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU General Public License
16* along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#include "DatabaseSettingsWidgetBrowser.h"
20#include "ui_DatabaseSettingsWidgetBrowser.h"
21
22#include <QProgressDialog>
23
24#include "browser/BrowserService.h"
25#include "browser/BrowserSettings.h"
26#include "core/Group.h"
27#include "core/Metadata.h"
28#include "gui/MessageBox.h"
29
30DatabaseSettingsWidgetBrowser::DatabaseSettingsWidgetBrowser(QWidget* parent)
31: DatabaseSettingsWidget(parent)
32, m_ui(new Ui::DatabaseSettingsWidgetBrowser())
33, m_customData(new CustomData(this))
34, m_customDataModel(new QStandardItemModel(this))
35{
36m_ui->setupUi(this);
37m_ui->removeCustomDataButton->setEnabled(false);
38m_ui->customDataTable->setModel(m_customDataModel);
39
40settingsWarning();
41
42// clang-format off
43connect(m_ui->customDataTable->selectionModel(),
44SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
45SLOT(toggleRemoveButton(QItemSelection)));
46connect(m_ui->customDataTable, SIGNAL(doubleClicked(QModelIndex)), SLOT(editIndex(QModelIndex)));
47connect(m_customDataModel, SIGNAL(itemChanged(QStandardItem*)), SLOT(editFinished(QStandardItem*)));
48// clang-format on
49
50connect(m_ui->removeCustomDataButton, SIGNAL(clicked()), SLOT(removeSelectedKey()));
51connect(m_ui->removeSharedEncryptionKeys, SIGNAL(clicked()), this, SLOT(removeSharedEncryptionKeys()));
52connect(m_ui->removeSharedEncryptionKeys, SIGNAL(clicked()), this, SLOT(updateSharedKeyList()));
53connect(m_ui->removeStoredPermissions, SIGNAL(clicked()), this, SLOT(removeStoredPermissions()));
54connect(m_ui->refreshDatabaseID, SIGNAL(clicked()), this, SLOT(refreshDatabaseID()));
55}
56
57DatabaseSettingsWidgetBrowser::~DatabaseSettingsWidgetBrowser()
58{
59}
60
61CustomData* DatabaseSettingsWidgetBrowser::customData() const
62{
63// Returns the current database customData from metadata. Otherwise return an empty customData member.
64if (m_db) {
65return m_db->metadata()->customData();
66}
67return m_customData;
68}
69
70void DatabaseSettingsWidgetBrowser::initialize()
71{
72updateModel();
73settingsWarning();
74}
75
76void DatabaseSettingsWidgetBrowser::uninitialize()
77{
78}
79
80void DatabaseSettingsWidgetBrowser::showEvent(QShowEvent* event)
81{
82QWidget::showEvent(event);
83}
84
85bool DatabaseSettingsWidgetBrowser::save()
86{
87return true;
88}
89
90void DatabaseSettingsWidgetBrowser::removeSelectedKey()
91{
92if (MessageBox::Yes
93!= MessageBox::question(this,
94tr("Delete the selected key?"),
95tr("Do you really want to delete the selected key?\n"
96"This may prevent connection to the browser plugin."),
97MessageBox::Yes | MessageBox::Cancel,
98MessageBox::Cancel)) {
99return;
100}
101
102const QItemSelectionModel* itemSelectionModel = m_ui->customDataTable->selectionModel();
103if (itemSelectionModel) {
104for (const QModelIndex& index : itemSelectionModel->selectedRows(0)) {
105QString key = index.data().toString();
106key.insert(0, CustomData::BrowserKeyPrefix);
107customData()->remove(key);
108}
109updateModel();
110}
111}
112
113void DatabaseSettingsWidgetBrowser::toggleRemoveButton(const QItemSelection& selected)
114{
115m_ui->removeCustomDataButton->setEnabled(!selected.isEmpty());
116}
117
118void DatabaseSettingsWidgetBrowser::updateModel()
119{
120m_customDataModel->clear();
121m_customDataModel->setHorizontalHeaderLabels({tr("Key"), tr("Value"), tr("Created")});
122
123for (const QString& key : customData()->keys()) {
124if (key.startsWith(CustomData::BrowserKeyPrefix)) {
125QString strippedKey = key;
126strippedKey.remove(CustomData::BrowserKeyPrefix);
127auto created = customData()->value(QString("%1_%2").arg(CustomData::Created, strippedKey));
128auto createdItem = new QStandardItem(created);
129createdItem->setEditable(false);
130m_customDataModel->appendRow(QList<QStandardItem*>()
131<< new QStandardItem(strippedKey)
132<< new QStandardItem(customData()->value(key)) << createdItem);
133}
134}
135
136m_ui->removeCustomDataButton->setEnabled(false);
137}
138
139void DatabaseSettingsWidgetBrowser::settingsWarning()
140{
141if (!browserSettings()->isEnabled()) {
142m_ui->removeSharedEncryptionKeys->setEnabled(false);
143m_ui->removeStoredPermissions->setEnabled(false);
144m_ui->customDataTable->setEnabled(false);
145m_ui->warningWidget->showMessage(tr("Enable Browser Integration to access these settings."),
146MessageWidget::Warning);
147m_ui->warningWidget->setCloseButtonVisible(false);
148m_ui->warningWidget->setAutoHideTimeout(-1);
149} else {
150m_ui->removeSharedEncryptionKeys->setEnabled(true);
151m_ui->removeStoredPermissions->setEnabled(true);
152m_ui->customDataTable->setEnabled(true);
153m_ui->warningWidget->hideMessage();
154}
155}
156
157void DatabaseSettingsWidgetBrowser::removeSharedEncryptionKeys()
158{
159if (MessageBox::Yes
160!= MessageBox::question(this,
161tr("Disconnect all browsers"),
162tr("Do you really want to disconnect all browsers?\n"
163"This may prevent connection to the browser plugin."),
164MessageBox::Yes | MessageBox::Cancel,
165MessageBox::Cancel)) {
166return;
167}
168
169QStringList keysToRemove;
170for (const QString& key : m_db->metadata()->customData()->keys()) {
171if (key.startsWith(CustomData::BrowserKeyPrefix)) {
172keysToRemove << key;
173}
174}
175
176if (keysToRemove.isEmpty()) {
177MessageBox::information(
178this, tr("No keys found"), tr("No shared encryption keys found in KeePassXC settings."), MessageBox::Ok);
179return;
180}
181
182for (const QString& key : keysToRemove) {
183m_db->metadata()->customData()->remove(key);
184}
185
186const int count = keysToRemove.count();
187MessageBox::information(this,
188tr("Removed keys from database"),
189tr("Successfully removed %n encryption key(s) from KeePassXC settings.", "", count),
190MessageBox::Ok);
191}
192
193void DatabaseSettingsWidgetBrowser::removeStoredPermissions()
194{
195if (MessageBox::Yes
196!= MessageBox::question(this,
197tr("Forget all site-specific settings on entries"),
198tr("Do you really want forget all site-specific settings on every entry?\n"
199"Permissions to access entries will be revoked."),
200MessageBox::Yes | MessageBox::Cancel,
201MessageBox::Cancel)) {
202return;
203}
204
205QList<Entry*> entries = m_db->rootGroup()->entriesRecursive();
206
207QProgressDialog progress(tr("Removing stored permissions…"), tr("Abort"), 0, entries.count());
208progress.setWindowModality(Qt::WindowModal);
209
210uint counter = 0;
211for (Entry* entry : entries) {
212if (progress.wasCanceled()) {
213return;
214}
215
216if (entry->customData()->contains(BrowserService::KEEPASSXCBROWSER_NAME)) {
217entry->beginUpdate();
218entry->customData()->remove(BrowserService::KEEPASSXCBROWSER_NAME);
219entry->endUpdate();
220++counter;
221}
222progress.setValue(progress.value() + 1);
223}
224progress.reset();
225
226if (counter > 0) {
227MessageBox::information(this,
228tr("Removed permissions"),
229tr("Successfully removed permissions from %n entry(s).", "", counter),
230MessageBox::Ok);
231} else {
232MessageBox::information(this,
233tr("No entry with permissions found!"),
234tr("The active database does not contain an entry with permissions."),
235MessageBox::Ok);
236}
237}
238
239void DatabaseSettingsWidgetBrowser::refreshDatabaseID()
240{
241if (MessageBox::Yes
242!= MessageBox::question(this,
243tr("Refresh database ID"),
244tr("Do you really want refresh the database ID?\n"
245"This is only necessary if your database is a copy of another and the "
246"browser extension cannot connect."),
247MessageBox::Yes | MessageBox::Cancel,
248MessageBox::Cancel)) {
249return;
250}
251
252m_db->rootGroup()->setUuid(QUuid::createUuid());
253}
254
255void DatabaseSettingsWidgetBrowser::editIndex(const QModelIndex& index)
256{
257Q_ASSERT(index.isValid());
258if (!index.isValid()) {
259return;
260}
261
262m_valueInEdit = index.data().toString();
263m_ui->customDataTable->edit(index);
264}
265
266void DatabaseSettingsWidgetBrowser::editFinished(QStandardItem* item)
267{
268const QItemSelectionModel* itemSelectionModel = m_ui->customDataTable->selectionModel();
269
270if (itemSelectionModel) {
271auto indexList = itemSelectionModel->selectedRows(item->column());
272if (indexList.length() > 0) {
273QString newValue = item->index().data().toString();
274
275// The key is edited
276if (item->column() == 0) {
277// Get the old key/value pair, remove it and replace it
278m_valueInEdit.insert(0, CustomData::BrowserKeyPrefix);
279auto tempValue = customData()->value(m_valueInEdit);
280newValue.insert(0, CustomData::BrowserKeyPrefix);
281
282m_db->metadata()->customData()->remove(m_valueInEdit);
283m_db->metadata()->customData()->set(newValue, tempValue);
284} else {
285// Replace just the value
286for (const QString& key : m_db->metadata()->customData()->keys()) {
287if (key.startsWith(CustomData::BrowserKeyPrefix)) {
288if (m_valueInEdit == m_db->metadata()->customData()->value(key)) {
289m_db->metadata()->customData()->set(key, newValue);
290break;
291}
292}
293}
294}
295
296updateModel();
297}
298}
299}
300
301// Updates the shared key list after the list is cleared
302void DatabaseSettingsWidgetBrowser::updateSharedKeyList()
303{
304updateModel();
305}
306