From bfc9e1e5d598f354dd39e5c2eb51d5e51585359b Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 28 Nov 2013 20:45:52 -0600 Subject: Verify access tokens before launching Minecraft Kind of an important thing to do... Heh... --- logic/auth/ValidateTask.cpp | 66 ++++++++++++++++++++++++ logic/auth/ValidateTask.h | 44 ++++++++++++++++ logic/auth/YggdrasilTask.cpp | 117 +++++++++++++++++++------------------------ logic/auth/YggdrasilTask.h | 2 + logic/tasks/Task.cpp | 15 ++++++ logic/tasks/Task.h | 14 ++++++ 6 files changed, 193 insertions(+), 65 deletions(-) create mode 100644 logic/auth/ValidateTask.cpp create mode 100644 logic/auth/ValidateTask.h (limited to 'logic') diff --git a/logic/auth/ValidateTask.cpp b/logic/auth/ValidateTask.cpp new file mode 100644 index 00000000..994d24e4 --- /dev/null +++ b/logic/auth/ValidateTask.cpp @@ -0,0 +1,66 @@ + +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include +#include +#include +#include +#include + +#include "logger/QsLog.h" + +ValidateTask::ValidateTask(MojangAccountPtr account, QObject* parent) : + YggdrasilTask(account, parent) +{ +} + +QJsonObject ValidateTask::getRequestContent() const +{ + QJsonObject req; + req.insert("accessToken", getMojangAccount()->accessToken()); + return req; +} + +bool ValidateTask::processResponse(QJsonObject responseData) +{ + // Assume that if processError wasn't called, then the request was successful. + emitSucceeded(); + return true; +} + +QString ValidateTask::getEndpoint() const +{ + return "validate"; +} + +QString ValidateTask::getStateMessage(const YggdrasilTask::State state) const +{ + switch (state) + { + case STATE_SENDING_REQUEST: + return tr("Validating Access Token: Sending request."); + case STATE_PROCESSING_RESPONSE: + return tr("Validating Access Token: Processing response."); + default: + return YggdrasilTask::getStateMessage(state); + } +} + + diff --git a/logic/auth/ValidateTask.h b/logic/auth/ValidateTask.h new file mode 100644 index 00000000..5bbc69c7 --- /dev/null +++ b/logic/auth/ValidateTask.h @@ -0,0 +1,44 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include +#include + +/** + * The validate task takes a MojangAccount and checks to make sure its access token is valid. + */ +class ValidateTask : public YggdrasilTask +{ +Q_OBJECT +public: + ValidateTask(MojangAccountPtr account, QObject* parent=0); + +protected: + virtual QJsonObject getRequestContent() const; + + virtual QString getEndpoint() const; + + virtual bool processResponse(QJsonObject responseData); + + QString getStateMessage(const YggdrasilTask::State state) const; + +private: +}; + diff --git a/logic/auth/YggdrasilTask.cpp b/logic/auth/YggdrasilTask.cpp index 39dfb749..31c8fbab 100644 --- a/logic/auth/YggdrasilTask.cpp +++ b/logic/auth/YggdrasilTask.cpp @@ -64,78 +64,65 @@ void YggdrasilTask::processReply(QNetworkReply* reply) // Wrong reply for some reason... return; - // Check for errors. - switch (reply->error()) + if (reply->error() == QNetworkReply::OperationCanceledError) { - case QNetworkReply::NoError: - { - // Try to parse the response regardless of the response code. - // Sometimes the auth server will give more information and an error code. - QJsonParseError jsonError; - QByteArray replyData = reply->readAll(); - QJsonDocument doc = QJsonDocument::fromJson(replyData, &jsonError); + emitFailed("Yggdrasil task cancelled."); + return; + } + else + { + // Try to parse the response regardless of the response code. + // Sometimes the auth server will give more information and an error code. + QJsonParseError jsonError; + QByteArray replyData = reply->readAll(); + QJsonDocument doc = QJsonDocument::fromJson(replyData, &jsonError); - // Check the response code. - int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + // Check the response code. + int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - switch (responseCode) + if (responseCode == 200) + { + // If the response code was 200, then there shouldn't be an error. Make sure anyways. + // Also, sometimes an empty reply indicates success. If there was no data received, + // pass an empty json object to the processResponse function. + if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0) { - case 200: + if (!processResponse(replyData.size() > 0 ? doc.object() : QJsonObject())) { - // If the response code was 200, then there shouldn't be an error. Make sure anyways. - switch (jsonError.error) - { - case QJsonParseError::NoError: - if (!processResponse(doc.object())) - { - YggdrasilTask::Error* err = getError(); - if (err) - emitFailed(err->getErrorMessage()); - else - emitFailed(tr("An unknown error occurred when processing the response from the authentication server.")); - } - else - { - emitSucceeded(); - } - break; - - default: - emitFailed(tr("Failed to parse Yggdrasil JSON response: \"%1\".").arg(jsonError.errorString())); - break; - } - break; + YggdrasilTask::Error* err = getError(); + if (err) + emitFailed(err->getErrorMessage()); + else + emitFailed(tr("An unknown error occurred when processing the response from the authentication server.")); + } + else + { + emitSucceeded(); } - - default: - // If the response code was something else, then Yggdrasil may have given us information about the error. - // If we can parse the response, then get information from it. Otherwise just say there was an unknown error. - switch (jsonError.error) - { - case QJsonParseError::NoError: - // We were able to parse the server's response. Woo! - // Call processError. If a subclass has overridden it then they'll handle their stuff there. - processError(doc.object()); - break; - - default: - // The server didn't say anything regarding the error. Give the user an unknown error. - emitFailed(tr("Login failed: Unknown HTTP code %1 encountered.").arg(responseCode)); - break; - } - break; } - - break; + else + { + emitFailed(tr("Failed to parse Yggdrasil JSON response: %1 at offset %2.").arg(jsonError.errorString()).arg(jsonError.offset)); + } + } + else + { + // If the response code was not 200, then Yggdrasil may have given us information about the error. + // If we can parse the response, then get information from it. Otherwise just say there was an unknown error. + if (jsonError.error == QJsonParseError::NoError) + { + // We were able to parse the server's response. Woo! + // Call processError. If a subclass has overridden it then they'll handle their stuff there. + QLOG_DEBUG() << "The request failed, but the server gave us an error message. Processing error."; + emitFailed(processError(doc.object())); + } + else + { + // The server didn't say anything regarding the error. Give the user an unknown error. + QLOG_DEBUG() << "The request failed and the server gave no error message. Unknown error."; + emitFailed(tr("An unknown error occurred when trying to communicate with the authentication server: %1").arg(reply->errorString())); + } } - - case QNetworkReply::OperationCanceledError: - emitFailed(tr("Login canceled.")); - break; - - default: - emitFailed(tr("An unknown error occurred when trying to communicate with the authentication server.")); - break; } } @@ -145,7 +132,7 @@ QString YggdrasilTask::processError(QJsonObject responseData) QJsonValue msgVal = responseData.value("errorMessage"); QJsonValue causeVal = responseData.value("cause"); - if (errorVal.isString() && msgVal.isString() && causeVal.isString()) + if (errorVal.isString() && msgVal.isString()) { m_error = new Error(errorVal.toString(""), msgVal.toString(""), causeVal.toString("")); return m_error->getDisplayMessage(); diff --git a/logic/auth/YggdrasilTask.h b/logic/auth/YggdrasilTask.h index 6aebae16..5a433aeb 100644 --- a/logic/auth/YggdrasilTask.h +++ b/logic/auth/YggdrasilTask.h @@ -99,6 +99,8 @@ protected: * If an error occurred, this should emit a failed signal and return false. * If Yggdrasil gave an error response, it should call setError() first, and then return false. * Otherwise, it should return true. + * Note: If the response from the server was blank, and the HTTP code was 200, this function is called with + * an empty QJsonObject. */ virtual bool processResponse(QJsonObject responseData) = 0; diff --git a/logic/tasks/Task.cpp b/logic/tasks/Task.cpp index 47214723..cb7a5443 100644 --- a/logic/tasks/Task.cpp +++ b/logic/tasks/Task.cpp @@ -53,6 +53,8 @@ void Task::start() void Task::emitFailed(QString reason) { m_running = false; + m_succeeded = false; + m_failReason = reason; QLOG_ERROR() << "Task failed: " << reason; emit failed(reason); } @@ -60,6 +62,8 @@ void Task::emitFailed(QString reason) void Task::emitSucceeded() { m_running = false; + m_succeeded = true; + QLOG_INFO() << "Task succeeded"; emit succeeded(); } @@ -67,3 +71,14 @@ bool Task::isRunning() const { return m_running; } + +bool Task::successful() const +{ + return m_succeeded; +} + +QString Task::failReason() const +{ + return m_failReason; +} + diff --git a/logic/tasks/Task.h b/logic/tasks/Task.h index 980b2af8..d08ef560 100644 --- a/logic/tasks/Task.h +++ b/logic/tasks/Task.h @@ -29,6 +29,18 @@ public: virtual void getProgress(qint64 ¤t, qint64 &total); virtual bool isRunning() const; + /*! + * True if this task was successful. + * If the task failed or is still running, returns false. + */ + virtual bool successful() const; + + /*! + * Returns the string that was passed to emitFailed as the error message when the task failed. + * If the task hasn't failed, returns an empty string. + */ + virtual QString failReason() const; + public slots: virtual void start(); @@ -48,4 +60,6 @@ protected: QString m_status; int m_progress = 0; bool m_running = false; + bool m_succeeded = false; + QString m_failReason = ""; }; -- cgit v1.2.3