diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ set(QT_MIN_VERSION "5.12.0") set(KF5_MIN_VERSION "5.66.0") +set(ACCOUNTSQT_DEP_VERSION "1.13") find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) @@ -46,7 +47,6 @@ Notifications NotifyConfig Attica - Wallet Runner GlobalAccel Declarative @@ -56,6 +56,24 @@ Config ) +find_package(AccountsQt5 ${ACCOUNTSQT_DEP_VERSION} CONFIG) +set_package_properties(AccountsQt5 PROPERTIES DESCRIPTION "Accounts management library for Qt applications" + URL "https://gitlab.com/accounts-sso/libaccounts-qt" + TYPE REQUIRED + PURPOSE "Required for building the OpenDesktop integration plugin") + +find_package(KAccounts REQUIRED) +set_package_properties(KAccounts PROPERTIES DESCRIPTION "Accounts management library for KDE applications" + TYPE REQUIRED + PURPOSE "Required for building the OpenDesktop integration plugin") + +find_package(PkgConfig REQUIRED) +pkg_check_modules(SignOnOAuth2 REQUIRED IMPORTED_TARGET signon-oauth2plugin) +set_package_properties(signon-oauth2plugin PROPERTIES DESCRIPTION "Plugin for SignOnQt5 which handles OAuth and OAuth2 logins" + URL "https://gitlab.com/accounts-sso/signon-plugin-oauth2" + TYPE RUNTIME + PURPOSE "Required for running the OpenDesktop integration plugin") + find_package(KF5Kirigami2 ${KF5_MIN_VERSION} CONFIG) set_package_properties(KF5Kirigami2 PROPERTIES DESCRIPTION "A QtQuick based components set" diff --git a/attica-kde/kdeplugin/CMakeLists.txt b/attica-kde/kdeplugin/CMakeLists.txt --- a/attica-kde/kdeplugin/CMakeLists.txt +++ b/attica-kde/kdeplugin/CMakeLists.txt @@ -7,7 +7,19 @@ ) add_library(attica_kde MODULE kdeplatformdependent.cpp ${SRCS}) -target_link_libraries(attica_kde KF5::KIOWidgets Qt5::Widgets KF5::KCMUtils KF5::Attica KF5::Wallet KF5::I18n ) - -install(TARGETS attica_kde DESTINATION ${KDE_INSTALL_PLUGINDIR}) +target_include_directories(attica_kde PRIVATE "${ACCOUNTSQT_INCLUDE_DIRS}") +target_link_libraries(attica_kde + PUBLIC + Qt5::Widgets + KF5::KIOWidgets + KF5::KCMUtils + KF5::Attica + KF5::I18n + KF5::Service + KAccounts + ${ACCOUNTSQT_LIBRARIES} +) +install(TARGETS attica_kde DESTINATION ${KDE_INSTALL_PLUGINDIR}) +kaccounts_add_provider(opendesktop.provider.in) +kaccounts_add_service(opendesktop-rating.service.in) diff --git a/attica-kde/kdeplugin/kdeplatformdependent.h b/attica-kde/kdeplugin/kdeplatformdependent.h --- a/attica-kde/kdeplugin/kdeplatformdependent.h +++ b/attica-kde/kdeplugin/kdeplatformdependent.h @@ -32,11 +32,6 @@ #include #include - -namespace KWallet { - class Wallet; -} - namespace Attica { class KdePlatformDependent : public QObject, public Attica::PlatformDependent @@ -64,12 +59,12 @@ QNetworkAccessManager* nam() override; private: - bool openWallet(bool force); + QNetworkRequest addOAuthToRequest(const QNetworkRequest& request); QNetworkRequest removeAuthFromRequest(const QNetworkRequest& request); + QString getAccessToken(const QUrl& baseUrl) const; KSharedConfigPtr m_config; - QNetworkAccessManager* m_accessManager = nullptr; - KWallet::Wallet* m_wallet = nullptr; + QNetworkAccessManager* m_accessManager{nullptr}; QHash > m_passwords; }; diff --git a/attica-kde/kdeplugin/kdeplatformdependent.cpp b/attica-kde/kdeplugin/kdeplatformdependent.cpp --- a/attica-kde/kdeplugin/kdeplatformdependent.cpp +++ b/attica-kde/kdeplugin/kdeplatformdependent.cpp @@ -3,6 +3,7 @@ Copyright (c) 2009 Eckhart Wörner Copyright (c) 2010 Frederik Gladhorn + Copyright (c) 2019 Dan Leinir Turthra Jensen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -26,19 +27,25 @@ #include "attica_plugin_debug.h" +#include #include -#include #include #include #include #include #include +#include #include +#include +#include +#include +#include + using namespace Attica; KdePlatformDependent::KdePlatformDependent() - : m_config(KSharedConfig::openConfig(QStringLiteral("atticarc"))), m_accessManager(nullptr), m_wallet(nullptr) + : m_config(KSharedConfig::openConfig(QStringLiteral("atticarc"))), m_accessManager(nullptr) { // FIXME: Investigate how to not leak this instance without crashing. m_accessManager = new QNetworkAccessManager(nullptr); @@ -53,42 +60,94 @@ KdePlatformDependent::~KdePlatformDependent() { - delete m_wallet; } -bool KdePlatformDependent::openWallet(bool force) +// TODO Cache the account (so we can call getAccount a WHOLE LOT of times without making the application super slow) +// TODO Also don't just cache it forever, so reset to nullptr every so often, so we pick up potential new stuff the user's done +QString KdePlatformDependent::getAccessToken(const QUrl& /*baseUrl*/) const { - if (m_wallet) { - return true; + QString accessToken; + QString idToken; + Accounts::Manager* accountsManager = KAccounts::accountsManager(); + if (accountsManager) { + static const QString serviceType{"opendesktop-rating"}; + Accounts::AccountIdList accountIds = accountsManager->accountList(serviceType); + // TODO Present the user with a choice in case there's more than one, but for now just pick the first successful one + // loop through the accounts, and attempt to get them + Accounts::Account* account{nullptr}; + qDebug() << accountIds; + for (const Accounts::AccountId& accountId : accountIds) { + account = accountsManager->account(accountId); + if (account) { + bool completed{false}; + qCDebug(ATTICA_PLUGIN_LOG) << "Fetching data for" << accountId; + GetCredentialsJob *job = new GetCredentialsJob(accountId, accountsManager); + connect(job, &KJob::finished, [&completed,&accessToken,&idToken](KJob* kjob){ + GetCredentialsJob *job = qobject_cast< GetCredentialsJob* >(kjob); + QVariantMap credentialsData = job->credentialsData(); + accessToken = credentialsData["AccessToken"].toString(); + idToken = credentialsData["IdToken"].toString(); + if (!accessToken.isEmpty()) { + qCDebug(ATTICA_PLUGIN_LOG) << "Credentials data was gottened"; + for (const QString& key : credentialsData.keys()) { + qCDebug(ATTICA_PLUGIN_LOG) << key << credentialsData[key]; + } + } + completed = true; + }); + connect(job, &KJob::result, [&completed](){ completed = true; }); + job->start(); + while(!completed) { + qApp->processEvents(); + } + if(!idToken.isEmpty()) { + qCDebug(ATTICA_PLUGIN_LOG) << "OpenID Access token retrieved for account" << account->id(); + break; + } + } + } + } else { + qCDebug(ATTICA_PLUGIN_LOG) << "No accounts manager could be fetched, so could not ask it for account details"; } - QString networkWallet = KWallet::Wallet::NetworkWallet(); - // if not forced, or the folder doesn't exist, don't try to open the wallet - if (force || (!KWallet::Wallet::folderDoesNotExist(networkWallet, QStringLiteral("Attica")))) { - m_wallet = KWallet::Wallet::openWallet(networkWallet, 0); + return idToken; +} + +QUrl baseUrlFromRequest(const QNetworkRequest& request) { + const QUrl url{request.url()}; + QString baseUrl = QString("%1://%2").arg(url.scheme()).arg(url.host()); + int port = url.port(); + if (port != -1) { + baseUrl.append(QString::number(port)); } + return url; +} - if (m_wallet) { - m_wallet->createFolder(QStringLiteral("Attica")); - m_wallet->setFolder(QStringLiteral("Attica")); - return true; +QNetworkRequest KdePlatformDependent::addOAuthToRequest(const QNetworkRequest& request) +{ + QNetworkRequest notConstReq = const_cast(request); + const QString token{getAccessToken(baseUrlFromRequest(request))}; + if (!token.isEmpty()) { + const QString bearer_format = QStringLiteral("Bearer %1"); + const QString bearer = bearer_format.arg(token); + notConstReq.setRawHeader("Authorization", bearer.toUtf8()); } - return false; + return notConstReq; } QNetworkReply* KdePlatformDependent::post(const QNetworkRequest& request, const QByteArray& data) { - return m_accessManager->post(removeAuthFromRequest(request), data); + return m_accessManager->post(addOAuthToRequest(removeAuthFromRequest(request)), data); } QNetworkReply* KdePlatformDependent::post(const QNetworkRequest& request, QIODevice* data) { - return m_accessManager->post(removeAuthFromRequest(request), data); + return m_accessManager->post(addOAuthToRequest(removeAuthFromRequest(request)), data); } QNetworkReply* KdePlatformDependent::get(const QNetworkRequest& request) { - return m_accessManager->get(removeAuthFromRequest(request)); + return m_accessManager->get(addOAuthToRequest(removeAuthFromRequest(request))); } QNetworkRequest KdePlatformDependent::removeAuthFromRequest(const QNetworkRequest& request) @@ -99,94 +158,35 @@ return notConstReq; } -bool KdePlatformDependent::saveCredentials(const QUrl& baseUrl, const QString& user, const QString& password) +bool KdePlatformDependent::saveCredentials(const QUrl& /*baseUrl*/, const QString& /*user*/, const QString& /*password*/) { - m_passwords[baseUrl.toString()] = qMakePair(user, password); - - if (!m_wallet && !openWallet(true)) { - - if (KMessageBox::warningContinueCancel(nullptr, i18n("Should the password be stored in the configuration file? This is unsafe.") - , i18n("Social Desktop Configuration")) - == KMessageBox::Cancel) { - return false; - } - - // use kconfig - KConfigGroup group(m_config, baseUrl.toString()); - group.writeEntry("user", user); - group.writeEntry("password", KStringHandler::obscure(password)); - qCDebug(ATTICA_PLUGIN_LOG) << "Saved credentials in KConfig"; - return true; - } - - // Remove the entry when user name is empty - if (user.isEmpty()) { - m_wallet->removeEntry(baseUrl.toString()); - return true; + // TODO KF6 This will want replacing with a call named something that suggests calling it shows accounts (and perhaps + // directly requests the accounts kcm to start adding a new account if it's not there, maybe even pre-fills the fields...) + KService::List services = KServiceTypeTrader::self()->query(QStringLiteral("KCModule"), QStringLiteral("Library == 'kcm_kaccounts'")); + // If we failed to get the kcm, tell the caller we failed + if (services.count() == 0) { + return false; } - - const QMap entries = { - { QStringLiteral("user"), user }, - { QStringLiteral("password"), password } - }; - qCDebug(ATTICA_PLUGIN_LOG) << "Saved credentials in KWallet"; - - return !m_wallet->writeMap(baseUrl.toString(), entries); + KService::Ptr service = services[0]; + qCDebug(ATTICA_PLUGIN_LOG) << "Launch the KAccounts control module" << service->name(); + return QProcess::startDetached(service->exec()); } bool KdePlatformDependent::hasCredentials(const QUrl& baseUrl) const { - if (m_passwords.contains(baseUrl.toString())) { - return true; - } - - QString networkWallet = KWallet::Wallet::NetworkWallet(); - if (!KWallet::Wallet::folderDoesNotExist(networkWallet, QStringLiteral("Attica")) && - !KWallet::Wallet::keyDoesNotExist(networkWallet, QStringLiteral("Attica"), baseUrl.toString())) { - qCDebug(ATTICA_PLUGIN_LOG) << "Found credentials in KWallet"; - return true; - } - - KConfigGroup group(m_config, baseUrl.toString()); - - const QString user = group.readEntry("user", QString()); - qCDebug(ATTICA_PLUGIN_LOG) << "Credentials found:" << !user.isEmpty(); - return !user.isEmpty(); + qCDebug(ATTICA_PLUGIN_LOG) << Q_FUNC_INFO; + return !getAccessToken(baseUrl).isEmpty(); } -bool KdePlatformDependent::loadCredentials(const QUrl& baseUrl, QString& user, QString& password) +bool KdePlatformDependent::loadCredentials(const QUrl& baseUrl, QString& user, QString& /*password*/) { - QString networkWallet = KWallet::Wallet::NetworkWallet(); - if (KWallet::Wallet::folderDoesNotExist(networkWallet, QStringLiteral("Attica")) && - KWallet::Wallet::keyDoesNotExist(networkWallet, QStringLiteral("Attica"), baseUrl.toString())) { - // use KConfig - KConfigGroup group(m_config, baseUrl.toString()); - user = group.readEntry("user", QString()); - password = KStringHandler::obscure(group.readEntry("password", QString())); - if (!user.isEmpty()) { - qCDebug(ATTICA_PLUGIN_LOG) << "Successfully loaded credentials from kconfig"; - m_passwords[baseUrl.toString()] = qMakePair(user, password); - return true; - } - return false; - } - - if (!m_wallet && !openWallet(true)) { - return false; - } - - QMap entries; - if (m_wallet->readMap(baseUrl.toString(), entries) != 0) { - return false; + qCDebug(ATTICA_PLUGIN_LOG) << Q_FUNC_INFO; + QString token = getAccessToken(baseUrl); + if (!token.isEmpty()) { + user = token; } - user = entries.value(QStringLiteral("user")); - password = entries.value(QStringLiteral("password")); - qCDebug(ATTICA_PLUGIN_LOG) << "Successfully loaded credentials."; - - m_passwords[baseUrl.toString()] = qMakePair(user, password); - - return true; + return !token.isEmpty(); } @@ -257,7 +257,3 @@ { return m_accessManager; } - -// TODO: re-enable, see https://community.kde.org/Frameworks/Porting_Notes -// Q_EXPORT_PLUGIN2(attica_kde, Attica::KdePlatformDependent) - diff --git a/attica-kde/kdeplugin/opendesktop-rating.service.in b/attica-kde/kdeplugin/opendesktop-rating.service.in new file mode 100644 --- /dev/null +++ b/attica-kde/kdeplugin/opendesktop-rating.service.in @@ -0,0 +1,8 @@ + + + opendesktop-rating + <_name msgctxt="OpenDesktop Rating and Commenting">Rating and Commenting + get-hot-new-stuff + opendesktop + kaccounts-providers + diff --git a/attica-kde/kdeplugin/opendesktop.provider.in b/attica-kde/kdeplugin/opendesktop.provider.in new file mode 100644 --- /dev/null +++ b/attica-kde/kdeplugin/opendesktop.provider.in @@ -0,0 +1,32 @@ + + + <_name>OpenDesktop + <_description>Vote on content and create reviews and comments on OpenDesktop.Org + get-hot-new-stuff + kaccounts-providers + .*opendesktop\.org + + +