summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/web_service/web_backend.cpp52
-rw-r--r--src/web_service/web_backend.h23
-rw-r--r--src/yuzu/configuration/configure_web.cpp73
-rw-r--r--src/yuzu/configuration/configure_web.ui12
4 files changed, 110 insertions, 50 deletions
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index dc149d2ed..6683f459f 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -2,10 +2,12 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <array>
#include <cstdlib>
#include <mutex>
#include <string>
#include <LUrlParser.h>
+#include <fmt/format.h>
#include <httplib.h>
#include "common/common_types.h"
#include "common/logging/log.h"
@@ -16,10 +18,10 @@ namespace WebService {
constexpr std::array<const char, 1> API_VERSION{'1'};
-constexpr u32 HTTP_PORT = 80;
-constexpr u32 HTTPS_PORT = 443;
+constexpr int HTTP_PORT = 80;
+constexpr int HTTPS_PORT = 443;
-constexpr u32 TIMEOUT_SECONDS = 30;
+constexpr std::size_t TIMEOUT_SECONDS = 30;
struct Client::Impl {
Impl(std::string host, std::string username, std::string token)
@@ -31,8 +33,9 @@ struct Client::Impl {
}
/// A generic function handles POST, GET and DELETE request together
- Common::WebResult GenericJson(const std::string& method, const std::string& path,
- const std::string& data, bool allow_anonymous) {
+ Common::WebResult GenericRequest(const std::string& method, const std::string& path,
+ const std::string& data, bool allow_anonymous,
+ const std::string& accept) {
if (jwt.empty()) {
UpdateJWT();
}
@@ -43,11 +46,11 @@ struct Client::Impl {
"Credentials needed"};
}
- auto result = GenericJson(method, path, data, jwt);
+ auto result = GenericRequest(method, path, data, accept, jwt);
if (result.result_string == "401") {
// Try again with new JWT
UpdateJWT();
- result = GenericJson(method, path, data, jwt);
+ result = GenericRequest(method, path, data, accept, jwt);
}
return result;
@@ -56,12 +59,13 @@ struct Client::Impl {
/**
* A generic function with explicit authentication method specified
* JWT is used if the jwt parameter is not empty
- * username + token is used if jwt is empty but username and token are not empty
- * anonymous if all of jwt, username and token are empty
+ * username + token is used if jwt is empty but username and token are
+ * not empty anonymous if all of jwt, username and token are empty
*/
- Common::WebResult GenericJson(const std::string& method, const std::string& path,
- const std::string& data, const std::string& jwt = "",
- const std::string& username = "", const std::string& token = "") {
+ Common::WebResult GenericRequest(const std::string& method, const std::string& path,
+ const std::string& data, const std::string& accept,
+ const std::string& jwt = "", const std::string& username = "",
+ const std::string& token = "") {
if (cli == nullptr) {
auto parsedUrl = LUrlParser::clParseURL::ParseURL(host);
int port;
@@ -132,8 +136,7 @@ struct Client::Impl {
return Common::WebResult{Common::WebResult::Code::WrongContent, ""};
}
- if (content_type->second.find("application/json") == std::string::npos &&
- content_type->second.find("text/html; charset=utf-8") == std::string::npos) {
+ if (content_type->second.find(accept) == std::string::npos) {
LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path,
content_type->second);
return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content"};
@@ -147,7 +150,7 @@ struct Client::Impl {
return;
}
- auto result = GenericJson("POST", "/jwt/internal", "", "", username, token);
+ auto result = GenericRequest("POST", "/jwt/internal", "", "text/html", "", username, token);
if (result.result_code != Common::WebResult::Code::Success) {
LOG_ERROR(WebService, "UpdateJWT failed");
} else {
@@ -180,16 +183,29 @@ Client::~Client() = default;
Common::WebResult Client::PostJson(const std::string& path, const std::string& data,
bool allow_anonymous) {
- return impl->GenericJson("POST", path, data, allow_anonymous);
+ return impl->GenericRequest("POST", path, data, allow_anonymous, "application/json");
}
Common::WebResult Client::GetJson(const std::string& path, bool allow_anonymous) {
- return impl->GenericJson("GET", path, "", allow_anonymous);
+ return impl->GenericRequest("GET", path, "", allow_anonymous, "application/json");
}
Common::WebResult Client::DeleteJson(const std::string& path, const std::string& data,
bool allow_anonymous) {
- return impl->GenericJson("DELETE", path, data, allow_anonymous);
+ return impl->GenericRequest("DELETE", path, data, allow_anonymous, "application/json");
+}
+
+Common::WebResult Client::GetPlain(const std::string& path, bool allow_anonymous) {
+ return impl->GenericRequest("GET", path, "", allow_anonymous, "text/plain");
+}
+
+Common::WebResult Client::GetImage(const std::string& path, bool allow_anonymous) {
+ return impl->GenericRequest("GET", path, "", allow_anonymous, "image/png");
+}
+
+Common::WebResult Client::GetExternalJWT(const std::string& audience) {
+ return impl->GenericRequest("POST", fmt::format("/jwt/external/{}", audience), "", false,
+ "text/html");
}
} // namespace WebService
diff --git a/src/web_service/web_backend.h b/src/web_service/web_backend.h
index c637e09df..04121f17e 100644
--- a/src/web_service/web_backend.h
+++ b/src/web_service/web_backend.h
@@ -46,6 +46,29 @@ public:
Common::WebResult DeleteJson(const std::string& path, const std::string& data,
bool allow_anonymous);
+ /**
+ * Gets a plain string from the specified path.
+ * @param path the URL segment after the host address.
+ * @param allow_anonymous If true, allow anonymous unauthenticated requests.
+ * @return the result of the request.
+ */
+ Common::WebResult GetPlain(const std::string& path, bool allow_anonymous);
+
+ /**
+ * Gets an PNG image from the specified path.
+ * @param path the URL segment after the host address.
+ * @param allow_anonymous If true, allow anonymous unauthenticated requests.
+ * @return the result of the request.
+ */
+ Common::WebResult GetImage(const std::string& path, bool allow_anonymous);
+
+ /**
+ * Requests an external JWT for the specific audience provided.
+ * @param audience the audience of the JWT requested.
+ * @return the result of the request.
+ */
+ Common::WebResult GetExternalJWT(const std::string& audience);
+
private:
struct Impl;
std::unique_ptr<Impl> impl;
diff --git a/src/yuzu/configuration/configure_web.cpp b/src/yuzu/configuration/configure_web.cpp
index 336b062b3..8637f5b3c 100644
--- a/src/yuzu/configuration/configure_web.cpp
+++ b/src/yuzu/configuration/configure_web.cpp
@@ -11,6 +11,31 @@
#include "yuzu/configuration/configure_web.h"
#include "yuzu/uisettings.h"
+static constexpr char token_delimiter{':'};
+
+static std::string GenerateDisplayToken(const std::string& username, const std::string& token) {
+ if (username.empty() || token.empty()) {
+ return {};
+ }
+
+ const std::string unencoded_display_token{username + token_delimiter + token};
+ QByteArray b{unencoded_display_token.c_str()};
+ QByteArray b64 = b.toBase64();
+ return b64.toStdString();
+}
+
+static std::string UsernameFromDisplayToken(const std::string& display_token) {
+ const std::string unencoded_display_token{
+ QByteArray::fromBase64(display_token.c_str()).toStdString()};
+ return unencoded_display_token.substr(0, unencoded_display_token.find(token_delimiter));
+}
+
+static std::string TokenFromDisplayToken(const std::string& display_token) {
+ const std::string unencoded_display_token{
+ QByteArray::fromBase64(display_token.c_str()).toStdString()};
+ return unencoded_display_token.substr(unencoded_display_token.find(token_delimiter) + 1);
+}
+
ConfigureWeb::ConfigureWeb(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureWeb>()) {
ui->setupUi(this);
@@ -63,13 +88,18 @@ void ConfigureWeb::SetConfiguration() {
ui->web_signup_link->setOpenExternalLinks(true);
ui->web_token_info_link->setOpenExternalLinks(true);
+ if (Settings::values.yuzu_username.empty()) {
+ ui->username->setText(tr("Unspecified"));
+ } else {
+ ui->username->setText(QString::fromStdString(Settings::values.yuzu_username));
+ }
+
ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry);
- ui->edit_username->setText(QString::fromStdString(Settings::values.yuzu_username));
- ui->edit_token->setText(QString::fromStdString(Settings::values.yuzu_token));
+ ui->edit_token->setText(QString::fromStdString(
+ GenerateDisplayToken(Settings::values.yuzu_username, Settings::values.yuzu_token)));
// Connect after setting the values, to avoid calling OnLoginChanged now
connect(ui->edit_token, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged);
- connect(ui->edit_username, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged);
user_verified = true;
@@ -80,12 +110,13 @@ void ConfigureWeb::ApplyConfiguration() {
Settings::values.enable_telemetry = ui->toggle_telemetry->isChecked();
UISettings::values.enable_discord_presence = ui->toggle_discordrpc->isChecked();
if (user_verified) {
- Settings::values.yuzu_username = ui->edit_username->text().toStdString();
- Settings::values.yuzu_token = ui->edit_token->text().toStdString();
+ Settings::values.yuzu_username =
+ UsernameFromDisplayToken(ui->edit_token->text().toStdString());
+ Settings::values.yuzu_token = TokenFromDisplayToken(ui->edit_token->text().toStdString());
} else {
- QMessageBox::warning(this, tr("Username and token not verified"),
- tr("Username and token were not verified. The changes to your "
- "username and/or token have not been saved."));
+ QMessageBox::warning(
+ this, tr("Token not verified"),
+ tr("Token was not verified. The change to your token has not been saved."));
}
}
@@ -96,17 +127,15 @@ void ConfigureWeb::RefreshTelemetryID() {
}
void ConfigureWeb::OnLoginChanged() {
- if (ui->edit_username->text().isEmpty() && ui->edit_token->text().isEmpty()) {
+ if (ui->edit_token->text().isEmpty()) {
user_verified = true;
const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("checked")).pixmap(16);
- ui->label_username_verified->setPixmap(pixmap);
ui->label_token_verified->setPixmap(pixmap);
} else {
user_verified = false;
const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("failed")).pixmap(16);
- ui->label_username_verified->setPixmap(pixmap);
ui->label_token_verified->setPixmap(pixmap);
}
}
@@ -114,10 +143,11 @@ void ConfigureWeb::OnLoginChanged() {
void ConfigureWeb::VerifyLogin() {
ui->button_verify_login->setDisabled(true);
ui->button_verify_login->setText(tr("Verifying..."));
- verify_watcher.setFuture(QtConcurrent::run([username = ui->edit_username->text().toStdString(),
- token = ui->edit_token->text().toStdString()] {
- return Core::VerifyLogin(username, token);
- }));
+ verify_watcher.setFuture(QtConcurrent::run(
+ [username = UsernameFromDisplayToken(ui->edit_token->text().toStdString()),
+ token = TokenFromDisplayToken(ui->edit_token->text().toStdString())] {
+ return Core::VerifyLogin(username, token);
+ }));
}
void ConfigureWeb::OnLoginVerified() {
@@ -127,16 +157,15 @@ void ConfigureWeb::OnLoginVerified() {
user_verified = true;
const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("checked")).pixmap(16);
- ui->label_username_verified->setPixmap(pixmap);
ui->label_token_verified->setPixmap(pixmap);
+ ui->username->setText(
+ QString::fromStdString(UsernameFromDisplayToken(ui->edit_token->text().toStdString())));
} else {
const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("failed")).pixmap(16);
- ui->label_username_verified->setPixmap(pixmap);
ui->label_token_verified->setPixmap(pixmap);
-
- QMessageBox::critical(
- this, tr("Verification failed"),
- tr("Verification failed. Check that you have entered your username and token "
- "correctly, and that your internet connection is working."));
+ ui->username->setText(tr("Unspecified"));
+ QMessageBox::critical(this, tr("Verification failed"),
+ tr("Verification failed. Check that you have entered your token "
+ "correctly, and that your internet connection is working."));
}
}
diff --git a/src/yuzu/configuration/configure_web.ui b/src/yuzu/configuration/configure_web.ui
index 2f4b9dd73..8c07d1165 100644
--- a/src/yuzu/configuration/configure_web.ui
+++ b/src/yuzu/configuration/configure_web.ui
@@ -55,11 +55,7 @@
</widget>
</item>
<item row="0" column="1" colspan="3">
- <widget class="QLineEdit" name="edit_username">
- <property name="maxLength">
- <number>36</number>
- </property>
- </widget>
+ <widget class="QLabel" name="username" />
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_token">
@@ -79,14 +75,10 @@
</property>
</widget>
</item>
- <item row="0" column="4">
- <widget class="QLabel" name="label_username_verified">
- </widget>
- </item>
<item row="1" column="1" colspan="3">
<widget class="QLineEdit" name="edit_token">
<property name="maxLength">
- <number>36</number>
+ <number>80</number>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>