From 89c7523f4ab406f0856af94b720385352d6aac52 Mon Sep 17 00:00:00 2001 From: bu4ak Date: Sat, 25 Apr 2026 00:15:26 +0300 Subject: [PATCH] Fix #13073: Respect password generator settings for new entries --- src/core/PasswordGenerator.cpp | 67 +++++++++++++++ src/core/PasswordGenerator.h | 2 + src/gui/entry/EditEntryWidget.cpp | 5 +- tests/TestPasswordGenerator.cpp | 130 ++++++++++++++++++++++++++++++ tests/TestPasswordGenerator.h | 1 + 5 files changed, 204 insertions(+), 1 deletion(-) diff --git a/src/core/PasswordGenerator.cpp b/src/core/PasswordGenerator.cpp index aa0f3e7172..c12ac7753e 100644 --- a/src/core/PasswordGenerator.cpp +++ b/src/core/PasswordGenerator.cpp @@ -18,6 +18,7 @@ #include "PasswordGenerator.h" +#include "core/Config.h" #include "crypto/Random.h" const int PasswordGenerator::DefaultLength = 32; @@ -33,6 +34,72 @@ PasswordGenerator::PasswordGenerator() { } +PasswordGenerator PasswordGenerator::createFromConfig() +{ + PasswordGenerator generator; + + generator.setLength(config()->get(Config::PasswordGenerator_Length).toInt()); + + PasswordGenerator::CharClasses classes; + if (config()->get(Config::PasswordGenerator_LowerCase).toBool()) { + classes |= PasswordGenerator::LowerLetters; + } + if (config()->get(Config::PasswordGenerator_UpperCase).toBool()) { + classes |= PasswordGenerator::UpperLetters; + } + if (config()->get(Config::PasswordGenerator_Numbers).toBool()) { + classes |= PasswordGenerator::Numbers; + } + if (config()->get(Config::PasswordGenerator_EASCII).toBool()) { + classes |= PasswordGenerator::EASCII; + } + + bool advanced = config()->get(Config::PasswordGenerator_AdvancedMode).toBool(); + if (!advanced) { + if (config()->get(Config::PasswordGenerator_SpecialChars).toBool()) { + classes |= PasswordGenerator::SpecialCharacters; + } + } else { + if (config()->get(Config::PasswordGenerator_Braces).toBool()) { + classes |= PasswordGenerator::Braces; + } + if (config()->get(Config::PasswordGenerator_Punctuation).toBool()) { + classes |= PasswordGenerator::Punctuation; + } + if (config()->get(Config::PasswordGenerator_Quotes).toBool()) { + classes |= PasswordGenerator::Quotes; + } + if (config()->get(Config::PasswordGenerator_Dashes).toBool()) { + classes |= PasswordGenerator::Dashes; + } + if (config()->get(Config::PasswordGenerator_Math).toBool()) { + classes |= PasswordGenerator::Math; + } + if (config()->get(Config::PasswordGenerator_Logograms).toBool()) { + classes |= PasswordGenerator::Logograms; + } + } + generator.setCharClasses(classes); + + if (advanced) { + generator.setCustomCharacterSet(config()->get(Config::PasswordGenerator_AdditionalChars).toString()); + generator.setExcludedCharacterSet(config()->get(Config::PasswordGenerator_ExcludedChars).toString()); + } + + PasswordGenerator::GeneratorFlags flags; + if (advanced) { + if (config()->get(Config::PasswordGenerator_ExcludeAlike).toBool()) { + flags |= PasswordGenerator::ExcludeLookAlike; + } + if (config()->get(Config::PasswordGenerator_EnsureEvery).toBool()) { + flags |= PasswordGenerator::CharFromEveryGroup; + } + } + generator.setFlags(flags); + + return generator; +} + void PasswordGenerator::setLength(int length) { m_length = length; diff --git a/src/core/PasswordGenerator.h b/src/core/PasswordGenerator.h index 34807bc051..c6eaea72f9 100644 --- a/src/core/PasswordGenerator.h +++ b/src/core/PasswordGenerator.h @@ -65,6 +65,8 @@ class PasswordGenerator void setExcludedCharacterSet(const QString& excludedCharacterSet); void reset(); + static PasswordGenerator createFromConfig(); + bool isValid() const; int getMinLength() const; diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 3a665c5cf4..6ab1a04186 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -961,7 +961,10 @@ void EditEntryWidget::loadEntry(Entry* entry, // Set an initial password for new entries if the option is enabled if (create && config()->get(Config::AutoGeneratePasswordForNewEntries).toBool()) { - PasswordGenerator generator; + PasswordGenerator generator = PasswordGenerator::createFromConfig(); + if (!generator.isValid()) { + generator = PasswordGenerator(); + } m_mainUi->passwordEdit->setText(generator.generatePassword()); } diff --git a/tests/TestPasswordGenerator.cpp b/tests/TestPasswordGenerator.cpp index 10b91f6af6..66aa8807cf 100644 --- a/tests/TestPasswordGenerator.cpp +++ b/tests/TestPasswordGenerator.cpp @@ -16,9 +16,11 @@ */ #include "TestPasswordGenerator.h" +#include "core/Config.h" #include "crypto/Crypto.h" #include +#include #include QTEST_GUILESS_MAIN(TestPasswordGenerator) @@ -281,3 +283,131 @@ void TestPasswordGenerator::testReset() QCOMPARE(m_generator.getExcludedCharacterSet(), default_generator.getExcludedCharacterSet()); QCOMPARE(m_generator.getLength(), default_generator.getLength()); } + +void TestPasswordGenerator::testCreateFromConfig() +{ + // Save current config values and restore on scope exit + auto savedLower = config()->get(Config::PasswordGenerator_LowerCase); + auto savedUpper = config()->get(Config::PasswordGenerator_UpperCase); + auto savedNumbers = config()->get(Config::PasswordGenerator_Numbers); + auto savedEASCII = config()->get(Config::PasswordGenerator_EASCII); + auto savedAdvanced = config()->get(Config::PasswordGenerator_AdvancedMode); + auto savedSpecialChars = config()->get(Config::PasswordGenerator_SpecialChars); + auto savedBraces = config()->get(Config::PasswordGenerator_Braces); + auto savedPunctuation = config()->get(Config::PasswordGenerator_Punctuation); + auto savedQuotes = config()->get(Config::PasswordGenerator_Quotes); + auto savedDashes = config()->get(Config::PasswordGenerator_Dashes); + auto savedMath = config()->get(Config::PasswordGenerator_Math); + auto savedLogograms = config()->get(Config::PasswordGenerator_Logograms); + auto savedAdditionalChars = config()->get(Config::PasswordGenerator_AdditionalChars); + auto savedExcludedChars = config()->get(Config::PasswordGenerator_ExcludedChars); + auto savedExcludeAlike = config()->get(Config::PasswordGenerator_ExcludeAlike); + auto savedEnsureEvery = config()->get(Config::PasswordGenerator_EnsureEvery); + auto savedLength = config()->get(Config::PasswordGenerator_Length); + + QScopeGuard guard([savedLower, + savedUpper, + savedNumbers, + savedEASCII, + savedAdvanced, + savedSpecialChars, + savedBraces, + savedPunctuation, + savedQuotes, + savedDashes, + savedMath, + savedLogograms, + savedAdditionalChars, + savedExcludedChars, + savedExcludeAlike, + savedEnsureEvery, + savedLength]() { + config()->set(Config::PasswordGenerator_LowerCase, savedLower); + config()->set(Config::PasswordGenerator_UpperCase, savedUpper); + config()->set(Config::PasswordGenerator_Numbers, savedNumbers); + config()->set(Config::PasswordGenerator_EASCII, savedEASCII); + config()->set(Config::PasswordGenerator_AdvancedMode, savedAdvanced); + config()->set(Config::PasswordGenerator_SpecialChars, savedSpecialChars); + config()->set(Config::PasswordGenerator_Braces, savedBraces); + config()->set(Config::PasswordGenerator_Punctuation, savedPunctuation); + config()->set(Config::PasswordGenerator_Quotes, savedQuotes); + config()->set(Config::PasswordGenerator_Dashes, savedDashes); + config()->set(Config::PasswordGenerator_Math, savedMath); + config()->set(Config::PasswordGenerator_Logograms, savedLogograms); + config()->set(Config::PasswordGenerator_AdditionalChars, savedAdditionalChars); + config()->set(Config::PasswordGenerator_ExcludedChars, savedExcludedChars); + config()->set(Config::PasswordGenerator_ExcludeAlike, savedExcludeAlike); + config()->set(Config::PasswordGenerator_EnsureEvery, savedEnsureEvery); + config()->set(Config::PasswordGenerator_Length, savedLength); + }); + + // Test non-advanced mode with special chars + config()->set(Config::PasswordGenerator_LowerCase, false); + config()->set(Config::PasswordGenerator_UpperCase, true); + config()->set(Config::PasswordGenerator_Numbers, true); + config()->set(Config::PasswordGenerator_EASCII, false); + config()->set(Config::PasswordGenerator_AdvancedMode, false); + config()->set(Config::PasswordGenerator_SpecialChars, true); + config()->set(Config::PasswordGenerator_ExcludeAlike, true); + config()->set(Config::PasswordGenerator_EnsureEvery, true); + config()->set(Config::PasswordGenerator_Length, 20); + + { + PasswordGenerator generator = PasswordGenerator::createFromConfig(); + QCOMPARE(generator.getLength(), 20); + QVERIFY(!(generator.getActiveClasses() & PasswordGenerator::LowerLetters)); + QVERIFY(generator.getActiveClasses() & PasswordGenerator::UpperLetters); + QVERIFY(generator.getActiveClasses() & PasswordGenerator::Numbers); + QVERIFY(generator.getActiveClasses() & PasswordGenerator::SpecialCharacters); + QVERIFY(!(generator.getFlags() & PasswordGenerator::ExcludeLookAlike)); + QVERIFY(!(generator.getFlags() & PasswordGenerator::CharFromEveryGroup)); + } + + // Test advanced mode with specific character classes + config()->set(Config::PasswordGenerator_AdvancedMode, true); + config()->set(Config::PasswordGenerator_SpecialChars, false); + config()->set(Config::PasswordGenerator_Braces, true); + config()->set(Config::PasswordGenerator_Punctuation, true); + config()->set(Config::PasswordGenerator_Quotes, false); + config()->set(Config::PasswordGenerator_Dashes, true); + config()->set(Config::PasswordGenerator_Math, false); + config()->set(Config::PasswordGenerator_Logograms, true); + config()->set(Config::PasswordGenerator_AdditionalChars, "abc"); + config()->set(Config::PasswordGenerator_ExcludedChars, "xyz"); + config()->set(Config::PasswordGenerator_ExcludeAlike, false); + config()->set(Config::PasswordGenerator_EnsureEvery, false); + config()->set(Config::PasswordGenerator_Length, 16); + + { + PasswordGenerator generator = PasswordGenerator::createFromConfig(); + QCOMPARE(generator.getLength(), 16); + QVERIFY(!(generator.getActiveClasses() & PasswordGenerator::LowerLetters)); + QVERIFY(generator.getActiveClasses() & PasswordGenerator::UpperLetters); + QVERIFY(generator.getActiveClasses() & PasswordGenerator::Numbers); + QVERIFY(generator.getActiveClasses() & PasswordGenerator::Braces); + QVERIFY(generator.getActiveClasses() & PasswordGenerator::Punctuation); + QVERIFY(!(generator.getActiveClasses() & PasswordGenerator::Quotes)); + QVERIFY(generator.getActiveClasses() & PasswordGenerator::Dashes); + QVERIFY(!(generator.getActiveClasses() & PasswordGenerator::Math)); + QVERIFY(generator.getActiveClasses() & PasswordGenerator::Logograms); + QVERIFY(!(generator.getFlags() & PasswordGenerator::ExcludeLookAlike)); + QVERIFY(!(generator.getFlags() & PasswordGenerator::CharFromEveryGroup)); + QCOMPARE(generator.getCustomCharacterSet(), QString("abc")); + QCOMPARE(generator.getExcludedCharacterSet(), QString("xyz")); + } + + // Test invalid config (no classes, no custom chars) — generator should be invalid + config()->set(Config::PasswordGenerator_LowerCase, false); + config()->set(Config::PasswordGenerator_UpperCase, false); + config()->set(Config::PasswordGenerator_Numbers, false); + config()->set(Config::PasswordGenerator_EASCII, false); + config()->set(Config::PasswordGenerator_AdvancedMode, false); + config()->set(Config::PasswordGenerator_SpecialChars, false); + config()->set(Config::PasswordGenerator_AdditionalChars, QString()); + config()->set(Config::PasswordGenerator_Length, 32); + + { + PasswordGenerator generator = PasswordGenerator::createFromConfig(); + QVERIFY(!generator.isValid()); + } +} diff --git a/tests/TestPasswordGenerator.h b/tests/TestPasswordGenerator.h index b5bfa7d237..8af2801163 100644 --- a/tests/TestPasswordGenerator.h +++ b/tests/TestPasswordGenerator.h @@ -43,6 +43,7 @@ private slots: void testValidity_data(); void testValidity(); void testReset(); + void testCreateFromConfig(); }; #endif // KEEPASSXC_TESTPASSWORDGENERATOR_H