Skip to content

Commit

Permalink
Add Browser Integration to Group Edit page
Browse files Browse the repository at this point in the history
Closes #1789 and closes #3998
  • Loading branch information
varjolintu authored and droidmonkey committed Oct 11, 2021
1 parent c7cdce6 commit b6716bd
Show file tree
Hide file tree
Showing 11 changed files with 523 additions and 65 deletions.
55 changes: 53 additions & 2 deletions share/translations/keepassxc_en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2513,6 +2513,10 @@ Would you like to correct it?</source>
Would you like to correct it?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Some Browser Integration settings are overridden by group settings.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditEntryWidgetAdvanced</name>
Expand Down Expand Up @@ -2968,8 +2972,55 @@ Would you like to correct it?</source>
<translation>Inherit from parent group (%1)</translation>
</message>
<message>
<source>Entry has unsaved changes</source>
<translation type="unfinished">Entry has unsaved changes</translation>
<source>Browser Integration</source>
<translation type="unfinished">Browser Integration</translation>
</message>
<message>
<source>Group has unsaved changes</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditGroupWidgetBrowser</name>
<message>
<source>Edit Group</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>These settings affect to the group&apos;s behaviour with the browser extension.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hide entries from browser extension:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hide entries from browser extension toggle for this and sub groups</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Skip Auto-Submit for entries:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Skip Auto-Submit toggle for this and sub groups</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use entries only with HTTP Basic Auth:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Only HTTP Auth toggle for this and sub groups</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Do not use entries with HTTP Basic Auth:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Do not use HTTP Auth toggle for this and sub groups</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
Expand Down
33 changes: 21 additions & 12 deletions src/browser/BrowserService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,18 +390,19 @@ QJsonArray BrowserService::findMatchingEntries(const QString& dbid,
QList<Entry*> pwEntriesToConfirm;
QList<Entry*> pwEntries;
for (auto* entry : searchEntries(siteUrlStr, formUrlStr, keyList)) {
if (entry->customData()->contains(BrowserService::OPTION_HIDE_ENTRY)
&& entry->customData()->value(BrowserService::OPTION_HIDE_ENTRY) == TRUE_STR) {
continue;
}
auto entryCustomData = entry->customData();

if (!httpAuth && entry->customData()->contains(BrowserService::OPTION_ONLY_HTTP_AUTH)
&& entry->customData()->value(BrowserService::OPTION_ONLY_HTTP_AUTH) == TRUE_STR) {
if (!httpAuth
&& ((entryCustomData->contains(BrowserService::OPTION_ONLY_HTTP_AUTH)
&& entryCustomData->value(BrowserService::OPTION_ONLY_HTTP_AUTH) == TRUE_STR)
|| entry->group()->resolveCustomDataTriState(BrowserService::OPTION_ONLY_HTTP_AUTH) == Group::Enable)) {
continue;
}

if (httpAuth && entry->customData()->contains(BrowserService::OPTION_NOT_HTTP_AUTH)
&& entry->customData()->value(BrowserService::OPTION_NOT_HTTP_AUTH) == TRUE_STR) {
if (httpAuth
&& ((entryCustomData->contains(BrowserService::OPTION_NOT_HTTP_AUTH)
&& entryCustomData->value(BrowserService::OPTION_NOT_HTTP_AUTH) == TRUE_STR)
|| entry->group()->resolveCustomDataTriState(BrowserService::OPTION_NOT_HTTP_AUTH) == Group::Enable)) {
continue;
}

Expand Down Expand Up @@ -614,12 +615,15 @@ BrowserService::searchEntries(const QSharedPointer<Database>& db, const QString&
}

for (const auto& group : rootGroup->groupsRecursive(true)) {
if (group->isRecycled() || !group->resolveSearchingEnabled()) {
if (group->isRecycled()
|| group->resolveCustomDataTriState(BrowserService::OPTION_HIDE_ENTRY) == Group::Enable) {
continue;
}

for (auto* entry : group->entries()) {
if (entry->isRecycled()) {
if (entry->isRecycled()
|| (entry->customData()->contains(BrowserService::OPTION_HIDE_ENTRY)
&& entry->customData()->value(BrowserService::OPTION_HIDE_ENTRY) == TRUE_STR)) {
continue;
}

Expand Down Expand Up @@ -870,8 +874,13 @@ QJsonObject BrowserService::prepareEntry(const Entry* entry)
res["expired"] = TRUE_STR;
}

if (entry->customData()->contains(BrowserService::OPTION_SKIP_AUTO_SUBMIT)) {
res["skipAutoSubmit"] = entry->customData()->value(BrowserService::OPTION_SKIP_AUTO_SUBMIT);
auto skipAutoSubmitGroup = entry->group()->resolveCustomDataTriState(BrowserService::OPTION_SKIP_AUTO_SUBMIT);
if (skipAutoSubmitGroup == Group::Inherit) {
if (entry->customData()->contains(BrowserService::OPTION_SKIP_AUTO_SUBMIT)) {
res["skipAutoSubmit"] = entry->customData()->value(BrowserService::OPTION_SKIP_AUTO_SUBMIT);
}
} else {
res["skipAutoSubmit"] = skipAutoSubmitGroup == Group::Enable ? TRUE_STR : FALSE_STR;
}

if (browserSettings()->supportKphFields()) {
Expand Down
10 changes: 6 additions & 4 deletions src/core/CustomData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,13 @@ void CustomData::remove(const QString& key)
{
emit aboutToBeRemoved(key);

m_data.remove(key);
if (m_data.contains(key)) {
m_data.remove(key);
updateLastModified();
emitModified();
}

updateLastModified();
emit removed(key);
emitModified();
}

void CustomData::rename(const QString& oldKey, const QString& newKey)
Expand Down Expand Up @@ -180,7 +182,7 @@ int CustomData::dataSize() const

void CustomData::updateLastModified()
{
if (m_data.size() == 1 && m_data.contains(LastModified)) {
if (m_data.isEmpty() || (m_data.size() == 1 && m_data.contains(LastModified))) {
m_data.remove(LastModified);
return;
}
Expand Down
31 changes: 30 additions & 1 deletion src/core/Group.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2010 Felix Geyer <[email protected]>
* Copyright (C) 2017 KeePassXC Team <[email protected]>
* Copyright (C) 2021 KeePassXC Team <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -276,6 +276,35 @@ const CustomData* Group::customData() const
return m_customData;
}

Group::TriState Group::resolveCustomDataTriState(const QString& key, bool checkParent) const
{
// If not defined, check our parent up to the root group
if (!m_customData->contains(key)) {
if (!m_parent || !checkParent) {
return Inherit;
} else {
return m_parent->resolveCustomDataTriState(key);
}
}

return m_customData->value(key) == TRUE_STR ? Enable : Disable;
}

void Group::setCustomDataTriState(const QString& key, const Group::TriState& value)
{
switch (value) {
case Enable:
m_customData->set(key, TRUE_STR);
break;
case Disable:
m_customData->set(key, FALSE_STR);
break;
case Inherit:
m_customData->remove(key);
break;
}
}

bool Group::equals(const Group* other, CompareItemOptions options) const
{
if (!other) {
Expand Down
4 changes: 3 additions & 1 deletion src/core/Group.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2010 Felix Geyer <[email protected]>
* Copyright (C) 2017 KeePassXC Team <[email protected]>
* Copyright (C) 2021 KeePassXC Team <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -101,6 +101,8 @@ class Group : public ModifiableObject
bool isEmpty() const;
CustomData* customData();
const CustomData* customData() const;
Group::TriState resolveCustomDataTriState(const QString& key, bool checkParent = true) const;
void setCustomDataTriState(const QString& key, const Group::TriState& value);

bool equals(const Group* other, CompareItemOptions options) const;

Expand Down
127 changes: 94 additions & 33 deletions src/gui/entry/EditEntryWidget.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2010 Felix Geyer <[email protected]>
* Copyright (C) 2020 KeePassXC Team <[email protected]>
* Copyright (C) 2021 KeePassXC Team <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -36,6 +36,7 @@
#include "core/Database.h"
#include "core/Entry.h"
#include "core/EntryAttributes.h"
#include "core/Group.h"
#include "core/Metadata.h"
#include "core/TimeDelta.h"
#ifdef WITH_XC_SSHAGENT
Expand Down Expand Up @@ -263,6 +264,11 @@ void EditEntryWidget::setupBrowser()
m_additionalURLsDataModel->setEntryAttributes(m_entryAttributes);
m_browserUi->additionalURLsView->setModel(m_additionalURLsDataModel);

m_browserUi->messageWidget->setCloseButtonVisible(false);
m_browserUi->messageWidget->setAutoHideTimeout(-1);
m_browserUi->messageWidget->setAnimate(false);
m_browserUi->messageWidget->setVisible(false);

// Use a custom item delegate to align the icon to the right side
auto iconDelegate = new URLModelIconDelegate(m_browserUi->additionalURLsView);
m_browserUi->additionalURLsView->setItemDelegate(iconDelegate);
Expand Down Expand Up @@ -296,14 +302,26 @@ void EditEntryWidget::updateBrowser()
return;
}

auto skip = m_browserUi->skipAutoSubmitCheckbox->isChecked();
auto hide = m_browserUi->hideEntryCheckbox->isChecked();
auto onlyHttpAuth = m_browserUi->onlyHttpAuthCheckbox->isChecked();
auto notHttpAuth = m_browserUi->notHttpAuthCheckbox->isChecked();
m_customData->set(BrowserService::OPTION_SKIP_AUTO_SUBMIT, (skip ? TRUE_STR : FALSE_STR));
m_customData->set(BrowserService::OPTION_HIDE_ENTRY, (hide ? TRUE_STR : FALSE_STR));
m_customData->set(BrowserService::OPTION_ONLY_HTTP_AUTH, (onlyHttpAuth ? TRUE_STR : FALSE_STR));
m_customData->set(BrowserService::OPTION_NOT_HTTP_AUTH, (notHttpAuth ? TRUE_STR : FALSE_STR));
// Only update the custom data if no group level settings are used (checkbox is enabled)
if (m_browserUi->hideEntryCheckbox->isEnabled()) {
auto hide = m_browserUi->hideEntryCheckbox->isChecked();
m_customData->set(BrowserService::OPTION_HIDE_ENTRY, (hide ? TRUE_STR : FALSE_STR));
}

if (m_browserUi->skipAutoSubmitCheckbox->isEnabled()) {
auto skip = m_browserUi->skipAutoSubmitCheckbox->isChecked();
m_customData->set(BrowserService::OPTION_SKIP_AUTO_SUBMIT, (skip ? TRUE_STR : FALSE_STR));
}

if (m_browserUi->onlyHttpAuthCheckbox->isEnabled()) {
auto onlyHttpAuth = m_browserUi->onlyHttpAuthCheckbox->isChecked();
m_customData->set(BrowserService::OPTION_ONLY_HTTP_AUTH, (onlyHttpAuth ? TRUE_STR : FALSE_STR));
}

if (m_browserUi->notHttpAuthCheckbox->isEnabled()) {
auto notHttpAuth = m_browserUi->notHttpAuthCheckbox->isChecked();
m_customData->set(BrowserService::OPTION_NOT_HTTP_AUTH, (notHttpAuth ? TRUE_STR : FALSE_STR));
}
}

void EditEntryWidget::insertURL()
Expand Down Expand Up @@ -941,34 +959,55 @@ void EditEntryWidget::setForms(Entry* entry, bool restore)
setupBrowser();
}

if (m_customData->contains(BrowserService::OPTION_SKIP_AUTO_SUBMIT)) {
// clang-format off
m_browserUi->skipAutoSubmitCheckbox->setChecked(m_customData->value(BrowserService::OPTION_SKIP_AUTO_SUBMIT) == TRUE_STR);
// clang-format on
} else {
m_browserUi->skipAutoSubmitCheckbox->setChecked(false);
}

if (m_customData->contains(BrowserService::OPTION_HIDE_ENTRY)) {
m_browserUi->hideEntryCheckbox->setChecked(m_customData->value(BrowserService::OPTION_HIDE_ENTRY)
== TRUE_STR);
} else {
m_browserUi->hideEntryCheckbox->setChecked(false);
auto hideEntriesCheckBoxEnabled = true;
auto skipAutoSubmitCheckBoxEnabled = true;
auto onlyHttpAuthCheckBoxEnabled = true;
auto notHttpAuthCheckBoxEnabled = true;
auto hideEntries = false;
auto skipAutoSubmit = false;
auto onlyHttpAuth = false;
auto notHttpAuth = false;

const auto group = m_entry->group();
if (group) {
hideEntries = group->resolveCustomDataTriState(BrowserService::OPTION_HIDE_ENTRY) == Group::Enable;
skipAutoSubmit = group->resolveCustomDataTriState(BrowserService::OPTION_SKIP_AUTO_SUBMIT) == Group::Enable;
onlyHttpAuth = group->resolveCustomDataTriState(BrowserService::OPTION_ONLY_HTTP_AUTH) == Group::Enable;
notHttpAuth = group->resolveCustomDataTriState(BrowserService::OPTION_NOT_HTTP_AUTH) == Group::Enable;

hideEntriesCheckBoxEnabled =
group->resolveCustomDataTriState(BrowserService::OPTION_HIDE_ENTRY) == Group::Inherit;
skipAutoSubmitCheckBoxEnabled =
group->resolveCustomDataTriState(BrowserService::OPTION_SKIP_AUTO_SUBMIT) == Group::Inherit;
onlyHttpAuthCheckBoxEnabled =
group->resolveCustomDataTriState(BrowserService::OPTION_ONLY_HTTP_AUTH) == Group::Inherit;
notHttpAuthCheckBoxEnabled =
group->resolveCustomDataTriState(BrowserService::OPTION_NOT_HTTP_AUTH) == Group::Inherit;
}

if (m_customData->contains(BrowserService::OPTION_ONLY_HTTP_AUTH)) {
m_browserUi->onlyHttpAuthCheckbox->setChecked(m_customData->value(BrowserService::OPTION_ONLY_HTTP_AUTH)
== TRUE_STR);
} else {
m_browserUi->onlyHttpAuthCheckbox->setChecked(false);
// Show information about group level settings
if (!hideEntriesCheckBoxEnabled || !skipAutoSubmitCheckBoxEnabled || !onlyHttpAuthCheckBoxEnabled
|| !notHttpAuthCheckBoxEnabled) {
m_browserUi->messageWidget->showMessage(
tr("Some Browser Integration settings are overridden by group settings."), MessageWidget::Information);
m_browserUi->messageWidget->setVisible(true);
}

if (m_customData->contains(BrowserService::OPTION_NOT_HTTP_AUTH)) {
m_browserUi->notHttpAuthCheckbox->setChecked(m_customData->value(BrowserService::OPTION_NOT_HTTP_AUTH)
== TRUE_STR);
} else {
m_browserUi->notHttpAuthCheckbox->setChecked(false);
}
// Disable checkboxes based on group level settings
updateBrowserIntegrationCheckbox(
m_browserUi->hideEntryCheckbox, hideEntriesCheckBoxEnabled, hideEntries, BrowserService::OPTION_HIDE_ENTRY);
updateBrowserIntegrationCheckbox(m_browserUi->skipAutoSubmitCheckbox,
skipAutoSubmitCheckBoxEnabled,
skipAutoSubmit,
BrowserService::OPTION_SKIP_AUTO_SUBMIT);
updateBrowserIntegrationCheckbox(m_browserUi->onlyHttpAuthCheckbox,
onlyHttpAuthCheckBoxEnabled,
onlyHttpAuth,
BrowserService::OPTION_ONLY_HTTP_AUTH);
updateBrowserIntegrationCheckbox(m_browserUi->notHttpAuthCheckbox,
notHttpAuthCheckBoxEnabled,
notHttpAuth,
BrowserService::OPTION_NOT_HTTP_AUTH);

m_browserUi->addURLButton->setEnabled(!m_history);
m_browserUi->removeURLButton->setEnabled(false);
Expand Down Expand Up @@ -1163,6 +1202,28 @@ void EditEntryWidget::updateEntryData(Entry* entry) const
#endif
}

void EditEntryWidget::updateBrowserIntegrationCheckbox(QCheckBox* checkBox,
bool enabled,
bool value,
const QString& option)
{
auto block = checkBox->signalsBlocked();
checkBox->blockSignals(true);

if (enabled) {
if (m_customData->contains(option)) {
checkBox->setChecked(m_customData->value(option) == TRUE_STR);
} else {
checkBox->setChecked(false);
}
} else {
checkBox->setChecked(value);
}
checkBox->setEnabled(enabled);

checkBox->blockSignals(block);
}

void EditEntryWidget::cancel()
{
if (m_history) {
Expand Down
Loading

0 comments on commit b6716bd

Please sign in to comment.