Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/keepassxreboot/keepassxc.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvarjolintu <sami.vanttinen@protonmail.com>2022-04-24 10:39:12 +0300
committerJonathan White <support@dmapps.us>2022-09-10 20:18:30 +0300
commitef6d8f113876d794803583962e74972aa9986bd5 (patch)
tree30df0e471507f9f01752169a8fcecf654f407e8b
parent612c1098eaa9cfa54fe4dbe9c6e11d3e2e18fdae (diff)
Browser: Asynchronous Access Confirm dialog
-rw-r--r--src/browser/BrowserAccessControlDialog.cpp53
-rw-r--r--src/browser/BrowserAccessControlDialog.h21
-rwxr-xr-xsrc/browser/BrowserAccessControlDialog.ui2
-rw-r--r--src/browser/BrowserAction.cpp37
-rw-r--r--src/browser/BrowserAction.h2
-rw-r--r--src/browser/BrowserService.cpp397
-rw-r--r--src/browser/BrowserService.h66
7 files changed, 385 insertions, 193 deletions
diff --git a/src/browser/BrowserAccessControlDialog.cpp b/src/browser/BrowserAccessControlDialog.cpp
index 85c940254..f55ea8b83 100644
--- a/src/browser/BrowserAccessControlDialog.cpp
+++ b/src/browser/BrowserAccessControlDialog.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2013 Francois Ferrand
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
+ * Copyright (C) 2022 KeePassXC Team <team@keepassxc.org>
*
* 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
@@ -21,25 +21,41 @@
#include <QUrl>
#include "core/Entry.h"
+#include <QCloseEvent>
BrowserAccessControlDialog::BrowserAccessControlDialog(QWidget* parent)
: QDialog(parent)
, m_ui(new Ui::BrowserAccessControlDialog())
+ , m_entriesAccepted(false)
{
setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
m_ui->setupUi(this);
- connect(m_ui->allowButton, SIGNAL(clicked()), SLOT(accept()));
- connect(m_ui->cancelButton, SIGNAL(clicked()), SLOT(reject()));
+ connect(m_ui->allowButton, SIGNAL(clicked()), SLOT(acceptSelections()));
+ connect(m_ui->denyButton, SIGNAL(clicked()), SLOT(rejectSelections()));
+ connect(this, SIGNAL(rejected()), this, SIGNAL(closed()));
}
BrowserAccessControlDialog::~BrowserAccessControlDialog()
{
}
-void BrowserAccessControlDialog::setItems(const QList<Entry*>& items, const QString& urlString, bool httpAuth)
+void BrowserAccessControlDialog::closeEvent(QCloseEvent* event)
{
+ // Emits closed signal when clicking X from title bar
+ emit closed();
+ event->accept();
+}
+
+void BrowserAccessControlDialog::setItems(const QList<Entry*>& entriesToConfirm,
+ const QList<Entry*>& allowedEntries,
+ const QString& urlString,
+ bool httpAuth)
+{
+ m_entriesToConfirm = entriesToConfirm;
+ m_allowedEntries = allowedEntries;
+
QUrl url(urlString);
m_ui->siteLabel->setText(m_ui->siteLabel->text().arg(
url.toDisplayString(QUrl::RemoveUserInfo | QUrl::RemovePath | QUrl::RemoveQuery | QUrl::RemoveFragment)));
@@ -47,11 +63,11 @@ void BrowserAccessControlDialog::setItems(const QList<Entry*>& items, const QStr
m_ui->rememberDecisionCheckBox->setVisible(!httpAuth);
m_ui->rememberDecisionCheckBox->setChecked(false);
- m_ui->itemsTable->setRowCount(items.count());
+ m_ui->itemsTable->setRowCount(entriesToConfirm.count());
m_ui->itemsTable->setColumnCount(2);
int row = 0;
- for (const auto& entry : items) {
+ for (const auto& entry : entriesToConfirm) {
auto item = new QTableWidgetItem();
item->setText(entry->title() + " - " + entry->username());
item->setData(Qt::UserRole, row);
@@ -61,11 +77,13 @@ void BrowserAccessControlDialog::setItems(const QList<Entry*>& items, const QStr
auto disableButton = new QPushButton(tr("Disable for this site"));
disableButton->setAutoDefault(false);
+
connect(disableButton, &QAbstractButton::pressed, [&, item] {
emit disableAccess(item);
m_ui->itemsTable->removeRow(item->row());
+
if (m_ui->itemsTable->rowCount() == 0) {
- reject();
+ emit closed();
}
});
m_ui->itemsTable->setCellWidget(row, 1, disableButton);
@@ -84,6 +102,11 @@ bool BrowserAccessControlDialog::remember() const
return m_ui->rememberDecisionCheckBox->isChecked();
}
+bool BrowserAccessControlDialog::entriesAccepted() const
+{
+ return m_entriesAccepted;
+}
+
QList<QTableWidgetItem*> BrowserAccessControlDialog::getSelectedEntries() const
{
QList<QTableWidgetItem*> selected;
@@ -107,3 +130,19 @@ QList<QTableWidgetItem*> BrowserAccessControlDialog::getNonSelectedEntries() con
}
return notSelected;
}
+
+void BrowserAccessControlDialog::acceptSelections()
+{
+ auto selectedEntries = getSelectedEntries();
+
+ m_entriesAccepted = true;
+ emit acceptEntries(selectedEntries, m_entriesToConfirm, m_allowedEntries);
+ emit closed();
+}
+
+void BrowserAccessControlDialog::rejectSelections()
+{
+ auto rejectedEntries = getNonSelectedEntries();
+ emit rejectEntries(rejectedEntries, m_entriesToConfirm);
+ emit closed();
+}
diff --git a/src/browser/BrowserAccessControlDialog.h b/src/browser/BrowserAccessControlDialog.h
index 0bf14ecc4..c41c6c77e 100644
--- a/src/browser/BrowserAccessControlDialog.h
+++ b/src/browser/BrowserAccessControlDialog.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2013 Francois Ferrand
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
+ * Copyright (C) 2022 KeePassXC Team <team@keepassxc.org>
*
* 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
@@ -37,17 +37,34 @@ public:
explicit BrowserAccessControlDialog(QWidget* parent = nullptr);
~BrowserAccessControlDialog() override;
- void setItems(const QList<Entry*>& items, const QString& urlString, bool httpAuth);
+ void setItems(const QList<Entry*>& entriesToConfirm,
+ const QList<Entry*>& allowedEntries,
+ const QString& urlString,
+ bool httpAuth);
bool remember() const;
+ bool entriesAccepted() const;
QList<QTableWidgetItem*> getSelectedEntries() const;
QList<QTableWidgetItem*> getNonSelectedEntries() const;
signals:
void disableAccess(QTableWidgetItem* item);
+ void acceptEntries(QList<QTableWidgetItem*> items, QList<Entry*> entriesToConfirm, QList<Entry*> allowedEntries);
+ void rejectEntries(QList<QTableWidgetItem*> items, QList<Entry*> entriesToConfirm);
+ void closed();
+
+public slots:
+ void acceptSelections();
+ void rejectSelections();
+
+private:
+ void closeEvent(QCloseEvent* event) override;
private:
QScopedPointer<Ui::BrowserAccessControlDialog> m_ui;
+ QList<Entry*> m_entriesToConfirm;
+ QList<Entry*> m_allowedEntries;
+ bool m_entriesAccepted;
};
#endif // BROWSERACCESSCONTROLDIALOG_H
diff --git a/src/browser/BrowserAccessControlDialog.ui b/src/browser/BrowserAccessControlDialog.ui
index bed26e6ab..4224c1633 100755
--- a/src/browser/BrowserAccessControlDialog.ui
+++ b/src/browser/BrowserAccessControlDialog.ui
@@ -97,7 +97,7 @@
</widget>
</item>
<item>
- <widget class="QPushButton" name="cancelButton">
+ <widget class="QPushButton" name="denyButton">
<property name="text">
<string>Deny All</string>
</property>
diff --git a/src/browser/BrowserAction.cpp b/src/browser/BrowserAction.cpp
index 70a0fb588..1d789d470 100644
--- a/src/browser/BrowserAction.cpp
+++ b/src/browser/BrowserAction.cpp
@@ -76,7 +76,7 @@ QJsonObject BrowserAction::handleAction(QLocalSocket* socket, const QJsonObject&
} else if (action.compare("test-associate") == 0) {
return handleTestAssociate(json, action);
} else if (action.compare("get-logins") == 0) {
- return handleGetLogins(json, action);
+ return handleGetLogins(socket, json, action);
} else if (action.compare("generate-password") == 0) {
return handleGeneratePassword(socket, json, action);
} else if (action.compare("set-login") == 0) {
@@ -231,10 +231,11 @@ QJsonObject BrowserAction::handleTestAssociate(const QJsonObject& json, const QS
return buildResponse(action, message, newNonce);
}
-QJsonObject BrowserAction::handleGetLogins(const QJsonObject& json, const QString& action)
+QJsonObject BrowserAction::handleGetLogins(QLocalSocket* socket, const QJsonObject& json, const QString& action)
{
const QString hash = browserService()->getDatabaseHash();
const QString nonce = json.value("nonce").toString();
+ const auto incrementedNonce = browserMessageBuilder()->incrementNonce(nonce);
const QString encrypted = json.value("message").toString();
if (!m_associated) {
@@ -263,21 +264,31 @@ QJsonObject BrowserAction::handleGetLogins(const QJsonObject& json, const QStrin
const QString formUrl = decrypted.value("submitUrl").toString();
const QString auth = decrypted.value("httpAuth").toString();
const bool httpAuth = auth.compare(TRUE_STR) == 0;
- const QJsonArray users = browserService()->findMatchingEntries(id, siteUrl, formUrl, "", keyList, httpAuth);
+ auto requestId = decrypted.value("requestID").toString();
- if (users.isEmpty()) {
- return getErrorReply(action, ERROR_KEEPASS_NO_LOGINS_FOUND);
- }
+ if (browserService()->isAccessConfirmRequested()) {
+ auto errorReply = getErrorReply(action, ERROR_KEEPASS_ACTION_CANCELLED_OR_DENIED);
- const QString newNonce = browserMessageBuilder()->incrementNonce(nonce);
+ if (!requestId.isEmpty()) {
+ errorReply["requestID"] = requestId;
+ }
- QJsonObject message = browserMessageBuilder()->buildMessage(newNonce);
- message["count"] = users.count();
- message["entries"] = users;
- message["hash"] = hash;
- message["id"] = id;
+ return errorReply;
+ }
- return buildResponse(action, message, newNonce);
+ browserService()->findEntries(socket,
+ incrementedNonce,
+ m_clientPublicKey,
+ m_secretKey,
+ id,
+ hash,
+ requestId,
+ siteUrl,
+ formUrl,
+ "",
+ keyList,
+ httpAuth);
+ return QJsonObject();
}
QJsonObject BrowserAction::handleGeneratePassword(QLocalSocket* socket, const QJsonObject& json, const QString& action)
diff --git a/src/browser/BrowserAction.h b/src/browser/BrowserAction.h
index 49c66b644..6f0b9a6cf 100644
--- a/src/browser/BrowserAction.h
+++ b/src/browser/BrowserAction.h
@@ -37,7 +37,7 @@ private:
QJsonObject handleGetDatabaseHash(const QJsonObject& json, const QString& action);
QJsonObject handleAssociate(const QJsonObject& json, const QString& action);
QJsonObject handleTestAssociate(const QJsonObject& json, const QString& action);
- QJsonObject handleGetLogins(const QJsonObject& json, const QString& action);
+ QJsonObject handleGetLogins(QLocalSocket* socket, const QJsonObject& json, const QString& action);
QJsonObject handleGeneratePassword(QLocalSocket* socket, const QJsonObject& json, const QString& action);
QJsonObject handleSetLogin(const QJsonObject& json, const QString& action);
QJsonObject handleLockDatabase(const QJsonObject& json, const QString& action);
diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp
index 38601de5b..cee32bb08 100644
--- a/src/browser/BrowserService.cpp
+++ b/src/browser/BrowserService.cpp
@@ -18,7 +18,6 @@
*/
#include "BrowserService.h"
-#include "BrowserAccessControlDialog.h"
#include "BrowserAction.h"
#include "BrowserEntryConfig.h"
#include "BrowserEntrySaveDialog.h"
@@ -65,9 +64,9 @@ Q_GLOBAL_STATIC(BrowserService, s_browserService);
BrowserService::BrowserService()
: QObject()
, m_browserHost(new BrowserHost)
- , m_dialogActive(false)
, m_bringToFrontRequested(false)
, m_passwordGeneratorRequested(false)
+ , m_accessConfirmRequested(false)
, m_prevWindowState(WindowState::Normal)
, m_keepassBrowserUUID(Tools::hexToUuid("de887cc3036343b8974b5911b8816224"))
{
@@ -315,6 +314,219 @@ QString BrowserService::getCurrentTotp(const QString& uuid)
return {};
}
+void BrowserService::findEntries(QLocalSocket* socket,
+ const QString& nonce,
+ const QString& publicKey,
+ const QString& secretKey,
+ const QString& dbid,
+ const QString& hash,
+ const QString& requestId,
+ const QString& siteUrl,
+ const QString& formUrl,
+ const QString& realm,
+ const StringPairList& keyList,
+ const bool httpAuth)
+{
+ Q_UNUSED(dbid);
+ const bool alwaysAllowAccess = browserSettings()->alwaysAllowAccess();
+ const bool ignoreHttpAuth = browserSettings()->httpAuthPermission();
+ const QString siteHost = QUrl(siteUrl).host();
+ const QString formHost = QUrl(formUrl).host();
+
+ // Check entries for authorization
+ QList<Entry*> entriesToConfirm;
+ QList<Entry*> allowedEntries;
+ for (auto* entry : searchEntries(siteUrl, formUrl, keyList)) {
+ auto entryCustomData = entry->customData();
+
+ if (!httpAuth
+ && ((entryCustomData->contains(BrowserService::OPTION_ONLY_HTTP_AUTH)
+ && entryCustomData->value(BrowserService::OPTION_ONLY_HTTP_AUTH) == TRUE_STR)
+ || entry->group()->resolveCustomDataTriState(BrowserService::OPTION_ONLY_HTTP_AUTH) == Group::Enable)) {
+ continue;
+ }
+
+ if (httpAuth
+ && ((entryCustomData->contains(BrowserService::OPTION_NOT_HTTP_AUTH)
+ && entryCustomData->value(BrowserService::OPTION_NOT_HTTP_AUTH) == TRUE_STR)
+ || entry->group()->resolveCustomDataTriState(BrowserService::OPTION_NOT_HTTP_AUTH) == Group::Enable)) {
+ continue;
+ }
+
+ // HTTP Basic Auth always needs a confirmation
+ if (!ignoreHttpAuth && httpAuth) {
+ entriesToConfirm.append(entry);
+ continue;
+ }
+
+ switch (checkAccess(entry, siteHost, formHost, realm)) {
+ case Denied:
+ continue;
+
+ case Unknown:
+ if (alwaysAllowAccess) {
+ allowedEntries.append(entry);
+ } else {
+ entriesToConfirm.append(entry);
+ }
+ break;
+
+ case Allowed:
+ allowedEntries.append(entry);
+ break;
+ }
+ }
+
+ if (entriesToConfirm.isEmpty()) {
+ sendCredentialsToClient(allowedEntries, socket, nonce, publicKey, secretKey, hash, dbid, siteUrl, formUrl);
+ return;
+ }
+
+ confirmEntries(socket,
+ nonce,
+ publicKey,
+ secretKey,
+ dbid,
+ hash,
+ requestId,
+ allowedEntries,
+ entriesToConfirm,
+ siteUrl,
+ siteHost,
+ formHost,
+ realm,
+ httpAuth);
+}
+
+void BrowserService::confirmEntries(QLocalSocket* socket,
+ const QString& incrementedNonce,
+ const QString& publicKey,
+ const QString& secretKey,
+ const QString& id,
+ const QString& hash,
+ const QString& requestId,
+ QList<Entry*>& allowedEntries,
+ QList<Entry*>& entriesToConfirm,
+ const QString& siteUrl,
+ const QString& siteHost,
+ const QString& formUrl,
+ const QString& realm,
+ const bool httpAuth)
+{
+ if (entriesToConfirm.isEmpty() || m_accessConfirmRequested) {
+ return;
+ }
+
+ if (!m_accessControlDialog) {
+
+ m_accessControlDialog.reset(new BrowserAccessControlDialog());
+
+ connect(
+ m_currentDatabaseWidget, SIGNAL(databaseLockRequested()), m_accessControlDialog.data(), SIGNAL(closed()));
+
+ connect(m_accessControlDialog.data(),
+ &BrowserAccessControlDialog::disableAccess,
+ m_accessControlDialog.data(),
+ [=](QTableWidgetItem* item) {
+ auto entry = entriesToConfirm[item->row()];
+ denyEntry(entry, siteHost, formUrl, realm);
+ });
+
+ connect(m_accessControlDialog.data(), &BrowserAccessControlDialog::closed, m_accessControlDialog.data(), [=] {
+ if (!m_accessControlDialog->entriesAccepted()) {
+ auto errorMessage =
+ browserMessageBuilder()->getErrorReply("get-logins", ERROR_KEEPASS_ACTION_CANCELLED_OR_DENIED);
+ errorMessage["requestID"] = requestId;
+ m_browserHost->sendClientMessage(socket, errorMessage);
+ }
+
+ m_accessControlDialog.reset();
+ hideWindow();
+ m_accessConfirmRequested = false;
+ });
+
+ connect(m_accessControlDialog.data(),
+ &BrowserAccessControlDialog::acceptEntries,
+ m_accessControlDialog.data(),
+ [=](QList<QTableWidgetItem*> items, QList<Entry*> entries, QList<Entry*> allowed) {
+ QList<Entry*> selectedEntries;
+
+ for (auto item : items) {
+ auto entry = entries[item->row()];
+ if (m_accessControlDialog->remember()) {
+ allowEntry(entry, siteHost, formUrl, realm);
+ }
+
+ selectedEntries.append(entry);
+ }
+
+ hideWindow();
+
+ if (!selectedEntries.isEmpty()) {
+ allowed.append(selectedEntries);
+ }
+
+ if (allowed.isEmpty()) {
+ return;
+ }
+
+ // Ensure that database is not locked when the popup was visible
+ if (!isDatabaseOpened()) {
+ return;
+ }
+
+ sendCredentialsToClient(
+ allowed, socket, incrementedNonce, publicKey, secretKey, hash, id, siteUrl, formUrl);
+ });
+
+ connect(m_accessControlDialog.data(),
+ &BrowserAccessControlDialog::rejectEntries,
+ m_accessControlDialog.data(),
+ [=](QList<QTableWidgetItem*> items, QList<Entry*> entries) {
+ Q_UNUSED(items); // We might need this later if single entries can be denied from the list
+ for (auto entry : entries) {
+ if (m_accessControlDialog->remember()) {
+ denyEntry(entry, siteHost, formUrl, realm);
+ }
+ }
+ });
+
+ m_accessControlDialog->setItems(entriesToConfirm, allowedEntries, siteUrl, httpAuth);
+ m_accessControlDialog->show();
+ }
+
+ m_accessConfirmRequested = true;
+ updateWindowState();
+}
+
+void BrowserService::sendCredentialsToClient(QList<Entry*>& allowedEntries,
+ QLocalSocket* socket,
+ const QString& incrementedNonce,
+ const QString& publicKey,
+ const QString& secretKey,
+ const QString& hash,
+ const QString& id,
+ const QString siteUrl,
+ const QString& formUrl)
+{
+ allowedEntries = sortEntries(allowedEntries, siteUrl, formUrl);
+
+ QJsonArray result;
+ for (auto* entry : allowedEntries) {
+ result.append(prepareEntry(entry));
+ }
+
+ QJsonObject message = browserMessageBuilder()->buildMessage(incrementedNonce);
+ message["count"] = result.count();
+ message["entries"] = result;
+ message["hash"] = hash;
+ message["id"] = id;
+
+ m_browserHost->sendClientMessage(
+ socket, browserMessageBuilder()->buildResponse("get-logins", message, incrementedNonce, publicKey, secretKey));
+ hideWindow();
+}
+
void BrowserService::showPasswordGenerator(QLocalSocket* socket,
const QString& incrementedNonce,
const QString& publicKey,
@@ -341,9 +553,11 @@ void BrowserService::showPasswordGenerator(QLocalSocket* socket,
[=](const QString& password) {
QJsonObject message = browserMessageBuilder()->buildMessage(incrementedNonce);
message["password"] = password;
- sendPassword(socket,
- browserMessageBuilder()->buildResponse(
- "generate-password", message, incrementedNonce, publicKey, secretKey));
+ m_browserHost->sendClientMessage(
+ socket,
+ browserMessageBuilder()->buildResponse(
+ "generate-password", message, incrementedNonce, publicKey, secretKey));
+ hideWindow();
});
}
@@ -353,15 +567,14 @@ void BrowserService::showPasswordGenerator(QLocalSocket* socket,
m_passwordGenerator->activateWindow();
}
-void BrowserService::sendPassword(QLocalSocket* socket, const QJsonObject& message)
+bool BrowserService::isPasswordGeneratorRequested() const
{
- m_browserHost->sendClientMessage(socket, message);
- hideWindow();
+ return m_passwordGeneratorRequested;
}
-bool BrowserService::isPasswordGeneratorRequested() const
+bool BrowserService::isAccessConfirmRequested() const
{
- return m_passwordGeneratorRequested;
+ return m_accessConfirmRequested;
}
QString BrowserService::storeKey(const QString& key)
@@ -426,91 +639,6 @@ QString BrowserService::getKey(const QString& id)
return db->metadata()->customData()->value(CustomData::BrowserKeyPrefix + id);
}
-QJsonArray BrowserService::findMatchingEntries(const QString& dbid,
- const QString& siteUrl,
- const QString& formUrl,
- const QString& realm,
- const StringPairList& keyList,
- const bool httpAuth)
-{
- Q_UNUSED(dbid);
- const bool alwaysAllowAccess = browserSettings()->alwaysAllowAccess();
- const bool ignoreHttpAuth = browserSettings()->httpAuthPermission();
- const QString siteHost = QUrl(siteUrl).host();
- const QString formHost = QUrl(formUrl).host();
-
- // Check entries for authorization
- QList<Entry*> pwEntriesToConfirm;
- QList<Entry*> pwEntries;
- for (auto* entry : searchEntries(siteUrl, formUrl, keyList)) {
- auto entryCustomData = entry->customData();
-
- if (!httpAuth
- && ((entryCustomData->contains(BrowserService::OPTION_ONLY_HTTP_AUTH)
- && entryCustomData->value(BrowserService::OPTION_ONLY_HTTP_AUTH) == TRUE_STR)
- || entry->group()->resolveCustomDataTriState(BrowserService::OPTION_ONLY_HTTP_AUTH) == Group::Enable)) {
- continue;
- }
-
- if (httpAuth
- && ((entryCustomData->contains(BrowserService::OPTION_NOT_HTTP_AUTH)
- && entryCustomData->value(BrowserService::OPTION_NOT_HTTP_AUTH) == TRUE_STR)
- || entry->group()->resolveCustomDataTriState(BrowserService::OPTION_NOT_HTTP_AUTH) == Group::Enable)) {
- continue;
- }
-
- // HTTP Basic Auth always needs a confirmation
- if (!ignoreHttpAuth && httpAuth) {
- pwEntriesToConfirm.append(entry);
- continue;
- }
-
- switch (checkAccess(entry, siteHost, formHost, realm)) {
- case Denied:
- continue;
-
- case Unknown:
- if (alwaysAllowAccess) {
- pwEntries.append(entry);
- } else {
- pwEntriesToConfirm.append(entry);
- }
- break;
-
- case Allowed:
- pwEntries.append(entry);
- break;
- }
- }
-
- // Confirm entries
- QList<Entry*> selectedEntriesToConfirm =
- confirmEntries(pwEntriesToConfirm, siteUrl, siteHost, formHost, realm, httpAuth);
- if (!selectedEntriesToConfirm.isEmpty()) {
- pwEntries.append(selectedEntriesToConfirm);
- }
-
- if (pwEntries.isEmpty()) {
- return {};
- }
-
- // Ensure that database is not locked when the popup was visible
- if (!isDatabaseOpened()) {
- return {};
- }
-
- // Sort results
- pwEntries = sortEntries(pwEntries, siteUrl, formUrl);
-
- // Fill the list
- QJsonArray result;
- for (auto* entry : pwEntries) {
- result.append(prepareEntry(entry));
- }
-
- return result;
-}
-
void BrowserService::addEntry(const QString& dbid,
const QString& login,
const QString& password,
@@ -822,13 +950,12 @@ void BrowserService::requestGlobalAutoType(const QString& search)
emit osUtils->globalShortcutTriggered("autotype", search);
}
-QList<Entry*>
-BrowserService::sortEntries(QList<Entry*>& pwEntries, const QString& siteUrlStr, const QString& formUrlStr)
+QList<Entry*> BrowserService::sortEntries(QList<Entry*>& pwEntries, const QString& siteUrl, const QString& formUrl)
{
// Build map of prioritized entries
QMultiMap<int, Entry*> priorities;
for (auto* entry : pwEntries) {
- priorities.insert(sortPriority(getEntryURLs(entry), siteUrlStr, formUrlStr), entry);
+ priorities.insert(sortPriority(getEntryURLs(entry), siteUrl, formUrl), entry);
}
auto keys = priorities.uniqueKeys();
@@ -847,66 +974,38 @@ BrowserService::sortEntries(QList<Entry*>& pwEntries, const QString& siteUrlStr,
return results;
}
-QList<Entry*> BrowserService::confirmEntries(QList<Entry*>& pwEntriesToConfirm,
- const QString& siteUrl,
- const QString& siteHost,
- const QString& formUrl,
- const QString& realm,
- const bool httpAuth)
+void BrowserService::allowEntry(Entry* entry, const QString& siteHost, const QString& formUrl, const QString& realm)
{
- if (pwEntriesToConfirm.isEmpty() || m_dialogActive) {
- return {};
- }
+ BrowserEntryConfig config;
+ config.load(entry);
+ config.allow(siteHost);
- m_dialogActive = true;
- updateWindowState();
- BrowserAccessControlDialog accessControlDialog;
+ if (!formUrl.isEmpty() && siteHost != formUrl) {
+ config.allow(formUrl);
+ }
- connect(m_currentDatabaseWidget, SIGNAL(databaseLockRequested()), &accessControlDialog, SLOT(reject()));
+ if (!realm.isEmpty()) {
+ config.setRealm(realm);
+ }
- connect(&accessControlDialog, &BrowserAccessControlDialog::disableAccess, [&](QTableWidgetItem* item) {
- auto entry = pwEntriesToConfirm[item->row()];
- BrowserEntryConfig config;
- config.load(entry);
- config.deny(siteHost);
- if (!formUrl.isEmpty() && siteHost != formUrl) {
- config.deny(formUrl);
- }
- if (!realm.isEmpty()) {
- config.setRealm(realm);
- }
- config.save(entry);
- });
+ config.save(entry);
+}
- accessControlDialog.setItems(pwEntriesToConfirm, siteUrl, httpAuth);
+void BrowserService::denyEntry(Entry* entry, const QString& siteHost, const QString& formUrl, const QString& realm)
+{
+ BrowserEntryConfig config;
+ config.load(entry);
+ config.deny(siteHost);
- QList<Entry*> allowedEntries;
- if (accessControlDialog.exec() == QDialog::Accepted) {
- const auto selectedEntries = accessControlDialog.getSelectedEntries();
- for (auto item : accessControlDialog.getSelectedEntries()) {
- auto entry = pwEntriesToConfirm[item->row()];
- if (accessControlDialog.remember()) {
- BrowserEntryConfig config;
- config.load(entry);
- config.allow(siteHost);
- if (!formUrl.isEmpty() && siteHost != formUrl) {
- config.allow(formUrl);
- }
- if (!realm.isEmpty()) {
- config.setRealm(realm);
- }
- config.save(entry);
- }
- allowedEntries.append(entry);
- }
+ if (!formUrl.isEmpty() && siteHost != formUrl) {
+ config.deny(formUrl);
}
- // Re-hide the application if it wasn't visible before
- hideWindow();
-
- m_dialogActive = false;
+ if (!realm.isEmpty()) {
+ config.setRealm(realm);
+ }
- return allowedEntries;
+ config.save(entry);
}
QJsonObject BrowserService::prepareEntry(const Entry* entry)
diff --git a/src/browser/BrowserService.h b/src/browser/BrowserService.h
index 1ab3342fb..27b977257 100644
--- a/src/browser/BrowserService.h
+++ b/src/browser/BrowserService.h
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2013 Francois Ferrand
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
- * Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
+ * Copyright (C) 2022 KeePassXC Team <team@keepassxc.org>
*
* 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
@@ -20,6 +20,7 @@
#ifndef BROWSERSERVICE_H
#define BROWSERSERVICE_H
+#include "BrowserAccessControlDialog.h"
#include "core/Entry.h"
#include "gui/PasswordGeneratorWidget.h"
@@ -64,12 +65,13 @@ public:
const QString& secretKey);
void sendPassword(QLocalSocket* socket, const QJsonObject& message);
bool isPasswordGeneratorRequested() const;
+ bool isAccessConfirmRequested() const;
void addEntry(const QString& dbid,
const QString& login,
const QString& password,
- const QString& siteUrlStr,
- const QString& formUrlStr,
+ const QString& siteUrl,
+ const QString& formUrl,
const QString& realm,
const QString& group,
const QString& groupUuid,
@@ -79,17 +81,21 @@ public:
const QString& uuid,
const QString& login,
const QString& password,
- const QString& siteUrlStr,
- const QString& formUrlStr);
+ const QString& siteUrl,
+ const QString& formUrl);
bool deleteEntry(const QString& uuid);
-
- QJsonArray findMatchingEntries(const QString& dbid,
- const QString& siteUrlStr,
- const QString& formUrlStr,
- const QString& realm,
- const StringPairList& keyList,
- const bool httpAuth = false);
-
+ void findEntries(QLocalSocket* socket,
+ const QString& nonce,
+ const QString& publicKey,
+ const QString& secretKey,
+ const QString& dbid,
+ const QString& hash,
+ const QString& requestId,
+ const QString& siteUrl,
+ const QString& formUrl,
+ const QString& realm,
+ const StringPairList& keyList,
+ const bool httpAuth = false);
void requestGlobalAutoType(const QString& search);
static void convertAttributesToCustomData(QSharedPointer<Database> db);
@@ -132,13 +138,32 @@ private:
QList<Entry*> searchEntries(const QSharedPointer<Database>& db, const QString& siteUrl, const QString& formUrl);
QList<Entry*> searchEntries(const QString& siteUrl, const QString& formUrl, const StringPairList& keyList);
QList<Entry*> sortEntries(QList<Entry*>& pwEntries, const QString& siteUrl, const QString& formUrl);
- QList<Entry*> confirmEntries(QList<Entry*>& pwEntriesToConfirm,
- const QString& siteUrl,
- const QString& siteHost,
- const QString& formUrl,
- const QString& realm,
- const bool httpAuth);
+ void confirmEntries(QLocalSocket* socket,
+ const QString& incrementedNonce,
+ const QString& publicKey,
+ const QString& secretKey,
+ const QString& id,
+ const QString& hash,
+ const QString& requestId,
+ QList<Entry*>& allowedEntries,
+ QList<Entry*>& entriesToConfirm,
+ const QString& siteUrl,
+ const QString& siteHost,
+ const QString& formUrl,
+ const QString& realm,
+ const bool httpAuth);
+ void sendCredentialsToClient(QList<Entry*>& allowedEntries,
+ QLocalSocket* socket,
+ const QString& incrementedNonce,
+ const QString& publicKey,
+ const QString& secretKey,
+ const QString& hash,
+ const QString& id,
+ const QString siteUrl,
+ const QString& formUrl);
QJsonObject prepareEntry(const Entry* entry);
+ void allowEntry(Entry* entry, const QString& siteHost, const QString& formUrl, const QString& realm);
+ void denyEntry(Entry* entry, const QString& siteHost, const QString& formUrl, const QString& realm);
QJsonArray getChildrenFromGroup(Group* group);
Access checkAccess(const Entry* entry, const QString& siteHost, const QString& formHost, const QString& realm);
Group* getDefaultEntryGroup(const QSharedPointer<Database>& selectedDb = {});
@@ -171,14 +196,15 @@ private:
QPointer<BrowserHost> m_browserHost;
QHash<QString, QSharedPointer<BrowserAction>> m_browserClients;
- bool m_dialogActive;
bool m_bringToFrontRequested;
bool m_passwordGeneratorRequested;
+ bool m_accessConfirmRequested;
WindowState m_prevWindowState;
QUuid m_keepassBrowserUUID;
QPointer<DatabaseWidget> m_currentDatabaseWidget;
QScopedPointer<PasswordGeneratorWidget> m_passwordGenerator;
+ QScopedPointer<BrowserAccessControlDialog> m_accessControlDialog;
Q_DISABLE_COPY(BrowserService);