File indexing completed on 2024-05-12 05:52:46

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 <QSignalSpy>
0013 #include <QString>
0014 #include <QTest>
0015 #include <QUuid>
0016 #include <QtDebug>
0017 
0018 static QString emptyIniResource(QLatin1String("empty-accounts.ini"));
0019 static QString corpusIniResource(QLatin1String("sample-accounts.ini"));
0020 static QString invalidIniResource(QLatin1String("invalid-accounts.ini"));
0021 
0022 class LoadAccountsTest: public QObject
0023 {
0024     Q_OBJECT
0025 private Q_SLOTS:
0026     void initTestCase(void);
0027     void emptyAccountsFile(void);
0028     void sampleAccountsFile(void);
0029     void invalidSampleAccountsFile(void);
0030 private:
0031     accounts::AccountSecret m_secret {&test::fakeRandom};
0032 };
0033 
0034 static QByteArray rawSecret = QByteArray::fromBase64(QByteArray("8juE9gJFLp3OgL4CxJ5v5q8sw+h7Vbn06+NY4uc="), QByteArray::Base64Encoding);
0035 static QByteArray rawNonce = QByteArray::fromBase64(QByteArray("QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB"), QByteArray::Base64Encoding);
0036 
0037 static qint64 dummyClock(void)
0038 {
0039     return 1'234'567'890LL;
0040 }
0041 
0042 void LoadAccountsTest::initTestCase(void)
0043 {
0044     QVERIFY2(test::ensureOutputDirectory(), "output directory should be available");
0045     QVERIFY2(test::copyResource(QStringLiteral(":/load-accounts/empty-accounts.ini"), emptyIniResource), "empty INI resource should be available as file");
0046     QVERIFY2(test::copyResource(QStringLiteral(":/load-accounts/sample-accounts.ini"), corpusIniResource), "test corpus INI resource should be available as file");
0047     QVERIFY2(test::copyResource(QStringLiteral(":/load-accounts/invalid-accounts.ini"), invalidIniResource), "invalid INI resource should be available as file");
0048     QVERIFY2(test::useDummyPassword(&m_secret), "should be able to set up the master key");
0049 }
0050 
0051 void LoadAccountsTest::emptyAccountsFile(void)
0052 {
0053     bool actionRun = false;
0054     const accounts::SettingsProvider settings([&actionRun](const accounts::PersistenceAction &action) -> void
0055     {
0056         QSettings data(test::path(emptyIniResource), QSettings::IniFormat);
0057         actionRun = true;
0058         action(data);
0059     });
0060 
0061     accounts::LoadAccounts uut(settings, &m_secret, &dummyClock);
0062 
0063     QSignalSpy hotpFound(&uut, &accounts::LoadAccounts::foundHotp);
0064     QSignalSpy totpFound(&uut, &accounts::LoadAccounts::foundTotp);
0065     QSignalSpy loadingError(&uut, &accounts::LoadAccounts::failedToLoadAllAccounts);
0066     QSignalSpy jobFinished(&uut, &accounts::LoadAccounts::finished);
0067 
0068     uut.run();
0069 
0070     QVERIFY2(test::signal_eventually_emitted_once(jobFinished), "job should be finished");
0071     QVERIFY2(actionRun, "accounts action should have run");
0072     QCOMPARE(hotpFound.count(), 0);
0073     QCOMPARE(totpFound.count(), 0);
0074     QCOMPARE(loadingError.count(), 0);
0075 }
0076 
0077 void LoadAccountsTest::sampleAccountsFile(void)
0078 {
0079     bool actionRun = false;
0080     const accounts::SettingsProvider settings([&actionRun](const accounts::PersistenceAction &action) -> void
0081     {
0082         QSettings data(test::path(corpusIniResource), QSettings::IniFormat);
0083         actionRun = true;
0084         action(data);
0085     });
0086 
0087     accounts::LoadAccounts uut(settings, &m_secret, &dummyClock);
0088 
0089     QSignalSpy hotpFound(&uut, &accounts::LoadAccounts::foundHotp);
0090     QSignalSpy totpFound(&uut, &accounts::LoadAccounts::foundTotp);
0091     QSignalSpy loadingError(&uut, &accounts::LoadAccounts::failedToLoadAllAccounts);
0092     QSignalSpy jobFinished(&uut, &accounts::LoadAccounts::finished);
0093 
0094     uut.run();
0095 
0096     QVERIFY2(test::signal_eventually_emitted_once(jobFinished), "job should be finished");
0097     QVERIFY2(actionRun, "accounts action should have run");
0098     QCOMPARE(hotpFound.count(), 2);
0099     QCOMPARE(totpFound.count(), 2);
0100     QCOMPARE(loadingError.count(), 0);
0101 
0102     const auto firstHotp = hotpFound.at(0);
0103     QCOMPARE(firstHotp.at(0).toUuid(), QUuid(QLatin1String("072a645d-6c26-57cc-81eb-d9ef3b9b39e2")));
0104     QCOMPARE(firstHotp.at(1).toString(), QLatin1String("valid-hotp-sample-1"));
0105     QCOMPARE(firstHotp.at(2).toString(), QString());
0106     QCOMPARE(firstHotp.at(3).toByteArray(), rawSecret);
0107     QCOMPARE(firstHotp.at(4).toByteArray(), rawNonce);
0108     QCOMPARE(firstHotp.at(5).toUInt(), 6U);
0109     QCOMPARE(firstHotp.at(6).toULongLong(), 0ULL);
0110     QCOMPARE(firstHotp.at(7).toBool(), false);
0111     QCOMPARE(firstHotp.at(8).toUInt(), 0U);
0112     QCOMPARE(firstHotp.at(9).toBool(), false);
0113 
0114     const auto secondHotp = hotpFound.at(1);
0115     QCOMPARE(secondHotp.at(0).toUuid(), QUuid(QLatin1String("437c23aa-2fb0-519a-9a34-a5a2671eea24")));
0116     QCOMPARE(secondHotp.at(1).toString(), QLatin1String("valid-hotp-sample-2"));
0117     QCOMPARE(secondHotp.at(2).toString(), QLatin1String("autotests"));
0118     QCOMPARE(secondHotp.at(3).toByteArray(), rawSecret);
0119     QCOMPARE(secondHotp.at(4).toByteArray(), rawNonce);
0120     QCOMPARE(secondHotp.at(5).toUInt(), 6U);
0121     QCOMPARE(secondHotp.at(6).toULongLong(), 0ULL);
0122     QCOMPARE(secondHotp.at(7).toBool(), true);
0123     QCOMPARE(secondHotp.at(8).toUInt(), 12U);
0124     QCOMPARE(secondHotp.at(9).toBool(), true);
0125 
0126     const auto firstTotp = totpFound.at(0);
0127     QCOMPARE(firstTotp.at(0).toUuid(), QUuid(QLatin1String("534cc72e-e9ec-5e39-a1ff-9f017c9be8cc")));
0128     QCOMPARE(firstTotp.at(1).toString(), QLatin1String("valid-totp-sample-1"));
0129     QCOMPARE(firstTotp.at(2).toString(), QString());
0130     QCOMPARE(firstHotp.at(3).toByteArray(), rawSecret);
0131     QCOMPARE(firstHotp.at(4).toByteArray(), rawNonce);
0132     QCOMPARE(firstTotp.at(5).toUInt(), 6);
0133     QCOMPARE(firstTotp.at(6).toUInt(), 30U);
0134     QCOMPARE(firstTotp.at(7).toDateTime(), QDateTime::fromMSecsSinceEpoch(0));
0135     QCOMPARE(firstTotp.at(8).value<accounts::Account::Hash>(), accounts::Account::Hash::Sha1);
0136 
0137     const auto secondTotp = totpFound.at(1);
0138     QCOMPARE(secondTotp.at(0).toUuid(), QUuid(QLatin1String("6537d6a5-005e-5a92-b560-b09df3c2e676")));
0139     QCOMPARE(secondTotp.at(1).toString(), QLatin1String("valid-totp-sample-2"));
0140     QCOMPARE(secondTotp.at(2).toString(), QLatin1String("autotests"));
0141     QCOMPARE(secondHotp.at(3).toByteArray(), rawSecret);
0142     QCOMPARE(secondHotp.at(4).toByteArray(), rawNonce);
0143     QCOMPARE(secondTotp.at(5).toUInt(), 6U);
0144     QCOMPARE(secondTotp.at(6).toUInt(), 30U);
0145     QCOMPARE(secondTotp.at(7).toDateTime(), QDateTime::fromMSecsSinceEpoch(1'234'567'890LL).toUTC());
0146     QCOMPARE(secondTotp.at(8).value<accounts::Account::Hash>(), accounts::Account::Hash::Sha256);
0147 }
0148 
0149 void LoadAccountsTest::invalidSampleAccountsFile(void)
0150 {
0151     bool actionRun = false;
0152     const accounts::SettingsProvider settings([&actionRun](const accounts::PersistenceAction &action) -> void
0153     {
0154         QSettings data(test::path(invalidIniResource), QSettings::IniFormat);
0155         actionRun = true;
0156         action(data);
0157     });
0158 
0159     accounts::LoadAccounts uut(settings, &m_secret, &dummyClock);
0160 
0161     QSignalSpy hotpFound(&uut, &accounts::LoadAccounts::foundHotp);
0162     QSignalSpy totpFound(&uut, &accounts::LoadAccounts::foundTotp);
0163     QSignalSpy loadingError(&uut, &accounts::LoadAccounts::failedToLoadAllAccounts);
0164     QSignalSpy jobFinished(&uut, &accounts::LoadAccounts::finished);
0165 
0166     uut.run();
0167 
0168     QVERIFY2(test::signal_eventually_emitted_once(jobFinished), "job should be finished");
0169     QVERIFY2(actionRun, "accounts action should have run");
0170     QCOMPARE(hotpFound.count(), 0);
0171     QCOMPARE(totpFound.count(), 0);
0172     QCOMPARE(loadingError.count(), 1);
0173 }
0174 
0175 QTEST_MAIN(LoadAccountsTest)
0176 
0177 #include "load-accounts.moc"