Skip to content

Commit

Permalink
Browser: Asynchronous Access Confirm dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
varjolintu authored and droidmonkey committed Sep 10, 2022
1 parent 612c109 commit ef6d8f1
Show file tree
Hide file tree
Showing 7 changed files with 385 additions and 193 deletions.
53 changes: 46 additions & 7 deletions src/browser/BrowserAccessControlDialog.cpp
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -21,37 +21,53 @@
#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)));

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);
Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -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();
}
21 changes: 19 additions & 2 deletions src/browser/BrowserAccessControlDialog.h
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -37,17 +37,34 @@ class BrowserAccessControlDialog : public QDialog
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
2 changes: 1 addition & 1 deletion src/browser/BrowserAccessControlDialog.ui
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
37 changes: 24 additions & 13 deletions src/browser/BrowserAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/browser/BrowserAction.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class BrowserAction
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);
Expand Down
Loading

0 comments on commit ef6d8f1

Please sign in to comment.