From 5c49e8d525efb33e6cbd9545394d0fcbd96c8073 Mon Sep 17 00:00:00 2001 From: varjolintu Date: Wed, 23 Jan 2019 09:38:01 +0200 Subject: [PATCH] Select group when adding credentials from browser extension --- src/browser/BrowserAction.cpp | 42 ++++++++++++++- src/browser/BrowserAction.h | 4 +- src/browser/BrowserService.cpp | 95 ++++++++++++++++++++++++++++++---- src/browser/BrowserService.h | 5 ++ src/core/Tools.cpp | 4 ++ src/core/Tools.h | 1 + 6 files changed, 140 insertions(+), 11 deletions(-) diff --git a/src/browser/BrowserAction.cpp b/src/browser/BrowserAction.cpp index ed3a40f6d4..fa1f20b099 100644 --- a/src/browser/BrowserAction.cpp +++ b/src/browser/BrowserAction.cpp @@ -85,6 +85,8 @@ QJsonObject BrowserAction::handleAction(const QJsonObject& json) return handleSetLogin(json, action); } else if (action.compare("lock-database", Qt::CaseSensitive) == 0) { return handleLockDatabase(json, action); + } else if (action.compare("get-database-groups", Qt::CaseSensitive) == 0) { + return handleGetDatabaseGroups(json, action); } // Action was not recognized @@ -320,10 +322,12 @@ QJsonObject BrowserAction::handleSetLogin(const QJsonObject& json, const QString const QString password = decrypted.value("password").toString(); const QString submitUrl = decrypted.value("submitUrl").toString(); const QString uuid = decrypted.value("uuid").toString(); + const QString group = decrypted.value("group").toString(); + const QString groupUuid = decrypted.value("groupUuid").toString(); const QString realm; if (uuid.isEmpty()) { - m_browserService.addEntry(id, login, password, url, submitUrl, realm); + m_browserService.addEntry(id, login, password, url, submitUrl, realm, group, groupUuid); } else { m_browserService.updateEntry(id, uuid, login, password, url, submitUrl); } @@ -368,6 +372,40 @@ QJsonObject BrowserAction::handleLockDatabase(const QJsonObject& json, const QSt return getErrorReply(action, ERROR_KEEPASS_DATABASE_HASH_NOT_RECEIVED); } +QJsonObject BrowserAction::handleGetDatabaseGroups(const QJsonObject& json, const QString& action) +{ + const QString hash = getDatabaseHash(); + const QString nonce = json.value("nonce").toString(); + const QString encrypted = json.value("message").toString(); + + QMutexLocker locker(&m_mutex); + if (!m_associated) { + return getErrorReply(action, ERROR_KEEPASS_ASSOCIATION_FAILED); + } + + const QJsonObject decrypted = decryptMessage(encrypted, nonce, action); + if (decrypted.isEmpty()) { + return getErrorReply(action, ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE); + } + + QString command = decrypted.value("action").toString(); + if (command.isEmpty() || command.compare("get-database-groups", Qt::CaseSensitive) != 0) { + return getErrorReply(action, ERROR_KEEPASS_INCORRECT_ACTION); + } + + const QJsonObject groups = m_browserService.getDatabaseGroups(); + if (groups.isEmpty()) { + return getErrorReply(action, ERROR_KEEPASS_NO_GROUPS_FOUND); + } + + const QString newNonce = incrementNonce(nonce); + + QJsonObject message = buildMessage(newNonce); + message["groups"] = groups; + + return buildResponse(action, message, newNonce); +} + QJsonObject BrowserAction::getErrorReply(const QString& action, const int errorCode) const { QJsonObject response; @@ -427,6 +465,8 @@ QString BrowserAction::getErrorMessage(const int errorCode) const return QObject::tr("No URL provided"); case ERROR_KEEPASS_NO_LOGINS_FOUND: return QObject::tr("No logins found"); + case ERROR_KEEPASS_NO_GROUPS_FOUND: + return QObject::tr("No groups found"); default: return QObject::tr("Unknown error"); } diff --git a/src/browser/BrowserAction.h b/src/browser/BrowserAction.h index 41a3b13dcd..29736ab4e7 100644 --- a/src/browser/BrowserAction.h +++ b/src/browser/BrowserAction.h @@ -45,7 +45,8 @@ class BrowserAction : public QObject ERROR_KEEPASS_INCORRECT_ACTION = 12, ERROR_KEEPASS_EMPTY_MESSAGE_RECEIVED = 13, ERROR_KEEPASS_NO_URL_PROVIDED = 14, - ERROR_KEEPASS_NO_LOGINS_FOUND = 15 + ERROR_KEEPASS_NO_LOGINS_FOUND = 15, + ERROR_KEEPASS_NO_GROUPS_FOUND = 16 }; public: @@ -64,6 +65,7 @@ class BrowserAction : public QObject QJsonObject handleGeneratePassword(const QJsonObject& json, const QString& action); QJsonObject handleSetLogin(const QJsonObject& json, const QString& action); QJsonObject handleLockDatabase(const QJsonObject& json, const QString& action); + QJsonObject handleGetDatabaseGroups(const QJsonObject& json, const QString& action); QJsonObject buildMessage(const QString& nonce) const; QJsonObject buildResponse(const QString& action, const QJsonObject& message, const QString& nonce); diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index a1ea5d6849..2224560ccb 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -56,7 +56,7 @@ BrowserService::BrowserService(DatabaseTabWidget* parent) , m_bringToFrontRequested(false) , m_wasMinimized(false) , m_wasHidden(false) - , m_keepassBrowserUUID(QUuid::fromRfc4122(QByteArray::fromHex("de887cc3036343b8974b5911b8816224"))) + , m_keepassBrowserUUID(Tools::hexToUuid("de887cc3036343b8974b5911b8816224")) { // Don't connect the signals when used from DatabaseSettingsWidgetBrowser (parent is nullptr) if (m_dbTabWidget) { @@ -148,6 +148,54 @@ QString BrowserService::getDatabaseRecycleBinUuid() return recycleBin->uuidToHex(); } +QJsonArray BrowserService::addChildrenToGroup(Group* group) +{ + QJsonArray groupList; + + if (!group) { + return groupList; + } + + for (const auto& c : group->children()) { + if (c == group->database()->metadata()->recycleBin()) { + continue; + } + + QJsonObject jsonGroup; + jsonGroup["name"] = c->name(); + jsonGroup["uuid"] = Tools::uuidToHex(c->uuid()); + jsonGroup["children"] = addChildrenToGroup(c); + groupList.push_back(jsonGroup); + } + return groupList; +} + +QJsonObject BrowserService::getDatabaseGroups() +{ + auto db = getDatabase(); + if (!db) { + return {}; + } + + Group* rootGroup = db->rootGroup(); + if (!rootGroup) { + return {}; + } + + QJsonObject root; + root["name"] = rootGroup->name(); + root["uuid"] = Tools::uuidToHex(rootGroup->uuid()); + root["children"] = addChildrenToGroup(rootGroup); + + QJsonArray groups; + groups.push_back(root); + + QJsonObject result; + result["groups"] = groups; + + return result; +} + QString BrowserService::storeKey(const QString& key) { QString id; @@ -292,6 +340,7 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id, void BrowserService::addEntry(const QString& id, const QString& login, const QString& password, const QString& url, const QString& submitUrl, const QString& realm, + const QString& group, const QString& groupUuid, QSharedPointer selectedDb) { if (thread() != QThread::currentThread()) { @@ -304,6 +353,8 @@ void BrowserService::addEntry(const QString& id, const QString& login, Q_ARG(QString, url), Q_ARG(QString, submitUrl), Q_ARG(QString, realm), + Q_ARG(QString, group), + Q_ARG(QString, groupUuid), Q_ARG(QSharedPointer, selectedDb)); } @@ -312,8 +363,8 @@ void BrowserService::addEntry(const QString& id, const QString& login, return; } - Group* group = findCreateAddEntryGroup(db); - if (!group) { + auto* addEntryGroup = findCreateAddEntryGroup(db); + if (!addEntryGroup) { return; } @@ -324,7 +375,17 @@ void BrowserService::addEntry(const QString& id, const QString& login, entry->setIcon(KEEPASSXCBROWSER_DEFAULT_ICON); entry->setUsername(login); entry->setPassword(password); - entry->setGroup(group); + entry->setGroup(addEntryGroup); + + // Select a group for the entry + if (!group.isEmpty()) { + if (db->rootGroup()) { + auto selectedGroup = db->rootGroup()->findGroupByUuid(Tools::hexToUuid(groupUuid)); + if (selectedGroup && selectedGroup->name() == group) { + entry->setGroup(selectedGroup); + } + } + } const QString host = QUrl(url).host(); const QString submitHost = QUrl(submitUrl).host(); @@ -364,10 +425,10 @@ void BrowserService::updateEntry(const QString& id, return; } - Entry* entry = db->rootGroup()->findEntryByUuid(QUuid::fromRfc4122(QByteArray::fromHex(uuid.toLatin1()))); + Entry* entry = db->rootGroup()->findEntryByUuid(Tools::hexToUuid(uuid)); if (!entry) { // If entry is not found for update, add a new one to the selected database - addEntry(id, login, password, url, submitUrl, "", db); + addEntry(id, login, password, url, submitUrl, "", "", "", db); return; } @@ -704,6 +765,21 @@ BrowserService::checkAccess(const Entry* entry, return Unknown; } +bool BrowserService::insideRecycleBin(Group* group) +{ + do { + if (group->parentGroup()) { + if (group->parentGroup() == group->database()->metadata()->recycleBin()) { + return true; + } + + group = group->parentGroup(); + } + } while (group && group->parentGroup() && group->parentGroup() != group->database()->rootGroup()); + + return false; +} + Group* BrowserService::findCreateAddEntryGroup(QSharedPointer selectedDb) { auto db = selectedDb ? selectedDb : getDatabase(); @@ -711,7 +787,7 @@ Group* BrowserService::findCreateAddEntryGroup(QSharedPointer selected return nullptr; } - Group* rootGroup = db->rootGroup(); + auto* rootGroup = db->rootGroup(); if (!rootGroup) { return nullptr; } @@ -719,8 +795,8 @@ Group* BrowserService::findCreateAddEntryGroup(QSharedPointer selected const QString groupName = QLatin1String(KEEPASSXCBROWSER_GROUP_NAME); // TODO: setting to decide where new keys are created - for (const Group* g : rootGroup->groupsRecursive(true)) { - if (g->name() == groupName) { + for (auto* g : rootGroup->groupsRecursive(true)) { + if (g->name() == groupName && !insideRecycleBin(g)) { return db->rootGroup()->findGroupByUuid(g->uuid()); } } @@ -959,6 +1035,7 @@ void BrowserService::hideWindow() const void BrowserService::raiseWindow(const bool force) { + m_wasMinimized = force; m_wasMinimized = getMainWindow()->isMinimized(); #ifdef Q_OS_MACOS m_wasHidden = macUtils()->isHidden(); diff --git a/src/browser/BrowserService.h b/src/browser/BrowserService.h index 8e1c45f4a4..a015f606fd 100644 --- a/src/browser/BrowserService.h +++ b/src/browser/BrowserService.h @@ -44,6 +44,7 @@ class BrowserService : public QObject bool openDatabase(bool triggerUnlock); QString getDatabaseRootUuid(); QString getDatabaseRecycleBinUuid(); + QJsonObject getDatabaseGroups(); QString getKey(const QString& id); void addEntry(const QString& id, const QString& login, @@ -51,6 +52,8 @@ class BrowserService : public QObject const QString& url, const QString& submitUrl, const QString& realm, + const QString& group, + const QString& groupUuid, QSharedPointer selectedDb = {}); QList searchEntries(QSharedPointer db, const QString& hostname, const QString& url); QList searchEntries(const QString& url, const StringPairList& keyList); @@ -114,6 +117,8 @@ public slots: QString baseDomain(const QString& url) const; QSharedPointer getDatabase(); QSharedPointer selectedDatabase(); + QJsonArray addChildrenToGroup(Group* group); + bool insideRecycleBin(Group* group); bool moveSettingsToCustomData(Entry* entry, const QString& name) const; int moveKeysToCustomData(Entry* entry, QSharedPointer db) const; bool checkLegacySettings(); diff --git a/src/core/Tools.cpp b/src/core/Tools.cpp index 7fc2a34622..3ec0515579 100644 --- a/src/core/Tools.cpp +++ b/src/core/Tools.cpp @@ -212,6 +212,10 @@ namespace Tools return QString::fromLatin1(uuid.toRfc4122().toHex()); } + QUuid hexToUuid(const QString& uuid) { + return QUuid::fromRfc4122(QByteArray::fromHex(uuid.toLatin1())); + } + Buffer::Buffer() : raw(nullptr) , size(0) diff --git a/src/core/Tools.h b/src/core/Tools.h index 6d190a4665..a2c09efe22 100644 --- a/src/core/Tools.h +++ b/src/core/Tools.h @@ -41,6 +41,7 @@ namespace Tools void sleep(int ms); void wait(int ms); QString uuidToHex(const QUuid& uuid); + QUuid hexToUuid(const QString& uuid); QRegularExpression convertToRegex(const QString& string, bool useWildcards = false, bool exactMatch = false,