File indexing completed on 2024-05-12 05:22:35

0001 /*
0002     autotests/keyselectioncombotest.cpp
0003 
0004     This file is part of libkleopatra's test suite.
0005     SPDX-FileCopyrightText: 2021 g10 Code GmbH
0006     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include <Libkleo/Formatting>
0012 #include <Libkleo/KeyCache>
0013 #include <Libkleo/KeySelectionCombo>
0014 
0015 #include <QSignalSpy>
0016 #include <QTest>
0017 
0018 #include <gpgme++/key.h>
0019 #include <gpgme++/keylistresult.h>
0020 
0021 #include <gpgme.h>
0022 
0023 #include <memory>
0024 
0025 using namespace Kleo;
0026 
0027 namespace
0028 {
0029 
0030 auto mapValidity(GpgME::UserID::Validity validity)
0031 {
0032     switch (validity) {
0033     default:
0034     case GpgME::UserID::Unknown:
0035         return GPGME_VALIDITY_UNKNOWN;
0036     case GpgME::UserID::Undefined:
0037         return GPGME_VALIDITY_UNDEFINED;
0038     case GpgME::UserID::Never:
0039         return GPGME_VALIDITY_NEVER;
0040     case GpgME::UserID::Marginal:
0041         return GPGME_VALIDITY_MARGINAL;
0042     case GpgME::UserID::Full:
0043         return GPGME_VALIDITY_FULL;
0044     case GpgME::UserID::Ultimate:
0045         return GPGME_VALIDITY_ULTIMATE;
0046     }
0047 }
0048 
0049 GpgME::Key createTestKey(const char *uid,
0050                          GpgME::Protocol protocol = GpgME::UnknownProtocol,
0051                          KeyCache::KeyUsage usage = KeyCache::KeyUsage::AnyUsage,
0052                          GpgME::UserID::Validity validity = GpgME::UserID::Full)
0053 {
0054     static int count = 0;
0055     count++;
0056 
0057     gpgme_key_t key;
0058     gpgme_key_from_uid(&key, uid);
0059     Q_ASSERT(key);
0060     Q_ASSERT(key->uids);
0061     if (protocol != GpgME::UnknownProtocol) {
0062         key->protocol = protocol == GpgME::OpenPGP ? GPGME_PROTOCOL_OpenPGP : GPGME_PROTOCOL_CMS;
0063     }
0064     const QByteArray fingerprint = QByteArray::number(count, 16).rightJustified(40, '0');
0065     key->fpr = strdup(fingerprint.constData());
0066     key->revoked = 0;
0067     key->expired = 0;
0068     key->disabled = 0;
0069     key->can_encrypt = int(usage == KeyCache::KeyUsage::AnyUsage || usage == KeyCache::KeyUsage::Encrypt);
0070     key->can_sign = int(usage == KeyCache::KeyUsage::AnyUsage || usage == KeyCache::KeyUsage::Sign);
0071     key->secret = 1;
0072     key->uids->validity = mapValidity(validity);
0073 
0074     return GpgME::Key(key, false);
0075 }
0076 
0077 auto testKey(const char *address, GpgME::Protocol protocol = GpgME::UnknownProtocol)
0078 {
0079     const auto email = GpgME::UserID::addrSpecFromString(address);
0080     const auto keys = KeyCache::instance()->findByEMailAddress(email);
0081     for (const auto &key : keys) {
0082         if (protocol == GpgME::UnknownProtocol || key.protocol() == protocol) {
0083             return key;
0084         }
0085     }
0086     return GpgME::Key();
0087 }
0088 
0089 void waitForKeySelectionComboBeingInitialized(const KeySelectionCombo *combo)
0090 {
0091     QVERIFY(combo);
0092 
0093     const auto spy = std::make_unique<QSignalSpy>(combo, &KeySelectionCombo::keyListingFinished);
0094     QVERIFY(spy->isValid());
0095     QVERIFY(spy->wait(10));
0096 }
0097 
0098 }
0099 
0100 class KeySelectionComboTest : public QObject
0101 {
0102     Q_OBJECT
0103 
0104 private Q_SLOTS:
0105     void initTestCase()
0106     {
0107         // force C locale for sorting
0108         QLocale::setDefault(QLocale::c());
0109     }
0110 
0111     void init()
0112     {
0113         // hold a reference to the key cache to avoid rebuilding while the test is running
0114         mKeyCache = KeyCache::instance();
0115 
0116         KeyCache::mutableInstance()->setKeys({
0117             createTestKey("sender@example.net", GpgME::OpenPGP, KeyCache::KeyCache::KeyUsage::AnyUsage),
0118             createTestKey("CN=,EMAIL=sender@example.net", GpgME::CMS, KeyCache::KeyCache::KeyUsage::AnyUsage),
0119             createTestKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP, KeyCache::KeyCache::KeyUsage::Encrypt),
0120             createTestKey("CN=Trusted S/MIME,EMAIL=prefer-smime@example.net", GpgME::CMS, KeyCache::KeyCache::KeyUsage::Encrypt),
0121             createTestKey("Marginal Validity <marginal-openpgp@example.net>", GpgME::OpenPGP, KeyCache::KeyCache::KeyUsage::Encrypt, GpgME::UserID::Marginal),
0122         });
0123     }
0124 
0125     void cleanup()
0126     {
0127         // verify that nobody else holds a reference to the key cache
0128         QVERIFY(mKeyCache.use_count() == 1);
0129         mKeyCache.reset();
0130     }
0131 
0132     void test__verify_test_keys()
0133     {
0134         QVERIFY(!testKey("sender@example.net", GpgME::OpenPGP).isNull());
0135         QVERIFY(!testKey("sender@example.net", GpgME::CMS).isNull());
0136         QVERIFY(!testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP).isNull());
0137         QVERIFY(!testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS).isNull());
0138         QVERIFY(!testKey("Marginal Validity <marginal-openpgp@example.net>", GpgME::OpenPGP).isNull());
0139     }
0140 
0141     void test__certificates_are_sorted_alphabetically()
0142     {
0143         const auto combo = std::make_unique<KeySelectionCombo>();
0144         waitForKeySelectionComboBeingInitialized(combo.get());
0145         QCOMPARE(combo->itemText(0).left(combo->itemText(0).indexOf(" (")), "Full Trust <prefer-openpgp@example.net>");
0146         QCOMPARE(combo->itemText(1).left(combo->itemText(1).indexOf(" (")), "Marginal Validity <marginal-openpgp@example.net>");
0147         QCOMPARE(combo->itemText(2).left(combo->itemText(2).indexOf(" (")), "Trusted S/MIME <prefer-smime@example.net>");
0148         // we don't check the order of the two "sender@example.net" keys
0149     }
0150 
0151     void test__after_initialization_default_key_is_current_key()
0152     {
0153         const auto combo = std::make_unique<KeySelectionCombo>();
0154         combo->setDefaultKey(QString::fromLatin1(testKey("Marginal Validity <marginal-openpgp@example.net>", GpgME::OpenPGP).primaryFingerprint()));
0155         waitForKeySelectionComboBeingInitialized(combo.get());
0156 
0157         QCOMPARE(combo->currentKey().primaryFingerprint(), testKey("Marginal Validity <marginal-openpgp@example.net>", GpgME::OpenPGP).primaryFingerprint());
0158     }
0159 
0160     void test__currently_selected_key_is_retained_if_cache_is_updated()
0161     {
0162         const auto combo = std::make_unique<KeySelectionCombo>();
0163         combo->setDefaultKey(QString::fromLatin1(testKey("Marginal Validity <marginal-openpgp@example.net>", GpgME::OpenPGP).primaryFingerprint()));
0164         waitForKeySelectionComboBeingInitialized(combo.get());
0165 
0166         combo->setCurrentKey(testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS));
0167 
0168         QCOMPARE(combo->currentKey().primaryFingerprint(), testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS).primaryFingerprint());
0169 
0170         Q_EMIT KeyCache::mutableInstance()->keyListingDone(GpgME::KeyListResult{});
0171 
0172         QCOMPARE(combo->currentKey().primaryFingerprint(), testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS).primaryFingerprint());
0173     }
0174 
0175     void test__default_key_is_selected_if_currently_selected_key_is_gone_after_model_update()
0176     {
0177         const auto combo = std::make_unique<KeySelectionCombo>();
0178         combo->setDefaultKey(QString::fromLatin1(testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP).primaryFingerprint()));
0179         waitForKeySelectionComboBeingInitialized(combo.get());
0180 
0181         combo->setCurrentKey(testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS));
0182 
0183         QCOMPARE(combo->currentKey().primaryFingerprint(), testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS).primaryFingerprint());
0184 
0185         KeyCache::mutableInstance()->setKeys({
0186             testKey("sender@example.net", GpgME::OpenPGP),
0187             testKey("sender@example.net", GpgME::CMS),
0188             testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP),
0189             testKey("Marginal Validity <marginal-openpgp@example.net>", GpgME::OpenPGP),
0190         });
0191 
0192         QCOMPARE(combo->currentKey().primaryFingerprint(), testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP).primaryFingerprint());
0193     }
0194 
0195     void test__currently_selected_custom_item_is_retained_if_cache_is_updated()
0196     {
0197         const auto combo = std::make_unique<KeySelectionCombo>();
0198         combo->prependCustomItem({}, {}, QStringLiteral("custom1"));
0199         combo->appendCustomItem({}, {}, QStringLiteral("custom2"));
0200         combo->setDefaultKey(QString::fromLatin1(testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP).primaryFingerprint()));
0201         waitForKeySelectionComboBeingInitialized(combo.get());
0202 
0203         combo->setCurrentIndex(combo->count() - 1);
0204         QCOMPARE(combo->currentData(), QStringLiteral("custom2"));
0205 
0206         Q_EMIT KeyCache::mutableInstance()->keyListingDone(GpgME::KeyListResult{});
0207 
0208         QCOMPARE(combo->currentData(), QStringLiteral("custom2"));
0209     }
0210 
0211     void test__default_key_is_selected_if_currently_selected_custom_item_is_gone_after_model_update()
0212     {
0213         const auto combo = std::make_unique<KeySelectionCombo>();
0214         combo->prependCustomItem({}, {}, QStringLiteral("custom1"));
0215         combo->appendCustomItem({}, {}, QStringLiteral("custom2"));
0216         combo->setDefaultKey(QString::fromLatin1(testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP).primaryFingerprint()));
0217         waitForKeySelectionComboBeingInitialized(combo.get());
0218 
0219         combo->setCurrentIndex(combo->count() - 1);
0220         QCOMPARE(combo->currentData(), QStringLiteral("custom2"));
0221 
0222         combo->removeCustomItem(QStringLiteral("custom2"));
0223 
0224         QCOMPARE(combo->currentKey().primaryFingerprint(), testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP).primaryFingerprint());
0225     }
0226 
0227 private:
0228     std::shared_ptr<const KeyCache> mKeyCache;
0229 };
0230 
0231 QTEST_MAIN(KeySelectionComboTest)
0232 #include "keyselectioncombotest.moc"