diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts index 409fa8a9d4..b7d5892882 100644 --- a/share/translations/keepassxc_en.ts +++ b/share/translations/keepassxc_en.ts @@ -6084,6 +6084,12 @@ Do you want to overwrite it? Cannot import Passkey file "%1". Private key is missing or malformed. + + Cannot import Passkey file "%1". +The following data is missing: +%2 + + PasswordEditWidget diff --git a/src/core/Tools.h b/src/core/Tools.h index 4316a44e84..85c1b53c09 100644 --- a/src/core/Tools.h +++ b/src/core/Tools.h @@ -22,6 +22,7 @@ #include "core/Global.h" #include +#include #include class QIODevice; @@ -100,6 +101,19 @@ namespace Tools return version; } + // Checks if all values are found inside the list. Returns a list of values not found. + template QList getMissingValuesFromList(const QList& list, const QList& required) + { + QList missingValues; + for (const auto& r : required) { + if (!list.contains(r)) { + missingValues << r; + } + } + + return missingValues; + } + QVariantMap qo2qvm(const QObject* object, const QStringList& ignoredProperties = {"objectName"}); QString substituteBackupFilePath(QString pattern, const QString& databasePath); diff --git a/src/gui/passkeys/PasskeyImporter.cpp b/src/gui/passkeys/PasskeyImporter.cpp index df83ddb70e..77e37c689b 100644 --- a/src/gui/passkeys/PasskeyImporter.cpp +++ b/src/gui/passkeys/PasskeyImporter.cpp @@ -22,6 +22,7 @@ #include "browser/BrowserService.h" #include "core/Entry.h" #include "core/Group.h" +#include "core/Tools.h" #include "gui/FileDialog.h" #include "gui/MessageBox.h" #include @@ -61,18 +62,20 @@ void PasskeyImporter::importSelectedFile(QFile& file, QSharedPointer& return; } - const auto relyingParty = passkeyObject["relyingParty"].toString(); - const auto url = passkeyObject["url"].toString(); - const auto username = passkeyObject["username"].toString(); - const auto credentialId = passkeyObject["credentialId"].toString(); - const auto userHandle = passkeyObject["userHandle"].toString(); const auto privateKey = passkeyObject["privateKey"].toString(); - - if (relyingParty.isEmpty() || username.isEmpty() || credentialId.isEmpty() || userHandle.isEmpty() - || privateKey.isEmpty()) { + const auto missingKeys = Tools::getMissingValuesFromList(passkeyObject.keys(), + QStringList() << "relyingParty" + << "url" + << "username" + << "credentialId" + << "userHandle" + << "privateKey"); + + if (!missingKeys.isEmpty()) { MessageBox::information(nullptr, tr("Cannot import Passkey"), - tr("Cannot import Passkey file \"%1\". Data is missing.").arg(file.fileName())); + tr("Cannot import Passkey file \"%1\".\nThe following data is missing:\n%2") + .arg(file.fileName(), missingKeys.join(", "))); } else if (!privateKey.startsWith("-----BEGIN PRIVATE KEY-----") || !privateKey.trimmed().endsWith("-----END PRIVATE KEY-----")) { MessageBox::information( @@ -80,6 +83,11 @@ void PasskeyImporter::importSelectedFile(QFile& file, QSharedPointer& tr("Cannot import Passkey"), tr("Cannot import Passkey file \"%1\". Private key is missing or malformed.").arg(file.fileName())); } else { + const auto relyingParty = passkeyObject["relyingParty"].toString(); + const auto url = passkeyObject["url"].toString(); + const auto username = passkeyObject["username"].toString(); + const auto credentialId = passkeyObject["credentialId"].toString(); + const auto userHandle = passkeyObject["userHandle"].toString(); showImportDialog(database, url, relyingParty, username, credentialId, userHandle, privateKey, entry); } } @@ -109,7 +117,7 @@ void PasskeyImporter::showImportDialog(QSharedPointer& database, // Store to entry if given directly if (entry) { browserService()->addPasskeyToEntry( - entry, relyingParty, relyingParty, username, userId, userHandle, privateKey); + entry, relyingParty, relyingParty, username, credentialId, userHandle, privateKey); return; } @@ -122,7 +130,7 @@ void PasskeyImporter::showImportDialog(QSharedPointer& database, auto selectedEntry = group->findEntryByUuid(passkeyImportDialog.getSelectedEntryUuid()); if (selectedEntry) { browserService()->addPasskeyToEntry( - selectedEntry, relyingParty, relyingParty, username, userId, userHandle, privateKey); + selectedEntry, relyingParty, relyingParty, username, credentialId, userHandle, privateKey); } } diff --git a/tests/TestTools.cpp b/tests/TestTools.cpp index f1cba482bf..1067aa565d 100644 --- a/tests/TestTools.cpp +++ b/tests/TestTools.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 KeePassXC Team + * Copyright (C) 2023 KeePassXC Team * * 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 @@ -239,3 +239,30 @@ void TestTools::testConvertToRegex_data() << input << static_cast(Tools::RegexConvertOpts::WILDCARD_UNLIMITED_MATCH) << QString(R"(te\|st.*t\?\[5\]\^\(test\)\;\'\,\.)"); } + +void TestTools::testArrayContainsValues() +{ + const auto values = QStringList() << "first" + << "second" + << "third"; + + // One missing + const auto result1 = Tools::getMissingValuesFromList(values, + QStringList() << "first" + << "second" + << "none"); + QCOMPARE(result1.length(), 1); + QCOMPARE(result1.first(), QString("none")); + + // All found + const auto result2 = Tools::getMissingValuesFromList(values, + QStringList() << "first" + << "second" + << "third"); + QCOMPARE(result2.length(), 0); + + // None are found + const auto numberValues = {1, 2, 3, 4, 5}; + const auto result3 = Tools::getMissingValuesFromList(numberValues, {6, 7, 8}); + QCOMPARE(result3.length(), 3); +} diff --git a/tests/TestTools.h b/tests/TestTools.h index 2e8cbb8bb0..377b00fdb8 100644 --- a/tests/TestTools.h +++ b/tests/TestTools.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 KeePassXC Team + * Copyright (C) 2023 KeePassXC Team * * 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 @@ -35,6 +35,7 @@ private slots: void testEscapeRegex_data(); void testConvertToRegex(); void testConvertToRegex_data(); + void testArrayContainsValues(); }; #endif // KEEPASSX_TESTTOOLS_H