From d382905af432f0078e4e3223092258e40a01362a Mon Sep 17 00:00:00 2001 From: varjolintu Date: Mon, 19 Feb 2018 13:51:06 +0200 Subject: [PATCH] Browser connection keys and rules are stored in custom data instead of attributes --- src/CMakeLists.txt | 1 + src/browser/BrowserAction.cpp | 12 - src/browser/BrowserAction.h | 4 - src/browser/BrowserEntryConfig.cpp | 6 +- src/browser/BrowserOptionDialog.cpp | 7 +- src/browser/BrowserOptionDialog.h | 10 +- src/browser/BrowserOptionDialog.ui | 36 +-- src/browser/BrowserService.cpp | 255 +++++++++-------- src/browser/BrowserService.h | 10 +- src/browser/NativeMessagingHost.cpp | 12 - src/browser/NativeMessagingHost.h | 4 - src/gui/MainWindow.cpp | 16 +- src/gui/dbsettings/DatabaseSettingsDialog.cpp | 12 + src/gui/dbsettings/DatabaseSettingsDialog.h | 2 + .../DatabaseSettingsWidgetBrowser.cpp | 260 ++++++++++++++++++ .../DatabaseSettingsWidgetBrowser.h | 82 ++++++ .../DatabaseSettingsWidgetBrowser.ui | 156 +++++++++++ 17 files changed, 673 insertions(+), 212 deletions(-) create mode 100644 src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp create mode 100644 src/gui/dbsettings/DatabaseSettingsWidgetBrowser.h create mode 100644 src/gui/dbsettings/DatabaseSettingsWidgetBrowser.ui diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 51add89687..24636220e9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -160,6 +160,7 @@ set(keepassx_SOURCES gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.cpp gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp gui/dbsettings/DatabaseSettingsWidgetMasterKey.cpp + gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp gui/settings/SettingsWidget.cpp gui/wizard/NewDatabaseWizard.cpp gui/wizard/NewDatabaseWizardPage.cpp diff --git a/src/browser/BrowserAction.cpp b/src/browser/BrowserAction.cpp index 60cf8506a8..fa082fee59 100644 --- a/src/browser/BrowserAction.cpp +++ b/src/browser/BrowserAction.cpp @@ -567,15 +567,3 @@ QString BrowserAction::incrementNonce(const QString& nonce) sodium_increment(n.data(), n.size()); return getQByteArray(n.data(), n.size()).toBase64(); } - -void BrowserAction::removeSharedEncryptionKeys() -{ - QMutexLocker locker(&m_mutex); - m_browserService.removeSharedEncryptionKeys(); -} - -void BrowserAction::removeStoredPermissions() -{ - QMutexLocker locker(&m_mutex); - m_browserService.removeStoredPermissions(); -} diff --git a/src/browser/BrowserAction.h b/src/browser/BrowserAction.h index 46fb9d04f3..5a7c83bf81 100644 --- a/src/browser/BrowserAction.h +++ b/src/browser/BrowserAction.h @@ -54,10 +54,6 @@ class BrowserAction : public QObject QJsonObject readResponse(const QJsonObject& json); -public slots: - void removeSharedEncryptionKeys(); - void removeStoredPermissions(); - private: QJsonObject handleAction(const QJsonObject& json); QJsonObject handleChangePublicKeys(const QJsonObject& json, const QString& action); diff --git a/src/browser/BrowserEntryConfig.cpp b/src/browser/BrowserEntryConfig.cpp index 90d9f107c4..47e8762a8b 100644 --- a/src/browser/BrowserEntryConfig.cpp +++ b/src/browser/BrowserEntryConfig.cpp @@ -21,7 +21,7 @@ #include "core/EntryAttributes.h" #include -static const char KEEPASSBROWSER_NAME[] = "KeePassXC-Browser Settings"; +static const char KEEPASSXCBROWSER_NAME[] = "KeePassXC-Browser Settings"; BrowserEntryConfig::BrowserEntryConfig(QObject* parent) : QObject(parent) @@ -82,7 +82,7 @@ void BrowserEntryConfig::setRealm(const QString& realm) bool BrowserEntryConfig::load(const Entry* entry) { - QString s = entry->attributes()->value(KEEPASSBROWSER_NAME); + QString s = entry->customData()->value(KEEPASSXCBROWSER_NAME); if (s.isEmpty()) { return false; } @@ -104,5 +104,5 @@ void BrowserEntryConfig::save(Entry* entry) QVariantMap v = qo2qv(this); QJsonObject o = QJsonObject::fromVariantMap(v); QByteArray json = QJsonDocument(o).toJson(QJsonDocument::Compact); - entry->attributes()->set(KEEPASSBROWSER_NAME, json); + entry->customData()->set(KEEPASSXCBROWSER_NAME, json); } diff --git a/src/browser/BrowserOptionDialog.cpp b/src/browser/BrowserOptionDialog.cpp index e2ed8420be..bcb06a3f0e 100644 --- a/src/browser/BrowserOptionDialog.cpp +++ b/src/browser/BrowserOptionDialog.cpp @@ -24,15 +24,14 @@ #include "ui_BrowserOptionDialog.h" #include -#include +#include "gui/MessageBox.h" -BrowserOptionDialog::BrowserOptionDialog(QWidget* parent) +BrowserOptionDialog::BrowserOptionDialog(DatabaseTabWidget* parent) : QWidget(parent) , m_ui(new Ui::BrowserOptionDialog()) + , m_dbTabWidget(parent) { m_ui->setupUi(this); - connect(m_ui->removeSharedEncryptionKeys, SIGNAL(clicked()), this, SIGNAL(removeSharedEncryptionKeys())); - connect(m_ui->removeStoredPermissions, SIGNAL(clicked()), this, SIGNAL(removeStoredPermissions())); m_ui->extensionLabel->setOpenExternalLinks(true); m_ui->extensionLabel->setText(tr("KeePassXC-Browser is needed for the browser integration to work.
Download it for %1 and %2.").arg( diff --git a/src/browser/BrowserOptionDialog.h b/src/browser/BrowserOptionDialog.h index a48504dfd9..f99492957f 100644 --- a/src/browser/BrowserOptionDialog.h +++ b/src/browser/BrowserOptionDialog.h @@ -21,7 +21,8 @@ #define BROWSEROPTIONDIALOG_H #include -#include +#include +#include "gui/DatabaseTabWidget.h" namespace Ui { @@ -33,22 +34,19 @@ class BrowserOptionDialog : public QWidget Q_OBJECT public: - explicit BrowserOptionDialog(QWidget* parent = nullptr); + explicit BrowserOptionDialog(DatabaseTabWidget* parent = nullptr); ~BrowserOptionDialog(); public slots: void loadSettings(); void saveSettings(); -signals: - void removeSharedEncryptionKeys(); - void removeStoredPermissions(); - private slots: void showProxyLocationFileDialog(); private: QScopedPointer m_ui; + DatabaseTabWidget* const m_dbTabWidget; }; #endif // BROWSEROPTIONDIALOG_H diff --git a/src/browser/BrowserOptionDialog.ui b/src/browser/BrowserOptionDialog.ui index 24589c147f..850dcb2d86 100755 --- a/src/browser/BrowserOptionDialog.ui +++ b/src/browser/BrowserOptionDialog.ui @@ -135,7 +135,7 @@ - + Qt::Vertical @@ -205,7 +205,7 @@ - + Qt::Vertical @@ -220,36 +220,6 @@ - - - - - - - 0 - 0 - - - - &Disconnect all browsers - - - - - - - - 0 - 0 - - - - Forget all remembered &permissions - - - - - @@ -366,7 +336,7 @@ - + Qt::Vertical diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index 086c150628..aafe45370f 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -34,10 +34,13 @@ #include "core/PasswordGenerator.h" #include "gui/MainWindow.h" -static const char KEEPASSXCBROWSER_NAME[] = "KeePassXC-Browser Settings"; -static const char ASSOCIATE_KEY_PREFIX[] = "Public Key: "; +const char BrowserService::KEEPASSXCBROWSER_NAME[] = "KeePassXC-Browser Settings"; +const char BrowserService::ASSOCIATE_KEY_PREFIX[] = "Public Key: "; static const char KEEPASSXCBROWSER_GROUP_NAME[] = "KeePassXC-Browser Passwords"; static int KEEPASSXCBROWSER_DEFAULT_ICON = 1; +// These are for the settings and password conversion +static const char KEEPASSHTTP_NAME[] = "KeePassHttp Settings"; +static const char KEEPASSHTTP_GROUP_NAME[] = "KeePassHttp Passwords"; BrowserService::BrowserService(DatabaseTabWidget* parent) : m_dbTabWidget(parent) @@ -106,12 +109,12 @@ QString BrowserService::getDatabaseRootUuid() { Database* db = getDatabase(); if (!db) { - return QString(); + return {}; } Group* rootGroup = db->rootGroup(); if (!rootGroup) { - return QString(); + return {}; } return rootGroup->uuidToHex(); @@ -121,46 +124,16 @@ QString BrowserService::getDatabaseRecycleBinUuid() { Database* db = getDatabase(); if (!db) { - return QString(); + return {}; } Group* recycleBin = db->metadata()->recycleBin(); if (!recycleBin) { - return QString(); + return {}; } return recycleBin->uuidToHex(); } -Entry* BrowserService::getConfigEntry(bool create) -{ - Entry* entry = nullptr; - Database* db = getDatabase(); - if (!db) { - return nullptr; - } - - entry = db->resolveEntry(m_keepassBrowserUUID); - if (!entry && create) { - entry = new Entry(); - entry->setTitle(QLatin1String(KEEPASSXCBROWSER_NAME)); - entry->setUuid(m_keepassBrowserUUID); - entry->setAutoTypeEnabled(false); - entry->setGroup(db->rootGroup()); - return entry; - } - - if (entry && entry->group() == db->metadata()->recycleBin()) { - if (!create) { - return nullptr; - } else { - entry->setGroup(db->rootGroup()); - return entry; - } - } - - return entry; -} - QString BrowserService::storeKey(const QString& key) { QString id; @@ -171,8 +144,8 @@ QString BrowserService::storeKey(const QString& key) return id; } - Entry* config = getConfigEntry(true); - if (!config) { + Database* db = getDatabase(); + if (!db) { return {}; } @@ -198,7 +171,7 @@ QString BrowserService::storeKey(const QString& key) return {}; } - contains = config->attributes()->contains(QLatin1String(ASSOCIATE_KEY_PREFIX) + id); + contains = db->metadata()->customData()->contains(QLatin1String(ASSOCIATE_KEY_PREFIX) + id); if (contains) { dialogResult = QMessageBox::warning(nullptr, tr("KeePassXC: Overwrite existing key?"), @@ -209,18 +182,18 @@ QString BrowserService::storeKey(const QString& key) } } while (contains && dialogResult == QMessageBox::No); - config->attributes()->set(QLatin1String(ASSOCIATE_KEY_PREFIX) + id, key, true); + db->metadata()->customData()->set(QLatin1String(ASSOCIATE_KEY_PREFIX) + id, key); return id; } QString BrowserService::getKey(const QString& id) { - Entry* config = getConfigEntry(); - if (!config) { - return QString(); + Database* db = getDatabase(); + if (!db) { + return {}; } - return config->attributes()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + id); + return db->metadata()->customData()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + id); } QJsonArray BrowserService::findMatchingEntries(const QString& id, @@ -421,12 +394,9 @@ QList BrowserService::searchEntries(const QString& url, const StringPair if (Database* db = dbWidget->database()) { // Check if database is connected with KeePassXC-Browser for (const StringPair keyPair : keyList) { - Entry* entry = db->resolveEntry(m_keepassBrowserUUID); - if (entry) { - QString key = entry->attributes()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + keyPair.first); - if (!key.isEmpty() && keyPair.second == key) { - databases << db; - } + QString key = db->metadata()->customData()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + keyPair.first); + if (!key.isEmpty() && keyPair.second == key) { + databases << db; } } } @@ -448,102 +418,70 @@ QList BrowserService::searchEntries(const QString& url, const StringPair return entries; } -void BrowserService::removeSharedEncryptionKeys() -{ - if (!isDatabaseOpened()) { - QMessageBox::critical(0, - tr("KeePassXC: Database locked!"), - tr("The active database is locked!\n" - "Please unlock the selected database or choose another one which is unlocked."), - QMessageBox::Ok); - return; - } - - Entry* entry = getConfigEntry(); - if (!entry) { - QMessageBox::information(0, - tr("KeePassXC: Settings not available!"), - tr("The active database does not contain a settings entry."), - QMessageBox::Ok); - return; - } - - QStringList keysToRemove; - for (const QString& key : entry->attributes()->keys()) { - if (key.startsWith(ASSOCIATE_KEY_PREFIX)) { - keysToRemove << key; - } - } - - if (keysToRemove.isEmpty()) { - QMessageBox::information(0, - tr("KeePassXC: No keys found"), - tr("No shared encryption keys found in KeePassXC settings."), - QMessageBox::Ok); - return; - } - - entry->beginUpdate(); - for (const QString& key : keysToRemove) { - entry->attributes()->remove(key); - } - entry->endUpdate(); - - const int count = keysToRemove.count(); - QMessageBox::information(0, - tr("KeePassXC: Removed keys from database"), - tr("Successfully removed %n encryption key(s) from KeePassXC settings.", "", count), - QMessageBox::Ok); -} - -void BrowserService::removeStoredPermissions() +void BrowserService::convertAttributesToCustomData(Database *currentDb) { - if (!isDatabaseOpened()) { - QMessageBox::critical(0, - tr("KeePassXC: Database locked!"), - tr("The active database is locked!\n" - "Please unlock the selected database or choose another one which is unlocked."), - QMessageBox::Ok); - return; - } - - Database* db = m_dbTabWidget->currentDatabaseWidget()->database(); + Database* db = currentDb ? currentDb : getDatabase(); if (!db) { return; } QList entries = db->rootGroup()->entriesRecursive(); - - QProgressDialog progress(tr("Removing stored permissions…"), tr("Abort"), 0, entries.count()); + QProgressDialog progress(tr("Converting attributes to custom data…"), tr("Abort"), 0, entries.count()); progress.setWindowModality(Qt::WindowModal); - uint counter = 0; + int counter = 0; + int keyCounter = 0; for (Entry* entry : entries) { if (progress.wasCanceled()) { return; } - if (entry->attributes()->contains(KEEPASSXCBROWSER_NAME)) { - entry->beginUpdate(); - entry->attributes()->remove(KEEPASSXCBROWSER_NAME); - entry->endUpdate(); + if (moveSettingsToCustomData(entry, KEEPASSHTTP_NAME)) { + ++counter; + } + if (moveSettingsToCustomData(entry, KEEPASSXCBROWSER_NAME)) { ++counter; } + + if (entry->title() == KEEPASSHTTP_NAME || entry->title() == KEEPASSXCBROWSER_NAME) { + keyCounter += moveKeysToCustomData(entry, db); + delete entry; + } + progress.setValue(progress.value() + 1); } progress.reset(); if (counter > 0) { - QMessageBox::information(0, - tr("KeePassXC: Removed permissions"), - tr("Successfully removed permissions from %n entry(s).", "", counter), + QMessageBox::information(0, tr("KeePassXC: Converted KeePassHTTP attributes"), + tr("Successfully converted attributes from %1 entry(s).\n" + "Moved %2 keys to custom data.", "").arg(counter).arg(keyCounter), + QMessageBox::Ok); + } else if (counter == 0 && keyCounter > 0) { + QMessageBox::information(0, tr("KeePassXC: Converted KeePassHTTP attributes"), + tr("Successfully moved %n keys to custom data.", "", keyCounter), QMessageBox::Ok); } else { - QMessageBox::information(0, - tr("KeePassXC: No entry with permissions found!"), - tr("The active database does not contain an entry with permissions."), + QMessageBox::information(0, tr("KeePassXC: No entry with KeePassHTTP attributes found!"), + tr("The active database does not contain an entry with KeePassHTTP attributes."), QMessageBox::Ok); } + + // Rename password groupName + Group* rootGroup = db->rootGroup(); + if (!rootGroup) { + return; + } + + const QString keePassBrowserGroupName = QLatin1String(KEEPASSXCBROWSER_GROUP_NAME); + const QString keePassHttpGroupName = QLatin1String(KEEPASSHTTP_GROUP_NAME); + + for (Group* g : rootGroup->groupsRecursive(true)) { + if (g->name() == keePassHttpGroupName) { + g->setName(keePassBrowserGroupName); + break; + } + } } QList BrowserService::sortEntries(QList& pwEntries, const QString& host, const QString& entryUrl) @@ -790,6 +728,75 @@ Database* BrowserService::getDatabase() return nullptr; } +bool BrowserService::moveSettingsToCustomData(Entry* entry, const QString& name) const +{ + if (entry->attributes()->contains(name)) { + QString attr = entry->attributes()->value(name); + entry->beginUpdate(); + if (!attr.isEmpty()) { + entry->customData()->set(KEEPASSXCBROWSER_NAME, attr); + } + entry->attributes()->remove(name); + entry->endUpdate(); + return true; + } + return false; +} + +int BrowserService::moveKeysToCustomData(Entry* entry, Database* db) const +{ + int keyCounter = 0; + for (const auto& key : entry->attributes()->keys()) { + if (key.contains(ASSOCIATE_KEY_PREFIX)) { + QString publicKey = key; + publicKey.remove(ASSOCIATE_KEY_PREFIX); + + // Add key to database custom data + if (db && !db->metadata()->customData()->contains(QLatin1String(ASSOCIATE_KEY_PREFIX) + publicKey)) { + db->metadata()->customData()->set(QLatin1String(ASSOCIATE_KEY_PREFIX) + publicKey, entry->attributes()->value(key)); + ++keyCounter; + } + } + } + + return keyCounter; +} + +bool BrowserService::checkLegacySettings() +{ + Database* db = getDatabase(); + if (!db) { + return false; + } + + bool legacySettingsFound = false; + QList entries = db->rootGroup()->entriesRecursive(); + for (const auto& e : entries) { + if ((e->attributes()->contains(KEEPASSHTTP_NAME) || e->attributes()->contains(KEEPASSXCBROWSER_NAME)) || + (e->title() == KEEPASSHTTP_NAME || e->title() == KEEPASSXCBROWSER_NAME)) { + legacySettingsFound = true; + break; + } + } + + if (!legacySettingsFound) { + return false; + } + + auto dialogResult = QMessageBox::warning(nullptr, + tr("KeePassXC: Legacy browser integration settings detected"), + tr("Legacy browser integration settings have been detected.\n" + "Do you want to upgrade the settings to the latest standard?\n" + "This is necessary to maintain compatibility with the browser plugin."), + QMessageBox::Yes | QMessageBox::No); + + if (dialogResult == QMessageBox::No) { + return false; + } + + return true; +} + void BrowserService::databaseLocked(DatabaseWidget* dbWidget) { if (dbWidget) { @@ -805,6 +812,10 @@ void BrowserService::databaseUnlocked(DatabaseWidget* dbWidget) m_bringToFrontRequested = false; } emit databaseUnlocked(); + + if (checkLegacySettings()) { + convertAttributesToCustomData(); + } } } diff --git a/src/browser/BrowserService.h b/src/browser/BrowserService.h index 10c3a786e3..45e8a6d1a6 100644 --- a/src/browser/BrowserService.h +++ b/src/browser/BrowserService.h @@ -54,8 +54,11 @@ class BrowserService : public QObject const QString& realm); QList searchEntries(Database* db, const QString& hostname, const QString& url); QList searchEntries(const QString& url, const StringPairList& keyList); - void removeSharedEncryptionKeys(); - void removeStoredPermissions(); + void convertAttributesToCustomData(Database *currentDb = nullptr); + +public: + static const char KEEPASSXCBROWSER_NAME[]; + static const char ASSOCIATE_KEY_PREFIX[]; public slots: QJsonArray findMatchingEntries(const QString& id, @@ -102,6 +105,9 @@ public slots: bool matchUrlScheme(const QString& url); bool removeFirstDomain(QString& hostname); Database* getDatabase(); + bool moveSettingsToCustomData(Entry* entry, const QString& name) const; + int moveKeysToCustomData(Entry* entry, Database* db) const; + bool checkLegacySettings(); private: DatabaseTabWidget* const m_dbTabWidget; diff --git a/src/browser/NativeMessagingHost.cpp b/src/browser/NativeMessagingHost.cpp index fc35bbec51..03f742020b 100644 --- a/src/browser/NativeMessagingHost.cpp +++ b/src/browser/NativeMessagingHost.cpp @@ -207,18 +207,6 @@ void NativeMessagingHost::disconnectSocket() } } -void NativeMessagingHost::removeSharedEncryptionKeys() -{ - QMutexLocker locker(&m_mutex); - m_browserService.removeSharedEncryptionKeys(); -} - -void NativeMessagingHost::removeStoredPermissions() -{ - QMutexLocker locker(&m_mutex); - m_browserService.removeStoredPermissions(); -} - void NativeMessagingHost::databaseLocked() { QJsonObject response; diff --git a/src/browser/NativeMessagingHost.h b/src/browser/NativeMessagingHost.h index fde8f051c1..869af9e24a 100644 --- a/src/browser/NativeMessagingHost.h +++ b/src/browser/NativeMessagingHost.h @@ -37,10 +37,6 @@ class NativeMessagingHost : public NativeMessagingBase void run(); void stop(); -public slots: - void removeSharedEncryptionKeys(); - void removeStoredPermissions(); - signals: void quit(); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 733ac01633..298a284381 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -62,7 +62,8 @@ class BrowserPlugin : public ISettingsPage { public: - BrowserPlugin(DatabaseTabWidget* tabWidget) + BrowserPlugin(DatabaseTabWidget* tabWidget) : + m_dbTabWidget(tabWidget) { m_nativeMessagingHost = QSharedPointer(new NativeMessagingHost(tabWidget, browserSettings()->isEnabled())); } @@ -83,13 +84,7 @@ class BrowserPlugin : public ISettingsPage QWidget* createWidget() override { - BrowserOptionDialog* dlg = new BrowserOptionDialog(); - QObject::connect(dlg, - SIGNAL(removeSharedEncryptionKeys()), - m_nativeMessagingHost.data(), - SLOT(removeSharedEncryptionKeys())); - QObject::connect( - dlg, SIGNAL(removeStoredPermissions()), m_nativeMessagingHost.data(), SLOT(removeStoredPermissions())); + BrowserOptionDialog* dlg = new BrowserOptionDialog(m_dbTabWidget); return dlg; } @@ -108,8 +103,9 @@ class BrowserPlugin : public ISettingsPage } } -private: - QSharedPointer m_nativeMessagingHost; + private: + QSharedPointer m_nativeMessagingHost; + DatabaseTabWidget* const m_dbTabWidget; }; #endif diff --git a/src/gui/dbsettings/DatabaseSettingsDialog.cpp b/src/gui/dbsettings/DatabaseSettingsDialog.cpp index 8b28ced042..3e20470625 100644 --- a/src/gui/dbsettings/DatabaseSettingsDialog.cpp +++ b/src/gui/dbsettings/DatabaseSettingsDialog.cpp @@ -21,6 +21,7 @@ #include "DatabaseSettingsWidgetGeneral.h" #include "DatabaseSettingsWidgetEncryption.h" #include "DatabaseSettingsWidgetMasterKey.h" +#include "DatabaseSettingsWidgetBrowser.h" #include "core/Config.h" #include "core/FilePath.h" @@ -34,6 +35,9 @@ DatabaseSettingsDialog::DatabaseSettingsDialog(QWidget* parent) , m_securityTabWidget(new QTabWidget(this)) , m_masterKeyWidget(new DatabaseSettingsWidgetMasterKey(this)) , m_encryptionWidget(new DatabaseSettingsWidgetEncryption(this)) +#ifdef WITH_XC_BROWSER + , m_browserWidget(new DatabaseSettingsWidgetBrowser(this)) +#endif { m_ui->setupUi(this); @@ -55,6 +59,11 @@ DatabaseSettingsDialog::DatabaseSettingsDialog(QWidget* parent) connect(m_ui->categoryList, SIGNAL(categoryChanged(int)), m_ui->stackedWidget, SLOT(setCurrentIndex(int))); connect(m_ui->advancedSettingsToggle, SIGNAL(toggled(bool)), SLOT(toggleAdvancedMode(bool))); +#ifdef WITH_XC_BROWSER + m_ui->categoryList->addCategory(tr("Browser Integration"), FilePath::instance()->icon("apps", "internet-web-browser")); + m_ui->stackedWidget->addWidget(m_browserWidget); +#endif + pageChanged(); } @@ -68,6 +77,9 @@ void DatabaseSettingsDialog::load(Database* db) m_generalWidget->load(db); m_masterKeyWidget->load(db); m_encryptionWidget->load(db); +#ifdef WITH_XC_BROWSER + m_browserWidget->load(db); +#endif m_ui->advancedSettingsToggle->setChecked(config()->get("GUI/AdvancedSettings", false).toBool()); m_db = db; } diff --git a/src/gui/dbsettings/DatabaseSettingsDialog.h b/src/gui/dbsettings/DatabaseSettingsDialog.h index 50fec32d6b..571ecc9d89 100644 --- a/src/gui/dbsettings/DatabaseSettingsDialog.h +++ b/src/gui/dbsettings/DatabaseSettingsDialog.h @@ -27,6 +27,7 @@ class Database; class DatabaseSettingsWidgetGeneral; class DatabaseSettingsWidgetEncryption; class DatabaseSettingsWidgetMasterKey; +class DatabaseSettingsWidgetBrowser; class QTabWidget; namespace Ui @@ -68,6 +69,7 @@ private slots: QPointer m_securityTabWidget; QPointer m_masterKeyWidget; QPointer m_encryptionWidget; + QPointer m_browserWidget; }; #endif // KEEPASSX_DATABASESETTINGSWIDGET_H diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp new file mode 100644 index 0000000000..4eeb06f1b9 --- /dev/null +++ b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "DatabaseSettingsWidgetBrowser.h" +#include "ui_DatabaseSettingsWidgetBrowser.h" +#include +#include "core/Clock.h" +#include "core/Database.h" +#include "core/Entry.h" +#include "core/Group.h" +#include "core/Metadata.h" +#include "gui/MessageBox.h" + +DatabaseSettingsWidgetBrowser::DatabaseSettingsWidgetBrowser(QWidget* parent) + : DatabaseSettingsWidget(parent), m_ui(new Ui::DatabaseSettingsWidgetBrowser()) + , m_customData(new CustomData(this)) + , m_customDataModel(new QStandardItemModel(this)) +#ifdef WITH_XC_BROWSER + , m_browserService(nullptr) +#endif +{ + m_ui->setupUi(this); + m_ui->removeCustomDataButton->setEnabled(false); + m_ui->customDataTable->setModel(m_customDataModel); + connect(m_ui->customDataTable->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), + SLOT(toggleRemoveButton(QItemSelection))); + connect(m_ui->removeCustomDataButton, SIGNAL(clicked()), SLOT(removeSelectedKey())); + connect(m_ui->convertToCustomData, SIGNAL(clicked()), this, SLOT(convertAttributesToCustomData())); + connect(m_ui->convertToCustomData, SIGNAL(clicked()), this, SLOT(updateSharedKeyList())); + connect(m_ui->removeSharedEncryptionKeys, SIGNAL(clicked()), this, SLOT(removeSharedEncryptionKeys())); + connect(m_ui->removeSharedEncryptionKeys, SIGNAL(clicked()), this, SLOT(updateSharedKeyList())); + connect(m_ui->removeStoredPermissions, SIGNAL(clicked()), this, SLOT(removeStoredPermissions())); +} + +DatabaseSettingsWidgetBrowser::~DatabaseSettingsWidgetBrowser() +{ +} + +CustomData* DatabaseSettingsWidgetBrowser::customData() const +{ + // Returns the current database customData from metadata. Otherwise return an empty customData member. + if (m_db) { + return m_db->metadata()->customData(); + } + return m_customData; +} + +void DatabaseSettingsWidgetBrowser::initialize() +{ + updateModel(); +} + +void DatabaseSettingsWidgetBrowser::uninitialize() +{ +} + +void DatabaseSettingsWidgetBrowser::showEvent(QShowEvent* event) +{ + QWidget::showEvent(event); +} + +bool DatabaseSettingsWidgetBrowser::save() +{ + return true; +} + +void DatabaseSettingsWidgetBrowser::removeSelectedKey() +{ + if (QMessageBox::Yes != MessageBox::question(this, + tr("Delete the selected key?"), + tr("Do you really want to delete the selected key?\n" + "This may prevent connection to the browser plugin."), + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel)) { + return; + } + + const QItemSelectionModel* itemSelectionModel = m_ui->customDataTable->selectionModel(); + if (itemSelectionModel) { + for (const QModelIndex& index : itemSelectionModel->selectedRows(0)) { + const QString key = index.data().toString(); + customData()->remove(key); + } + updateModel(); + } +} + +void DatabaseSettingsWidgetBrowser::toggleRemoveButton(const QItemSelection& selected) +{ + m_ui->removeCustomDataButton->setEnabled(!selected.isEmpty()); +} + +void DatabaseSettingsWidgetBrowser::updateModel() +{ + m_customDataModel->clear(); + m_customDataModel->setHorizontalHeaderLabels({tr("Key"), tr("Value")}); + + for (const QString& key : customData()->keys()) { + m_customDataModel->appendRow(QList() + << new QStandardItem(key) + << new QStandardItem(customData()->value(key))); + } + + m_ui->removeCustomDataButton->setEnabled(false); +} + +void DatabaseSettingsWidgetBrowser::removeSharedEncryptionKeys() +{ +#ifdef WITH_XC_BROWSER + if (QMessageBox::Yes != MessageBox::question(this, + tr("Disconnect all browsers"), + tr("Do you really want to disconnect all browsers?\n" + "This may prevent connection to the browser plugin."), + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel)) { + return; + } + + QStringList keysToRemove; + for (const QString& key : m_db->metadata()->customData()->keys()) { + if (key.startsWith(BrowserService::ASSOCIATE_KEY_PREFIX)) { + keysToRemove << key; + } + } + + if (keysToRemove.isEmpty()) { + QMessageBox::information(0, + tr("KeePassXC: No keys found"), + tr("No shared encryption keys found in KeePassXC settings."), + QMessageBox::Ok); + return; + } + + for (const QString& key : keysToRemove) { + m_db->metadata()->customData()->remove(key); + } + + const int count = keysToRemove.count(); + QMessageBox::information(0, + tr("KeePassXC: Removed keys from database"), + tr("Successfully removed %n encryption key(s) from KeePassXC settings.", "", count), + QMessageBox::Ok); +#endif +} + +void DatabaseSettingsWidgetBrowser::removeStoredPermissions() +{ +#ifdef WITH_XC_BROWSER + if (QMessageBox::Yes != MessageBox::question(this, + tr("Forget all site-specific settings on entries"), + tr("Do you really want forget all site-specific settings on every entry?\n" + "Permissions to access entries will be revoked."), + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel)) { + return; + } + + QList entries = m_db->rootGroup()->entriesRecursive(); + + QProgressDialog progress(tr("Removing stored permissions…"), tr("Abort"), 0, entries.count()); + progress.setWindowModality(Qt::WindowModal); + + uint counter = 0; + for (Entry* entry : entries) { + if (progress.wasCanceled()) { + return; + } + + if (entry->customData()->contains(BrowserService::KEEPASSXCBROWSER_NAME)) { + entry->beginUpdate(); + entry->customData()->remove(BrowserService::KEEPASSXCBROWSER_NAME); + entry->endUpdate(); + ++counter; + } + progress.setValue(progress.value() + 1); + } + progress.reset(); + + if (counter > 0) { + QMessageBox::information(0, + tr("KeePassXC: Removed permissions"), + tr("Successfully removed permissions from %n entry(s).", "", counter), + QMessageBox::Ok); + } else { + QMessageBox::information(0, + tr("KeePassXC: No entry with permissions found!"), + tr("The active database does not contain an entry with permissions."), + QMessageBox::Ok); + } +#endif +} + +void DatabaseSettingsWidgetBrowser::convertAttributesToCustomData() +{ +#ifdef WITH_XC_BROWSER + if (QMessageBox::Yes != MessageBox::question(this, + tr("Move KeePassHTTP attributes to custom data"), + tr("Do you really want to move all legacy browser integration data to the latest standard?\n" + "This is necessary to maintain compatibility with the browser plugin."), + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel)) { + return; + } + + m_browserService.convertAttributesToCustomData(m_db); +#endif +} + +// Updates the shared key list after the list is cleared +void DatabaseSettingsWidgetBrowser::updateSharedKeyList() +{ + updateModel(); +} + +bool DatabaseSettingsWidgetBrowser::moveSettingsToCustomData(Entry* entry, const QString& name) const +{ +#ifdef WITH_XC_BROWSER + if (entry->attributes()->contains(name)) { + QString attr = entry->attributes()->value(name); + entry->beginUpdate(); + if (!attr.isEmpty()) { + entry->customData()->set(BrowserService::KEEPASSXCBROWSER_NAME, attr); + } + entry->attributes()->remove(name); + entry->endUpdate(); + return true; + } +#endif + return false; +} + +int DatabaseSettingsWidgetBrowser::moveKeysToCustomData(Entry* entry, Database* db) const +{ + int keyCounter = 0; +#ifdef WITH_XC_BROWSER + for (const auto& key : entry->attributes()->keys()) { + if (key.contains(BrowserService::ASSOCIATE_KEY_PREFIX)) { + QString publicKey = key; + publicKey.remove(BrowserService::ASSOCIATE_KEY_PREFIX); + + // Add key to database custom data + if (db && !db->metadata()->customData()->contains(QLatin1String(BrowserService::ASSOCIATE_KEY_PREFIX) + publicKey)) { + db->metadata()->customData()->set(QLatin1String(BrowserService::ASSOCIATE_KEY_PREFIX) + publicKey, entry->attributes()->value(key)); + ++keyCounter; + } + } + } +#endif + return keyCounter; +} diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.h b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.h new file mode 100644 index 0000000000..c5fe160543 --- /dev/null +++ b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSXC_DATABASESETTINGSWIDGETBROWSER_H +#define KEEPASSXC_DATABASESETTINGSWIDGETBROWSER_H + +#include "DatabaseSettingsWidget.h" + +#include +#include +#include +#include +#include "core/CustomData.h" +#include "gui/DatabaseTabWidget.h" +#ifdef WITH_XC_BROWSER +#include "browser/BrowserService.h" +#endif + +class Database; +namespace Ui +{ +class DatabaseSettingsWidgetBrowser; +} + +class DatabaseSettingsWidgetBrowser : public DatabaseSettingsWidget +{ + Q_OBJECT + +public: + explicit DatabaseSettingsWidgetBrowser(QWidget* parent = nullptr); + Q_DISABLE_COPY(DatabaseSettingsWidgetBrowser); + ~DatabaseSettingsWidgetBrowser() override; + + CustomData* customData() const; + inline bool hasAdvancedMode() const override { return false; } + +public slots: + void initialize() override; + void uninitialize() override; + bool save() override; + +private slots: + void removeSelectedKey(); + void toggleRemoveButton(const QItemSelection& selected); + void updateSharedKeyList(); + void removeSharedEncryptionKeys(); + void removeStoredPermissions(); + void convertAttributesToCustomData(); + +private: + void updateModel(); + bool moveSettingsToCustomData(Entry* entry, const QString& name) const; + int moveKeysToCustomData(Entry* entry, Database* db) const; + +protected: + void showEvent(QShowEvent* event) override; + + const QScopedPointer m_ui; + +private: + QPointer m_customData; + QPointer m_customDataModel; +#ifdef WITH_XC_BROWSER + BrowserService m_browserService; +#endif +}; + +#endif //KEEPASSXC_DATABASESETTINGSWIDGETBROWSER_H diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.ui b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.ui new file mode 100644 index 0000000000..00679e032f --- /dev/null +++ b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.ui @@ -0,0 +1,156 @@ + + + DatabaseSettingsWidgetBrowser + + + + 0 + 0 + 453 + 374 + + + + + 0 + 0 + + + + + 450 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + KeePassXC-Browser settings + + + + + + + + + 0 + 0 + + + + &Disconnect all browsers + + + + + + + + 0 + 0 + + + + Forg&et all site-specific settings on entries + + + + + + + + + + + + 0 + 0 + + + + Move KeePassHTTP attributes to KeePassXC-Browser &custom data + + + + + + + + + + + + Stored keys + + + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SelectRows + + + 200 + + + true + + + false + + + + + + + + + Remove + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + +