mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-02-18 05:14:16 -05:00
Confirmation dialog to list entries.
This commit is contained in:
parent
eef51f26f0
commit
ad67eac257
@ -101,6 +101,8 @@ set(keepassx_SOURCES
|
||||
gui/group/EditGroupWidget.cpp
|
||||
gui/group/GroupModel.cpp
|
||||
gui/group/GroupView.cpp
|
||||
http/AccessControlDialog.cpp
|
||||
http/EntryConfig.cpp
|
||||
http/Protocol.cpp
|
||||
http/Server.cpp
|
||||
http/Service.cpp
|
||||
@ -166,6 +168,8 @@ set(keepassx_MOC
|
||||
gui/group/EditGroupWidget.h
|
||||
gui/group/GroupModel.h
|
||||
gui/group/GroupView.h
|
||||
http/AccessControlDialog.h
|
||||
http/EntryConfig.h
|
||||
http/Protocol.h
|
||||
http/Server.h
|
||||
http/Service.h
|
||||
@ -196,6 +200,7 @@ set(keepassx_FORMS
|
||||
gui/entry/EditEntryWidgetHistory.ui
|
||||
gui/entry/EditEntryWidgetMain.ui
|
||||
gui/group/EditGroupWidgetMain.ui
|
||||
http/AccessControlDialog.ui
|
||||
)
|
||||
|
||||
if(MINGW)
|
||||
|
52
src/http/AccessControlDialog.cpp
Normal file
52
src/http/AccessControlDialog.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
/**
|
||||
***************************************************************************
|
||||
* @file AccessControlDialog.cpp
|
||||
*
|
||||
* @brief
|
||||
*
|
||||
* Copyright (C) 2013
|
||||
*
|
||||
* @author Francois Ferrand
|
||||
* @date 4/2013
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
#include "AccessControlDialog.h"
|
||||
#include "ui_AccessControlDialog.h"
|
||||
#include "core/Entry.h"
|
||||
|
||||
AccessControlDialog::AccessControlDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::AccessControlDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
connect(ui->allowButton, SIGNAL(clicked()), this, SLOT(accept()));
|
||||
connect(ui->denyButton, SIGNAL(clicked()), this, SLOT(reject()));
|
||||
}
|
||||
|
||||
AccessControlDialog::~AccessControlDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void AccessControlDialog::setUrl(const QString &url)
|
||||
{
|
||||
ui->label->setText(QString(tr("%1 has requested access to passwords for the following item(s).\n"
|
||||
"Please select whether you want to allow access.")).arg(QUrl(url).host()));
|
||||
}
|
||||
|
||||
void AccessControlDialog::setItems(const QList<Entry *> &items)
|
||||
{
|
||||
Q_FOREACH (Entry * entry, items)
|
||||
ui->itemsList->addItem(entry->title() + " - " + entry->username());
|
||||
}
|
||||
|
||||
bool AccessControlDialog::remember() const
|
||||
{
|
||||
return ui->rememberDecisionCheckBox->isChecked();
|
||||
}
|
||||
|
||||
void AccessControlDialog::setRemember(bool r)
|
||||
{
|
||||
ui->rememberDecisionCheckBox->setChecked(r);
|
||||
}
|
42
src/http/AccessControlDialog.h
Normal file
42
src/http/AccessControlDialog.h
Normal file
@ -0,0 +1,42 @@
|
||||
/**
|
||||
***************************************************************************
|
||||
* @file AccessControlDialog.h
|
||||
*
|
||||
* @brief
|
||||
*
|
||||
* Copyright (C) 2013
|
||||
*
|
||||
* @author Francois Ferrand
|
||||
* @date 4/2013
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef ACCESSCONTROLDIALOG_H
|
||||
#define ACCESSCONTROLDIALOG_H
|
||||
|
||||
#include <QtGui/QDialog>
|
||||
|
||||
class Entry;
|
||||
|
||||
namespace Ui {
|
||||
class AccessControlDialog;
|
||||
}
|
||||
|
||||
class AccessControlDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AccessControlDialog(QWidget *parent = 0);
|
||||
~AccessControlDialog();
|
||||
|
||||
void setUrl(const QString & url);
|
||||
void setItems(const QList<Entry *> & items);
|
||||
bool remember() const;
|
||||
void setRemember(bool r);
|
||||
|
||||
private:
|
||||
Ui::AccessControlDialog *ui;
|
||||
};
|
||||
|
||||
#endif // ACCESSCONTROLDIALOG_H
|
69
src/http/AccessControlDialog.ui
Normal file
69
src/http/AccessControlDialog.ui
Normal file
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AccessControlDialog</class>
|
||||
<widget class="QDialog" name="AccessControlDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>221</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>KeyPassX/Http: Confirm Access</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="itemsList"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="rememberDecisionCheckBox">
|
||||
<property name="text">
|
||||
<string>Remember this decision</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="allowButton">
|
||||
<property name="text">
|
||||
<string>Allow</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="denyButton">
|
||||
<property name="text">
|
||||
<string>Deny</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
100
src/http/EntryConfig.cpp
Normal file
100
src/http/EntryConfig.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
/**
|
||||
***************************************************************************
|
||||
* @file EntryConfig.cpp
|
||||
*
|
||||
* @brief
|
||||
*
|
||||
* Copyright (C) 2013
|
||||
*
|
||||
* @author Francois Ferrand
|
||||
* @date 4/2013
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
#include "EntryConfig.h"
|
||||
#include "core/Entry.h"
|
||||
#include "core/EntryAttributes.h"
|
||||
#include "qjson/parser.h"
|
||||
#include "qjson/qobjecthelper.h"
|
||||
#include "qjson/serializer.h"
|
||||
|
||||
static const char KEEPASSHTTP_NAME[] = "KeePassHttp Settings"; //TODO: duplicated string (also in Service.cpp)
|
||||
|
||||
EntryConfig::EntryConfig(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
QStringList EntryConfig::allowedHosts() const
|
||||
{
|
||||
return m_allowedHosts.toList();
|
||||
}
|
||||
|
||||
void EntryConfig::setAllowedHosts(const QStringList &allowedHosts)
|
||||
{
|
||||
m_allowedHosts = allowedHosts.toSet();
|
||||
}
|
||||
|
||||
QStringList EntryConfig::deniedHosts() const
|
||||
{
|
||||
return m_deniedHosts.toList();
|
||||
}
|
||||
|
||||
void EntryConfig::setDeniedHosts(const QStringList &deniedHosts)
|
||||
{
|
||||
m_deniedHosts = deniedHosts.toSet();
|
||||
}
|
||||
|
||||
bool EntryConfig::isAllowed(const QString &host)
|
||||
{
|
||||
return m_allowedHosts.contains(host);
|
||||
}
|
||||
|
||||
void EntryConfig::allow(const QString &host)
|
||||
{
|
||||
m_allowedHosts.insert(host);
|
||||
m_deniedHosts.remove(host);
|
||||
}
|
||||
|
||||
bool EntryConfig::isDenied(const QString &host)
|
||||
{
|
||||
return m_deniedHosts.contains(host);
|
||||
}
|
||||
|
||||
void EntryConfig::deny(const QString &host)
|
||||
{
|
||||
m_deniedHosts.insert(host);
|
||||
m_allowedHosts.remove(host);
|
||||
}
|
||||
|
||||
QString EntryConfig::realm() const
|
||||
{
|
||||
return m_realm;
|
||||
}
|
||||
|
||||
void EntryConfig::setRealm(const QString &realm)
|
||||
{
|
||||
m_realm = realm;
|
||||
}
|
||||
|
||||
bool EntryConfig::load(const Entry *entry)
|
||||
{
|
||||
QString s = entry->attributes()->value(KEEPASSHTTP_NAME);
|
||||
if (s.isEmpty())
|
||||
return false;
|
||||
|
||||
bool isOk = false;
|
||||
QVariant v = QJson::Parser().parse(s.toUtf8(), &isOk);
|
||||
if (!isOk || !v.type() == QVariant::Map)
|
||||
return false;
|
||||
|
||||
QJson::QObjectHelper::qvariant2qobject(v.toMap(), this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void EntryConfig::save(Entry *entry)
|
||||
{
|
||||
QVariant v = QJson::QObjectHelper::qobject2qvariant(this, QJson::QObjectHelper::Flag_None);
|
||||
QByteArray json = QJson::Serializer().serialize(v);
|
||||
entry->attributes()->set(KEEPASSHTTP_NAME, json);
|
||||
}
|
54
src/http/EntryConfig.h
Normal file
54
src/http/EntryConfig.h
Normal file
@ -0,0 +1,54 @@
|
||||
/**
|
||||
***************************************************************************
|
||||
* @file EntryConfig.h
|
||||
*
|
||||
* @brief
|
||||
*
|
||||
* Copyright (C) 2013
|
||||
*
|
||||
* @author Francois Ferrand
|
||||
* @date 4/2013
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef ENTRYCONFIG_H
|
||||
#define ENTRYCONFIG_H
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QSet>
|
||||
|
||||
class Entry;
|
||||
|
||||
class EntryConfig : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QStringList Allow READ allowedHosts WRITE setAllowedHosts)
|
||||
Q_PROPERTY(QStringList Deny READ deniedHosts WRITE setDeniedHosts )
|
||||
Q_PROPERTY(QString Realm READ realm WRITE setRealm )
|
||||
|
||||
public:
|
||||
EntryConfig(QObject * object = 0);
|
||||
|
||||
bool load(const Entry * entry);
|
||||
void save(Entry * entry);
|
||||
bool isAllowed(const QString & host);
|
||||
void allow(const QString & host);
|
||||
bool isDenied(const QString & host);
|
||||
void deny(const QString & host);
|
||||
QString realm() const;
|
||||
void setRealm(const QString &realm);
|
||||
|
||||
private:
|
||||
QStringList allowedHosts() const;
|
||||
void setAllowedHosts(const QStringList &allowedHosts);
|
||||
QStringList deniedHosts() const;
|
||||
void setDeniedHosts(const QStringList &deniedHosts);
|
||||
|
||||
QSet<QString> m_allowedHosts;
|
||||
QSet<QString> m_deniedHosts;
|
||||
QString m_realm;
|
||||
};
|
||||
|
||||
#endif // ENTRYCONFIG_H
|
@ -11,17 +11,22 @@
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
#include <QtGui/QInputDialog>
|
||||
#include <QtGui/QMessageBox>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include "Service.h"
|
||||
#include "Protocol.h"
|
||||
#include "EntryConfig.h"
|
||||
#include "AccessControlDialog.h"
|
||||
|
||||
#include "core/Database.h"
|
||||
#include "core/Entry.h"
|
||||
#include "core/Group.h"
|
||||
#include "core/Metadata.h"
|
||||
#include "core/Uuid.h"
|
||||
#include "core/PasswordGenerator.h"
|
||||
#include <QtGui/QInputDialog>
|
||||
#include <QtGui/QMessageBox>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
|
||||
Service::Service(DatabaseTabWidget *parent) :
|
||||
KeepassHttpProtocol::Server(parent),
|
||||
@ -121,8 +126,8 @@ QString Service::storeKey(const QString &key)
|
||||
//Indicate who wants to associate, and request user to enter the 'name' of association key
|
||||
id = QInputDialog::getText(0, tr("KeyPassX/Http: New key association request"),
|
||||
tr("You have received an association request for the above key. If you would like to "
|
||||
"allow it access to your KeePassX database give it a unique name to identify and a"
|
||||
"ccept it."),
|
||||
"allow it access to your KeePassX database give it a unique name to identify and"
|
||||
"accept it."),
|
||||
QLineEdit::Normal, QString(), &ok);
|
||||
if (!ok || id.isEmpty())
|
||||
return QString();
|
||||
@ -182,31 +187,83 @@ QList<Entry*> Service::searchEntries(const QString &text)
|
||||
return entries;
|
||||
}
|
||||
|
||||
Service::Access Service::checkAccess(const Entry *entry, const QString & host, const QString & submitHost, const QString & realm)
|
||||
{
|
||||
EntryConfig config;
|
||||
if (!config.load(entry))
|
||||
return Unknown; //not configured
|
||||
if ((config.isAllowed(host)) && (submitHost.isEmpty() || config.isAllowed(submitHost)))
|
||||
return Allowed; //allowed
|
||||
if ((config.isDenied(host)) || (!submitHost.isEmpty() && config.isDenied(submitHost)))
|
||||
return Denied; //denied
|
||||
if (!realm.isEmpty() && config.realm() != realm)
|
||||
return Denied;
|
||||
return Unknown; //not configured for this host
|
||||
}
|
||||
|
||||
QList<KeepassHttpProtocol::Entry> Service::findMatchingEntries(const QString &id, const QString &url, const QString &submitUrl, const QString &realm)
|
||||
{
|
||||
QList<KeepassHttpProtocol::Entry> result;
|
||||
QList<Entry*> pwEntriesToConfirm;
|
||||
|
||||
bool autoAccept = false; //TODO: setting!
|
||||
const QString host = QUrl(url).host();
|
||||
const QString submitHost = QUrl(submitUrl).host();
|
||||
const QList<Entry*> pwEntries = searchEntries(url);
|
||||
Q_FOREACH (Entry * entry, pwEntries) {
|
||||
//Filter accepted/denied entries
|
||||
// if (c.Allow.Contains(formHost) && (submitHost == null || c.Allow.Contains(submitHost)))
|
||||
// return true;
|
||||
// if (c.Deny.Contains(formHost) || (submitHost != null && c.Deny.Contains(submitHost)))
|
||||
// return false;
|
||||
// if (realm != null && c.Realm != realm)
|
||||
// return false;
|
||||
|
||||
//If we are unsure for some entries:
|
||||
//- balloon to grant accessc if possible
|
||||
//- if clicked, show confirmation dialog --> accept/reject (w/ list of items?)
|
||||
// The website XXX wants to access your credentials
|
||||
// MORE (---> if clicked, shows the list of returned entries)
|
||||
// [x] Ask me again [Allow] [Deny]
|
||||
// If accepted, store that entry can be accessed without confirmation
|
||||
//- else, show only items which do not require validation
|
||||
//Check entries for authorization
|
||||
Q_FOREACH (Entry * entry, pwEntries) {
|
||||
switch(checkAccess(entry, host, submitHost, realm)) {
|
||||
case Denied:
|
||||
continue;
|
||||
|
||||
//TODO: sort [--> need a flag], or do this in Server class [--> need an extra 'sort order' key in Entry, and we always compute it]
|
||||
result << KeepassHttpProtocol::Entry(entry->title(), entry->username(), entry->password(), entry->uuid().toHex());
|
||||
case Unknown:
|
||||
if (!autoAccept) {
|
||||
pwEntriesToConfirm.append(entry);
|
||||
break;
|
||||
}
|
||||
//fall through
|
||||
case Allowed:
|
||||
result << KeepassHttpProtocol::Entry(entry->title(), entry->username(), entry->password(), entry->uuid().toHex());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//If unsure, ask user for confirmation
|
||||
if (!pwEntriesToConfirm.isEmpty()) {
|
||||
//TODO: balloon to grant access + timeout
|
||||
|
||||
AccessControlDialog dlg;
|
||||
dlg.setUrl(url);
|
||||
dlg.setItems(pwEntriesToConfirm);
|
||||
//dlg.setRemember(); //TODO: setting!
|
||||
|
||||
int res = dlg.exec();
|
||||
if (dlg.remember()) {
|
||||
Q_FOREACH (Entry * entry, pwEntries) {
|
||||
EntryConfig config;
|
||||
config.load(entry);
|
||||
if (res == QDialog::Accepted) {
|
||||
config.allow(host);
|
||||
if (!submitHost.isEmpty() && host != submitHost)
|
||||
config.allow(submitHost);
|
||||
} else if (res == QDialog::Rejected) {
|
||||
config.deny(host);
|
||||
if (!submitHost.isEmpty() && host != submitHost)
|
||||
config.deny(submitHost);
|
||||
}
|
||||
if (!realm.isEmpty())
|
||||
config.setRealm(realm);
|
||||
config.save(entry);
|
||||
}
|
||||
}
|
||||
if (res == QDialog::Accepted) {
|
||||
Q_FOREACH (Entry * entry, pwEntries)
|
||||
result << KeepassHttpProtocol::Entry(entry->title(), entry->username(), entry->password(), entry->uuid().toHex());
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: sort [--> need a flag], or do this in Server class [--> need an extra 'sort order' key in Entry, and we always compute it]
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -259,6 +316,16 @@ void Service::addEntry(const QString &id, const QString &login, const QString &p
|
||||
entry->setUsername(login);
|
||||
entry->setPassword(password);
|
||||
entry->setGroup(group);
|
||||
|
||||
const QString host = QUrl(url).host();
|
||||
const QString submitHost = QUrl(submitUrl).host();
|
||||
EntryConfig config;
|
||||
config.allow(host);
|
||||
if (!submitHost.isEmpty())
|
||||
config.allow(submitHost);
|
||||
if (!realm.isEmpty())
|
||||
config.setRealm(realm);
|
||||
config.save(entry);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,8 +39,10 @@ public:
|
||||
virtual QString generatePassword();
|
||||
|
||||
private:
|
||||
enum Access { Denied, Unknown, Allowed};
|
||||
Entry* getConfigEntry(bool create = false);
|
||||
bool matchUrlScheme(const QString &url);
|
||||
Access checkAccess(const Entry *entry, const QString & host, const QString & submitHost, const QString & realm);
|
||||
bool removeFirstDomain(QString &hostname);
|
||||
Group *findCreateAddEntryGroup();
|
||||
QList<Entry *> searchEntries(const QString &text);
|
||||
|
Loading…
x
Reference in New Issue
Block a user