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

0001 /*
0002     SPDX-FileCopyrightText: 2018 Daniel Vrátil <dvratil@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "account.h"
0008 #include "accountstorage_kwallet_p.h"
0009 #include "debug.h"
0010 
0011 #include <KWallet>
0012 
0013 #include <QDateTime>
0014 #include <QJsonArray>
0015 #include <QJsonDocument>
0016 #include <QJsonObject>
0017 
0018 using namespace KGAPI2;
0019 
0020 namespace
0021 {
0022 
0023 static const QString FolderName = QStringLiteral("LibKGAPI");
0024 static const QString AccountNameKey = QStringLiteral("name");
0025 static const QString ScopesKey = QStringLiteral("scopes");
0026 static const QString AccessTokenKey = QStringLiteral("accessToken");
0027 static const QString RefreshTokenKey = QStringLiteral("refreshToken");
0028 static const QString ExpiresKey = QStringLiteral("expires");
0029 
0030 }
0031 
0032 AccountStorage *KWalletStorageFactory::create() const
0033 {
0034     return new KWalletStorage();
0035 }
0036 
0037 KWalletStorage::KWalletStorage()
0038 {
0039 }
0040 
0041 KWalletStorage::~KWalletStorage()
0042 {
0043     delete mWallet;
0044 }
0045 
0046 void KWalletStorage::open(const std::function<void(bool)> &callback)
0047 {
0048     const auto openedCallback = [=](bool opened) {
0049         mWalletOpening = false;
0050         if (!opened) {
0051             qCWarning(KGAPIDebug, "KWallet: failed to open");
0052             mWallet->deleteLater();
0053             mWallet = nullptr;
0054             callback(false);
0055             return;
0056         }
0057 
0058         if (mWallet->currentFolder() == FolderName) {
0059             callback(true);
0060             return;
0061         }
0062 
0063         if (!mWallet->hasFolder(FolderName)) {
0064             if (!mWallet->createFolder(FolderName)) {
0065                 qCWarning(KGAPIDebug, "KWallet: failed to create a new folder");
0066                 callback(false);
0067                 return;
0068             }
0069         }
0070         if (!mWallet->setFolder(FolderName)) {
0071             qCWarning(KGAPIDebug, "KWallet: failed to change folder");
0072             callback(false);
0073             return;
0074         }
0075 
0076         callback(true);
0077     };
0078 
0079     if (mWallet) {
0080         if (mWallet->isOpen(KWallet::Wallet::NetworkWallet())) {
0081             callback(true);
0082             return;
0083         }
0084 
0085         if (mWalletOpening) {
0086             QObject::connect(mWallet, &KWallet::Wallet::walletOpened, mWallet, openedCallback);
0087             return;
0088         }
0089 
0090         delete mWallet;
0091     }
0092 
0093     mWalletOpening = true;
0094     mWallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), 0, KWallet::Wallet::Asynchronous);
0095     if (!mWallet) {
0096         qCWarning(KGAPIDebug, "KWallet: failed to open wallet (maybe it's disabled?");
0097         callback(false);
0098     } else {
0099         QObject::connect(mWallet, &KWallet::Wallet::walletOpened, mWallet, [this]() {
0100             mWalletOpening = false;
0101         });
0102         QObject::connect(mWallet, &KWallet::Wallet::walletOpened, mWallet, openedCallback);
0103     }
0104 }
0105 
0106 bool KWalletStorage::opened() const
0107 {
0108     return mWallet && mWallet->isOpen(KWallet::Wallet::NetworkWallet());
0109 }
0110 
0111 AccountPtr KWalletStorage::getAccount(const QString &apiKey, const QString &accountName)
0112 {
0113     if (!opened()) {
0114         qCWarning(KGAPIDebug, "Trying to get an account from a closed wallet!");
0115         return {};
0116     }
0117 
0118     QMap<QString, QString> accounts;
0119     mWallet->readMap(apiKey, accounts);
0120     const auto accountIt = accounts.constFind(accountName);
0121     if (accountIt == accounts.cend()) {
0122         return {};
0123     }
0124 
0125     return parseAccount(*accountIt);
0126 }
0127 
0128 bool KWalletStorage::storeAccount(const QString &apiKey, const AccountPtr &account)
0129 {
0130     if (!opened()) {
0131         qCWarning(KGAPIDebug, "Trying to store an account in a closed wallet!");
0132         return false;
0133     }
0134 
0135     QMap<QString, QString> accounts;
0136     if (mWallet->readMap(apiKey, accounts) == 0) {
0137         accounts[account->accountName()] = serializeAccount(account);
0138         if (mWallet->writeMap(apiKey, accounts) != 0) {
0139             qCWarning(KGAPIDebug, "KWallet: failed to write accounts map");
0140             return false;
0141         }
0142     } else {
0143         qCWarning(KGAPIDebug, "KWallet: failed to read accounts map");
0144         return false;
0145     }
0146 
0147     return true;
0148 }
0149 
0150 void KWalletStorage::removeAccount(const QString &apiKey, const QString &accountName)
0151 {
0152     if (!opened()) {
0153         qCWarning(KGAPIDebug, "Trying to remove an account from a closed wallet!");
0154         return;
0155     }
0156 
0157     QMap<QString, QString> accounts;
0158     if (mWallet->readMap(apiKey, accounts) == 0) {
0159         if (accounts.remove(accountName)) {
0160             if (!mWallet->writeMap(apiKey, accounts)) {
0161                 qCWarning(KGAPIDebug, "KWallet: failed to write accounts map");
0162             }
0163         }
0164     } else {
0165         qCWarning(KGAPIDebug, "KWallet: failed to read accounts map");
0166     }
0167 }
0168 
0169 AccountPtr KWalletStorage::parseAccount(const QString &str) const
0170 {
0171     const auto doc = QJsonDocument::fromJson(str.toUtf8());
0172     if (doc.isNull()) {
0173         qCWarning(KGAPIDebug, "Failed to parse account returned from KWallet");
0174         return {};
0175     }
0176     const auto obj = doc.object();
0177     QJsonArray scopesArray = obj.value(ScopesKey).toArray();
0178     QList<QUrl> scopes;
0179     scopes.reserve(scopesArray.size());
0180     std::transform(scopesArray.constBegin(), scopesArray.constEnd(), std::back_inserter(scopes), [](const QJsonValue &val) {
0181         return QUrl::fromEncoded(val.toString().toUtf8());
0182     });
0183     auto acc = AccountPtr::create(obj.value(AccountNameKey).toString(), obj.value(AccessTokenKey).toString(), obj.value(RefreshTokenKey).toString(), scopes);
0184     acc->setExpireDateTime(QDateTime::fromString(obj.value(ExpiresKey).toString(), Qt::ISODate));
0185     return acc;
0186 }
0187 
0188 QString KWalletStorage::serializeAccount(const AccountPtr &account) const
0189 {
0190     QJsonArray scopesArray;
0191     const auto scopes = account->scopes();
0192     std::transform(scopes.cbegin(), scopes.cend(), std::back_inserter(scopesArray), [](const QUrl &scope) {
0193         return scope.toString(QUrl::FullyEncoded);
0194     });
0195     return QString::fromUtf8(QJsonDocument({{AccountNameKey, account->accountName()},
0196                                             {AccessTokenKey, account->accessToken()},
0197                                             {RefreshTokenKey, account->refreshToken()},
0198                                             {ExpiresKey, account->expireDateTime().toString(Qt::ISODate)},
0199                                             {ScopesKey, scopesArray}})
0200                                  .toJson(QJsonDocument::Compact));
0201 }