From 2eec43e0f2b735e043d38a64311c2b63bce26288 Mon Sep 17 00:00:00 2001 From: Lukas Erlacher Date: Wed, 9 Aug 2017 16:14:31 +1000 Subject: [PATCH] Insert KeepassHTTP Auth Key into multiple DBs Basic implementation of allowing user to select which DBs to insert KeepassHTTP Authentication Keys into. For #535 --- src/http/KeyAcceptDialog.cpp | 30 ++++++-- src/http/KeyAcceptDialog.h | 8 ++- src/http/KeyAcceptDialog.ui | 9 ++- src/http/Server.h | 4 +- src/http/Service.cpp | 131 +++++++++++++++++++++++++---------- src/http/Service.h | 5 +- 6 files changed, 139 insertions(+), 48 deletions(-) diff --git a/src/http/KeyAcceptDialog.cpp b/src/http/KeyAcceptDialog.cpp index 61f50f066c..a7a762cf4f 100644 --- a/src/http/KeyAcceptDialog.cpp +++ b/src/http/KeyAcceptDialog.cpp @@ -43,7 +43,9 @@ KeyAcceptDialog::~KeyAcceptDialog() void KeyAcceptDialog::setItems(const QList &items) { - QStandardItemModel* listViewModel = (QStandardItemModel*) ui->databasesListView->model(); + QStandardItemModel* listViewModel = qobject_cast(ui->databasesListView->model()); + + listViewModel->clear(); for (QString item: items) { QStandardItem* listItem = new QStandardItem(item); @@ -54,9 +56,20 @@ void KeyAcceptDialog::setItems(const QList &items) } } +void KeyAcceptDialog::setItemEnabled(int itemIndex, bool enabled) +{ + QStandardItemModel* listViewModel = qobject_cast(ui->databasesListView->model()); + + QStandardItem* item = listViewModel->item(itemIndex); + + if (item) { + item->setEnabled(enabled); + } +} + void KeyAcceptDialog::setItemChecked(int itemIndex, bool checked) { - QStandardItemModel* listViewModel = (QStandardItemModel*) ui->databasesListView->model(); + QStandardItemModel* listViewModel = qobject_cast(ui->databasesListView->model()); QStandardItem* item = listViewModel->item(itemIndex); @@ -69,19 +82,19 @@ void KeyAcceptDialog::setItemChecked(int itemIndex, bool checked) } } -QList* KeyAcceptDialog::getCheckedItems() +QList KeyAcceptDialog::getCheckedItems() { - QStandardItemModel* listViewModel = (QStandardItemModel*) ui->databasesListView->model(); + QStandardItemModel* listViewModel = qobject_cast(ui->databasesListView->model()); int numRows = listViewModel->rowCount(); //XXX Memory Leak - QList* resultList = new QList; + QList resultList; for (int row = 0; row < numRows; row++) { QStandardItem* item = listViewModel->item(row); if (item->checkState() == Qt::Checked) { - resultList->append(row); + resultList << row; } } @@ -92,3 +105,8 @@ QString KeyAcceptDialog::getKeyName() { return ui->keyNameLineEdit->text(); } + +//TODO +//void KeyAcceptDialog::databaseUnlocked(DatabaseWidget* dbWidget) +//{ +//} diff --git a/src/http/KeyAcceptDialog.h b/src/http/KeyAcceptDialog.h index ab26b604b3..4afe382d93 100644 --- a/src/http/KeyAcceptDialog.h +++ b/src/http/KeyAcceptDialog.h @@ -36,10 +36,16 @@ class KeyAcceptDialog : public QDialog void setItems(const QList & items); void setItemChecked(int itemIndex, bool checked); - QList* getCheckedItems(); + void setItemEnabled(int itemIndex, bool enabled); + QList getCheckedItems(); QString getKeyName(); +//TODO +//public slots: +// void databaseUnlocked(DatabaseWidget* dbWidget); +// void databaseLocked(DatabaseWidget* dbWidget); + private: QScopedPointer ui; }; diff --git a/src/http/KeyAcceptDialog.ui b/src/http/KeyAcceptDialog.ui index db520431c8..cb0876ee15 100644 --- a/src/http/KeyAcceptDialog.ui +++ b/src/http/KeyAcceptDialog.ui @@ -6,8 +6,8 @@ 0 0 - 403 - 233 + 469 + 292 @@ -39,7 +39,10 @@ - <html><head/><body><p>Which databases should this key be valid for?<br>(The currently selected database is pre-selected)</p></body></html> + <html><head/><body><p>Which databases should this key be valid for?<br>(The currently selected database is pre-selected; grayed-out databases are not opened - open them to enable accepting key!)</p></body></html> + + + true diff --git a/src/http/Server.h b/src/http/Server.h index 08cdfa24a8..0018fc98d8 100644 --- a/src/http/Server.h +++ b/src/http/Server.h @@ -22,6 +22,8 @@ #include #include +#include "gui/DatabaseTabWidget.h" + namespace qhttp { namespace server { class QHttpServer; @@ -44,7 +46,7 @@ class Server : public QObject public: explicit Server(QObject *parent = 0); - virtual bool isDatabaseOpened() const = 0; + virtual bool isDatabaseOpened(DatabaseWidget* dbWidget = NULL) const = 0; virtual bool openDatabase() = 0; virtual QString getDatabaseRootUuid() = 0; virtual QString getDatabaseRecycleBinUuid() = 0; diff --git a/src/http/Service.cpp b/src/http/Service.cpp index 6995255d90..c0a06de928 100644 --- a/src/http/Service.cpp +++ b/src/http/Service.cpp @@ -79,9 +79,36 @@ Entry* Service::getConfigEntry(bool create) return NULL; } -bool Service::isDatabaseOpened() const +QList Service::getConfigEntries(bool create) { - if (DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget()) + QList entries; + + for (int i = 0; i < m_dbTabWidget->count(); i++) + if (DatabaseWidget* dbWidget = qobject_cast(m_dbTabWidget->widget(i))) + if (Database* db = dbWidget->database()) { + Entry* entry = db->resolveEntry(KEEPASSHTTP_UUID); + if (!entry && create) { + entry = new Entry(); + entry->setTitle(QLatin1String(KEEPASSHTTP_NAME)); + entry->setUuid(KEEPASSHTTP_UUID); + entry->setAutoTypeEnabled(false); + entry->setGroup(db->rootGroup()); + } else if (entry && entry->group() == db->metadata()->recycleBin()) { + if (create) + entry->setGroup(db->rootGroup()); + else + entry = NULL; + } + entries << entry; + } + return entries; +} + +bool Service::isDatabaseOpened(DatabaseWidget* dbWidget) const +{ + if (!dbWidget) + dbWidget = m_dbTabWidget->currentDatabaseWidget(); + if (dbWidget) switch(dbWidget->currentMode()) { case DatabaseWidget::None: case DatabaseWidget::LockedMode: @@ -147,46 +174,78 @@ QString Service::getKey(const QString &id) return QString(); } -QString Service::storeKey(const QString &key) +QList Service::getKeys(const QString &id) { - QString id; - if (DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget()) - if (Database* db = dbWidget->database()) - if (Entry* config = getConfigEntry(true)) { - KeyAcceptDialog dlg; - QList dbNames; - - //TODO get more databases in - dbNames.append(m_dbTabWidget->tabText(m_dbTabWidget->currentIndex())); - - dlg.setItems(dbNames); - - + QList keys; + QList entries = getConfigEntries(); + for (Entry* config : entries) { + keys << config->attributes()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + id); + } + return keys; +} - do { - bool ok; - //Indicate who wants to associate, and request user to enter the 'name' of association key +//XXX +//bool Service::attributeExists(QList entries, const QString &attribute) +bool Service::attributeExists(QList entries, const QString attribute) +{ + for (Entry* entry: entries) + if (entry->attributes()->contains(attribute)) + return true; + return false; +} - int res = dlg.exec(); +QString Service::storeKey(const QString &key) +{ + QString id; + QList dbWidgets; + QList dbTexts; + DatabaseWidget* currDbWidget = m_dbTabWidget->currentDatabaseWidget(); + for (int i = 0; i < m_dbTabWidget->count(); i++) { + dbWidgets << qobject_cast(m_dbTabWidget->widget(i)); + dbTexts << m_dbTabWidget->tabText(i); + } - if (res == QDialog::Accepted) { + QList configs = getConfigEntries(true); - //TODO use this - QList* databases = dlg.getCheckedItems(); - id = dlg.getKeyName(); - } else { - return QString(); - } + if (dbWidgets.count() && configs.count()) { + KeyAcceptDialog dlg; + QList dbNames; - //Warn if association key already exists - } while(config->attributes()->contains(QLatin1String(ASSOCIATE_KEY_PREFIX) + id) && - QMessageBox::warning(0, tr("KeePassXC: Overwrite existing key?"), - tr("A shared encryption-key with the name \"%1\" already exists.\nDo you want to overwrite it?").arg(id), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::No); + dlg.setItems(dbTexts); + for (int i=0; iattributes()->set(QLatin1String(ASSOCIATE_KEY_PREFIX) + id, key, true); - } + do { + bool ok; + //Indicate who wants to associate, and request user to enter the 'name' of association key + + int res = dlg.exec(); + + if (res != QDialog::Accepted) + return QString(); + + //Warn if association key already exists + } while(attributeExists(configs, QLatin1String(ASSOCIATE_KEY_PREFIX) + id) && + QMessageBox::warning(0, tr("KeePassXC: Overwrite existing key?"), + tr("A shared encryption-key with the name \"%1\" already exists.\nDo you want to overwrite it?").arg(id), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::No); + QList selectedDatabases = dlg.getCheckedItems(); + id = dlg.getKeyName(); +// for (int i=0; iattributes()->set(QLatin1String(ASSOCIATE_KEY_PREFIX) + id, key, true); +// } + for (int dbIdx: selectedDatabases) { + qWarning("adding key to db: %d", dbIdx); + configs.at(dbIdx)->attributes()->set(QLatin1String(ASSOCIATE_KEY_PREFIX) + id, key, true); + } + } return id; } @@ -534,7 +593,7 @@ QString Service::generatePassword() void Service::removeSharedEncryptionKeys() { - if (!isDatabaseOpened()) { + if (!isDatabaseOpened(NULL)) { 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."), @@ -573,7 +632,7 @@ void Service::removeSharedEncryptionKeys() void Service::removeStoredPermissions() { - if (!isDatabaseOpened()) { + if (!isDatabaseOpened(NULL)) { 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."), diff --git a/src/http/Service.h b/src/http/Service.h index d60d884bb2..bab9cc6bfa 100644 --- a/src/http/Service.h +++ b/src/http/Service.h @@ -30,11 +30,12 @@ class Service : public KeepassHttpProtocol::Server public: explicit Service(DatabaseTabWidget* parent = 0); - virtual bool isDatabaseOpened() const; + virtual bool isDatabaseOpened(DatabaseWidget* dbWidget) const; virtual bool openDatabase(); virtual QString getDatabaseRootUuid(); virtual QString getDatabaseRecycleBinUuid(); virtual QString getKey(const QString& id); + virtual QList getKeys(const QString& id); virtual QString storeKey(const QString& key); virtual QList findMatchingEntries(const QString& id, const QString& url, const QString& submitUrl, const QString& realm); virtual int countMatchingEntries(const QString& id, const QString& url, const QString& submitUrl, const QString& realm); @@ -49,7 +50,9 @@ public slots: private: enum Access { Denied, Unknown, Allowed}; + QList getConfigEntries(bool create = false); Entry* getConfigEntry(bool create = false); + bool attributeExists(QList entries, const QString attribute); bool matchUrlScheme(const QString& url); Access checkAccess(const Entry* entry, const QString& host, const QString& submitHost, const QString& realm); bool removeFirstDomain(QString& hostname);