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 }