File indexing completed on 2024-05-12 05:52:47
0001 /* 0002 * SPDX-License-Identifier: GPL-3.0-or-later 0003 * SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com> 0004 */ 0005 #include "account/actions_p.h" 0006 0007 #include "../test-utils/output.h" 0008 #include "../test-utils/secret.h" 0009 #include "../../test-utils/spy.h" 0010 #include "../../secrets/test-utils/random.h" 0011 0012 #include <QFile> 0013 #include <QSignalSpy> 0014 #include <QString> 0015 #include <QTest> 0016 #include <QUuid> 0017 #include <QtDebug> 0018 0019 #include <limits> 0020 0021 Q_DECLARE_METATYPE(std::optional<uint>); 0022 0023 class SaveHotpTest: public QObject 0024 { 0025 Q_OBJECT 0026 private Q_SLOTS: 0027 void initTestCase(void); 0028 void validHotp(void); 0029 void validHotp_data(void); 0030 void invalidHotp(void); 0031 void invalidHotp_data(void); 0032 private: 0033 accounts::AccountSecret m_secret {&test::fakeRandom}; 0034 }; 0035 0036 static void define_test_data(void) 0037 { 0038 QTest::addColumn<QUuid>("id"); 0039 QTest::addColumn<QString>("name"); 0040 QTest::addColumn<QString>("issuer"); 0041 QTest::addColumn<quint64>("counter"); 0042 QTest::addColumn<int>("tokenLength"); 0043 QTest::addColumn<std::optional<uint>>("offset"); 0044 QTest::addColumn<bool>("checksum"); 0045 QTest::addColumn<QString>("actualAccountsIni"); 0046 QTest::addColumn<QString>("expectedAccountsIni"); 0047 } 0048 0049 static void define_test_case(const char * label, const QUuid &id, const QString &accountName, const QString &issuer, quint64 counter, int tokenLength, const std::optional<uint> &offset, bool checksum, const QString &actualIni, const QString &expectedIni) 0050 { 0051 QTest::newRow(label) << id << accountName << issuer << counter << tokenLength << offset << checksum << actualIni << expectedIni; 0052 } 0053 0054 void SaveHotpTest::validHotp(void) 0055 { 0056 QFETCH(QUuid, id); 0057 QFETCH(QString, name); 0058 QFETCH(QString, issuer); 0059 QFETCH(quint64, counter); 0060 QFETCH(int, tokenLength); 0061 QFETCH(std::optional<uint>, offset); 0062 QFETCH(bool, checksum); 0063 QFETCH(QString, actualAccountsIni); 0064 QFETCH(QString, expectedAccountsIni); 0065 0066 const QString actual = test::path(actualAccountsIni); 0067 const QString lock = test::path(actualAccountsIni + QLatin1String(".lock")); 0068 bool actionRun = false; 0069 0070 const accounts::SettingsProvider settings([&actual, &actionRun](const accounts::PersistenceAction &action) -> void 0071 { 0072 QSettings data(actual, QSettings::IniFormat); 0073 actionRun = true; 0074 action(data); 0075 }); 0076 0077 std::optional<secrets::EncryptedSecret> tokenSecret = test::encrypt(&m_secret, QByteArray("Hello, world!")); 0078 QVERIFY2(tokenSecret, "should be able to encrypt the token secret"); 0079 0080 accounts::SaveHotp uut(settings, id, name, issuer, *tokenSecret, counter, tokenLength, offset, checksum); 0081 QSignalSpy invalidAccount(&uut, &accounts::SaveHotp::invalid); 0082 QSignalSpy savedAccount(&uut, &accounts::SaveHotp::saved); 0083 QSignalSpy jobFinished(&uut, &accounts::SaveHotp::finished); 0084 0085 uut.run(); 0086 0087 QVERIFY2(test::signal_eventually_emitted_once(savedAccount), "account should be saved"); 0088 QVERIFY2(actionRun, "accounts action should have run"); 0089 0090 QFile result(actual); 0091 QVERIFY2(result.exists(), "accounts file should have been created"); 0092 QCOMPARE(test::slurp(actual), test::slurp(expectedAccountsIni)); 0093 0094 QFile lockFile(lock); 0095 QVERIFY2(!lockFile.exists(), "lock file should no longer exist"); 0096 0097 QVERIFY2(test::signal_eventually_emitted_once(jobFinished), "job should be finished"); 0098 QCOMPARE(invalidAccount.count(), 0); 0099 QCOMPARE(savedAccount.count(), 1); 0100 } 0101 0102 void SaveHotpTest::invalidHotp(void) 0103 { 0104 QFETCH(QUuid, id); 0105 QFETCH(QString, name); 0106 QFETCH(QString, issuer); 0107 QFETCH(quint64, counter); 0108 QFETCH(int, tokenLength); 0109 QFETCH(std::optional<uint>, offset); 0110 QFETCH(bool, checksum); 0111 QFETCH(QString, actualAccountsIni); 0112 QFETCH(QString, expectedAccountsIni); 0113 0114 const QString actual = test::path(actualAccountsIni); 0115 const QString lock = test::path(actualAccountsIni + QLatin1String(".lock")); 0116 bool actionRun = false; 0117 0118 const accounts::SettingsProvider settings([&actual, &actionRun](const accounts::PersistenceAction &action) -> void 0119 { 0120 QSettings data(actual, QSettings::IniFormat); 0121 actionRun = true; 0122 action(data); 0123 }); 0124 0125 std::optional<secrets::EncryptedSecret> tokenSecret = test::encrypt(&m_secret, QByteArray("Hello, world!")); 0126 QVERIFY2(tokenSecret, "should be able to encrypt the token secret"); 0127 0128 accounts::SaveHotp uut(settings, id, name, issuer, *tokenSecret, counter, tokenLength, offset, checksum); 0129 QSignalSpy invalidAccount(&uut, &accounts::SaveHotp::invalid); 0130 QSignalSpy savedAccount(&uut, &accounts::SaveHotp::saved); 0131 QSignalSpy jobFinished(&uut, &accounts::SaveHotp::finished); 0132 0133 uut.run(); 0134 0135 QVERIFY2(test::signal_eventually_emitted_once(jobFinished), "job should be finished"); 0136 QCOMPARE(invalidAccount.count(), 1); 0137 QVERIFY2(!actionRun, "accounts action should not have run"); 0138 QCOMPARE(savedAccount.count(), 0); 0139 0140 QFile result(actual); 0141 QVERIFY2(!result.exists(), "accounts file should not have been created"); 0142 0143 QFile lockFile(lock); 0144 QVERIFY2(!lockFile.exists(), "lock file should not have been created"); 0145 } 0146 0147 void SaveHotpTest::validHotp_data(void) 0148 { 0149 define_test_data(); 0150 define_test_case("valid-hotp-sample-1", QUuid("072a645d-6c26-57cc-81eb-d9ef3b9b39e2"), QLatin1String("valid-hotp-sample-1"), QString(), 6U, 0151 0U, std::nullopt, false, 0152 QLatin1String("save-valid-hotp-accounts-1.ini"), QLatin1String(":/save-hotp/expected-accounts-1.ini")); 0153 define_test_case("valid-hotp-sample-2", QUuid("437c23aa-2fb0-519a-9a34-a5a2671eea24"), QLatin1String("valid-hotp-sample-2"), QLatin1String("autotests"), 6U, 0154 0U, std::optional<uint>(12U), true, 0155 QLatin1String("save-valid-hotp-accounts-2.ini"), QLatin1String(":/save-hotp/expected-accounts-2.ini")); 0156 } 0157 0158 void SaveHotpTest::invalidHotp_data(void) 0159 { 0160 define_test_data(); 0161 define_test_case("null UUID", QUuid(), QLatin1String("null UUID"), QString(), 6U, 0162 0U, std::nullopt, false, 0163 QLatin1String("save-hotp-dummy-accounts-1.ini"), QString()); 0164 define_test_case("null account name", QUuid("00611bbf-5e0b-5c6a-9847-ad865315ce86"), QString(), QString(), 6U, 0165 0U, std::nullopt, false, 0166 QLatin1String("save-hotp-dummy-accounts-2.ini"), QString()); 0167 define_test_case("empty account name", QUuid("1e42b907-99d8-5da3-a59b-89b257e49c83"), QLatin1String(""), QString(), 6U, 0168 0U, std::nullopt, false, 0169 QLatin1String("save-hotp-dummy-accounts-3.ini"), QString()); 0170 define_test_case("empty issuer name", QUuid("533b406b-ad04-5203-a26f-5deb0afeba22"), QLatin1String("empty issuer name"), QLatin1String(""), 6U, 0171 0U, std::nullopt, false, 0172 QLatin1String("save-hotp-dummy-accounts-4.ini"), QString()); 0173 define_test_case("invalid issuer name", QUuid("1c1ffa42-bb9f-5413-a8a7-6c5b0eb8a36f"), QLatin1String("invalid issuer name"), QLatin1String(":"), 6U, 0174 0U, std::nullopt, false, 0175 QLatin1String("save-hotp-dummy-accounts-5.ini"), QString()); 0176 define_test_case("tokenLength too small", QUuid("bca12e13-4b5b-5e4e-b162-3b86a6284dea"), QLatin1String("tokenLength too small"), QString(), 5U, 0177 0U, std::nullopt, false, 0178 QLatin1String("save-hotp-dummy-accounts-6.ini"), QString()); 0179 define_test_case("tokenLength too large", QUuid("5c10d530-fb22-5438-848d-3d4d1f738610"), QLatin1String("tokenLength too large"), QString(), 11U, 0180 0U, std::nullopt, false, 0181 QLatin1String("save-hotp-dummy-accounts-7.ini"), QString()); 0182 define_test_case("offset too large", QUuid("d0f545da-cc1e-57aa-b793-d856757f33e8"), QLatin1String("offset too large"), QString(), 6U, 0183 0U, std::optional<uint>(17U), false, 0184 QLatin1String("save-hotp-dummy-accounts-8.ini"), QString()); 0185 define_test_case("offset out of range", QUuid("b31acaca-ee8f-54aa-948e-d67789fbe74c"), QLatin1String("offset out of range"), QString(), 6U, 0186 0U, std::optional<uint>(std::numeric_limits<uint>::max()), false, 0187 QLatin1String("save-hotp-dummy-accounts-9.ini"), QString()); 0188 } 0189 0190 void SaveHotpTest::initTestCase(void) 0191 { 0192 QVERIFY2(test::ensureOutputDirectory(), "output directory should be available"); 0193 QVERIFY2(test::useDummyPassword(&m_secret), "should be able to set up the master key"); 0194 } 0195 0196 QTEST_MAIN(SaveHotpTest) 0197 0198 #include "save-hotp.moc"