File indexing completed on 2024-11-24 03:41:03

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2021 Slava Aseev <nullptrnine@basealt.ru>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 #include "fdo_secrets_test.h"
0008 #include "mockkwalletd.cpp"
0009 // cannot be in mockkwalletd.cpp, as CMake's automoc does not look there
0010 #include "moc_ktimeout.cpp"
0011 #include "moc_kwalletd.cpp"
0012 #include "static_mock.hpp"
0013 
0014 void FdoSecretsTest::initTestCase()
0015 {
0016     static QCA::Initializer init{};
0017 }
0018 
0019 void FdoSecretsTest::serviceStaticFunctions()
0020 {
0021     auto labels = Testset<QString, FdoUniqueLabel>{
0022         {"label", {"label", -1}},
0023         {"label__0_", {"label", 0}},
0024         {"label___1_", {"label_", 1}},
0025         {"__0_label___200_", {"__0_label_", 200}},
0026     };
0027 
0028     runTestset(FdoUniqueLabel::fromName, labels);
0029     runRevTestset(
0030         [](const FdoUniqueLabel &l) {
0031             return l.toName();
0032         },
0033         labels);
0034 
0035     runTestset(KWalletFreedesktopService::wrapToCollectionPath,
0036                Testset<QString, QString>{
0037                    {"/org/freedesktop/secrets/collection/abcd", "/org/freedesktop/secrets/collection/abcd"},
0038                    {"/org/freedesktop/secrets/collection/abcd/2", "/org/freedesktop/secrets/collection/abcd"},
0039                    {"/org/freedesktop/secrets/collection/abcd/2/2/3/4", "/org/freedesktop/secrets/collection/abcd"},
0040                });
0041 
0042     QCOMPARE(KWalletFreedesktopService::nextPromptPath().path(), "/org/freedesktop/secrets/prompt/p0");
0043     QCOMPARE(KWalletFreedesktopService::nextPromptPath().path(), "/org/freedesktop/secrets/prompt/p1");
0044 }
0045 
0046 void FdoSecretsTest::collectionStaticFunctions()
0047 {
0048     auto dirNameTestset = Testset<EntryLocation, FdoUniqueLabel>{
0049         {{FDO_SECRETS_DEFAULT_DIR, "entry1"}, {"entry1", -1}},
0050         {{FDO_SECRETS_DEFAULT_DIR, "entry__3_"}, {"entry", 3}},
0051         {{"Passwords", "password__"}, {"Passwords/password__", -1}},
0052         {{"Passwords__3_", "password__200_"}, {"Passwords__3_/password", 200}},
0053         {{"", "password"}, {"/password", -1}},
0054         {{"", "/"}, {"//", -1}},
0055         {{FDO_SECRETS_DEFAULT_DIR, "/"}, {"/", -1}},
0056         {{FDO_SECRETS_DEFAULT_DIR, "/__2_"}, {"/", 2}},
0057         {{"https:", "/foobar.org/"}, {"https://foobar.org/", -1}},
0058         {{"https:", "/foobar.org/__80_"}, {"https://foobar.org/", 80}},
0059     };
0060 
0061     runTestset(
0062         [](const EntryLocation &l) {
0063             return l.toUniqueLabel();
0064         },
0065         dirNameTestset);
0066     runRevTestset(
0067         [](const FdoUniqueLabel &l) {
0068             return l.toEntryLocation();
0069         },
0070         dirNameTestset);
0071 }
0072 
0073 void FdoSecretsTest::cleanup()
0074 {
0075     SET_FUNCTION_RESULT(KWalletD::wallets, QStringList());
0076 }
0077 
0078 void FdoSecretsTest::precreatedWallets()
0079 {
0080     const QStringList wallets = {"wallet1", "wallet2", "wallet2__0_", "wallet2__1_"};
0081     SET_FUNCTION_RESULT(KWalletD::wallets, wallets);
0082     SET_FUNCTION_RESULT_OVERLOADED(KWalletD::isOpen, true, bool(KWalletD::*)(int));
0083 
0084     std::unique_ptr<KWalletD> kwalletd{new KWalletD};
0085     std::unique_ptr<KWalletFreedesktopService> service{new KWalletFreedesktopService(kwalletd.get())};
0086 
0087     QCOMPARE(wallets.size(), service->collections().size());
0088     for (const auto &walletName : wallets) {
0089         auto collection = service->getCollectionByWalletName(walletName);
0090         QVERIFY(collection);
0091         QVERIFY(collection->label() == "wallet1" || collection->label() == "wallet2");
0092     }
0093 
0094     auto firstCollection = service->getCollectionByWalletName(wallets.front());
0095     auto &item1 = firstCollection->pushNewItem(FdoUniqueLabel{"item1", -1}, QDBusObjectPath(firstCollection->fdoObjectPath().path() + "/0"));
0096     QCOMPARE(item1.fdoObjectPath().path(), (firstCollection->fdoObjectPath().path() + "/0"));
0097     QCOMPARE(&item1, service->getItemByObjectPath(item1.fdoObjectPath()));
0098 }
0099 
0100 void FdoSecretsTest::aliases()
0101 {
0102     std::unique_ptr<KWalletD> kwalletd{new KWalletD};
0103     std::unique_ptr<KWalletFreedesktopService> service{new KWalletFreedesktopService(kwalletd.get())};
0104 
0105     service->createCollectionAlias("alias", "walletName");
0106     service->createCollectionAlias("alias2", "walletName");
0107     service->createCollectionAlias("alias3", "walletName300");
0108     service->updateCollectionAlias("alias3", "walletName");
0109     QSet<QString> checkAliases = {"alias", "alias2", "alias3"};
0110     const QStringList aliases = service->readAliasesFor("walletName");
0111     for (const auto &alias : aliases)
0112         checkAliases.remove(alias);
0113     QVERIFY(checkAliases.isEmpty());
0114 
0115     service->removeAlias("alias");
0116     service->removeAlias("alias2");
0117     service->removeAlias("alias3");
0118     QVERIFY(service->readAliasesFor("walletName").isEmpty());
0119 }
0120 
0121 struct SetupSessionT {
0122     QDBusObjectPath sessionPath;
0123     QCA::SymmetricKey symmetricKey;
0124     QByteArray error;
0125 };
0126 
0127 #define SETUP_SESSION_VERIFY(cond)                                                                                                                             \
0128     do {                                                                                                                                                       \
0129         if (!(cond))                                                                                                                                           \
0130             return SetupSessionT{QDBusObjectPath(), QCA::SymmetricKey(), #cond};                                                                               \
0131     } while (false)
0132 
0133 SetupSessionT setupSession(KWalletFreedesktopService *service)
0134 {
0135     SetupSessionT result;
0136     QCA::KeyGenerator keygen;
0137     auto dlGroup = QCA::DLGroup(keygen.createDLGroup(QCA::IETF_1024));
0138     if (dlGroup.isNull()) {
0139         result.error = "createDLGroup failed, maybe libqca-ossl is missing";
0140         return result;
0141     }
0142 
0143     auto privateKey = QCA::PrivateKey(keygen.createDH(dlGroup));
0144     auto publicKey = QCA::PublicKey(privateKey);
0145 
0146     auto connection = QDBusConnection::sessionBus();
0147     auto message = QDBusMessage::createSignal("dummy", "dummy", "dummy");
0148 
0149     auto pubKeyBytes = publicKey.toDH().y().toArray().toByteArray();
0150     auto sessionPubKeyVariant = service->OpenSession("dh-ietf1024-sha256-aes128-cbc-pkcs7", QDBusVariant(pubKeyBytes), result.sessionPath);
0151     SETUP_SESSION_VERIFY(result.sessionPath.path() != "/");
0152     SETUP_SESSION_VERIFY(sessionPubKeyVariant.variant().canConvert<QByteArray>());
0153 
0154     auto servicePublicKeyBytes = sessionPubKeyVariant.variant().toByteArray();
0155     SETUP_SESSION_VERIFY(!servicePublicKeyBytes.isEmpty());
0156 
0157     auto servicePublicKey = QCA::DHPublicKey(dlGroup, QCA::BigInteger(QCA::SecureArray(servicePublicKeyBytes)));
0158     auto commonSecret = privateKey.deriveKey(servicePublicKey);
0159     result.symmetricKey = QCA::HKDF().makeKey(commonSecret, {}, {}, FDO_SECRETS_CIPHER_KEY_SIZE);
0160 
0161     return result;
0162 }
0163 
0164 void FdoSecretsTest::items()
0165 {
0166     const QStringList wallets = {"wallet1"};
0167     const QStringList folders = {FDO_SECRETS_DEFAULT_DIR};
0168     const QStringList entries = {"item1", "item2", "item3"};
0169     SET_FUNCTION_RESULT(KWalletD::wallets, wallets);
0170     SET_FUNCTION_RESULT(KWalletD::folderList, folders);
0171     SET_FUNCTION_RESULT(KWalletD::entryList, entries);
0172 
0173     SET_FUNCTION_IMPL(KWalletD::entryType, [](int, const QString &, const QString &key, const QString &) -> int {
0174         if (key == "item1")
0175             return KWallet::Wallet::Password;
0176         else if (key == "item2")
0177             return KWallet::Wallet::Map;
0178         else if (key == "item3")
0179             return KWallet::Wallet::Stream;
0180 
0181         QTEST_ASSERT(false);
0182         return KWallet::Wallet::Unknown;
0183     });
0184 
0185     QString _secretHolder1 = "It's a password";
0186     QByteArray _secretHolder2;
0187     QByteArray _secretHolder3;
0188 
0189     {
0190         QByteArray a = "It's a";
0191         QString b = "stream";
0192 
0193         QDataStream ds{&_secretHolder2, QIODevice::WriteOnly};
0194         ds << a << b;
0195     }
0196 
0197     {
0198         StrStrMap map;
0199         map["it's a"] = "map";
0200 
0201         QDataStream ds{&_secretHolder3, QIODevice::WriteOnly};
0202         ds << map;
0203     }
0204 
0205     SET_FUNCTION_IMPL(KWalletD::readPassword, [&](int, const QString &, const QString &key, const QString &) -> QString {
0206         QTEST_ASSERT(key == "item1");
0207         return _secretHolder1;
0208     });
0209 
0210     SET_FUNCTION_IMPL(KWalletD::readEntry, [&](int, const QString &, const QString &key, const QString &) -> QByteArray {
0211         QTEST_ASSERT(key == "item3" || key == "item2");
0212         if (key == "item2")
0213             return _secretHolder2;
0214         else
0215             return _secretHolder3;
0216     });
0217 
0218     SET_FUNCTION_IMPL(KWalletD::writePassword, [&](int, const QString &, const QString &key, const QString &value, const QString &) -> int {
0219         QTEST_ASSERT(key == "item1");
0220         _secretHolder1 = value;
0221         return 0;
0222     });
0223 
0224     using writeEntryT = int (KWalletD::*)(int, const QString &, const QString &, const QByteArray &, int, const QString &);
0225     SET_FUNCTION_IMPL_OVERLOADED(KWalletD::writeEntry,
0226                                  writeEntryT,
0227                                  [&](int, const QString &, const QString &key, const QByteArray &value, int, const QString &) -> int {
0228                                      QTEST_ASSERT(key == "item3" || key == "item2");
0229                                      if (key == "item2")
0230                                          _secretHolder2 = value;
0231                                      else
0232                                          _secretHolder3 = value;
0233                                      return 0;
0234                                  });
0235 
0236     std::unique_ptr<KWalletD> kwalletd{new KWalletD};
0237     std::unique_ptr<KWalletFreedesktopService> service{new KWalletFreedesktopService(kwalletd.get())};
0238 
0239     auto collection = service->getCollectionByWalletName("wallet1");
0240     QVERIFY(collection);
0241 
0242     /* Write some attributes */
0243     {
0244         collection->itemAttributes().newItem({FDO_SECRETS_DEFAULT_DIR, "item1"});
0245         collection->itemAttributes().newItem({FDO_SECRETS_DEFAULT_DIR, "item2"});
0246         collection->itemAttributes().newItem({FDO_SECRETS_DEFAULT_DIR, "item3"});
0247         collection->itemAttributes().setParam({FDO_SECRETS_DEFAULT_DIR, "item3"}, FDO_KEY_CREATED, 100200300ULL);
0248         collection->itemAttributes().setParam({FDO_SECRETS_DEFAULT_DIR, "item3"}, FDO_KEY_MODIFIED, 100200301ULL);
0249         auto attribs = collection->itemAttributes().getAttributes({FDO_SECRETS_DEFAULT_DIR, "item3"});
0250         attribs["Attrib1"] = "value1";
0251         attribs["Attrib2"] = "value2";
0252         collection->itemAttributes().setAttributes({FDO_SECRETS_DEFAULT_DIR, "item3"}, attribs);
0253     }
0254 
0255     /* Create collection */
0256     using OpenAsyncT = int (KWalletD::*)(const QString &, qlonglong, const QString &, bool, const QDBusConnection &, const QDBusMessage &);
0257     bool openAsyncCalled = false;
0258     SET_FUNCTION_IMPL_OVERLOADED(KWalletD::openAsync,
0259                                  OpenAsyncT,
0260                                  [&](const QString &, qlonglong, const QString &, bool, const QDBusConnection &, const QDBusMessage &) -> int {
0261                                      openAsyncCalled = true;
0262                                      return 0;
0263                                  });
0264 
0265     QDBusObjectPath promptPath;
0266     service->Unlock({collection->fdoObjectPath()}, promptPath);
0267     auto prompt = service->getPromptByObjectPath(promptPath);
0268     QVERIFY(prompt);
0269     prompt->Prompt("wndid");
0270     Q_EMIT kwalletd->walletAsyncOpened(0, 0);
0271     SET_FUNCTION_RESULT_OVERLOADED(KWalletD::isOpen, true, bool(KWalletD::*)(int));
0272     QVERIFY(!collection->locked());
0273 
0274     auto item1 = collection->findItemByEntryLocation({FDO_SECRETS_DEFAULT_DIR, "item1"});
0275     auto item2 = collection->findItemByEntryLocation({FDO_SECRETS_DEFAULT_DIR, "item2"});
0276     auto item3 = collection->findItemByEntryLocation({FDO_SECRETS_DEFAULT_DIR, "item3"});
0277     QVERIFY(item1 && item2 && item3);
0278 
0279     auto message = QDBusMessage::createSignal("dummy", "dummy", "dummy");
0280     auto [sessionPath, symmetricKey, errorStr] = setupSession(service.get());
0281     QVERIFY2(errorStr.isEmpty(), errorStr.constData());
0282 
0283     /* Check secrets */
0284     auto secret1 = item1->GetSecret(sessionPath);
0285     service->desecret(message, secret1);
0286     QCOMPARE(secret1.value.toByteArray(), "It's a password");
0287     // QCOMPARE(secret1.mimeType, "text/plain");
0288 
0289     auto secret2 = item2->GetSecret(sessionPath);
0290     service->desecret(message, secret2);
0291     QByteArray secretBytes = secret2.value.toByteArray();
0292     QDataStream ds{secretBytes};
0293     QByteArray a;
0294     QString b;
0295     ds >> a >> b;
0296 
0297     QCOMPARE(secret2.mimeType, "application/octet-stream");
0298     QCOMPARE(a, "It's a");
0299     QCOMPARE(b, "stream");
0300 
0301     auto secret3 = item3->GetSecret(sessionPath);
0302     service->desecret(message, secret3);
0303     auto bytes3 = secret3.value.toByteArray();
0304     QDataStream ds2(bytes3);
0305     StrStrMap map3;
0306     ds2 >> map3;
0307 
0308     QVERIFY(map3.find("it's a") != map3.end() && map3["it's a"] == "map");
0309     QCOMPARE(item3->created(), 100200300);
0310     QCOMPARE(item3->modified(), 100200301);
0311     QCOMPARE(item3->attributes()["Attrib1"], "value1");
0312     QCOMPARE(item3->attributes()["Attrib2"], "value2");
0313 
0314     /* Set new secrets */
0315     secret1.value = QByteArray("It's a new password");
0316     secret1.mimeType = "text/plain";
0317     service->ensecret(message, secret1);
0318     item1->SetSecret(secret1);
0319     secret1 = item1->GetSecret(sessionPath);
0320     service->desecret(message, secret1);
0321     QCOMPARE(secret1.value.toByteArray(), "It's a new password");
0322     QCOMPARE(secret1.mimeType, "text/plain");
0323 
0324     secret2.value = QByteArray("It's a new secret");
0325     secret2.mimeType = "application/octet-stream";
0326     service->ensecret(message, secret2);
0327     item2->SetSecret(secret2);
0328     auto attribs = item2->attributes();
0329     attribs["newAttrib"] = ")))";
0330     item2->setAttributes(attribs);
0331 
0332     secret2 = item2->GetSecret(sessionPath);
0333     service->desecret(message, secret2);
0334     QCOMPARE(secret2.value.toByteArray(), "It's a new secret");
0335     QCOMPARE(item2->attributes()["newAttrib"], ")))");
0336 
0337     /* Search items */
0338     attribs.clear();
0339     attribs["Attrib1"] = "value1";
0340     QList<QDBusObjectPath> lockedItems;
0341     auto unlockedItems = service->SearchItems(attribs, lockedItems);
0342     QCOMPARE(unlockedItems.size(), 1);
0343     QCOMPARE(unlockedItems.front(), item3->fdoObjectPath());
0344 }
0345 
0346 void FdoSecretsTest::createLockUnlockCollection()
0347 {
0348     std::unique_ptr<KWalletD> kwalletd{new KWalletD};
0349     std::unique_ptr<KWalletFreedesktopService> service{new KWalletFreedesktopService(kwalletd.get())};
0350 
0351     /* Create collection */
0352     using OpenAsyncT = int (KWalletD::*)(const QString &, qlonglong, const QString &, bool, const QDBusConnection &, const QDBusMessage &);
0353     bool openAsyncCalled = false;
0354     SET_FUNCTION_IMPL_OVERLOADED(KWalletD::openAsync,
0355                                  OpenAsyncT,
0356                                  [&](const QString &, qlonglong, const QString &, bool, const QDBusConnection &, const QDBusMessage &) -> int {
0357                                      openAsyncCalled = true;
0358                                      return 0;
0359                                  });
0360 
0361     QVariantMap props;
0362     props["org.freedesktop.Secret.Collection.Label"] = QString("walletName");
0363     QDBusObjectPath promptPath;
0364     service->CreateCollection(props, "", promptPath);
0365     auto prompt = service->getPromptByObjectPath(promptPath);
0366     QVERIFY(prompt);
0367     prompt->Prompt("wndid");
0368     QVERIFY(openAsyncCalled);
0369     Q_EMIT kwalletd->walletAsyncOpened(0, 0);
0370 
0371     auto createdCollection = service->getCollectionByWalletName("walletName");
0372     QVERIFY(createdCollection);
0373     QCOMPARE(createdCollection->label(), "walletName");
0374 
0375     /* Check aliases */
0376     service->createCollectionAlias("alias", "walletName");
0377     service->createCollectionAlias("alias2", "walletName");
0378     service->createCollectionAlias("alias3", "walletName");
0379 
0380     QCOMPARE(service->resolveIfAlias(QStringLiteral(FDO_ALIAS_PATH) + "alias"), createdCollection->fdoObjectPath().path());
0381     QCOMPARE(service->resolveIfAlias(QStringLiteral(FDO_ALIAS_PATH) + "alias2"), createdCollection->fdoObjectPath().path());
0382     QCOMPARE(service->resolveIfAlias(QStringLiteral(FDO_ALIAS_PATH) + "alias3"), createdCollection->fdoObjectPath().path());
0383     QCOMPARE(service->ReadAlias("alias"), createdCollection->fdoObjectPath());
0384 
0385     service->removeAlias("alias");
0386     service->removeAlias("alias2");
0387     service->removeAlias("alias3");
0388 
0389     /* Lock/Unlock */
0390     auto lockedObjects = service->Lock({createdCollection->fdoObjectPath()}, promptPath);
0391     QCOMPARE(lockedObjects.size(), 1);
0392     QCOMPARE(lockedObjects.front(), createdCollection->fdoObjectPath());
0393     SET_FUNCTION_RESULT_OVERLOADED(KWalletD::isOpen, false, bool(KWalletD::*)(int));
0394     QVERIFY(createdCollection->locked());
0395 
0396     service->Unlock({createdCollection->fdoObjectPath()}, promptPath);
0397     prompt = service->getPromptByObjectPath(promptPath);
0398     QVERIFY(prompt);
0399     openAsyncCalled = false;
0400     prompt->Prompt("wndid");
0401     QVERIFY(openAsyncCalled);
0402     Q_EMIT kwalletd->walletAsyncOpened(0, 0);
0403     SET_FUNCTION_RESULT_OVERLOADED(KWalletD::isOpen, true, bool(KWalletD::*)(int));
0404     QVERIFY(!createdCollection->locked());
0405 }
0406 
0407 void FdoSecretsTest::session()
0408 {
0409     std::unique_ptr<KWalletD> kwalletd{new KWalletD};
0410     std::unique_ptr<KWalletFreedesktopService> service{new KWalletFreedesktopService(kwalletd.get())};
0411 
0412     auto message = QDBusMessage::createSignal("dummy", "dummy", "dummy");
0413     auto [sessionPath, symmetricKey, errorStr] = setupSession(service.get());
0414 
0415     /* Generate secret */
0416     auto secret = FreedesktopSecret(sessionPath, QByteArray("It's a secret"), "text/plain");
0417     QVERIFY(service->ensecret(message, secret));
0418 
0419     /* Try to decrypt by hand with symmetricKey */
0420     auto cipher = QCA::Cipher("aes128", QCA::Cipher::CBC, QCA::Cipher::PKCS7, QCA::Decode, symmetricKey, secret.parameters);
0421     QCA::SecureArray result;
0422     result.append(cipher.update(QCA::MemoryRegion(secret.value.toByteArray())));
0423     result.append(cipher.final());
0424 
0425     QCOMPARE(QString::fromUtf8(result.toByteArray()), "It's a secret");
0426 
0427     /* Try to decrypt by session */
0428     QVERIFY(service->desecret(message, secret));
0429     QCOMPARE(secret.value.toByteArray(), QByteArray("It's a secret"));
0430 }
0431 
0432 void FdoSecretsTest::attributes()
0433 {
0434     KWalletFreedesktopAttributes attribs{"test"};
0435 
0436     attribs.newItem({"dir", "name"});
0437 
0438     attribs.setParam({"dir", "name"}, "param1", 0xff00ff00ff00ff00);
0439     attribs.setParam({"dir", "name"}, "param2", "string_param");
0440 
0441     QCOMPARE(attribs.getULongLongParam({"dir", "name"}, "param1", 0), 0xff00ff00ff00ff00);
0442     QCOMPARE(attribs.getStringParam({"dir", "name"}, "param2", ""), "string_param");
0443 
0444     attribs.renameLabel({"dir", "name"}, {"newdir", "newname"});
0445 
0446     QCOMPARE(attribs.getULongLongParam({"newdir", "newname"}, "param1", 0), 0xff00ff00ff00ff00);
0447     QCOMPARE(attribs.getStringParam({"newdir", "newname"}, "param2", ""), "string_param");
0448     QCOMPARE(attribs.getULongLongParam({"dir", "name"}, "param1", 0xdef017), 0xdef017);
0449     QCOMPARE(attribs.getStringParam({"dir", "name"}, "param2", "default"), "default");
0450 
0451     attribs.setParam({"newdir", "newname"}, "param1", 100200300ULL);
0452     attribs.setParam({"newdir", "newname"}, "param2", "another_string_param");
0453 
0454     QCOMPARE(attribs.getULongLongParam({"newdir", "newname"}, "param1", 0), 100200300ULL);
0455     QCOMPARE(attribs.getStringParam({"newdir", "newname"}, "param2", ""), "another_string_param");
0456 
0457     QVERIFY(attribs.getAttributes({"newdir", "newname"}).empty());
0458 
0459     StrStrMap attribMap;
0460     attribMap["key1"] = "value1";
0461     attribMap["key2"] = "value2";
0462 
0463     attribs.setAttributes({"newdir", "newname"}, attribMap);
0464     QCOMPARE(attribs.getAttributes({"newdir", "newname"}), attribMap);
0465 
0466     attribs.setAttributes({"dir", "name"}, attribMap);
0467     /* Item not exists - expects empty attributes map */
0468     QVERIFY(attribs.getAttributes({"dir", "name"}).empty());
0469 
0470     attribs.setParam({"dir1", "name1"}, "param1", "some_param");
0471     QCOMPARE(attribs.getStringParam({"dir1", "name1"}, "param1", "default"), "default");
0472 }
0473 
0474 void FdoSecretsTest::walletNameEncodeDecode()
0475 {
0476 #define ENCODE_DECODE_CHECK(DECODED, ENCODED)                                                                                                                  \
0477     do {                                                                                                                                                       \
0478         auto encodedResult = KWallet::Backend::encodeWalletName(DECODED);                                                                                      \
0479         auto decodedResult = KWallet::Backend::decodeWalletName(ENCODED);                                                                                      \
0480         QCOMPARE(encodedResult, ENCODED);                                                                                                                      \
0481         QCOMPARE(decodedResult, DECODED);                                                                                                                      \
0482     } while (false)
0483 
0484     ENCODE_DECODE_CHECK("/", ";2F");
0485     QString allowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789^&'@{}[],$=!-#()%.+_\r\n\t\f\v ";
0486     ENCODE_DECODE_CHECK(allowedChars, allowedChars);
0487     ENCODE_DECODE_CHECK("a/b/c\\", "a;2Fb;2Fc;5C");
0488     ENCODE_DECODE_CHECK("/\\/", ";2F;5C;2F");
0489     ENCODE_DECODE_CHECK(";;;", ";3B;3B;3B");
0490     ENCODE_DECODE_CHECK(";3B", ";3B3B");
0491 
0492 #undef ENCODE_DECODE_CHECK
0493 }
0494 
0495 QTEST_GUILESS_MAIN(FdoSecretsTest)
0496 
0497 #include "moc_fdo_secrets_test.cpp"