diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts index 183837fae..b93ebac90 100644 --- a/share/translations/keepassxc_en.ts +++ b/share/translations/keepassxc_en.ts @@ -10158,6 +10158,10 @@ This option is deprecated, use --set-key-file instead. Weak Passwords + + TOTP Entries + + TagView diff --git a/src/core/EntrySearcher.cpp b/src/core/EntrySearcher.cpp index 085c5bcdb..cb9e135fe 100644 --- a/src/core/EntrySearcher.cpp +++ b/src/core/EntrySearcher.cpp @@ -221,6 +221,13 @@ bool EntrySearcher::searchEntryImpl(const Entry* entry) } found = false; break; + case Field::Has: + if (term.word.compare("totp", Qt::CaseInsensitive) == 0) { + found = entry->hasTotp(); + break; + } + found = false; + break; case Field::Uuid: found = term.regex.match(entry->uuidToHex()).hasMatch(); break; @@ -260,6 +267,7 @@ void EntrySearcher::parseSearchTerms(const QString& searchString) {QStringLiteral("group"), Field::Group}, {QStringLiteral("tag"), Field::Tag}, {QStringLiteral("is"), Field::Is}, + {QStringLiteral("has"), Field::Has}, {QStringLiteral("uuid"), Field::Uuid}}; // Group 1 = modifiers, Group 2 = field, Group 3 = quoted string, Group 4 = unquoted string diff --git a/src/core/EntrySearcher.h b/src/core/EntrySearcher.h index 0134054bf..a15916fb0 100644 --- a/src/core/EntrySearcher.h +++ b/src/core/EntrySearcher.h @@ -41,6 +41,7 @@ public: Group, Tag, Is, + Has, Uuid }; diff --git a/src/gui/tag/TagModel.cpp b/src/gui/tag/TagModel.cpp index dfa4e943d..a19f263d8 100644 --- a/src/gui/tag/TagModel.cpp +++ b/src/gui/tag/TagModel.cpp @@ -30,7 +30,8 @@ TagModel::TagModel(QObject* parent) { m_defaultSearches << qMakePair(tr("Clear Search"), QString("")) << qMakePair(tr("All Entries"), QString("*")) << qMakePair(tr("Expired"), QString("is:expired")) - << qMakePair(tr("Weak Passwords"), QString("is:weak")); + << qMakePair(tr("Weak Passwords"), QString("is:weak")) + << qMakePair(tr("TOTP Entries"), QString("has:totp")); } TagModel::~TagModel() = default; diff --git a/tests/TestEntrySearcher.cpp b/tests/TestEntrySearcher.cpp index 7a7e45323..d002a3166 100644 --- a/tests/TestEntrySearcher.cpp +++ b/tests/TestEntrySearcher.cpp @@ -18,6 +18,7 @@ #include "TestEntrySearcher.h" #include "core/Group.h" #include "core/Tools.h" +#include "core/Totp.h" #include @@ -394,3 +395,42 @@ void TestEntrySearcher::testUUIDSearch() m_searchResult = m_entrySearcher.search("uuid:" + Tools::uuidToHex(uuid1), m_rootGroup); QCOMPARE(m_searchResult.count(), 1); } + +void TestEntrySearcher::testTotpSearch() +{ + auto entry1 = new Entry(); + entry1->setGroup(m_rootGroup); + entry1->setTitle("Regular Entry"); + + auto entry2 = new Entry(); + entry2->setGroup(m_rootGroup); + entry2->setTitle("TOTP Entry"); + // Set up TOTP on entry2 + auto totpSettings = Totp::createSettings("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ", 6, 30); + entry2->setTotp(totpSettings); + + auto entry3 = new Entry(); + entry3->setGroup(m_rootGroup); + entry3->setTitle("Another TOTP Entry"); + // Set up TOTP on entry3 + auto totpSettings2 = Totp::createSettings("MFRGG43UEBUXGIDBKRWXAZLSMUQGG6LQ", 6, 30); + entry3->setTotp(totpSettings2); + + // Test searching for TOTP entries + m_searchResult = m_entrySearcher.search("has:totp", m_rootGroup); + QCOMPARE(m_searchResult.count(), 2); + QVERIFY(m_searchResult.contains(entry2)); + QVERIFY(m_searchResult.contains(entry3)); + QVERIFY(!m_searchResult.contains(entry1)); + + // Test case insensitive search + m_searchResult = m_entrySearcher.search("has:TOTP", m_rootGroup); + QCOMPARE(m_searchResult.count(), 2); + + // Test excluding TOTP entries + m_searchResult = m_entrySearcher.search("!has:totp", m_rootGroup); + QCOMPARE(m_searchResult.count(), 1); + QVERIFY(m_searchResult.contains(entry1)); + QVERIFY(!m_searchResult.contains(entry2)); + QVERIFY(!m_searchResult.contains(entry3)); +} diff --git a/tests/TestEntrySearcher.h b/tests/TestEntrySearcher.h index 2ca81a742..54a45d578 100644 --- a/tests/TestEntrySearcher.h +++ b/tests/TestEntrySearcher.h @@ -39,6 +39,7 @@ private slots: void testGroup(); void testSkipProtected(); void testUUIDSearch(); + void testTotpSearch(); private: Group* m_rootGroup;