Browser: Add a new group setting for omitting WWW subdomain when matching URLs

This commit is contained in:
varjolintu 2022-06-05 10:37:00 +03:00 committed by Jonathan White
parent a2aac7066c
commit 6cb6f1f007
6 changed files with 130 additions and 68 deletions

View File

@ -3088,6 +3088,14 @@ Would you like to correct it?</source>
<source>Do not use HTTP Auth toggle for this and sub groups</source> <source>Do not use HTTP Auth toggle for this and sub groups</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Omit WWW subdomain from matching:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Omit WWW subdomain from matching toggle for this and sub groups</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>EditGroupWidgetKeeShare</name> <name>EditGroupWidgetKeeShare</name>

View File

@ -56,6 +56,7 @@ const QString BrowserService::OPTION_SKIP_AUTO_SUBMIT = QStringLiteral("BrowserS
const QString BrowserService::OPTION_HIDE_ENTRY = QStringLiteral("BrowserHideEntry"); const QString BrowserService::OPTION_HIDE_ENTRY = QStringLiteral("BrowserHideEntry");
const QString BrowserService::OPTION_ONLY_HTTP_AUTH = QStringLiteral("BrowserOnlyHttpAuth"); const QString BrowserService::OPTION_ONLY_HTTP_AUTH = QStringLiteral("BrowserOnlyHttpAuth");
const QString BrowserService::OPTION_NOT_HTTP_AUTH = QStringLiteral("BrowserNotHttpAuth"); const QString BrowserService::OPTION_NOT_HTTP_AUTH = QStringLiteral("BrowserNotHttpAuth");
const QString BrowserService::OPTION_OMIT_WWW = QStringLiteral("BrowserOmitWww");
// Multiple URL's // Multiple URL's
const QString BrowserService::ADDITIONAL_URL = QStringLiteral("KP2A_URL"); const QString BrowserService::ADDITIONAL_URL = QStringLiteral("KP2A_URL");
@ -426,8 +427,8 @@ QString BrowserService::getKey(const QString& id)
} }
QJsonArray BrowserService::findMatchingEntries(const QString& dbid, QJsonArray BrowserService::findMatchingEntries(const QString& dbid,
const QString& siteUrlStr, const QString& siteUrl,
const QString& formUrlStr, const QString& formUrl,
const QString& realm, const QString& realm,
const StringPairList& keyList, const StringPairList& keyList,
const bool httpAuth) const bool httpAuth)
@ -435,13 +436,13 @@ QJsonArray BrowserService::findMatchingEntries(const QString& dbid,
Q_UNUSED(dbid); Q_UNUSED(dbid);
const bool alwaysAllowAccess = browserSettings()->alwaysAllowAccess(); const bool alwaysAllowAccess = browserSettings()->alwaysAllowAccess();
const bool ignoreHttpAuth = browserSettings()->httpAuthPermission(); const bool ignoreHttpAuth = browserSettings()->httpAuthPermission();
const QString siteHost = QUrl(siteUrlStr).host(); const QString siteHost = QUrl(siteUrl).host();
const QString formHost = QUrl(formUrlStr).host(); const QString formHost = QUrl(formUrl).host();
// Check entries for authorization // Check entries for authorization
QList<Entry*> pwEntriesToConfirm; QList<Entry*> pwEntriesToConfirm;
QList<Entry*> pwEntries; QList<Entry*> pwEntries;
for (auto* entry : searchEntries(siteUrlStr, formUrlStr, keyList)) { for (auto* entry : searchEntries(siteUrl, formUrl, keyList)) {
auto entryCustomData = entry->customData(); auto entryCustomData = entry->customData();
if (!httpAuth if (!httpAuth
@ -484,7 +485,7 @@ QJsonArray BrowserService::findMatchingEntries(const QString& dbid,
// Confirm entries // Confirm entries
QList<Entry*> selectedEntriesToConfirm = QList<Entry*> selectedEntriesToConfirm =
confirmEntries(pwEntriesToConfirm, siteUrlStr, siteHost, formHost, realm, httpAuth); confirmEntries(pwEntriesToConfirm, siteUrl, siteHost, formHost, realm, httpAuth);
if (!selectedEntriesToConfirm.isEmpty()) { if (!selectedEntriesToConfirm.isEmpty()) {
pwEntries.append(selectedEntriesToConfirm); pwEntries.append(selectedEntriesToConfirm);
} }
@ -499,7 +500,7 @@ QJsonArray BrowserService::findMatchingEntries(const QString& dbid,
} }
// Sort results // Sort results
pwEntries = sortEntries(pwEntries, siteUrlStr, formUrlStr); pwEntries = sortEntries(pwEntries, siteUrl, formUrl);
// Fill the list // Fill the list
QJsonArray result; QJsonArray result;
@ -513,8 +514,8 @@ QJsonArray BrowserService::findMatchingEntries(const QString& dbid,
void BrowserService::addEntry(const QString& dbid, void BrowserService::addEntry(const QString& dbid,
const QString& login, const QString& login,
const QString& password, const QString& password,
const QString& siteUrlStr, const QString& siteUrl,
const QString& formUrlStr, const QString& formUrl,
const QString& realm, const QString& realm,
const QString& group, const QString& group,
const QString& groupUuid, const QString& groupUuid,
@ -530,8 +531,8 @@ void BrowserService::addEntry(const QString& dbid,
auto* entry = new Entry(); auto* entry = new Entry();
entry->setUuid(QUuid::createUuid()); entry->setUuid(QUuid::createUuid());
entry->setTitle(QUrl(siteUrlStr).host()); entry->setTitle(QUrl(siteUrl).host());
entry->setUrl(siteUrlStr); entry->setUrl(siteUrl);
entry->setIcon(KEEPASSXCBROWSER_DEFAULT_ICON); entry->setIcon(KEEPASSXCBROWSER_DEFAULT_ICON);
entry->setUsername(login); entry->setUsername(login);
entry->setPassword(password); entry->setPassword(password);
@ -550,8 +551,8 @@ void BrowserService::addEntry(const QString& dbid,
entry->setGroup(getDefaultEntryGroup(db)); entry->setGroup(getDefaultEntryGroup(db));
} }
const QString host = QUrl(siteUrlStr).host(); const QString host = QUrl(siteUrl).host();
const QString submitHost = QUrl(formUrlStr).host(); const QString submitHost = QUrl(formUrl).host();
BrowserEntryConfig config; BrowserEntryConfig config;
config.allow(host); config.allow(host);
@ -572,8 +573,8 @@ bool BrowserService::updateEntry(const QString& dbid,
const QString& uuid, const QString& uuid,
const QString& login, const QString& login,
const QString& password, const QString& password,
const QString& siteUrlStr, const QString& siteUrl,
const QString& formUrlStr) const QString& formUrl)
{ {
// TODO: select database based on this key id // TODO: select database based on this key id
Q_UNUSED(dbid); Q_UNUSED(dbid);
@ -585,7 +586,7 @@ bool BrowserService::updateEntry(const QString& dbid,
Entry* entry = db->rootGroup()->findEntryByUuid(Tools::hexToUuid(uuid)); Entry* entry = db->rootGroup()->findEntryByUuid(Tools::hexToUuid(uuid));
if (!entry) { if (!entry) {
// If entry is not found for update, add a new one to the selected database // If entry is not found for update, add a new one to the selected database
addEntry(dbid, login, password, siteUrlStr, formUrlStr, "", "", "", db); addEntry(dbid, login, password, siteUrl, formUrl, "", "", "", db);
return true; return true;
} }
@ -614,7 +615,7 @@ bool BrowserService::updateEntry(const QString& dbid,
dialogResult = MessageBox::question( dialogResult = MessageBox::question(
nullptr, nullptr,
tr("KeePassXC: Update Entry"), tr("KeePassXC: Update Entry"),
tr("Do you want to update the information in %1 - %2?").arg(QUrl(siteUrlStr).host(), username), tr("Do you want to update the information in %1 - %2?").arg(QUrl(siteUrl).host(), username),
MessageBox::Save | MessageBox::Cancel, MessageBox::Save | MessageBox::Cancel,
MessageBox::Cancel, MessageBox::Cancel,
MessageBox::Raise); MessageBox::Raise);
@ -663,7 +664,7 @@ bool BrowserService::deleteEntry(const QString& uuid)
} }
QList<Entry*> QList<Entry*>
BrowserService::searchEntries(const QSharedPointer<Database>& db, const QString& siteUrlStr, const QString& formUrlStr) BrowserService::searchEntries(const QSharedPointer<Database>& db, const QString& siteUrl, const QString& formUrl)
{ {
QList<Entry*> entries; QList<Entry*> entries;
auto* rootGroup = db->rootGroup(); auto* rootGroup = db->rootGroup();
@ -677,6 +678,9 @@ BrowserService::searchEntries(const QSharedPointer<Database>& db, const QString&
continue; continue;
} }
const auto omitWwwSubdomain =
group->resolveCustomDataTriState(BrowserService::OPTION_OMIT_WWW) == Group::Enable;
for (auto* entry : group->entries()) { for (auto* entry : group->entries()) {
if (entry->isRecycled() if (entry->isRecycled()
|| (entry->customData()->contains(BrowserService::OPTION_HIDE_ENTRY) || (entry->customData()->contains(BrowserService::OPTION_HIDE_ENTRY)
@ -684,16 +688,7 @@ BrowserService::searchEntries(const QSharedPointer<Database>& db, const QString&
continue; continue;
} }
// Search for additional URL's starting with KP2A_URL if (!shouldIncludeEntry(entry, siteUrl, formUrl, omitWwwSubdomain)) {
for (const auto& key : entry->attributes()->keys()) {
if (key.startsWith(ADDITIONAL_URL) && handleURL(entry->attributes()->value(key), siteUrlStr, formUrlStr)
&& !entries.contains(entry)) {
entries.append(entry);
continue;
}
}
if (!handleEntry(entry, siteUrlStr, formUrlStr)) {
continue; continue;
} }
@ -708,7 +703,7 @@ BrowserService::searchEntries(const QSharedPointer<Database>& db, const QString&
} }
QList<Entry*> QList<Entry*>
BrowserService::searchEntries(const QString& siteUrlStr, const QString& formUrlStr, const StringPairList& keyList) BrowserService::searchEntries(const QString& siteUrl, const QString& formUrl, const StringPairList& keyList)
{ {
// Check if database is connected with KeePassXC-Browser // Check if database is connected with KeePassXC-Browser
auto databaseConnected = [&](const QSharedPointer<Database>& db) { auto databaseConnected = [&](const QSharedPointer<Database>& db) {
@ -738,11 +733,11 @@ BrowserService::searchEntries(const QString& siteUrlStr, const QString& formUrlS
} }
// Search entries matching the hostname // Search entries matching the hostname
QString hostname = QUrl(siteUrlStr).host(); QString hostname = QUrl(siteUrl).host();
QList<Entry*> entries; QList<Entry*> entries;
do { do {
for (const auto& db : databases) { for (const auto& db : databases) {
entries << searchEntries(db, siteUrlStr, formUrlStr); entries << searchEntries(db, siteUrl, formUrl);
} }
} while (entries.isEmpty() && removeFirstDomain(hostname)); } while (entries.isEmpty() && removeFirstDomain(hostname));
@ -853,9 +848,9 @@ BrowserService::sortEntries(QList<Entry*>& pwEntries, const QString& siteUrlStr,
} }
QList<Entry*> BrowserService::confirmEntries(QList<Entry*>& pwEntriesToConfirm, QList<Entry*> BrowserService::confirmEntries(QList<Entry*>& pwEntriesToConfirm,
const QString& siteUrlStr, const QString& siteUrl,
const QString& siteHost, const QString& siteHost,
const QString& formUrlStr, const QString& formUrl,
const QString& realm, const QString& realm,
const bool httpAuth) const bool httpAuth)
{ {
@ -874,8 +869,8 @@ QList<Entry*> BrowserService::confirmEntries(QList<Entry*>& pwEntriesToConfirm,
BrowserEntryConfig config; BrowserEntryConfig config;
config.load(entry); config.load(entry);
config.deny(siteHost); config.deny(siteHost);
if (!formUrlStr.isEmpty() && siteHost != formUrlStr) { if (!formUrl.isEmpty() && siteHost != formUrl) {
config.deny(formUrlStr); config.deny(formUrl);
} }
if (!realm.isEmpty()) { if (!realm.isEmpty()) {
config.setRealm(realm); config.setRealm(realm);
@ -883,7 +878,7 @@ QList<Entry*> BrowserService::confirmEntries(QList<Entry*>& pwEntriesToConfirm,
config.save(entry); config.save(entry);
}); });
accessControlDialog.setItems(pwEntriesToConfirm, siteUrlStr, httpAuth); accessControlDialog.setItems(pwEntriesToConfirm, siteUrl, httpAuth);
QList<Entry*> allowedEntries; QList<Entry*> allowedEntries;
if (accessControlDialog.exec() == QDialog::Accepted) { if (accessControlDialog.exec() == QDialog::Accepted) {
@ -894,8 +889,8 @@ QList<Entry*> BrowserService::confirmEntries(QList<Entry*>& pwEntriesToConfirm,
BrowserEntryConfig config; BrowserEntryConfig config;
config.load(entry); config.load(entry);
config.allow(siteHost); config.allow(siteHost);
if (!formUrlStr.isEmpty() && siteHost != formUrlStr) { if (!formUrl.isEmpty() && siteHost != formUrl) {
config.allow(formUrlStr); config.allow(formUrl);
} }
if (!realm.isEmpty()) { if (!realm.isEmpty()) {
config.setRealm(realm); config.setRealm(realm);
@ -1006,14 +1001,14 @@ Group* BrowserService::getDefaultEntryGroup(const QSharedPointer<Database>& sele
// Returns the maximum sort priority given a set of match urls and the // Returns the maximum sort priority given a set of match urls and the
// extension provided site and form url. // extension provided site and form url.
int BrowserService::sortPriority(const QStringList& urls, const QString& siteUrlStr, const QString& formUrlStr) int BrowserService::sortPriority(const QStringList& urls, const QString& siteUrl, const QString& formUrl)
{ {
QList<int> priorityList; QList<int> priorityList;
// NOTE: QUrl::matches is utterly broken in Qt < 5.11, so we work around that // NOTE: QUrl::matches is utterly broken in Qt < 5.11, so we work around that
// by removing parts of the url that we don't match and direct matching others // by removing parts of the url that we don't match and direct matching others
const auto stdOpts = QUrl::RemoveFragment | QUrl::RemoveUserInfo; const auto stdOpts = QUrl::RemoveFragment | QUrl::RemoveUserInfo;
const auto siteUrl = QUrl(siteUrlStr).adjusted(stdOpts); const auto adjustedSiteUrl = QUrl(siteUrl).adjusted(stdOpts);
const auto formUrl = QUrl(formUrlStr).adjusted(stdOpts); const auto adjustedFormUrl = QUrl(formUrl).adjusted(stdOpts);
auto getPriority = [&](const QString& givenUrl) { auto getPriority = [&](const QString& givenUrl) {
auto url = QUrl::fromUserInput(givenUrl).adjusted(stdOpts); auto url = QUrl::fromUserInput(givenUrl).adjusted(stdOpts);
@ -1031,38 +1026,38 @@ int BrowserService::sortPriority(const QStringList& urls, const QString& siteUrl
// Reject invalid urls and hosts, except 'localhost', and scheme mismatch // Reject invalid urls and hosts, except 'localhost', and scheme mismatch
if (!url.isValid() || (!url.host().contains(".") && url.host() != "localhost") if (!url.isValid() || (!url.host().contains(".") && url.host() != "localhost")
|| url.scheme() != siteUrl.scheme()) { || url.scheme() != adjustedSiteUrl.scheme()) {
return 0; return 0;
} }
// Exact match with site url or form url // Exact match with site url or form url
if (url.matches(siteUrl, QUrl::None) || url.matches(formUrl, QUrl::None)) { if (url.matches(adjustedSiteUrl, QUrl::None) || url.matches(adjustedFormUrl, QUrl::None)) {
return 100; return 100;
} }
// Exact match without the query string // Exact match without the query string
if (url.matches(siteUrl, QUrl::RemoveQuery) || url.matches(formUrl, QUrl::RemoveQuery)) { if (url.matches(adjustedSiteUrl, QUrl::RemoveQuery) || url.matches(adjustedFormUrl, QUrl::RemoveQuery)) {
return 90; return 90;
} }
// Parent directory match // Parent directory match
if (url.isParentOf(siteUrl) || url.isParentOf(formUrl)) { if (url.isParentOf(adjustedSiteUrl) || url.isParentOf(adjustedFormUrl)) {
return 85; return 85;
} }
// Match without path (ie, FQDN match), form url prioritizes lower than site url // Match without path (ie, FQDN match), form url prioritizes lower than site url
if (url.host() == siteUrl.host()) { if (url.host() == adjustedSiteUrl.host()) {
return 80; return 80;
} }
if (url.host() == formUrl.host()) { if (url.host() == adjustedFormUrl.host()) {
return 70; return 70;
} }
// Site/form url ends with given url (subdomain mismatch) // Site/form url ends with given url (subdomain mismatch)
if (siteUrl.host().endsWith(url.host())) { if (adjustedSiteUrl.host().endsWith(url.host())) {
return 60; return 60;
} }
if (formUrl.host().endsWith(url.host())) { if (adjustedFormUrl.host().endsWith(url.host())) {
return 50; return 50;
} }
@ -1108,7 +1103,10 @@ bool BrowserService::removeFirstDomain(QString& hostname)
/* Test if a search URL matches a custom entry. If the URL has the schema "keepassxc", some special checks will be made. /* Test if a search URL matches a custom entry. If the URL has the schema "keepassxc", some special checks will be made.
* Otherwise, this simply delegates to handleURL(). */ * Otherwise, this simply delegates to handleURL(). */
bool BrowserService::handleEntry(Entry* entry, const QString& url, const QString& submitUrl) bool BrowserService::shouldIncludeEntry(Entry* entry,
const QString& url,
const QString& submitUrl,
const bool omitWwwSubdomain)
{ {
// Use this special scheme to find entries by UUID // Use this special scheme to find entries by UUID
if (url.startsWith("keepassxc://by-uuid/")) { if (url.startsWith("keepassxc://by-uuid/")) {
@ -1116,10 +1114,21 @@ bool BrowserService::handleEntry(Entry* entry, const QString& url, const QString
} else if (url.startsWith("keepassxc://by-path/")) { } else if (url.startsWith("keepassxc://by-path/")) {
return url.endsWith("by-path/" + entry->path()); return url.endsWith("by-path/" + entry->path());
} }
return handleURL(entry->url(), url, submitUrl);
const auto allEntryUrls = entry->getAllUrls();
for (const auto& entryUrl : allEntryUrls) {
if (handleURL(entryUrl, url, submitUrl, omitWwwSubdomain)) {
return true;
}
} }
bool BrowserService::handleURL(const QString& entryUrl, const QString& siteUrlStr, const QString& formUrlStr) return false;
}
bool BrowserService::handleURL(const QString& entryUrl,
const QString& siteUrl,
const QString& formUrl,
const bool omitWwwSubdomain)
{ {
if (entryUrl.isEmpty()) { if (entryUrl.isEmpty()) {
return false; return false;
@ -1136,9 +1145,14 @@ bool BrowserService::handleURL(const QString& entryUrl, const QString& siteUrlSt
} }
} }
// Remove WWW subdomain from matching if group setting is enabled
if (omitWwwSubdomain && entryQUrl.host().startsWith("www.")) {
entryQUrl.setHost(entryQUrl.host().remove("www."));
}
// Make a direct compare if a local file is used // Make a direct compare if a local file is used
if (siteUrlStr.startsWith("file://")) { if (siteUrl.startsWith("file://")) {
return entryUrl == formUrlStr; return entryUrl == formUrl;
} }
// URL host validation fails // URL host validation fails
@ -1147,7 +1161,7 @@ bool BrowserService::handleURL(const QString& entryUrl, const QString& siteUrlSt
} }
// Match port, if used // Match port, if used
QUrl siteQUrl(siteUrlStr); QUrl siteQUrl(siteUrl);
if (entryQUrl.port() > 0 && entryQUrl.port() != siteQUrl.port()) { if (entryQUrl.port() > 0 && entryQUrl.port() != siteQUrl.port()) {
return false; return false;
} }

View File

@ -99,6 +99,7 @@ public:
static const QString OPTION_HIDE_ENTRY; static const QString OPTION_HIDE_ENTRY;
static const QString OPTION_ONLY_HTTP_AUTH; static const QString OPTION_ONLY_HTTP_AUTH;
static const QString OPTION_NOT_HTTP_AUTH; static const QString OPTION_NOT_HTTP_AUTH;
static const QString OPTION_OMIT_WWW;
static const QString ADDITIONAL_URL; static const QString ADDITIONAL_URL;
signals: signals:
@ -128,26 +129,29 @@ private:
Hidden Hidden
}; };
QList<Entry*> QList<Entry*> searchEntries(const QSharedPointer<Database>& db, const QString& siteUrl, const QString& formUrl);
searchEntries(const QSharedPointer<Database>& db, const QString& siteUrlStr, const QString& formUrlStr); QList<Entry*> searchEntries(const QString& siteUrl, const QString& formUrl, const StringPairList& keyList);
QList<Entry*> searchEntries(const QString& siteUrlStr, const QString& formUrlStr, const StringPairList& keyList); QList<Entry*> sortEntries(QList<Entry*>& pwEntries, const QString& siteUrl, const QString& formUrl);
QList<Entry*> sortEntries(QList<Entry*>& pwEntries, const QString& siteUrlStr, const QString& formUrlStr);
QList<Entry*> confirmEntries(QList<Entry*>& pwEntriesToConfirm, QList<Entry*> confirmEntries(QList<Entry*>& pwEntriesToConfirm,
const QString& siteUrlStr, const QString& siteUrl,
const QString& siteHost, const QString& siteHost,
const QString& formUrlStr, const QString& formUrl,
const QString& realm, const QString& realm,
const bool httpAuth); const bool httpAuth);
QJsonObject prepareEntry(const Entry* entry); QJsonObject prepareEntry(const Entry* entry);
QJsonArray getChildrenFromGroup(Group* group); QJsonArray getChildrenFromGroup(Group* group);
Access checkAccess(const Entry* entry, const QString& siteHost, const QString& formHost, const QString& realm); Access checkAccess(const Entry* entry, const QString& siteHost, const QString& formHost, const QString& realm);
Group* getDefaultEntryGroup(const QSharedPointer<Database>& selectedDb = {}); Group* getDefaultEntryGroup(const QSharedPointer<Database>& selectedDb = {});
int sortPriority(const QStringList& urls, const QString& siteUrlStr, const QString& formUrlStr); int sortPriority(const QStringList& urls, const QString& siteUrl, const QString& formUrl);
bool schemeFound(const QString& url); bool schemeFound(const QString& url);
bool isIpAddress(const QString& host) const; bool isIpAddress(const QString& host) const;
bool removeFirstDomain(QString& hostname); bool removeFirstDomain(QString& hostname);
bool handleEntry(Entry* entry, const QString& url, const QString& submitUrl); bool
bool handleURL(const QString& entryUrl, const QString& siteUrlStr, const QString& formUrlStr); shouldIncludeEntry(Entry* entry, const QString& url, const QString& submitUrl, const bool omitWwwSubdomain = false);
bool handleURL(const QString& entryUrl,
const QString& siteUrl,
const QString& formUrl,
const bool omitWwwSubdomain = false);
QString getTopLevelDomainFromUrl(const QString& url) const; QString getTopLevelDomainFromUrl(const QString& url) const;
QString baseDomain(const QString& hostname) const; QString baseDomain(const QString& hostname) const;
QSharedPointer<Database> getDatabase(); QSharedPointer<Database> getDatabase();

View File

@ -219,6 +219,7 @@ void EditGroupWidget::loadGroup(Group* group, bool create, const QSharedPointer<
auto inheritSkipSubmit = false; auto inheritSkipSubmit = false;
auto inheritOnlyHttp = false; auto inheritOnlyHttp = false;
auto inheritNoHttp = false; auto inheritNoHttp = false;
auto inheritOmitWww = false;
auto parent = group->parentGroup(); auto parent = group->parentGroup();
if (parent) { if (parent) {
@ -226,12 +227,14 @@ void EditGroupWidget::loadGroup(Group* group, bool create, const QSharedPointer<
inheritSkipSubmit = parent->resolveCustomDataTriState(BrowserService::OPTION_SKIP_AUTO_SUBMIT); inheritSkipSubmit = parent->resolveCustomDataTriState(BrowserService::OPTION_SKIP_AUTO_SUBMIT);
inheritOnlyHttp = parent->resolveCustomDataTriState(BrowserService::OPTION_ONLY_HTTP_AUTH); inheritOnlyHttp = parent->resolveCustomDataTriState(BrowserService::OPTION_ONLY_HTTP_AUTH);
inheritNoHttp = parent->resolveCustomDataTriState(BrowserService::OPTION_NOT_HTTP_AUTH); inheritNoHttp = parent->resolveCustomDataTriState(BrowserService::OPTION_NOT_HTTP_AUTH);
inheritOmitWww = parent->resolveCustomDataTriState(BrowserService::OPTION_OMIT_WWW);
} }
addTriStateItems(m_browserUi->browserIntegrationHideEntriesComboBox, inheritHideEntries); addTriStateItems(m_browserUi->browserIntegrationHideEntriesComboBox, inheritHideEntries);
addTriStateItems(m_browserUi->browserIntegrationSkipAutoSubmitComboBox, inheritSkipSubmit); addTriStateItems(m_browserUi->browserIntegrationSkipAutoSubmitComboBox, inheritSkipSubmit);
addTriStateItems(m_browserUi->browserIntegrationOnlyHttpAuthComboBox, inheritOnlyHttp); addTriStateItems(m_browserUi->browserIntegrationOnlyHttpAuthComboBox, inheritOnlyHttp);
addTriStateItems(m_browserUi->browserIntegrationNotHttpAuthComboBox, inheritNoHttp); addTriStateItems(m_browserUi->browserIntegrationNotHttpAuthComboBox, inheritNoHttp);
addTriStateItems(m_browserUi->browserIntegrationOmitWwwCombobox, inheritOmitWww);
m_browserUi->browserIntegrationHideEntriesComboBox->setCurrentIndex( m_browserUi->browserIntegrationHideEntriesComboBox->setCurrentIndex(
indexFromTriState(group->resolveCustomDataTriState(BrowserService::OPTION_HIDE_ENTRY, false))); indexFromTriState(group->resolveCustomDataTriState(BrowserService::OPTION_HIDE_ENTRY, false)));
@ -241,6 +244,8 @@ void EditGroupWidget::loadGroup(Group* group, bool create, const QSharedPointer<
indexFromTriState(group->resolveCustomDataTriState(BrowserService::OPTION_ONLY_HTTP_AUTH, false))); indexFromTriState(group->resolveCustomDataTriState(BrowserService::OPTION_ONLY_HTTP_AUTH, false)));
m_browserUi->browserIntegrationNotHttpAuthComboBox->setCurrentIndex( m_browserUi->browserIntegrationNotHttpAuthComboBox->setCurrentIndex(
indexFromTriState(group->resolveCustomDataTriState(BrowserService::OPTION_NOT_HTTP_AUTH, false))); indexFromTriState(group->resolveCustomDataTriState(BrowserService::OPTION_NOT_HTTP_AUTH, false)));
m_browserUi->browserIntegrationOmitWwwCombobox->setCurrentIndex(
indexFromTriState(group->resolveCustomDataTriState(BrowserService::OPTION_OMIT_WWW, false)));
} }
#endif #endif
@ -309,6 +314,9 @@ void EditGroupWidget::apply()
m_temporaryGroup->setCustomDataTriState( m_temporaryGroup->setCustomDataTriState(
BrowserService::OPTION_NOT_HTTP_AUTH, BrowserService::OPTION_NOT_HTTP_AUTH,
triStateFromIndex(m_browserUi->browserIntegrationNotHttpAuthComboBox->currentIndex())); triStateFromIndex(m_browserUi->browserIntegrationNotHttpAuthComboBox->currentIndex()));
m_temporaryGroup->setCustomDataTriState(
BrowserService::OPTION_OMIT_WWW,
triStateFromIndex(m_browserUi->browserIntegrationOmitWwwCombobox->currentIndex()));
} }
#endif #endif

View File

@ -145,7 +145,24 @@
   </property>    </property>
   </widget>    </widget>
   </item>    </item>
   <item row="4" column="0"> <item row="4" column="0">
   <widget class="QLabel" name="browserIntegrationOmitWwwLabel">
   <property name="text">
   <string>Omit WWW subdomain from matching:</string>
   </property>
   <property name="alignment">
   <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
   </property>
   </widget>
   </item>
   <item row="4" column="1">
   <widget class="QComboBox" name="browserIntegrationOmitWwwCombobox">
   <property name="accessibleName">
   <string>Omit WWW subdomain from matching toggle for this and sub groups</string>
   </property>
   </widget>
   </item>
   <item row="5" column="0">
   <spacer name="verticalSpacer_1">    <spacer name="verticalSpacer_1">
   <property name="orientation">    <property name="orientation">
   <enum>Qt::Vertical</enum>    <enum>Qt::Vertical</enum>
@ -168,6 +185,7 @@
<tabstop>browserIntegrationSkipAutoSubmitComboBox</tabstop> <tabstop>browserIntegrationSkipAutoSubmitComboBox</tabstop>
<tabstop>browserIntegrationOnlyHttpAuthComboBox</tabstop> <tabstop>browserIntegrationOnlyHttpAuthComboBox</tabstop>
<tabstop>browserIntegrationNotHttpAuthComboBox</tabstop> <tabstop>browserIntegrationNotHttpAuthComboBox</tabstop>
<tabstop>browserIntegrationOmitWwwCombobox</tabstop>
</tabstops> </tabstops>
<resources/> <resources/>
<connections/> <connections/>

View File

@ -281,7 +281,7 @@ void TestBrowser::compareEntriesByPath(QSharedPointer<Database> db, QList<Entry*
for (Entry* entry : entries) { for (Entry* entry : entries) {
QString testUrl = "keepassxc://by-path/" + path + entry->title(); QString testUrl = "keepassxc://by-path/" + path + entry->title();
/* Look for an entry with that path. First using handleEntry, then through the search */ /* Look for an entry with that path. First using handleEntry, then through the search */
QCOMPARE(m_browserService->handleEntry(entry, testUrl, ""), true); QCOMPARE(m_browserService->shouldIncludeEntry(entry, testUrl, ""), true);
auto result = m_browserService->searchEntries(db, testUrl, ""); auto result = m_browserService->searchEntries(db, testUrl, "");
QCOMPARE(result.length(), 1); QCOMPARE(result.length(), 1);
QCOMPARE(result[0], entry); QCOMPARE(result[0], entry);
@ -311,7 +311,7 @@ void TestBrowser::testSearchEntriesByUUID()
for (Entry* entry : entries) { for (Entry* entry : entries) {
QString testUrl = "keepassxc://by-uuid/" + entry->uuidToHex(); QString testUrl = "keepassxc://by-uuid/" + entry->uuidToHex();
/* Look for an entry with that UUID. First using handleEntry, then through the search */ /* Look for an entry with that UUID. First using handleEntry, then through the search */
QCOMPARE(m_browserService->handleEntry(entry, testUrl, ""), true); QCOMPARE(m_browserService->shouldIncludeEntry(entry, testUrl, ""), true);
auto result = m_browserService->searchEntries(db, testUrl, ""); auto result = m_browserService->searchEntries(db, testUrl, "");
QCOMPARE(result.length(), 1); QCOMPARE(result.length(), 1);
QCOMPARE(result[0], entry); QCOMPARE(result[0], entry);
@ -329,7 +329,7 @@ void TestBrowser::testSearchEntriesByUUID()
QString testUrl = "keepassxc://by-uuid/" + uuid; QString testUrl = "keepassxc://by-uuid/" + uuid;
for (Entry* entry : entries) { for (Entry* entry : entries) {
QCOMPARE(m_browserService->handleEntry(entry, testUrl, ""), false); QCOMPARE(m_browserService->shouldIncludeEntry(entry, testUrl, ""), false);
} }
auto result = m_browserService->searchEntries(db, testUrl, ""); auto result = m_browserService->searchEntries(db, testUrl, "");
@ -439,6 +439,16 @@ void TestBrowser::testSubdomainsAndPaths()
QCOMPARE(result[2]->url(), QString("http://www.github.com")); QCOMPARE(result[2]->url(), QString("http://www.github.com"));
QCOMPARE(result[3]->url(), QString("www.github.com/")); QCOMPARE(result[3]->url(), QString("www.github.com/"));
// With www subdomain omitted
root->setCustomDataTriState(BrowserService::OPTION_OMIT_WWW, Group::Enable);
result = m_browserService->searchEntries(db, "https://github.com", "https://github.com/session");
root->setCustomDataTriState(BrowserService::OPTION_OMIT_WWW, Group::Inherit);
QCOMPARE(result.length(), 4);
QCOMPARE(result[0]->url(), QString("https://www.github.com/login/page.xml"));
QCOMPARE(result[1]->url(), QString("https://github.com"));
QCOMPARE(result[2]->url(), QString("http://www.github.com"));
QCOMPARE(result[3]->url(), QString("www.github.com/"));
// With scheme matching there should be only 1 result // With scheme matching there should be only 1 result
browserSettings()->setMatchUrlScheme(true); browserSettings()->setMatchUrlScheme(true);
result = m_browserService->searchEntries(db, "https://github.com", "https://github.com/session"); result = m_browserService->searchEntries(db, "https://github.com", "https://github.com/session");