Skip to content

Commit

Permalink
Select group when adding credentials from browser extension
Browse files Browse the repository at this point in the history
  • Loading branch information
varjolintu committed Jan 29, 2019
1 parent d662992 commit 2ad87a0
Show file tree
Hide file tree
Showing 10 changed files with 212 additions and 43 deletions.
42 changes: 41 additions & 1 deletion src/browser/BrowserAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
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;
Expand Down Expand Up @@ -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");
}
Expand Down
4 changes: 3 additions & 1 deletion src/browser/BrowserAction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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);
Expand Down
130 changes: 98 additions & 32 deletions src/browser/BrowserService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -77,7 +77,8 @@ bool BrowserService::isDatabaseOpened() const
return false;
}

return dbWidget->currentMode() == DatabaseWidget::Mode::ViewMode || dbWidget->currentMode() == DatabaseWidget::Mode::EditMode;
return dbWidget->currentMode() == DatabaseWidget::Mode::ViewMode
|| dbWidget->currentMode() == DatabaseWidget::Mode::EditMode;
}

bool BrowserService::openDatabase(bool triggerUnlock)
Expand All @@ -91,7 +92,8 @@ bool BrowserService::openDatabase(bool triggerUnlock)
return false;
}

if (dbWidget->currentMode() == DatabaseWidget::Mode::ViewMode || dbWidget->currentMode() == DatabaseWidget::Mode::EditMode) {
if (dbWidget->currentMode() == DatabaseWidget::Mode::ViewMode
|| dbWidget->currentMode() == DatabaseWidget::Mode::EditMode) {
return true;
}

Expand All @@ -114,7 +116,8 @@ void BrowserService::lockDatabase()
return;
}

if (dbWidget->currentMode() == DatabaseWidget::Mode::ViewMode || dbWidget->currentMode() == DatabaseWidget::Mode::EditMode) {
if (dbWidget->currentMode() == DatabaseWidget::Mode::ViewMode
|| dbWidget->currentMode() == DatabaseWidget::Mode::EditMode) {
dbWidget->lock();
}
}
Expand Down Expand Up @@ -148,6 +151,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;
Expand Down Expand Up @@ -289,9 +340,14 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id,
return result;
}

void BrowserService::addEntry(const QString& id, const QString& login,
const QString& password, const QString& url,
const QString& submitUrl, const QString& realm,
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<Database> selectedDb)
{
if (thread() != QThread::currentThread()) {
Expand All @@ -304,6 +360,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<Database>, selectedDb));
}

Expand All @@ -312,8 +370,8 @@ void BrowserService::addEntry(const QString& id, const QString& login,
return;
}

Group* group = findCreateAddEntryGroup(db);
if (!group) {
auto* addEntryGroup = findCreateAddEntryGroup(db);
if (!addEntryGroup) {
return;
}

Expand All @@ -324,7 +382,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();
Expand Down Expand Up @@ -364,10 +432,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;
}

Expand All @@ -392,12 +460,13 @@ void BrowserService::updateEntry(const QString& id,
MessageBox::Button dialogResult = MessageBox::No;
if (!browserSettings()->alwaysAllowUpdate()) {
raiseWindow();
dialogResult = MessageBox::question(nullptr,
tr("KeePassXC: Update Entry"),
tr("Do you want to update the information in %1 - %2?")
.arg(QUrl(url).host(), username),
MessageBox::Save | MessageBox::Cancel,
MessageBox::Cancel, MessageBox::Raise);
dialogResult = MessageBox::question(
nullptr,
tr("KeePassXC: Update Entry"),
tr("Do you want to update the information in %1 - %2?").arg(QUrl(url).host(), username),
MessageBox::Save | MessageBox::Cancel,
MessageBox::Cancel,
MessageBox::Raise);
}

if (browserSettings()->alwaysAllowUpdate() || dialogResult == MessageBox::Save) {
Expand Down Expand Up @@ -453,7 +522,7 @@ QList<Entry*> BrowserService::searchEntries(const QString& url, const StringPair
for (int i = 0; i < count; ++i) {
if (auto* dbWidget = qobject_cast<DatabaseWidget*>(m_dbTabWidget->widget(i))) {
if (const auto& db = dbWidget->database()) {
// Check if database is connected with KeePassXC-Browser
// Check if database is connected with KeePassXC-Browser
for (const StringPair& keyPair : keyList) {
QString key =
db->metadata()->customData()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + keyPair.first);
Expand Down Expand Up @@ -680,10 +749,7 @@ QJsonObject BrowserService::prepareEntry(const Entry* entry)
}

BrowserService::Access
BrowserService::checkAccess(const Entry* entry,
const QString& host,
const QString& submitHost,
const QString& realm)
BrowserService::checkAccess(const Entry* entry, const QString& host, const QString& submitHost, const QString& realm)
{
BrowserEntryConfig config;
if (!config.load(entry)) {
Expand Down Expand Up @@ -711,16 +777,16 @@ Group* BrowserService::findCreateAddEntryGroup(QSharedPointer<Database> selected
return nullptr;
}

Group* rootGroup = db->rootGroup();
auto* rootGroup = db->rootGroup();
if (!rootGroup) {
return nullptr;
}

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 && !g->isRecycled()) {
return db->rootGroup()->findGroupByUuid(g->uuid());
}
}
Expand Down Expand Up @@ -841,12 +907,12 @@ QSharedPointer<Database> BrowserService::getDatabase()
QSharedPointer<Database> BrowserService::selectedDatabase()
{
QList<DatabaseWidget*> databaseWidgets;
for (int i = 0; ; ++i) {
for (int i = 0;; ++i) {
auto* dbWidget = m_dbTabWidget->databaseWidgetFromIndex(i);
// Add only open databases
if (dbWidget && dbWidget->database()->hasKey() &&
(dbWidget->currentMode() == DatabaseWidget::Mode::ViewMode ||
dbWidget->currentMode() == DatabaseWidget::Mode::EditMode)) {
if (dbWidget && dbWidget->database()->hasKey()
&& (dbWidget->currentMode() == DatabaseWidget::Mode::ViewMode
|| dbWidget->currentMode() == DatabaseWidget::Mode::EditMode)) {
databaseWidgets.push_back(dbWidget);
continue;
}
Expand Down Expand Up @@ -919,8 +985,8 @@ bool BrowserService::checkLegacySettings()
bool legacySettingsFound = false;
QList<Entry*> entries = db->rootGroup()->entriesRecursive();
for (const auto& e : entries) {
if ((e->attributes()->contains(KEEPASSHTTP_NAME) || e->attributes()->contains(KEEPASSXCBROWSER_NAME)) ||
(e->title() == KEEPASSHTTP_NAME || e->title().contains(KEEPASSXCBROWSER_NAME, Qt::CaseInsensitive))) {
if ((e->attributes()->contains(KEEPASSHTTP_NAME) || e->attributes()->contains(KEEPASSXCBROWSER_NAME))
|| (e->title() == KEEPASSHTTP_NAME || e->title().contains(KEEPASSXCBROWSER_NAME, Qt::CaseInsensitive))) {
legacySettingsFound = true;
break;
}
Expand Down
9 changes: 5 additions & 4 deletions src/browser/BrowserService.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,16 @@ 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,
const QString& password,
const QString& url,
const QString& submitUrl,
const QString& realm,
const QString& group,
const QString& groupUuid,
QSharedPointer<Database> selectedDb = {});
QList<Entry*> searchEntries(QSharedPointer<Database> db, const QString& hostname, const QString& url);
QList<Entry*> searchEntries(const QString& url, const StringPairList& keyList);
Expand Down Expand Up @@ -102,10 +105,7 @@ public slots:
const QString& submitHost,
const QString& realm);
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(QSharedPointer<Database> selectedDb = {});
int
sortPriority(const Entry* entry, const QString& host, const QString& submitUrl, const QString& baseSubmitUrl) const;
Expand All @@ -114,6 +114,7 @@ public slots:
QString baseDomain(const QString& url) const;
QSharedPointer<Database> getDatabase();
QSharedPointer<Database> selectedDatabase();
QJsonArray addChildrenToGroup(Group* group);
bool moveSettingsToCustomData(Entry* entry, const QString& name) const;
int moveKeysToCustomData(Entry* entry, QSharedPointer<Database> db) const;
bool checkLegacySettings();
Expand Down
Loading

0 comments on commit 2ad87a0

Please sign in to comment.