mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-11-18 06:01:20 -05:00
FdoSecrets: add smarter handling of database unlock requests
This commit implements the following logic: * If there're already unlocked collections, just use those, * otherwise, show the unlock dialog until there's an unlocked and exposed collection. * Fixes #7574
This commit is contained in:
parent
8711d31f24
commit
07755c324a
6 changed files with 151 additions and 32 deletions
|
|
@ -210,8 +210,7 @@ namespace FdoSecrets
|
|||
return {};
|
||||
}
|
||||
|
||||
DBusResult
|
||||
Collection::searchItems(const DBusClientPtr& client, const StringStringMap& attributes, QList<Item*>& items)
|
||||
DBusResult Collection::searchItems(const DBusClientPtr&, const StringStringMap& attributes, QList<Item*>& items)
|
||||
{
|
||||
items.clear();
|
||||
|
||||
|
|
@ -220,24 +219,6 @@ namespace FdoSecrets
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (backendLocked() && settings()->unlockBeforeSearch()) {
|
||||
// do a blocking unlock prompt first.
|
||||
// while technically not correct, this should improve compatibility.
|
||||
// see issue #4443
|
||||
auto prompt = PromptBase::Create<UnlockPrompt>(service(), QSet<Collection*>{this}, QSet<Item*>{});
|
||||
if (!prompt) {
|
||||
return QDBusError::InternalError;
|
||||
}
|
||||
// we don't know the windowId to show the prompt correctly,
|
||||
// but the default of showing over the KPXC main window should be good enough
|
||||
ret = prompt->prompt(client, "");
|
||||
// blocking wait
|
||||
QEventLoop loop;
|
||||
connect(prompt, &PromptBase::completed, &loop, &QEventLoop::quit);
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
// check again because the prompt may be cancelled
|
||||
if (backendLocked()) {
|
||||
// searchItems should work, whether `this` is locked or not.
|
||||
// however, we can't search items the same way as in gnome-keying,
|
||||
|
|
|
|||
|
|
@ -184,6 +184,30 @@ namespace FdoSecrets
|
|||
return {};
|
||||
}
|
||||
|
||||
DBusResult Service::unlockedCollections(QList<Collection*>& unlocked) const
|
||||
{
|
||||
auto ret = collections(unlocked);
|
||||
if (ret.err()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// filter out locked collections
|
||||
auto it = unlocked.begin();
|
||||
while (it != unlocked.end()) {
|
||||
bool isLocked = true;
|
||||
ret = (*it)->locked(isLocked);
|
||||
if (ret.err()) {
|
||||
return ret;
|
||||
}
|
||||
if (isLocked) {
|
||||
it = unlocked.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
DBusResult Service::openSession(const DBusClientPtr& client,
|
||||
const QString& algorithm,
|
||||
const QVariant& input,
|
||||
|
|
@ -242,15 +266,43 @@ namespace FdoSecrets
|
|||
DBusResult Service::searchItems(const DBusClientPtr& client,
|
||||
const StringStringMap& attributes,
|
||||
QList<Item*>& unlocked,
|
||||
QList<Item*>& locked) const
|
||||
QList<Item*>& locked)
|
||||
{
|
||||
QList<Collection*> colls;
|
||||
auto ret = collections(colls);
|
||||
// we can only search unlocked collections
|
||||
QList<Collection*> unlockedColls;
|
||||
auto ret = unlockedCollections(unlockedColls);
|
||||
if (ret.err()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (const auto& coll : asConst(colls)) {
|
||||
while (unlockedColls.isEmpty() && settings()->unlockBeforeSearch()) {
|
||||
// enable compatibility mode by making sure at least one database is unlocked
|
||||
QEventLoop loop;
|
||||
bool wasAccepted = false;
|
||||
connect(this, &Service::doneUnlockDatabaseInDialog, &loop, [&](bool accepted) {
|
||||
wasAccepted = accepted;
|
||||
loop.quit();
|
||||
});
|
||||
|
||||
doUnlockAnyDatabaseInDialog();
|
||||
|
||||
// blocking wait
|
||||
loop.exec();
|
||||
|
||||
if (!wasAccepted) {
|
||||
// user cancelled, do not proceed
|
||||
qWarning() << "user cancelled";
|
||||
return {};
|
||||
}
|
||||
|
||||
// need to recompute this because collections may disappear while in event loop
|
||||
ret = unlockedCollections(unlockedColls);
|
||||
if (ret.err()) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& coll : asConst(unlockedColls)) {
|
||||
QList<Item*> items;
|
||||
ret = coll->searchItems(client, attributes, items);
|
||||
if (ret.err()) {
|
||||
|
|
@ -524,7 +576,7 @@ namespace FdoSecrets
|
|||
}
|
||||
|
||||
// check if the db is already being unlocked to prevent multiple dialogs for the same db
|
||||
if (m_unlockingDb.contains(dbWidget)) {
|
||||
if (m_unlockingAnyDatabase || m_unlockingDb.contains(dbWidget)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -536,21 +588,33 @@ namespace FdoSecrets
|
|||
m_databases->unlockDatabaseInDialog(dbWidget, DatabaseOpenDialog::Intent::None);
|
||||
}
|
||||
|
||||
void Service::doUnlockAnyDatabaseInDialog()
|
||||
{
|
||||
if (m_unlockingAnyDatabase || !m_unlockingDb.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
m_unlockingAnyDatabase = true;
|
||||
|
||||
m_databases->unlockAnyDatabaseInDialog(DatabaseOpenDialog::Intent::None);
|
||||
}
|
||||
|
||||
void Service::onDatabaseUnlockDialogFinished(bool accepted, DatabaseWidget* dbWidget)
|
||||
{
|
||||
if (!m_unlockingDb.contains(dbWidget)) {
|
||||
if (!m_unlockingAnyDatabase && !m_unlockingDb.contains(dbWidget)) {
|
||||
// not our concern
|
||||
return;
|
||||
}
|
||||
|
||||
if (!accepted) {
|
||||
emit doneUnlockDatabaseInDialog(false, dbWidget);
|
||||
m_unlockingAnyDatabase = false;
|
||||
m_unlockingDb.remove(dbWidget);
|
||||
} else {
|
||||
// delay the done signal to when the database is actually done with unlocking
|
||||
// this is a oneshot connection to prevent superfluous signals
|
||||
auto conn = connect(dbWidget, &DatabaseWidget::databaseUnlocked, this, [dbWidget, this]() {
|
||||
emit doneUnlockDatabaseInDialog(true, dbWidget);
|
||||
m_unlockingAnyDatabase = false;
|
||||
disconnect(m_unlockingDb.take(dbWidget));
|
||||
});
|
||||
m_unlockingDb[dbWidget] = conn;
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ namespace FdoSecrets
|
|||
Q_INVOKABLE DBusResult searchItems(const DBusClientPtr& client,
|
||||
const StringStringMap& attributes,
|
||||
QList<Item*>& unlocked,
|
||||
QList<Item*>& locked) const;
|
||||
QList<Item*>& locked);
|
||||
|
||||
Q_INVOKABLE DBusResult unlock(const DBusClientPtr& client,
|
||||
const QList<DBusObject*>& objects,
|
||||
|
|
@ -125,6 +125,12 @@ namespace FdoSecrets
|
|||
*/
|
||||
void doUnlockDatabaseInDialog(DatabaseWidget* dbWidget);
|
||||
|
||||
/**
|
||||
* Async, connect to signal doneUnlockDatabaseInDialog for finish notification
|
||||
* @param dbWidget
|
||||
*/
|
||||
void doUnlockAnyDatabaseInDialog();
|
||||
|
||||
private slots:
|
||||
void ensureDefaultAlias();
|
||||
|
||||
|
|
@ -154,6 +160,8 @@ namespace FdoSecrets
|
|||
*/
|
||||
Collection* findCollection(const DatabaseWidget* db) const;
|
||||
|
||||
DBusResult unlockedCollections(QList<Collection*>& unlocked) const;
|
||||
|
||||
private:
|
||||
FdoSecretsPlugin* m_plugin{nullptr};
|
||||
QPointer<DatabaseTabWidget> m_databases{};
|
||||
|
|
@ -165,6 +173,7 @@ namespace FdoSecrets
|
|||
QList<Session*> m_sessions{};
|
||||
|
||||
bool m_insideEnsureDefaultAlias{false};
|
||||
bool m_unlockingAnyDatabase{false};
|
||||
// list of db currently has unlock dialog shown
|
||||
QHash<const DatabaseWidget*, QMetaObject::Connection> m_unlockingDb{};
|
||||
QSet<const DatabaseWidget*> m_lockingDb{}; // list of db being locking
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ public slots:
|
|||
void closeDatabaseFromSender();
|
||||
void unlockDatabaseInDialog(DatabaseWidget* dbWidget, DatabaseOpenDialog::Intent intent);
|
||||
void unlockDatabaseInDialog(DatabaseWidget* dbWidget, DatabaseOpenDialog::Intent intent, const QString& filePath);
|
||||
void unlockAnyDatabaseInDialog(DatabaseOpenDialog::Intent intent);
|
||||
void relockPendingDatabase();
|
||||
|
||||
void showDatabaseSecurity();
|
||||
|
|
@ -106,7 +107,6 @@ private:
|
|||
QSharedPointer<Database> execNewDatabaseWizard();
|
||||
void updateLastDatabases(const QString& filename);
|
||||
bool warnOnExport();
|
||||
void unlockAnyDatabaseInDialog(DatabaseOpenDialog::Intent intent);
|
||||
void displayUnlockDialog();
|
||||
|
||||
QPointer<DatabaseWidgetStateSync> m_dbWidgetStateSync;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue