Choose database for saving or updating entries from KeePassXC-Browser (#2391)

This commit is contained in:
Sami Vänttinen 2018-10-19 21:44:08 +03:00 committed by Jonathan White
parent bb16dc6d01
commit b8d2d5d877
13 changed files with 307 additions and 22 deletions

View File

@ -22,13 +22,13 @@
BrowserAccessControlDialog::BrowserAccessControlDialog(QWidget* parent) BrowserAccessControlDialog::BrowserAccessControlDialog(QWidget* parent)
: QDialog(parent) : QDialog(parent)
, ui(new Ui::BrowserAccessControlDialog()) , m_ui(new Ui::BrowserAccessControlDialog())
{ {
this->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); this->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
ui->setupUi(this); m_ui->setupUi(this);
connect(ui->allowButton, SIGNAL(clicked()), this, SLOT(accept())); connect(m_ui->allowButton, SIGNAL(clicked()), this, SLOT(accept()));
connect(ui->denyButton, SIGNAL(clicked()), this, SLOT(reject())); connect(m_ui->denyButton, SIGNAL(clicked()), this, SLOT(reject()));
} }
BrowserAccessControlDialog::~BrowserAccessControlDialog() BrowserAccessControlDialog::~BrowserAccessControlDialog()
@ -37,7 +37,7 @@ BrowserAccessControlDialog::~BrowserAccessControlDialog()
void BrowserAccessControlDialog::setUrl(const QString& url) void BrowserAccessControlDialog::setUrl(const QString& url)
{ {
ui->label->setText(QString(tr("%1 has requested access to passwords for the following item(s).\n" m_ui->label->setText(QString(tr("%1 has requested access to passwords for the following item(s).\n"
"Please select whether you want to allow access.")) "Please select whether you want to allow access."))
.arg(QUrl(url).host())); .arg(QUrl(url).host()));
} }
@ -45,16 +45,16 @@ void BrowserAccessControlDialog::setUrl(const QString& url)
void BrowserAccessControlDialog::setItems(const QList<Entry*>& items) void BrowserAccessControlDialog::setItems(const QList<Entry*>& items)
{ {
for (Entry* entry : items) { for (Entry* entry : items) {
ui->itemsList->addItem(entry->title() + " - " + entry->username()); m_ui->itemsList->addItem(entry->title() + " - " + entry->username());
} }
} }
bool BrowserAccessControlDialog::remember() const bool BrowserAccessControlDialog::remember() const
{ {
return ui->rememberDecisionCheckBox->isChecked(); return m_ui->rememberDecisionCheckBox->isChecked();
} }
void BrowserAccessControlDialog::setRemember(bool r) void BrowserAccessControlDialog::setRemember(bool r)
{ {
ui->rememberDecisionCheckBox->setChecked(r); m_ui->rememberDecisionCheckBox->setChecked(r);
} }

View File

@ -43,7 +43,7 @@ public:
void setRemember(bool r); void setRemember(bool r);
private: private:
QScopedPointer<Ui::BrowserAccessControlDialog> ui; QScopedPointer<Ui::BrowserAccessControlDialog> m_ui;
}; };
#endif // BROWSERACCESSCONTROLDIALOG_H #endif // BROWSERACCESSCONTROLDIALOG_H

View File

@ -322,7 +322,7 @@ QJsonObject BrowserAction::handleSetLogin(const QJsonObject& json, const QString
if (uuid.isEmpty()) { if (uuid.isEmpty()) {
m_browserService.addEntry(id, login, password, url, submitUrl, realm); m_browserService.addEntry(id, login, password, url, submitUrl, realm);
} else { } else {
m_browserService.updateEntry(id, uuid, login, password, url); m_browserService.updateEntry(id, uuid, login, password, url, submitUrl);
} }
const QString newNonce = incrementNonce(nonce); const QString newNonce = incrementNonce(nonce);

View File

@ -0,0 +1,79 @@
/*
* Copyright (C) 2013 Francois Ferrand
* Copyright (C) 2018 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 "BrowserEntrySaveDialog.h"
#include "ui_BrowserEntrySaveDialog.h"
BrowserEntrySaveDialog::BrowserEntrySaveDialog(QWidget* parent)
: QDialog(parent)
, m_ui(new Ui::BrowserEntrySaveDialog())
{
this->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
m_ui->setupUi(this);
connect(m_ui->okButton, SIGNAL(clicked()), this, SLOT(accept()));
connect(m_ui->cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
m_ui->itemsList->setSelectionMode(QAbstractItemView::SingleSelection);
m_ui->label->setText(QString(tr("You have multiple databases open.\n"
"Please select the correct database for saving credentials.")));
}
BrowserEntrySaveDialog::~BrowserEntrySaveDialog()
{
}
int BrowserEntrySaveDialog::setItems(QList<DatabaseWidget*>& databaseWidgets, DatabaseWidget* currentWidget) const
{
uint counter = 0;
int activeIndex = -1;
for (const auto dbWidget : databaseWidgets) {
QString databaseName = dbWidget->getDatabaseName();
QString databaseFileName = dbWidget->getDatabaseFileName();
QListWidgetItem* item = new QListWidgetItem();
item->setData(Qt::UserRole, counter);
// Show database name (and filename if the name has been set in metadata)
if (databaseName == databaseFileName) {
item->setText(databaseFileName);
} else {
item->setText(QString("%1 (%2)").arg(databaseName, databaseFileName));
}
if (currentWidget == dbWidget) {
activeIndex = counter;
}
m_ui->itemsList->addItem(item);
++counter;
}
// This must be done after the whole list is filled
if (activeIndex >= 0) {
m_ui->itemsList->item(activeIndex)->setSelected(true);
}
m_ui->itemsList->selectAll();
return databaseWidgets.length();
}
QList<QListWidgetItem*> BrowserEntrySaveDialog::getSelected() const
{
return m_ui->itemsList->selectedItems();
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (C) 2013 Francois Ferrand
* Copyright (C) 2018 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 BROWSERENTRYSAVEDIALOG_H
#define BROWSERENTRYSAVEDIALOG_H
#include <QDialog>
#include <QScopedPointer>
#include <QListWidgetItem>
#include "gui/DatabaseTabWidget.h"
class Entry;
namespace Ui
{
class BrowserEntrySaveDialog;
}
class BrowserEntrySaveDialog : public QDialog
{
Q_OBJECT
public:
explicit BrowserEntrySaveDialog(QWidget* parent = nullptr);
~BrowserEntrySaveDialog() override;
int setItems(QList<DatabaseWidget*>& databaseWidgets, DatabaseWidget* currentWidget) const;
QList<QListWidgetItem *> getSelected() const;
private:
QScopedPointer<Ui::BrowserEntrySaveDialog> m_ui;
};
#endif // BROWSERENTRYSAVEDIALOG_H

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BrowserEntrySaveDialog</class>
<widget class="QDialog" name="BrowserEntrySaveDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>221</height>
</rect>
</property>
<property name="windowTitle">
<string>KeePassXC-Browser Save Entry</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="itemsList"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="okButton">
<property name="text">
<string>Ok</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -26,6 +26,7 @@
#include "BrowserService.h" #include "BrowserService.h"
#include "BrowserAccessControlDialog.h" #include "BrowserAccessControlDialog.h"
#include "BrowserEntryConfig.h" #include "BrowserEntryConfig.h"
#include "BrowserEntrySaveDialog.h"
#include "BrowserSettings.h" #include "BrowserSettings.h"
#include "core/Database.h" #include "core/Database.h"
#include "core/EntrySearcher.h" #include "core/EntrySearcher.h"
@ -289,14 +290,33 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id,
return result; return result;
} }
void BrowserService::addEntry(const QString&, void BrowserService::addEntry(const QString& id,
const QString& login, const QString& login,
const QString& password, const QString& password,
const QString& url, const QString& url,
const QString& submitUrl, const QString& submitUrl,
const QString& realm) const QString& realm,
Database* selectedDb)
{ {
Group* group = findCreateAddEntryGroup(); if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this,
"addEntry",
Qt::BlockingQueuedConnection,
Q_ARG(const QString&, id),
Q_ARG(const QString&, login),
Q_ARG(const QString&, password),
Q_ARG(const QString&, url),
Q_ARG(const QString&, submitUrl),
Q_ARG(const QString&, realm),
Q_ARG(Database*, selectedDb));
}
Database* db = selectedDb ? selectedDb : selectedDatabase();
if (!db) {
return;
}
Group* group = findCreateAddEntryGroup(db);
if (!group) { if (!group) {
return; return;
} }
@ -328,7 +348,8 @@ void BrowserService::updateEntry(const QString& id,
const QString& uuid, const QString& uuid,
const QString& login, const QString& login,
const QString& password, const QString& password,
const QString& url) const QString& url,
const QString& submitUrl)
{ {
if (thread() != QThread::currentThread()) { if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, QMetaObject::invokeMethod(this,
@ -338,16 +359,19 @@ void BrowserService::updateEntry(const QString& id,
Q_ARG(const QString&, uuid), Q_ARG(const QString&, uuid),
Q_ARG(const QString&, login), Q_ARG(const QString&, login),
Q_ARG(const QString&, password), Q_ARG(const QString&, password),
Q_ARG(const QString&, url)); Q_ARG(const QString&, url),
Q_ARG(const QString&, submitUrl));
} }
Database* db = getDatabase(); Database* db = selectedDatabase();
if (!db) { if (!db) {
return; return;
} }
Entry* entry = db->resolveEntry(QUuid::fromRfc4122(QByteArray::fromHex(uuid.toLatin1()))); Entry* entry = db->resolveEntry(QUuid::fromRfc4122(QByteArray::fromHex(uuid.toLatin1())));
if (!entry) { if (!entry) {
// If entry is not found for update, add a new one to the selected database
addEntry(id, login, password, url, submitUrl, "", db);
return; return;
} }
@ -679,9 +703,9 @@ BrowserService::checkAccess(const Entry* entry, const QString& host, const QStri
return Unknown; return Unknown;
} }
Group* BrowserService::findCreateAddEntryGroup() Group* BrowserService::findCreateAddEntryGroup(Database* selectedDb)
{ {
Database* db = getDatabase(); Database* db = selectedDb ? selectedDb : getDatabase();
if (!db) { if (!db) {
return nullptr; return nullptr;
} }
@ -813,6 +837,42 @@ Database* BrowserService::getDatabase()
return nullptr; return nullptr;
} }
Database* BrowserService::selectedDatabase()
{
QList<DatabaseWidget*> databaseWidgets;
for (int i = 0;; ++i) {
const auto dbStruct = m_dbTabWidget->indexDatabaseManagerStruct(i);
// Add only open databases
if (dbStruct.dbWidget && dbStruct.dbWidget->dbHasKey() &&
(dbStruct.dbWidget->currentMode() == DatabaseWidget::ViewMode ||
dbStruct.dbWidget->currentMode() == DatabaseWidget::EditMode)) {
databaseWidgets.push_back(dbStruct.dbWidget);
continue;
}
// Break out if dbStruct.dbWidget is nullptr
break;
}
BrowserEntrySaveDialog browserEntrySaveDialog;
int openDatabaseCount = browserEntrySaveDialog.setItems(databaseWidgets, m_dbTabWidget->currentDatabaseWidget());
if (openDatabaseCount > 1) {
int res = browserEntrySaveDialog.exec();
if (res == QDialog::Accepted) {
const auto selectedDatabase = browserEntrySaveDialog.getSelected();
if (selectedDatabase.length() > 0) {
int index = selectedDatabase[0]->data(Qt::UserRole).toUInt();
return databaseWidgets[index]->database();
}
} else {
return nullptr;
}
}
// Return current database
return getDatabase();
}
void BrowserService::databaseLocked(DatabaseWidget* dbWidget) void BrowserService::databaseLocked(DatabaseWidget* dbWidget)
{ {
if (dbWidget) { if (dbWidget) {

View File

@ -51,7 +51,8 @@ public:
const QString& password, const QString& password,
const QString& url, const QString& url,
const QString& submitUrl, const QString& submitUrl,
const QString& realm); const QString& realm,
Database* selectedDb = nullptr);
QList<Entry*> searchEntries(Database* db, const QString& hostname, const QString& url); QList<Entry*> searchEntries(Database* db, const QString& hostname, const QString& url);
QList<Entry*> searchEntries(const QString& url, const StringPairList& keyList); QList<Entry*> searchEntries(const QString& url, const StringPairList& keyList);
void removeSharedEncryptionKeys(); void removeSharedEncryptionKeys();
@ -68,7 +69,8 @@ public slots:
const QString& uuid, const QString& uuid,
const QString& login, const QString& login,
const QString& password, const QString& password,
const QString& url); const QString& url,
const QString& submitUrl);
void databaseLocked(DatabaseWidget* dbWidget); void databaseLocked(DatabaseWidget* dbWidget);
void databaseUnlocked(DatabaseWidget* dbWidget); void databaseUnlocked(DatabaseWidget* dbWidget);
void activateDatabaseChanged(DatabaseWidget* dbWidget); void activateDatabaseChanged(DatabaseWidget* dbWidget);
@ -96,13 +98,14 @@ private:
const QString& realm); const QString& realm);
QJsonObject prepareEntry(const Entry* entry); QJsonObject prepareEntry(const Entry* entry);
Access checkAccess(const Entry* entry, const QString& host, const QString& submitHost, const QString& realm); Access checkAccess(const Entry* entry, const QString& host, const QString& submitHost, const QString& realm);
Group* findCreateAddEntryGroup(); Group* findCreateAddEntryGroup(Database* selectedDb = nullptr);
int int
sortPriority(const Entry* entry, const QString& host, const QString& submitUrl, const QString& baseSubmitUrl) const; sortPriority(const Entry* entry, const QString& host, const QString& submitUrl, const QString& baseSubmitUrl) const;
bool matchUrlScheme(const QString& url); bool matchUrlScheme(const QString& url);
bool removeFirstDomain(QString& hostname); bool removeFirstDomain(QString& hostname);
QString baseDomain(const QString& url) const; QString baseDomain(const QString& url) const;
Database* getDatabase(); Database* getDatabase();
Database* selectedDatabase();
private: private:
DatabaseTabWidget* const m_dbTabWidget; DatabaseTabWidget* const m_dbTabWidget;

View File

@ -23,6 +23,7 @@ if(WITH_XC_BROWSER)
BrowserAction.cpp BrowserAction.cpp
BrowserClients.cpp BrowserClients.cpp
BrowserEntryConfig.cpp BrowserEntryConfig.cpp
BrowserEntrySaveDialog.cpp
BrowserOptionDialog.cpp BrowserOptionDialog.cpp
BrowserService.cpp BrowserService.cpp
BrowserSettings.cpp BrowserSettings.cpp

View File

@ -555,6 +555,7 @@ void DatabaseTabWidget::updateTabName(Database* db)
const DatabaseManagerStruct& dbStruct = m_dbList.value(db); const DatabaseManagerStruct& dbStruct = m_dbList.value(db);
QString tabName; QString tabName;
QString fileName;
if (dbStruct.fileInfo.exists()) { if (dbStruct.fileInfo.exists()) {
if (db->metadata()->name().isEmpty()) { if (db->metadata()->name().isEmpty()) {
@ -563,6 +564,7 @@ void DatabaseTabWidget::updateTabName(Database* db)
tabName = db->metadata()->name(); tabName = db->metadata()->name();
} }
fileName = dbStruct.fileInfo.fileName();
setTabToolTip(index, dbStruct.fileInfo.absoluteFilePath()); setTabToolTip(index, dbStruct.fileInfo.absoluteFilePath());
} else { } else {
if (db->metadata()->name().isEmpty()) { if (db->metadata()->name().isEmpty()) {
@ -580,6 +582,9 @@ void DatabaseTabWidget::updateTabName(Database* db)
tabName.append("*"); tabName.append("*");
} }
dbStruct.dbWidget->setDatabaseName(tabName);
dbStruct.dbWidget->setDatabaseFileName(fileName);
setTabText(index, tabName); setTabText(index, tabName);
emit tabNameChanged(); emit tabNameChanged();
} }

View File

@ -56,6 +56,7 @@ public:
void mergeDatabase(const QString& fileName); void mergeDatabase(const QString& fileName);
DatabaseWidget* currentDatabaseWidget(); DatabaseWidget* currentDatabaseWidget();
bool hasLockableDatabases() const; bool hasLockableDatabases() const;
DatabaseManagerStruct indexDatabaseManagerStruct(int index);
static const int LastDatabasesCount; static const int LastDatabasesCount;
@ -110,7 +111,6 @@ private:
void deleteDatabase(Database* db); void deleteDatabase(Database* db);
int databaseIndex(Database* db); int databaseIndex(Database* db);
Database* indexDatabase(int index); Database* indexDatabase(int index);
DatabaseManagerStruct indexDatabaseManagerStruct(int index);
Database* databaseFromDatabaseWidget(DatabaseWidget* dbWidget); Database* databaseFromDatabaseWidget(DatabaseWidget* dbWidget);
void insertDatabase(Database* db, const DatabaseManagerStruct& dbStruct); void insertDatabase(Database* db, const DatabaseManagerStruct& dbStruct);
void updateLastDatabases(const QString& filename); void updateLastDatabases(const QString& filename);

View File

@ -1487,6 +1487,26 @@ bool DatabaseWidget::isRecycleBinSelected() const
return m_groupView->currentGroup() && m_groupView->currentGroup() == m_db->metadata()->recycleBin(); return m_groupView->currentGroup() && m_groupView->currentGroup() == m_db->metadata()->recycleBin();
} }
QString DatabaseWidget::getDatabaseName() const
{
return m_databaseName;
}
void DatabaseWidget::setDatabaseName(const QString& databaseName)
{
m_databaseName = databaseName;
}
QString DatabaseWidget::getDatabaseFileName() const
{
return m_databaseFileName;
}
void DatabaseWidget::setDatabaseFileName(const QString& databaseFileName)
{
m_databaseFileName = databaseFileName;
}
void DatabaseWidget::emptyRecycleBin() void DatabaseWidget::emptyRecycleBin()
{ {
if (!isRecycleBinSelected()) { if (!isRecycleBinSelected()) {

View File

@ -112,6 +112,10 @@ public:
void blockAutoReload(bool block = true); void blockAutoReload(bool block = true);
void refreshSearch(); void refreshSearch();
bool isRecycleBinSelected() const; bool isRecycleBinSelected() const;
QString getDatabaseName() const;
void setDatabaseName(const QString& databaseName);
QString getDatabaseFileName() const;
void setDatabaseFileName(const QString& databaseFileName);
signals: signals:
void closeRequest(); void closeRequest();
@ -238,6 +242,8 @@ private:
QUuid m_entryBeforeLock; QUuid m_entryBeforeLock;
MessageWidget* m_messageWidget; MessageWidget* m_messageWidget;
EntryPreviewWidget* m_previewView; EntryPreviewWidget* m_previewView;
QString m_databaseName;
QString m_databaseFileName;
// Search state // Search state
QString m_lastSearchText; QString m_lastSearchText;