Browser connection keys and rules are stored in custom data instead of attributes (#1497)

This commit is contained in:
Sami Vänttinen 2018-10-24 17:49:53 +03:00 committed by Jonathan White
parent cfa1eca249
commit efdb43dc53
17 changed files with 680 additions and 213 deletions

View File

@ -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)

View File

@ -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();
}

View File

@ -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);

View File

@ -21,7 +21,7 @@
#include "core/EntryAttributes.h"
#include <QtCore>
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);
}

View File

@ -24,15 +24,12 @@
#include "ui_BrowserOptionDialog.h"
#include <QFileDialog>
#include <QMessageBox>
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. <br />Download it for %1 and %2.").arg(

View File

@ -21,6 +21,7 @@
#define BROWSEROPTIONDIALOG_H
#include <QScopedPointer>
#include <QPointer>
#include <QWidget>
namespace Ui
@ -40,10 +41,6 @@ public slots:
void loadSettings();
void saveSettings();
signals:
void removeSharedEncryptionKeys();
void removeStoredPermissions();
private slots:
void showProxyLocationFileDialog();

View File

@ -145,7 +145,7 @@
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
@ -215,7 +215,7 @@
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
@ -230,36 +230,6 @@
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QPushButton" name="removeSharedEncryptionKeys">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&amp;Disconnect all browsers</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="removeStoredPermissions">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Forget all remembered &amp;permissions</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
@ -376,7 +346,7 @@
</layout>
</item>
<item>
<spacer name="verticalSpacer_4">
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>

View File

@ -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<Entry*> 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<Entry*> 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<Entry*> 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<Entry*> BrowserService::sortEntries(QList<Entry*>& 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<Entry*> 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();
}
}
}

View File

@ -55,8 +55,12 @@ public:
Database* selectedDb = nullptr);
QList<Entry*> searchEntries(Database* db, const QString& hostname, const QString& url);
QList<Entry*> 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;

View File

@ -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;

View File

@ -37,10 +37,6 @@ public:
void run();
void stop();
public slots:
void removeSharedEncryptionKeys();
void removeStoredPermissions();
signals:
void quit();

View File

@ -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<NativeMessagingHost> m_nativeMessagingHost;
private:
QSharedPointer<NativeMessagingHost> m_nativeMessagingHost;
};
#endif

View File

@ -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;
}

View File

@ -19,6 +19,7 @@
#define KEEPASSX_DATABASESETTINGSWIDGET_H
#include "gui/DialogyWidget.h"
#include "config-keepassx.h"
#include <QScopedPointer>
#include <QPointer>
@ -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<QTabWidget> m_securityTabWidget;
QPointer<DatabaseSettingsWidgetMasterKey> m_masterKeyWidget;
QPointer<DatabaseSettingsWidgetEncryption> m_encryptionWidget;
#ifdef WITH_XC_BROWSER
QPointer<DatabaseSettingsWidgetBrowser> m_browserWidget;
#endif
};
#endif // KEEPASSX_DATABASESETTINGSWIDGET_H

View File

@ -0,0 +1,245 @@
/*
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2018 Sami Vänttinen <sami.vanttinen@protonmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "DatabaseSettingsWidgetBrowser.h"
#include <QProgressDialog>
#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<QStandardItem*>()
<< 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<Entry*> 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();
}

View File

@ -0,0 +1,78 @@
/*
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2018 Sami Vänttinen <sami.vanttinen@protonmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef KEEPASSXC_DATABASESETTINGSWIDGETBROWSER_H
#define KEEPASSXC_DATABASESETTINGSWIDGETBROWSER_H
#include "DatabaseSettingsWidget.h"
#include <QPointer>
#include <QScopedPointer>
#include <QStandardItemModel>
#include <QItemSelection>
#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<Ui::DatabaseSettingsWidgetBrowser> m_ui;
private:
QPointer<CustomData> m_customData;
QPointer<QStandardItemModel> m_customDataModel;
BrowserService m_browserService;
};
#endif //KEEPASSXC_DATABASESETTINGSWIDGETBROWSER_H

View File

@ -0,0 +1,174 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DatabaseSettingsWidgetBrowser</class>
<widget class="QWidget" name="DatabaseSettingsWidgetBrowser">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>453</width>
<height>374</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>450</width>
<height>0</height>
</size>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="MessageWidget" name="warningWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>KeePassXC-Browser settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="removeSharedEncryptionKeys">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&amp;Disconnect all browsers</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="removeStoredPermissions">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Forg&amp;et all site-specific settings on entries</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="convertToCustomData">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Move KeePassHTTP attributes to KeePassXC-Browser &amp;custom data</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Stored keys</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QGroupBox" name="groupBox">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QTableView" name="customDataTable">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>200</number>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QPushButton" name="removeCustomDataButton">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>MessageWidget</class>
<extends>QWidget</extends>
<header>gui/MessageWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>