mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2026-03-11 08:54:48 +00:00
Add site permissions table to edit entry view
This commit is contained in:
parent
b45cea0d9c
commit
1db0294dfe
4 changed files with 322 additions and 3 deletions
|
|
@ -3095,6 +3095,34 @@ Would you like to correct it?</source>
|
|||
<source>Failed to decrypt SSH key, ensure password is correct.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Site</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Allowed</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Yes</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Deny</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Allow</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure you want to remove this site?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>EditEntryWidgetAdvanced</name>
|
||||
|
|
@ -3302,6 +3330,22 @@ Would you like to correct it?</source>
|
|||
<source>Do not send this entry to the browser for HTTP Auth dialogs. If enabled, HTTP Auth dialogs will not show this entry for selection.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Site Permissions</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Allow or deny site</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Allow</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove selected site</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>EditEntryWidgetHistory</name>
|
||||
|
|
|
|||
|
|
@ -47,7 +47,11 @@
|
|||
#endif
|
||||
#ifdef WITH_XC_BROWSER
|
||||
#include "EntryURLModel.h"
|
||||
#include "browser/BrowserMessageBuilder.h"
|
||||
#include "browser/BrowserService.h"
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#endif
|
||||
#include "gui/Clipboard.h"
|
||||
#include "gui/EditWidgetIcons.h"
|
||||
|
|
@ -335,6 +339,18 @@ void EditEntryWidget::setupBrowser()
|
|||
connect(m_additionalURLsDataModel,
|
||||
SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)),
|
||||
SLOT(updateCurrentAttribute()));
|
||||
connect(m_browserUi->sitePermissionsTable->selectionModel(),
|
||||
SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
|
||||
this,
|
||||
SLOT(sitePermissionSelectionChanged()));
|
||||
connect(m_browserUi->sitePermissionsTable, SIGNAL(cellDoubleClicked(int, int)), this, SLOT(editSite()));
|
||||
connect(m_browserUi->sitePermissionsTable,
|
||||
SIGNAL(itemChanged(QTableWidgetItem*)),
|
||||
this,
|
||||
SLOT(editSiteFinished(QTableWidgetItem*)));
|
||||
connect(m_browserUi->allowDenyButton, SIGNAL(clicked()), SLOT(allowDenyChangedForSite()));
|
||||
connect(m_browserUi->removeSiteButton, SIGNAL(clicked()), SLOT(removeCurrentSite()));
|
||||
connect(m_browserUi->editSiteButton, SIGNAL(clicked()), SLOT(editSite()));
|
||||
// clang-format on
|
||||
}
|
||||
}
|
||||
|
|
@ -342,6 +358,7 @@ void EditEntryWidget::setupBrowser()
|
|||
void EditEntryWidget::updateBrowserModified()
|
||||
{
|
||||
m_browserSettingsChanged = true;
|
||||
setModified(true);
|
||||
}
|
||||
|
||||
void EditEntryWidget::updateBrowser()
|
||||
|
|
@ -376,6 +393,27 @@ void EditEntryWidget::updateBrowser()
|
|||
if (m_browserUi->notHttpAuthCheckbox->isEnabled()) {
|
||||
changeValue(BrowserService::OPTION_NOT_HTTP_AUTH, m_browserUi->notHttpAuthCheckbox->isChecked());
|
||||
}
|
||||
|
||||
// Update site permissions
|
||||
if (!m_sitePermissions.isEmpty()) {
|
||||
QJsonArray allowedSites;
|
||||
QJsonArray deniedSites;
|
||||
|
||||
for (const auto& site : m_sitePermissions) {
|
||||
if (site.second) {
|
||||
allowedSites.append(site.first);
|
||||
} else {
|
||||
deniedSites.append(site.first);
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject sitePermissions;
|
||||
sitePermissions["Allow"] = allowedSites;
|
||||
sitePermissions["Deny"] = deniedSites;
|
||||
sitePermissions["Realm"] = QString();
|
||||
const auto sitePermissionsStr = QString(QJsonDocument(sitePermissions).toJson(QJsonDocument::Compact));
|
||||
m_customData->set(BrowserService::KEEPASSXCBROWSER_NAME, sitePermissionsStr);
|
||||
}
|
||||
}
|
||||
|
||||
void EditEntryWidget::insertURL()
|
||||
|
|
@ -459,6 +497,153 @@ void EditEntryWidget::entryURLEdited(const QString& url)
|
|||
{
|
||||
m_additionalURLsDataModel->setEntryUrl(url);
|
||||
}
|
||||
|
||||
void EditEntryWidget::initializeSitePermissionsTable()
|
||||
{
|
||||
if (!m_customData->hasKey(BrowserService::KEEPASSXCBROWSER_NAME)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get JSON from custom data
|
||||
const auto sitePermissions = m_customData->value(BrowserService::KEEPASSXCBROWSER_NAME);
|
||||
const auto sitePermissionsJson = browserMessageBuilder()->getJsonObject(sitePermissions.toUtf8());
|
||||
if (!sitePermissionsJson["Allow"].isArray() || !sitePermissionsJson["Deny"].isArray()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_sitePermissions.clear();
|
||||
|
||||
// Parse allowed URLs
|
||||
const auto allowedUrls = sitePermissionsJson["Allow"].toArray();
|
||||
for (const auto& allowedUrl : allowedUrls) {
|
||||
if (!allowedUrl.isString()) {
|
||||
continue;
|
||||
}
|
||||
m_sitePermissions.append(qMakePair(allowedUrl.toString(), true));
|
||||
}
|
||||
|
||||
// Parse denied URLs
|
||||
const auto deniedUrls = sitePermissionsJson["Deny"].toArray();
|
||||
for (const auto& deniedUrl : deniedUrls) {
|
||||
if (!deniedUrl.isString()) {
|
||||
continue;
|
||||
}
|
||||
m_sitePermissions.append(qMakePair(deniedUrl.toString(), false));
|
||||
}
|
||||
|
||||
m_browserUi->sitePermissionsTable->setColumnCount(2);
|
||||
m_browserUi->sitePermissionsTable->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
m_browserUi->sitePermissionsTable->setHorizontalHeaderLabels({tr("Site"), tr("Allowed")});
|
||||
m_browserUi->sitePermissionsTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
m_browserUi->sitePermissionsTable->setColumnWidth(1, 100);
|
||||
m_browserUi->sitePermissionsTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);
|
||||
|
||||
updateSitePermissionsTable();
|
||||
}
|
||||
|
||||
void EditEntryWidget::updateSitePermissionsTable()
|
||||
{
|
||||
m_browserUi->sitePermissionsTable->setRowCount(m_sitePermissions.size());
|
||||
|
||||
int row = 0;
|
||||
for (const auto& sitePermission : m_sitePermissions) {
|
||||
const auto urlItem = new QTableWidgetItem(sitePermission.first);
|
||||
const auto allowDenyItem = new QTableWidgetItem(sitePermission.second ? tr("Yes") : tr("No"));
|
||||
|
||||
// Allow edit on site only
|
||||
urlItem->setData(Qt::UserRole, row);
|
||||
urlItem->setFlags(urlItem->flags() | Qt::ItemIsEditable);
|
||||
allowDenyItem->setData(Qt::UserRole, row);
|
||||
allowDenyItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||
|
||||
m_browserUi->sitePermissionsTable->setItem(row, 0, urlItem);
|
||||
m_browserUi->sitePermissionsTable->setItem(row, 1, allowDenyItem);
|
||||
m_browserUi->sitePermissionsTable->item(row, 1)->setTextAlignment(Qt::AlignCenter | Qt::AlignHCenter);
|
||||
++row;
|
||||
}
|
||||
}
|
||||
|
||||
void EditEntryWidget::sitePermissionSelectionChanged()
|
||||
{
|
||||
const auto currentItem = m_browserUi->sitePermissionsTable->currentIndex();
|
||||
if (!currentItem.isValid()) {
|
||||
m_browserUi->allowDenyButton->setEnabled(false);
|
||||
m_browserUi->removeSiteButton->setEnabled(false);
|
||||
m_browserUi->editSiteButton->setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto currentSite = m_sitePermissions.at(currentItem.row());
|
||||
m_browserUi->allowDenyButton->setText(currentSite.second ? tr("Deny") : tr("Allow"));
|
||||
m_browserUi->allowDenyButton->setEnabled(true);
|
||||
m_browserUi->removeSiteButton->setEnabled(true);
|
||||
m_browserUi->editSiteButton->setEnabled(true);
|
||||
}
|
||||
|
||||
void EditEntryWidget::allowDenyChangedForSite()
|
||||
{
|
||||
const auto currentItem = m_browserUi->sitePermissionsTable->currentIndex();
|
||||
if (!currentItem.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto currentSite = m_sitePermissions.at(currentItem.row());
|
||||
const auto newValue = !currentSite.second;
|
||||
|
||||
m_sitePermissions.replace(currentItem.row(), qMakePair(currentSite.first, newValue));
|
||||
m_browserUi->allowDenyButton->setText(newValue ? tr("Deny") : tr("Allow"));
|
||||
|
||||
updateSitePermissionsTable();
|
||||
updateBrowserModified();
|
||||
}
|
||||
|
||||
void EditEntryWidget::removeCurrentSite()
|
||||
{
|
||||
const auto index = m_browserUi->sitePermissionsTable->currentIndex();
|
||||
if (index.isValid()) {
|
||||
const auto result = MessageBox::question(this,
|
||||
tr("Confirm Removal"),
|
||||
tr("Are you sure you want to remove this site?"),
|
||||
MessageBox::Remove | MessageBox::Cancel,
|
||||
MessageBox::Cancel);
|
||||
if (result != MessageBox::Remove) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_sitePermissions.removeAt(index.row());
|
||||
if (m_sitePermissions.isEmpty()) {
|
||||
m_browserUi->allowDenyButton->setEnabled(false);
|
||||
m_browserUi->removeSiteButton->setEnabled(false);
|
||||
m_browserUi->editSiteButton->setEnabled(false);
|
||||
}
|
||||
|
||||
updateSitePermissionsTable();
|
||||
updateBrowserModified();
|
||||
}
|
||||
}
|
||||
|
||||
void EditEntryWidget::editSite()
|
||||
{
|
||||
const auto currentItem = m_browserUi->sitePermissionsTable->currentItem();
|
||||
if (!currentItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_browserUi->sitePermissionsTable->editItem(currentItem);
|
||||
}
|
||||
|
||||
void EditEntryWidget::editSiteFinished(QTableWidgetItem* item)
|
||||
{
|
||||
if (item->column() == 0 && item->row() <= m_sitePermissions.size()) {
|
||||
const auto currentSite = m_sitePermissions.at(item->row());
|
||||
if (currentSite.first != item->text()) {
|
||||
m_sitePermissions.replace(item->row(), qMakePair(item->text(), currentSite.second));
|
||||
|
||||
updateSitePermissionsTable();
|
||||
updateBrowserModified();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void EditEntryWidget::setupProperties()
|
||||
|
|
@ -556,6 +741,8 @@ void EditEntryWidget::setupEntryUpdate()
|
|||
connect(m_browserUi->addURLButton, SIGNAL(toggled(bool)), SLOT(setModified()));
|
||||
connect(m_browserUi->removeURLButton, SIGNAL(toggled(bool)), SLOT(setModified()));
|
||||
connect(m_browserUi->editURLButton, SIGNAL(toggled(bool)), SLOT(setModified()));
|
||||
connect(m_browserUi->allowDenyButton, SIGNAL(toggled(bool)), SLOT(setModified()));
|
||||
connect(m_browserUi->removeSiteButton, SIGNAL(toggled(bool)), SLOT(setModified()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
@ -1145,6 +1332,7 @@ void EditEntryWidget::setForms(Entry* entry, bool restore)
|
|||
}
|
||||
}
|
||||
|
||||
initializeSitePermissionsTable();
|
||||
setPageHidden(m_browserWidget, !config()->get(Config::Browser_Enabled).toBool());
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright (C) 2026 KeePassXC Team <team@keepassxc.org>
|
||||
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
|
||||
* Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* 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
|
||||
|
|
@ -44,6 +44,7 @@ class QMenu;
|
|||
class QScrollArea;
|
||||
class QSortFilterProxyModel;
|
||||
class QStringListModel;
|
||||
class QTableWidgetItem;
|
||||
#ifdef WITH_XC_SSHAGENT
|
||||
#include "sshagent/KeeAgentSettings.h"
|
||||
class OpenSSHKey;
|
||||
|
|
@ -148,6 +149,11 @@ private slots:
|
|||
void editCurrentURL();
|
||||
void updateCurrentURL();
|
||||
void entryURLEdited(const QString& url);
|
||||
void sitePermissionSelectionChanged();
|
||||
void allowDenyChangedForSite();
|
||||
void removeCurrentSite();
|
||||
void editSite();
|
||||
void editSiteFinished(QTableWidgetItem* item);
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
|
@ -157,6 +163,8 @@ private:
|
|||
void setupAutoType();
|
||||
#ifdef WITH_XC_BROWSER
|
||||
void setupBrowser();
|
||||
void initializeSitePermissionsTable();
|
||||
void updateSitePermissionsTable();
|
||||
#endif
|
||||
#ifdef WITH_XC_SSHAGENT
|
||||
void setupSSHAgent();
|
||||
|
|
@ -207,6 +215,7 @@ private:
|
|||
bool m_browserSettingsChanged;
|
||||
QWidget* const m_browserWidget;
|
||||
EntryURLModel* const m_additionalURLsDataModel;
|
||||
QList<QPair<QString, bool>> m_sitePermissions;
|
||||
#endif
|
||||
EditWidgetProperties* const m_editWidgetProperties;
|
||||
QWidget* const m_historyWidget;
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>374</width>
|
||||
<height>348</height>
|
||||
<width>532</width>
|
||||
<height>469</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_1">
|
||||
|
|
@ -155,6 +155,84 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Site Permissions</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QTableWidget" name="sitePermissionsTable">
|
||||
<property name="accessibleName">
|
||||
<string>Site Permissions</string>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QPushButton" name="allowDenyButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>Allow or deny site</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Allow</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="removeSiteButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>Remove selected site</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="editSiteButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Edit</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
|
|
|
|||
Loading…
Reference in a new issue