diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 56726e43d..37fe0b553 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -222,6 +222,7 @@ add_subdirectory(browser) add_subdirectory(proxy) if(WITH_XC_BROWSER) set(keepassxcbrowser_LIB keepassxcbrowser) + set(keepassx_SOURCES ${keepassx_SOURCES} gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp) endif() add_subdirectory(autotype) diff --git a/src/browser/BrowserAction.cpp b/src/browser/BrowserAction.cpp index e0c3a8501..fcbc318ee 100644 --- a/src/browser/BrowserAction.cpp +++ b/src/browser/BrowserAction.cpp @@ -566,15 +566,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 46fb9d04f..5a7c83bf8 100644 --- a/src/browser/BrowserAction.h +++ b/src/browser/BrowserAction.h @@ -54,10 +54,6 @@ public: 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 90d9f107c..47e8762a8 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 a52f53e10..41194e9b6 100644 --- a/src/browser/BrowserOptionDialog.cpp +++ b/src/browser/BrowserOptionDialog.cpp @@ -24,15 +24,12 @@ #include "ui_BrowserOptionDialog.h" #include -#include BrowserOptionDialog::BrowserOptionDialog(QWidget* parent) : QWidget(parent) , m_ui(new Ui::BrowserOptionDialog()) { 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 a48504dfd..5423f010f 100644 --- a/src/browser/BrowserOptionDialog.h +++ b/src/browser/BrowserOptionDialog.h @@ -21,6 +21,7 @@ #define BROWSEROPTIONDIALOG_H #include +#include #include namespace Ui @@ -40,10 +41,6 @@ public slots: void loadSettings(); void saveSettings(); -signals: - void removeSharedEncryptionKeys(); - void removeStoredPermissions(); - private slots: void showProxyLocationFileDialog(); diff --git a/src/browser/BrowserOptionDialog.ui b/src/browser/BrowserOptionDialog.ui index fa5cda367..9a951b33d 100755 --- a/src/browser/BrowserOptionDialog.ui +++ b/src/browser/BrowserOptionDialog.ui @@ -145,7 +145,7 @@ - + Qt::Vertical @@ -215,7 +215,7 @@ - + Qt::Vertical @@ -230,36 +230,6 @@ - - - - - - - 0 - 0 - - - - &Disconnect all browsers - - - - - - - - 0 - 0 - - - - Forget all remembered &permissions - - - - - @@ -376,7 +346,7 @@ - + Qt::Vertical diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index 9a0f07f28..d66637ff5 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -35,10 +35,14 @@ #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[] = "KPXC_BROWSER_"; static const char KEEPASSXCBROWSER_GROUP_NAME[] = "KeePassXC-Browser Passwords"; static int KEEPASSXCBROWSER_DEFAULT_ICON = 1; +// These are for the settings and password conversion +const char BrowserService::LEGACY_ASSOCIATE_KEY_PREFIX[] = "Public Key: "; +static const char KEEPASSHTTP_NAME[] = "KeePassHttp Settings"; +static const char KEEPASSHTTP_GROUP_NAME[] = "KeePassHttp Passwords"; BrowserService::BrowserService(DatabaseTabWidget* parent) : m_dbTabWidget(parent) @@ -46,12 +50,15 @@ BrowserService::BrowserService(DatabaseTabWidget* parent) , m_bringToFrontRequested(false) , m_keepassBrowserUUID(QUuid::fromRfc4122(QByteArray::fromHex("de887cc3036343b8974b5911b8816224"))) { - connect(m_dbTabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), this, SLOT(databaseLocked(DatabaseWidget*))); - connect(m_dbTabWidget, SIGNAL(databaseUnlocked(DatabaseWidget*)), this, SLOT(databaseUnlocked(DatabaseWidget*))); - connect(m_dbTabWidget, - SIGNAL(activateDatabaseChanged(DatabaseWidget*)), - this, - SLOT(activateDatabaseChanged(DatabaseWidget*))); + // Don't connect the signals when used from DatabaseSettingsWidgetBrowser (parent is nullptr) + if (m_dbTabWidget) { + connect(m_dbTabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), this, SLOT(databaseLocked(DatabaseWidget*))); + connect(m_dbTabWidget, SIGNAL(databaseUnlocked(DatabaseWidget*)), this, SLOT(databaseUnlocked(DatabaseWidget*))); + connect(m_dbTabWidget, + SIGNAL(activateDatabaseChanged(DatabaseWidget*)), + this, + SLOT(activateDatabaseChanged(DatabaseWidget*))); + } } bool BrowserService::isDatabaseOpened() const @@ -107,12 +114,12 @@ QString BrowserService::getDatabaseRootUuid() { Database* db = getDatabase(); if (!db) { - return QString(); + return {}; } Group* rootGroup = db->rootGroup(); if (!rootGroup) { - return QString(); + return {}; } return rootGroup->uuidToHex(); @@ -122,46 +129,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; @@ -172,8 +149,8 @@ QString BrowserService::storeKey(const QString& key) return id; } - Entry* config = getConfigEntry(true); - if (!config) { + Database* db = getDatabase(); + if (!db) { return {}; } @@ -199,7 +176,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?"), @@ -210,18 +187,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, @@ -445,12 +422,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; } } } @@ -472,102 +446,70 @@ QList BrowserService::searchEntries(const QString& url, const StringPair return entries; } -void BrowserService::removeSharedEncryptionKeys() +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; - } - - 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() -{ - 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) @@ -873,6 +815,75 @@ Database* BrowserService::selectedDatabase() return getDatabase(); } +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(LEGACY_ASSOCIATE_KEY_PREFIX)) { + QString publicKey = key; + publicKey.remove(LEGACY_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) { @@ -888,6 +899,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 165901a87..482bf99cb 100644 --- a/src/browser/BrowserService.h +++ b/src/browser/BrowserService.h @@ -55,8 +55,12 @@ public: Database* selectedDb = nullptr); 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[]; + static const char LEGACY_ASSOCIATE_KEY_PREFIX[]; public slots: QJsonArray findMatchingEntries(const QString& id, @@ -106,6 +110,9 @@ private: QString baseDomain(const QString& url) const; Database* getDatabase(); Database* selectedDatabase(); + 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 fc35bbec5..03f742020 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 fde8f051c..869af9e24 100644 --- a/src/browser/NativeMessagingHost.h +++ b/src/browser/NativeMessagingHost.h @@ -37,10 +37,6 @@ public: 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 bee57516e..08068202d 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -84,12 +84,6 @@ public: 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())); return dlg; } @@ -108,8 +102,8 @@ public: } } -private: - QSharedPointer m_nativeMessagingHost; + private: + QSharedPointer m_nativeMessagingHost; }; #endif diff --git a/src/gui/dbsettings/DatabaseSettingsDialog.cpp b/src/gui/dbsettings/DatabaseSettingsDialog.cpp index 8b28ced04..7dd96030c 100644 --- a/src/gui/dbsettings/DatabaseSettingsDialog.cpp +++ b/src/gui/dbsettings/DatabaseSettingsDialog.cpp @@ -21,6 +21,9 @@ #include "DatabaseSettingsWidgetGeneral.h" #include "DatabaseSettingsWidgetEncryption.h" #include "DatabaseSettingsWidgetMasterKey.h" +#ifdef WITH_XC_BROWSER +#include "DatabaseSettingsWidgetBrowser.h" +#endif #include "core/Config.h" #include "core/FilePath.h" @@ -34,6 +37,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 +61,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 +79,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 50fec32d6..2dd457cd0 100644 --- a/src/gui/dbsettings/DatabaseSettingsDialog.h +++ b/src/gui/dbsettings/DatabaseSettingsDialog.h @@ -19,6 +19,7 @@ #define KEEPASSX_DATABASESETTINGSWIDGET_H #include "gui/DialogyWidget.h" +#include "config-keepassx.h" #include #include @@ -27,6 +28,9 @@ class Database; class DatabaseSettingsWidgetGeneral; class DatabaseSettingsWidgetEncryption; class DatabaseSettingsWidgetMasterKey; +#ifdef WITH_XC_BROWSER +class DatabaseSettingsWidgetBrowser; +#endif class QTabWidget; namespace Ui @@ -68,6 +72,9 @@ private: QPointer m_securityTabWidget; QPointer m_masterKeyWidget; QPointer m_encryptionWidget; +#ifdef WITH_XC_BROWSER + QPointer m_browserWidget; +#endif }; #endif // KEEPASSX_DATABASESETTINGSWIDGET_H diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp new file mode 100644 index 000000000..0dae1486d --- /dev/null +++ b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * Copyright (C) 2018 Sami Vänttinen + * + * 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 +#include "core/Clock.h" +#include "core/Database.h" +#include "core/Entry.h" +#include "core/Group.h" +#include "core/Metadata.h" +#include "gui/MessageBox.h" +#include "browser/BrowserSettings.h" +#include "ui_DatabaseSettingsWidgetBrowser.h" + +DatabaseSettingsWidgetBrowser::DatabaseSettingsWidgetBrowser(QWidget* parent) + : DatabaseSettingsWidget(parent), m_ui(new Ui::DatabaseSettingsWidgetBrowser()) + , m_customData(new CustomData(this)) + , m_customDataModel(new QStandardItemModel(this)) + , m_browserService(nullptr) +{ + m_ui->setupUi(this); + m_ui->removeCustomDataButton->setEnabled(false); + m_ui->customDataTable->setModel(m_customDataModel); + + settingsWarning(); + 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(); + settingsWarning(); +} + +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)) { + QString key = index.data().toString(); + key.insert(0, BrowserService::ASSOCIATE_KEY_PREFIX); + 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()) { + if (key.startsWith(BrowserService::ASSOCIATE_KEY_PREFIX)) { + QString strippedKey = key; + strippedKey.remove(BrowserService::ASSOCIATE_KEY_PREFIX); + m_customDataModel->appendRow(QList() + << new QStandardItem(strippedKey) + << new QStandardItem(customData()->value(key))); + } + } + + m_ui->removeCustomDataButton->setEnabled(false); +} + +void DatabaseSettingsWidgetBrowser::settingsWarning() +{ + if (!browserSettings()->isEnabled()) { + m_ui->convertToCustomData->setEnabled(false); + m_ui->removeSharedEncryptionKeys->setEnabled(false); + m_ui->removeStoredPermissions->setEnabled(false); + m_ui->customDataTable->setEnabled(false); + m_ui->warningWidget->showMessage(tr("Enable Browser Integration to access these settings."), MessageWidget::Warning); + m_ui->warningWidget->setCloseButtonVisible(false); + m_ui->warningWidget->setAutoHideTimeout(-1); + } else { + m_ui->convertToCustomData->setEnabled(true); + m_ui->removeSharedEncryptionKeys->setEnabled(true); + m_ui->removeStoredPermissions->setEnabled(true); + m_ui->customDataTable->setEnabled(true); + m_ui->warningWidget->hideMessage(); + } +} + +void DatabaseSettingsWidgetBrowser::removeSharedEncryptionKeys() +{ + 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(this, + 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(this, + tr("KeePassXC: Removed keys from database"), + tr("Successfully removed %n encryption key(s) from KeePassXC settings.", "", count), + QMessageBox::Ok); +} + +void DatabaseSettingsWidgetBrowser::removeStoredPermissions() +{ + 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(this, + tr("KeePassXC: Removed permissions"), + tr("Successfully removed permissions from %n entry(s).", "", counter), + QMessageBox::Ok); + } else { + QMessageBox::information(this, + tr("KeePassXC: No entry with permissions found!"), + tr("The active database does not contain an entry with permissions."), + QMessageBox::Ok); + } +} + +void DatabaseSettingsWidgetBrowser::convertAttributesToCustomData() +{ + 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); +} + +// Updates the shared key list after the list is cleared +void DatabaseSettingsWidgetBrowser::updateSharedKeyList() +{ + updateModel(); +} + diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.h b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.h new file mode 100644 index 000000000..0c56ede57 --- /dev/null +++ b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * Copyright (C) 2018 Sami Vänttinen + * + * 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" +#include "browser/BrowserService.h" + +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(); + void settingsWarning(); + +protected: + void showEvent(QShowEvent* event) override; + + const QScopedPointer m_ui; + +private: + QPointer m_customData; + QPointer m_customDataModel; + BrowserService m_browserService; +}; + +#endif //KEEPASSXC_DATABASESETTINGSWIDGETBROWSER_H diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.ui b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.ui new file mode 100644 index 000000000..dfd814bdc --- /dev/null +++ b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.ui @@ -0,0 +1,174 @@ + + + DatabaseSettingsWidgetBrowser + + + + 0 + 0 + 453 + 374 + + + + + 0 + 0 + + + + + 450 + 0 + + + + + 0 + + + 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 + + + + + + + + + + + + + + + + + MessageWidget + QWidget +
gui/MessageWidget.h
+ 1 +
+
+ + +