Skip to content

Commit

Permalink
Add support for multiple URLs
Browse files Browse the repository at this point in the history
  • Loading branch information
varjolintu committed Oct 16, 2019
1 parent e50261a commit e1a84b2
Show file tree
Hide file tree
Showing 13 changed files with 550 additions and 75 deletions.
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ add_subdirectory(proxy)
if(WITH_XC_BROWSER)
set(keepassxcbrowser_LIB keepassxcbrowser)
set(keepassx_SOURCES ${keepassx_SOURCES} gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp)
set(keepassx_SOURCES ${keepassx_SOURCES} gui/entry/EntryURLModel.cpp)
endif()

add_subdirectory(autotype)
Expand Down
120 changes: 72 additions & 48 deletions src/browser/BrowserService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,20 @@
#include "gui/macutils/MacUtils.h"
#endif

const char BrowserService::KEEPASSXCBROWSER_NAME[] = "KeePassXC-Browser Settings";
const char BrowserService::KEEPASSXCBROWSER_OLD_NAME[] = "keepassxc-browser Settings";
const char BrowserService::ASSOCIATE_KEY_PREFIX[] = "KPXC_BROWSER_";
static const char KEEPASSXCBROWSER_GROUP_NAME[] = "KeePassXC-Browser Passwords";
const QString BrowserService::KEEPASSXCBROWSER_NAME = QStringLiteral("KeePassXC-Browser Settings");
const QString BrowserService::KEEPASSXCBROWSER_OLD_NAME = QStringLiteral("keepassxc-browser Settings");
const QString BrowserService::ASSOCIATE_KEY_PREFIX = QStringLiteral("KPXC_BROWSER_");
static const QString KEEPASSXCBROWSER_GROUP_NAME = QStringLiteral("KeePassXC-Browser Passwords");
static int KEEPASSXCBROWSER_DEFAULT_ICON = 1;
// These are for the settings and password conversion
const char BrowserService::LEGACY_ASSOCIATE_KEY_PREFIX[] = "Public Key: ";
static const char KEEPASSHTTP_NAME[] = "KeePassHttp Settings";
static const char KEEPASSHTTP_GROUP_NAME[] = "KeePassHttp Passwords";
const QString BrowserService::LEGACY_ASSOCIATE_KEY_PREFIX = QStringLiteral("Public Key: ");
static const QString KEEPASSHTTP_NAME = QStringLiteral("KeePassHttp Settings");
static const QString KEEPASSHTTP_GROUP_NAME = QStringLiteral("KeePassHttp Passwords");
// Extra entry related options saved in custom data
const char BrowserService::OPTION_SKIP_AUTO_SUBMIT[] = "BrowserSkipAutoSubmit";
const char BrowserService::OPTION_HIDE_ENTRY[] = "BrowserHideEntry";
const QString BrowserService::OPTION_SKIP_AUTO_SUBMIT = QStringLiteral("BrowserSkipAutoSubmit");
const QString BrowserService::OPTION_HIDE_ENTRY = QStringLiteral("BrowserHideEntry");
// Multiple URL's
const QString BrowserService::ADDITIONAL_URL = QStringLiteral("KP2A_URL");

BrowserService::BrowserService(DatabaseTabWidget* parent)
: m_dbTabWidget(parent)
Expand Down Expand Up @@ -320,7 +322,7 @@ QString BrowserService::storeKey(const QString& key)
return {};
}

contains = db->metadata()->customData()->contains(QLatin1String(ASSOCIATE_KEY_PREFIX) + id);
contains = db->metadata()->customData()->contains(ASSOCIATE_KEY_PREFIX + id);
if (contains) {
dialogResult = MessageBox::warning(nullptr,
tr("KeePassXC: Overwrite existing key?"),
Expand All @@ -333,7 +335,7 @@ QString BrowserService::storeKey(const QString& key)
} while (contains && dialogResult == MessageBox::Cancel);

hideWindow();
db->metadata()->customData()->set(QLatin1String(ASSOCIATE_KEY_PREFIX) + id, key);
db->metadata()->customData()->set(ASSOCIATE_KEY_PREFIX + id, key);
return id;
}

Expand All @@ -344,7 +346,7 @@ QString BrowserService::getKey(const QString& id)
return {};
}

return db->metadata()->customData()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + id);
return db->metadata()->customData()->value(ASSOCIATE_KEY_PREFIX + id);
}

QJsonArray BrowserService::findMatchingEntries(const QString& id,
Expand Down Expand Up @@ -377,9 +379,9 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id,
// Check entries for authorization
QList<Entry*> pwEntriesToConfirm;
QList<Entry*> pwEntries;
for (Entry* entry : searchEntries(url, keyList)) {
if (entry->customData()->contains(BrowserService::OPTION_HIDE_ENTRY) &&
entry->customData()->value(BrowserService::OPTION_HIDE_ENTRY) == "true") {
for (auto* entry : searchEntries(url, keyList)) {
if (entry->customData()->contains(BrowserService::OPTION_HIDE_ENTRY)
&& entry->customData()->value(BrowserService::OPTION_HIDE_ENTRY) == "true") {
continue;
}

Expand Down Expand Up @@ -425,7 +427,7 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id,
pwEntries = sortEntries(pwEntries, host, submitUrl);

// Fill the list
for (Entry* entry : pwEntries) {
for (auto* entry : pwEntries) {
result.append(prepareEntry(entry));
}

Expand Down Expand Up @@ -588,22 +590,30 @@ BrowserService::searchEntries(const QSharedPointer<Database>& db, const QString&
return entries;
}

for (Entry* entry : EntrySearcher().search(baseDomain(hostname), rootGroup)) {
QString entryUrl = entry->url();
QUrl entryQUrl(entryUrl);
QString entryScheme = entryQUrl.scheme();
QUrl qUrl(url);

// Ignore entry if port or scheme defined in the URL doesn't match
if ((entryQUrl.port() > 0 && entryQUrl.port() != qUrl.port())
|| (browserSettings()->matchUrlScheme() && !entryScheme.isEmpty()
&& entryScheme.compare(qUrl.scheme()) != 0)) {
for (const auto& group : rootGroup->groupsRecursive(true)) {
if (group->isRecycled() || !group->resolveSearchingEnabled()) {
continue;
}

// Filter to match hostname in URL field
if ((!entryUrl.isEmpty() && hostname.contains(entryUrl))
|| (matchUrlScheme(entryUrl) && hostname.endsWith(entryQUrl.host()))) {
for (auto* entry: group->entries()) {
if (entry->isRecycled()) {
continue;
}

// Search for additional URL's starting with KP2A_URL
if (entry->attributes()->keys().contains(ADDITIONAL_URL)) {
for (const auto& key : entry->attributes()->keys()) {
if (key.startsWith(ADDITIONAL_URL) && handleURL(entry->attributes()->value(key), hostname, url)) {
entries.append(entry);
continue;
}
}
}

if (!handleURL(entry->url(), hostname, url)) {
continue;
}

entries.append(entry);
}
}
Expand All @@ -616,7 +626,7 @@ QList<Entry*> BrowserService::searchEntries(const QString& url, const StringPair
// Check if database is connected with KeePassXC-Browser
auto databaseConnected = [&](const QSharedPointer<Database>& db) {
for (const StringPair& keyPair : keyList) {
QString key = db->metadata()->customData()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + keyPair.first);
QString key = db->metadata()->customData()->value(ASSOCIATE_KEY_PREFIX + keyPair.first);
if (!key.isEmpty() && keyPair.second == key) {
return true;
}
Expand Down Expand Up @@ -668,7 +678,7 @@ void BrowserService::convertAttributesToCustomData(const QSharedPointer<Database

int counter = 0;
int keyCounter = 0;
for (Entry* entry : entries) {
for (auto* entry : entries) {
if (progress.wasCanceled()) {
return;
}
Expand Down Expand Up @@ -721,12 +731,9 @@ void BrowserService::convertAttributesToCustomData(const QSharedPointer<Database
return;
}

const QString keePassBrowserGroupName = QLatin1String(KEEPASSXCBROWSER_GROUP_NAME);
const QString keePassHttpGroupName = QLatin1String(KEEPASSHTTP_GROUP_NAME);

for (Group* g : rootGroup->groupsRecursive(true)) {
if (g->name() == keePassHttpGroupName) {
g->setName(keePassBrowserGroupName);
for (auto* g : rootGroup->groupsRecursive(true)) {
if (g->name() == KEEPASSHTTP_GROUP_NAME) {
g->setName(KEEPASSXCBROWSER_GROUP_NAME);
break;
}
}
Expand All @@ -745,7 +752,7 @@ QList<Entry*> BrowserService::sortEntries(QList<Entry*>& pwEntries, const QStrin

// Build map of prioritized entries
QMultiMap<int, Entry*> priorities;
for (Entry* entry : pwEntries) {
for (auto* entry : pwEntries) {
priorities.insert(sortPriority(entry, host, submitUrl, baseSubmitUrl), entry);
}

Expand Down Expand Up @@ -801,7 +808,7 @@ bool BrowserService::confirmEntries(QList<Entry*>& pwEntriesToConfirm,

int res = accessControlDialog.exec();
if (accessControlDialog.remember()) {
for (Entry* entry : pwEntriesToConfirm) {
for (auto* entry : pwEntriesToConfirm) {
BrowserEntryConfig config;
config.load(entry);
if (res == QDialog::Accepted) {
Expand Down Expand Up @@ -853,8 +860,8 @@ QJsonObject BrowserService::prepareEntry(const Entry* entry)
if (browserSettings()->supportKphFields()) {
const EntryAttributes* attr = entry->attributes();
QJsonArray stringFields;
for (const QString& key : attr->keys()) {
if (key.startsWith(QLatin1String("KPH: "))) {
for (const auto& key : attr->keys()) {
if (key.startsWith("KPH: ")) {
QJsonObject sField;
sField[key] = entry->resolveMultiplePlaceholders(attr->value(key));
stringFields.append(sField);
Expand Down Expand Up @@ -899,17 +906,15 @@ Group* BrowserService::getDefaultEntryGroup(const QSharedPointer<Database>& sele
return nullptr;
}

const QString groupName = QLatin1String(KEEPASSXCBROWSER_GROUP_NAME);

for (auto* g : rootGroup->groupsRecursive(true)) {
if (g->name() == groupName && !g->isRecycled()) {
if (g->name() == KEEPASSXCBROWSER_GROUP_NAME && !g->isRecycled()) {
return db->rootGroup()->findGroupByUuid(g->uuid());
}
}

auto* group = new Group();
group->setUuid(QUuid::createUuid());
group->setName(groupName);
group->setName(KEEPASSXCBROWSER_GROUP_NAME);
group->setIcon(KEEPASSXCBROWSER_DEFAULT_ICON);
group->setParent(rootGroup);
return group;
Expand Down Expand Up @@ -987,6 +992,26 @@ bool BrowserService::removeFirstDomain(QString& hostname)
return false;
}

bool BrowserService::handleURL(const QString& entryUrl, const QString& hostname, const QString& url) {
QUrl entryQUrl(entryUrl);
QString entryScheme = entryQUrl.scheme();
QUrl qUrl(url);

// Ignore entry if port or scheme defined in the URL doesn't match
if ((entryQUrl.port() > 0 && entryQUrl.port() != qUrl.port())
|| (browserSettings()->matchUrlScheme() && !entryScheme.isEmpty()
&& entryScheme.compare(qUrl.scheme()) != 0)) {
return false;
}

// Filter to match hostname in URL field
if ((!entryUrl.isEmpty() && hostname.contains(entryUrl))
|| (matchUrlScheme(entryUrl) && hostname.endsWith(entryQUrl.host()))) {
return true;
}
return false;
};

/**
* Gets the base domain of URL.
*
Expand Down Expand Up @@ -1080,9 +1105,8 @@ int BrowserService::moveKeysToCustomData(Entry* entry, const QSharedPointer<Data
publicKey.remove(LEGACY_ASSOCIATE_KEY_PREFIX);

// Add key to database custom data
if (db && !db->metadata()->customData()->contains(QLatin1String(ASSOCIATE_KEY_PREFIX) + publicKey)) {
db->metadata()->customData()->set(QLatin1String(ASSOCIATE_KEY_PREFIX) + publicKey,
entry->attributes()->value(key));
if (db && !db->metadata()->customData()->contains(ASSOCIATE_KEY_PREFIX + publicKey)) {
db->metadata()->customData()->set(ASSOCIATE_KEY_PREFIX + publicKey, entry->attributes()->value(key));
++keyCounter;
}
}
Expand Down
14 changes: 8 additions & 6 deletions src/browser/BrowserService.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,13 @@ class BrowserService : public QObject
void convertAttributesToCustomData(const QSharedPointer<Database>& currentDb = {});

public:
static const char KEEPASSXCBROWSER_NAME[];
static const char KEEPASSXCBROWSER_OLD_NAME[];
static const char ASSOCIATE_KEY_PREFIX[];
static const char LEGACY_ASSOCIATE_KEY_PREFIX[];
static const char OPTION_SKIP_AUTO_SUBMIT[];
static const char OPTION_HIDE_ENTRY[];
static const QString KEEPASSXCBROWSER_NAME;
static const QString KEEPASSXCBROWSER_OLD_NAME;
static const QString ASSOCIATE_KEY_PREFIX;
static const QString LEGACY_ASSOCIATE_KEY_PREFIX;
static const QString OPTION_SKIP_AUTO_SUBMIT;
static const QString OPTION_HIDE_ENTRY;
static const QString ADDITIONAL_URL;

public slots:
QJsonArray findMatchingEntries(const QString& id,
Expand Down Expand Up @@ -129,6 +130,7 @@ public slots:
sortPriority(const Entry* entry, const QString& host, const QString& submitUrl, const QString& baseSubmitUrl) const;
bool matchUrlScheme(const QString& url);
bool removeFirstDomain(QString& hostname);
bool handleURL(const QString& entryUrl, const QString& hostname, const QString& url);
QString baseDomain(const QString& url) const;
QSharedPointer<Database> getDatabase();
QSharedPointer<Database> selectedDatabase();
Expand Down
Loading

0 comments on commit e1a84b2

Please sign in to comment.