diff options
| author | isanae <14251494+isanae@users.noreply.github.com> | 2019-07-11 02:31:59 -0400 |
|---|---|---|
| committer | isanae <14251494+isanae@users.noreply.github.com> | 2019-07-11 02:31:59 -0400 |
| commit | ad29525e982f83343dbd70e17b914e0a30adb662 (patch) | |
| tree | 09ee6531679787412b3e16bcdbbe6156a20f9a8c | |
| parent | 5e5681709ab878f3aa6cf1344af44e8ae9544987 (diff) | |
moved NexusSSOLogin to nxmaccessmanager
added logs for manual key validation, will need to rewrite a few things
| -rw-r--r-- | src/nxmaccessmanager.cpp | 166 | ||||
| -rw-r--r-- | src/nxmaccessmanager.h | 64 | ||||
| -rw-r--r-- | src/settingsdialog.cpp | 198 | ||||
| -rw-r--r-- | src/settingsdialog.h | 52 |
4 files changed, 250 insertions, 230 deletions
diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp index 8bbfd536..a274261d 100644 --- a/src/nxmaccessmanager.cpp +++ b/src/nxmaccessmanager.cpp @@ -44,6 +44,8 @@ using namespace std::chrono_literals; const QString NexusBaseUrl("https://api.nexusmods.com/v1"); const std::chrono::seconds NXMAccessManager::ValidationTimeout = 10s; +const QString NexusSSO("wss://sso.nexusmods.com"); +const QString NexusSSOPage("https://www.nexusmods.com/sso?id=%1&application=modorganizer2"); ValidationProgressDialog::ValidationProgressDialog(std::chrono::seconds t) @@ -94,7 +96,10 @@ void ValidationProgressDialog::start() void ValidationProgressDialog::stop() { - m_timer->stop(); + if (m_timer) { + m_timer->stop(); + } + hide(); } @@ -119,6 +124,153 @@ void ValidationProgressDialog::onTimer() } +NexusSSOLogin::NexusSSOLogin() + : m_keyReceived(false), m_active(false) +{ + QObject::connect( + &m_socket, &QWebSocket::connected, + [&]{ onConnected(); }); + + QObject::connect( + &m_socket, qOverload<QAbstractSocket::SocketError>(&QWebSocket::error), + [&](auto&& e){ onError(e); }); + + QObject::connect( + &m_socket, &QWebSocket::textMessageReceived, + [&](auto&& s){ onMessage(s); }); + + QObject::connect( + &m_socket, &QWebSocket::disconnected, + [&]{ onDisconnected(); }); + + QObject::connect(&m_timeout, &QTimer::timeout, [&]{ onTimeout(); }); +} + +void NexusSSOLogin::start() +{ + m_active = true; + setState(ConnectingToSSO); + m_timeout.start(NXMAccessManager::ValidationTimeout); + m_socket.open(NexusSSO); +} + +void NexusSSOLogin::cancel() +{ + abort(); + setState(Cancelled); +} + +void NexusSSOLogin::close() +{ + m_active = false; + m_timeout.stop(); + m_socket.close(); +} + +void NexusSSOLogin::abort() +{ + m_active = false; + m_timeout.stop(); + m_socket.abort(); +} + +bool NexusSSOLogin::isActive() const +{ + return m_active; +} + +void NexusSSOLogin::setState(States s, const QString& error) +{ + if (stateChanged) { + stateChanged(s, error); + } +} + +void NexusSSOLogin::onConnected() +{ + setState(WaitingForToken); + + m_keyReceived = false; + + //if (m_guid.isEmpty()) { + boost::uuids::random_generator generator; + boost::uuids::uuid sessionId = generator(); + m_guid = boost::uuids::to_string(sessionId).c_str(); + //} + + QJsonObject data; + data.insert(QString("id"), QJsonValue(m_guid)); + //data.insert(QString("token"), QJsonValue(m_token)); + data.insert(QString("protocol"), 2); + + const QString message = QJsonDocument(data).toJson(); + m_socket.sendTextMessage(message); +} + +void NexusSSOLogin::onMessage(const QString& s) +{ + const QJsonDocument doc = QJsonDocument::fromJson(s.toUtf8()); + const QVariantMap root = doc.object().toVariantMap(); + + if (!root["success"].toBool()) { + close(); + + setState(Error, QString("There was a problem with SSO initialization: %1") + .arg(root["error"].toString())); + + return; + } + + const QVariantMap data = root["data"].toMap(); + + if (data.contains("connection_token")) { + // first answer + m_token = data["connection_token"].toString(); + + // open browser + const auto url = NexusSSOPage.arg(m_guid); + shell::OpenLink(url); + + m_timeout.stop(); + setState(WaitingForBrowser); + } else { + // second answer + const auto key = data["api_key"].toString(); + close(); + + if (keyChanged) { + keyChanged(key); + } + + setState(Finished); + } +} + +void NexusSSOLogin::onDisconnected() +{ + if (m_active) { + m_active = false; + + if (!m_keyReceived) { + setState(ClosedByRemote); + } + } +} + +void NexusSSOLogin::onError(QAbstractSocket::SocketError e) +{ + if (m_active) { + setState(Error, m_socket.errorString()); + } +} + +void NexusSSOLogin::onTimeout() +{ + abort(); + setState(Timeout); +} + + NXMAccessManager::NXMAccessManager(QObject *parent, const QString &moVersion) : QNetworkAccessManager(parent) , m_ValidateReply(nullptr) @@ -192,7 +344,7 @@ void NXMAccessManager::clearCookies() } } -void NXMAccessManager::startValidationCheck() +void NXMAccessManager::startValidationCheck(bool showProgress) { qDebug("Checking Nexus API Key..."); QString requestString = NexusBaseUrl + "/users/validate"; @@ -205,7 +357,9 @@ void NXMAccessManager::startValidationCheck() request.setRawHeader("Application-Name", "MO2"); request.setRawHeader("Application-Version", m_MOVersion.toUtf8()); - m_ProgressDialog->start(); + if (showProgress) { + m_ProgressDialog->start(); + } QCoreApplication::processEvents(); // for some reason the whole app hangs during the login. This way the user has at least a little feedback @@ -245,13 +399,13 @@ bool NXMAccessManager::validateWaiting() const } -void NXMAccessManager::apiCheck(const QString &apiKey, bool force) +void NXMAccessManager::apiCheck(const QString &apiKey, ApiCheckFlags flags) { if (m_ValidateReply != nullptr) { return; } - if (force) { + if (flags & Force) { m_ValidateState = VALIDATE_NOT_CHECKED; } @@ -261,7 +415,7 @@ void NXMAccessManager::apiCheck(const QString &apiKey, bool force) } m_ApiKey = apiKey; - startValidationCheck(); + startValidationCheck((flags & HideProgress) == 0); } diff --git a/src/nxmaccessmanager.h b/src/nxmaccessmanager.h index cbec0530..08a799f9 100644 --- a/src/nxmaccessmanager.h +++ b/src/nxmaccessmanager.h @@ -27,6 +27,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include <QProgressDialog> #include <QElapsedTimer> #include <QDialogButtonBox> +#include <QWebSocket> #include <set> namespace MOBase { class IPluginGame; } @@ -60,6 +61,52 @@ private: }; +class NexusSSOLogin +{ +public: + enum States + { + ConnectingToSSO, + WaitingForToken, + WaitingForBrowser, + Finished, + Timeout, + ClosedByRemote, + Cancelled, + Error + }; + + std::function<void (QString)> keyChanged; + std::function<void (States, QString)> stateChanged; + + NexusSSOLogin(); + + void start(); + void cancel(); + + bool isActive() const; + +private: + QWebSocket m_socket; + QString m_guid; + bool m_keyReceived; + QString m_token; + bool m_active; + QTimer m_timeout; + + void setState(States s, const QString& error={}); + + void close(); + void abort(); + + void onConnected(); + void onMessage(const QString& s); + void onDisconnected(); + void onError(QAbstractSocket::SocketError e); + void onTimeout(); +}; + + /** * @brief access manager extended to handle nxm links **/ @@ -67,6 +114,15 @@ class NXMAccessManager : public QNetworkAccessManager { Q_OBJECT public: + enum ApiCheckFlagsEnum + { + NoFlags = 0, + Force, + HideProgress + }; + + Q_DECLARE_FLAGS(ApiCheckFlags, ApiCheckFlagsEnum) + static const std::chrono::seconds ValidationTimeout; explicit NXMAccessManager(QObject *parent, const QString &moVersion); @@ -80,7 +136,7 @@ public: bool validateAttempted() const; bool validateWaiting() const; - void apiCheck(const QString &apiKey, bool force=false); + void apiCheck(const QString &apiKey, ApiCheckFlags flags=NoFlags); void showCookies() const; @@ -91,8 +147,6 @@ public: QString apiKey() const; void clearApiKey(); - void startValidationCheck(); - void refuseValidation(); signals: @@ -145,6 +199,10 @@ private: VALIDATE_REFUSED, VALIDATE_VALID } m_ValidateState = VALIDATE_NOT_CHECKED; + + void startValidationCheck(bool showProgress); }; +Q_DECLARE_OPERATORS_FOR_FLAGS(NXMAccessManager::ApiCheckFlags); + #endif // NXMACCESSMANAGER_H diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index d1ace6a5..0131d20b 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -28,7 +28,6 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include "settings.h" #include "instancemanager.h" #include "nexusinterface.h" -#include "nxmaccessmanager.h" #include "plugincontainer.h" #include <boost/uuid/uuid_generators.hpp> @@ -49,9 +48,6 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. using namespace MOBase; -const QString NexusSSO("wss://sso.nexusmods.com"); -const QString NexusSSOPage("https://www.nexusmods.com/sso?id=%1&application=modorganizer2"); - class NexusManualKeyDialog : public QDialog { public: @@ -100,154 +96,6 @@ private: }; - -NexusSSOLogin::NexusSSOLogin() - : m_keyReceived(false), m_active(false) -{ - QObject::connect( - &m_socket, &QWebSocket::connected, - [&]{ onConnected(); }); - - QObject::connect( - &m_socket, qOverload<QAbstractSocket::SocketError>(&QWebSocket::error), - [&](auto&& e){ onError(e); }); - - QObject::connect( - &m_socket, &QWebSocket::textMessageReceived, - [&](auto&& s){ onMessage(s); }); - - QObject::connect( - &m_socket, &QWebSocket::disconnected, - [&]{ onDisconnected(); }); - - QObject::connect(&m_timeout, &QTimer::timeout, [&]{ onTimeout(); }); -} - -void NexusSSOLogin::start() -{ - m_active = true; - setState(ConnectingToSSO); - m_timeout.start(NXMAccessManager::ValidationTimeout); - m_socket.open(NexusSSO); -} - -void NexusSSOLogin::cancel() -{ - abort(); - setState(Cancelled); -} - -void NexusSSOLogin::close() -{ - m_active = false; - m_timeout.stop(); - m_socket.close(); -} - -void NexusSSOLogin::abort() -{ - m_active = false; - m_timeout.stop(); - m_socket.abort(); -} - -bool NexusSSOLogin::isActive() const -{ - return m_active; -} - -void NexusSSOLogin::setState(States s, const QString& error) -{ - if (stateChanged) { - stateChanged(s, error); - } -} - -void NexusSSOLogin::onConnected() -{ - setState(WaitingForToken); - - m_keyReceived = false; - - //if (m_guid.isEmpty()) { - boost::uuids::random_generator generator; - boost::uuids::uuid sessionId = generator(); - m_guid = boost::uuids::to_string(sessionId).c_str(); - //} - - QJsonObject data; - data.insert(QString("id"), QJsonValue(m_guid)); - //data.insert(QString("token"), QJsonValue(m_token)); - data.insert(QString("protocol"), 2); - - const QString message = QJsonDocument(data).toJson(); - m_socket.sendTextMessage(message); -} - -void NexusSSOLogin::onMessage(const QString& s) -{ - const QJsonDocument doc = QJsonDocument::fromJson(s.toUtf8()); - const QVariantMap root = doc.object().toVariantMap(); - - if (!root["success"].toBool()) { - close(); - - setState(Error, QString("There was a problem with SSO initialization: %1") - .arg(root["error"].toString())); - - return; - } - - const QVariantMap data = root["data"].toMap(); - - if (data.contains("connection_token")) { - // first answer - m_token = data["connection_token"].toString(); - - // open browser - const auto url = NexusSSOPage.arg(m_guid); - shell::OpenLink(url); - - m_timeout.stop(); - setState(WaitingForBrowser); - } else { - // second answer - const auto key = data["api_key"].toString(); - close(); - - if (keyChanged) { - keyChanged(key); - } - - setState(Finished); - } -} - -void NexusSSOLogin::onDisconnected() -{ - if (m_active) { - m_active = false; - - if (!m_keyReceived) { - setState(ClosedByRemote); - } - } -} - -void NexusSSOLogin::onError(QAbstractSocket::SocketError e) -{ - if (m_active) { - setState(Error, m_socket.errorString()); - } -} - -void NexusSSOLogin::onTimeout() -{ - abort(); - setState(Timeout); -} - - SettingsDialog::SettingsDialog(PluginContainer *pluginContainer, Settings* settings, QWidget *parent) : TutorableDialog("SettingsDialog", parent) , ui(new Ui::SettingsDialog) @@ -259,11 +107,22 @@ SettingsDialog::SettingsDialog(PluginContainer *pluginContainer, Settings* setti m_nexusLogin.keyChanged = [&](auto&& s){ onKeyChanged(s); }; m_nexusLogin.stateChanged = [&](auto&& s, auto&& e){ onStateChanged(s, e); }; + connect( + NexusInterface::instance(m_PluginContainer)->getAccessManager(), + &NXMAccessManager::validateSuccessful, + [&]{ onManualKeyValidation(true, ""); }); + + connect( + NexusInterface::instance(m_PluginContainer)->getAccessManager(), + &NXMAccessManager::validateFailed, + [&](auto&& e){ onManualKeyValidation(false, e); }); + + ui->setupUi(this); ui->pluginSettingsList->setStyleSheet("QTreeWidget::item {padding-right: 10px;}"); - QShortcut *delShortcut - = new QShortcut(QKeySequence(Qt::Key_Delete), ui->pluginBlacklist); + QShortcut *delShortcut = new QShortcut( + QKeySequence(Qt::Key_Delete), ui->pluginBlacklist); connect(delShortcut, SIGNAL(activated()), this, SLOT(deleteBlacklistItem())); updateNexusButtons(); @@ -556,7 +415,11 @@ void SettingsDialog::on_nexusManualKey_clicked() clearKey(); } else { if (setKey(key)) { - testApiKey(); + ui->nexusLog->clear(); + ui->nexusLog->addItem(tr("Checking API key...")); + + NexusInterface::instance(m_PluginContainer)->getAccessManager()->apiCheck( + key, NXMAccessManager::Force | NXMAccessManager::HideProgress); } } } @@ -583,11 +446,6 @@ void SettingsDialog::onStateChanged(NexusSSOLogin::States s, const QString& e) switch (s) { - case NexusSSOLogin::Idle: - { - break; - } - case NexusSSOLogin::ConnectingToSSO: { log = tr("Connecting to Nexus..."); @@ -649,6 +507,15 @@ void SettingsDialog::onStateChanged(NexusSSOLogin::States s, const QString& e) updateNexusButtons(); } +void SettingsDialog::onManualKeyValidation(bool success, const QString& e) +{ + if (success) { + ui->nexusLog->addItem("Connected."); + } else { + ui->nexusLog->addItem("Error: " + e); + } +} + bool SettingsDialog::setKey(const QString& key) { m_keyChanged = true; @@ -668,17 +535,6 @@ bool SettingsDialog::clearKey() return ret; } -void SettingsDialog::testApiKey() -{ - QString key; - if (!m_settings->getNexusApiKey(key)) { - qWarning().nospace() << "can't test API key, nothing stored"; - return; - } - - NexusInterface::instance(m_PluginContainer)->getAccessManager()->apiCheck(key, true); -} - void SettingsDialog::updateNexusButtons() { if (m_nexusLogin.isActive()) { diff --git a/src/settingsdialog.h b/src/settingsdialog.h index aee447d7..507214dd 100644 --- a/src/settingsdialog.h +++ b/src/settingsdialog.h @@ -21,12 +21,9 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #define SETTINGSDIALOG_H #include "tutorabledialog.h" +#include "nxmaccessmanager.h" #include <iplugin.h> -#include <QComboBox> -#include <QDialog> -#include <QWebSocket> #include <QListWidgetItem> -#include <QTimer> class PluginContainer; class Settings; @@ -35,51 +32,6 @@ namespace Ui { class SettingsDialog; } -class NexusSSOLogin -{ -public: - enum States - { - Idle, - ConnectingToSSO, - WaitingForToken, - WaitingForBrowser, - Finished, - Timeout, - ClosedByRemote, - Cancelled, - Error - }; - - std::function<void (QString)> keyChanged; - std::function<void (States, QString)> stateChanged; - - NexusSSOLogin(); - - void start(); - void cancel(); - - bool isActive() const; - -private: - QWebSocket m_socket; - QString m_guid; - bool m_keyReceived; - QString m_token; - bool m_active; - QTimer m_timeout; - - void setState(States s, const QString& error={}); - - void close(); - void abort(); - - void onConnected(); - void onMessage(const QString& s); - void onDisconnected(); - void onError(QAbstractSocket::SocketError e); - void onTimeout(); -}; /** * dialog used to change settings for Mod Organizer. On top of the @@ -197,9 +149,9 @@ private: void updateNexusButtons(); void fetchNexusApiKey(); - void testApiKey(); void onKeyChanged(const QString& key); void onStateChanged(NexusSSOLogin::States s, const QString& e); + void onManualKeyValidation(bool success, const QString& e); }; #endif // SETTINGSDIALOG_H |
