437 lines · 16.5 KB · C++
RawDownload
////  LoginDialog.cpp//  interface/src/ui////  Created by Bradley Austin Davis on 2015/04/14//  Copyright 2015 High Fidelity, Inc.//  Copyright 2020 Vircadia contributors.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "LoginDialog.h" #include <QtGui/QDesktopServices>#include <QtCore/QJsonArray>#include <QtCore/QJsonDocument>#include <QtNetwork/QNetworkReply> #include <plugins/PluginManager.h>#include <plugins/SteamClientPlugin.h>#include <plugins/OculusPlatformPlugin.h>#include <shared/GlobalAppProperties.h>#include <ui/TabletScriptingInterface.h>#include <UserActivityLogger.h> #include "AccountManager.h"#include "DomainAccountManager.h"#include "DependencyManager.h"#include "DialogsManager.h"#include "Menu.h" #include "Application.h"#include "scripting/HMDScriptingInterface.h"#include "Constants.h"#include "scripting/DesktopScriptingInterface.h" HIFI_QML_DEF(LoginDialog) static const QUrl TABLET_LOGIN_DIALOG_URL("dialogs/TabletLoginDialog.qml");const QUrl LOGIN_DIALOG = PathUtils::qmlUrl("OverlayLoginDialog.qml"); LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent) {    auto accountManager = DependencyManager::get<AccountManager>();    auto domainAccountManager = DependencyManager::get<DomainAccountManager>();    // the login hasn't been dismissed yet if the user isn't logged in and is encouraged to login.#if !defined(Q_OS_ANDROID)    connect(accountManager.data(), &AccountManager::loginComplete,        this, &LoginDialog::handleLoginCompleted);    connect(accountManager.data(), &AccountManager::loginFailed,            this, &LoginDialog::handleLoginFailed);    connect(domainAccountManager.data(), &DomainAccountManager::loginComplete,        this, &LoginDialog::handleLoginCompleted);    connect(domainAccountManager.data(), &DomainAccountManager::loginFailed,            this, &LoginDialog::handleLoginFailed);    connect(qApp, &Application::loginDialogFocusEnabled, this, &LoginDialog::focusEnabled);    connect(qApp, &Application::loginDialogFocusDisabled, this, &LoginDialog::focusDisabled);    connect(this, SIGNAL(dismissedLoginDialog()), qApp, SLOT(onDismissedLoginDialog()));#endif} LoginDialog::~LoginDialog() {} void LoginDialog::showWithSelection() {    auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();    auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));    auto hmd = DependencyManager::get<HMDScriptingInterface>();     if (!qApp->isHMDMode()) {        emit DependencyManager::get<DesktopScriptingInterface>()->uiFocusChanged(true);        if (qApp->getLoginDialogPoppedUp()) {            LoginDialog::show();            return;        } else {            if (!tablet->isPathLoaded(TABLET_LOGIN_DIALOG_URL)) {                tablet->loadQMLSource(TABLET_LOGIN_DIALOG_URL);            }        }    } else {        if (!qApp->getLoginDialogPoppedUp()) {            tablet->initialScreen(TABLET_LOGIN_DIALOG_URL);        } else {            qApp->createLoginDialog();        }    }     if (!hmd->getShouldShowTablet()) {        hmd->openTablet();    }} void LoginDialog::toggleAction() {    auto accountManager = DependencyManager::get<AccountManager>();    QAction* loginAction = Menu::getInstance()->getActionForOption(MenuOption::Login);    Q_CHECK_PTR(loginAction);    static QMetaObject::Connection connection;    if (connection) {        disconnect(connection);    }     if (accountManager->isLoggedIn()) {        // change the menu item to logout        loginAction->setText("Directory Service: Logout " + accountManager->getAccountInfo().getUsername());        connection = connect(loginAction, &QAction::triggered, accountManager.data(), &AccountManager::logout);    } else {        // change the menu item to login        loginAction->setText("Directory Service: Log In / Sign Up");        connection = connect(loginAction, &QAction::triggered, [] {            // if not in login state, show.            if (!qApp->getLoginDialogPoppedUp()) {                auto dialogsManager = DependencyManager::get<DialogsManager>();                dialogsManager->setMetaverseLoginState();                LoginDialog::showWithSelection();            }        });    }} bool LoginDialog::isSteamRunning() const {    auto steamClient = PluginManager::getInstance()->getSteamClientPlugin();    return steamClient && steamClient->isRunning();} bool LoginDialog::isOculusRunning() const {    auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin();    return (oculusPlatformPlugin && oculusPlatformPlugin->isRunning());} QString LoginDialog::oculusUserID() const {    if (auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin()) {        return oculusPlatformPlugin->getOculusUserID();    }    return "";} void LoginDialog::dismissLoginDialog() {    QAction* loginAction = Menu::getInstance()->getActionForOption(MenuOption::Login);    Q_CHECK_PTR(loginAction);    loginAction->setEnabled(true);     emit dismissedLoginDialog();} void LoginDialog::login(const QString& username, const QString& password) const {    qDebug() << "Attempting to login" << username;    DependencyManager::get<AccountManager>()->requestAccessToken(username, password);} void LoginDialog::loginDomain(const QString& username, const QString& password) const {    qDebug() << "Attempting to login" << username << "into a domain";    DependencyManager::get<DomainAccountManager>()->requestAccessToken(username, password);} void LoginDialog::loginThroughOculus() {   qDebug() << "Attempting to login through Oculus";    if (auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin()) {        oculusPlatformPlugin->requestNonceAndUserID([] (QString nonce, QString oculusID) {            DependencyManager::get<AccountManager>()->requestAccessTokenWithOculus(nonce, oculusID);        });    }} void LoginDialog::linkOculus() {    qDebug() << "Attempting to link Oculus account";    if (auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin()) {        oculusPlatformPlugin->requestNonceAndUserID([this] (QString nonce, QString oculusID) {            if (nonce.isEmpty() || oculusID.isEmpty()) {                emit handleLoginFailed();                return;            }             JSONCallbackParameters callbackParams;            callbackParams.callbackReceiver = this;            callbackParams.jsonCallbackMethod = "linkCompleted";            callbackParams.errorCallbackMethod = "linkFailed";            const QString LINK_OCULUS_PATH = "/api/v1/user/oculus/link";             QJsonObject payload;            payload["oculus_nonce"] = nonce;            payload["oculus_id"] = oculusID;             auto accountManager = DependencyManager::get<AccountManager>();            accountManager->sendRequest(LINK_OCULUS_PATH, AccountManagerAuth::Required,                                        QNetworkAccessManager::PostOperation, callbackParams,                                        QJsonDocument(payload).toJson());        });    }} void LoginDialog::createAccountFromOculus(QString email, QString username, QString password) {    qDebug() << "Attempting to create account from Oculus info";    if (auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin()) {        oculusPlatformPlugin->requestNonceAndUserID([this, email, username, password] (QString nonce, QString oculusID) {            if (nonce.isEmpty() || oculusID.isEmpty()) {                emit handleLoginFailed();                return;            }             JSONCallbackParameters callbackParams;            callbackParams.callbackReceiver = this;            callbackParams.jsonCallbackMethod = "createCompleted";            callbackParams.errorCallbackMethod = "createFailed";             const QString CREATE_ACCOUNT_FROM_OCULUS_PATH = "/api/v1/user/oculus/create";             QJsonObject payload;            payload["oculus_nonce"] = nonce;            payload["oculus_id"] = oculusID;            if (!email.isEmpty()) {                payload["email"] = email;            }            if (!username.isEmpty()) {                payload["username"] = username;            }            if (!password.isEmpty()) {                payload["password"] = password;            }             auto accountManager = DependencyManager::get<AccountManager>();            accountManager->sendRequest(CREATE_ACCOUNT_FROM_OCULUS_PATH, AccountManagerAuth::None,                                        QNetworkAccessManager::PostOperation, callbackParams,                                        QJsonDocument(payload).toJson());            });    }} void LoginDialog::loginThroughSteam() {    qDebug() << "Attempting to login through Steam";    if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {        steamClient->requestTicket([this](Ticket ticket) {            if (ticket.isNull()) {                emit handleLoginFailed();                return;            }             DependencyManager::get<AccountManager>()->requestAccessTokenWithSteam(ticket);        });    }} void LoginDialog::linkSteam() {    qDebug() << "Attempting to link Steam account";    if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {        steamClient->requestTicket([this](Ticket ticket) {            if (ticket.isNull()) {                emit handleLoginFailed();                return;            }             JSONCallbackParameters callbackParams;            callbackParams.callbackReceiver = this;            callbackParams.jsonCallbackMethod = "linkCompleted";            callbackParams.errorCallbackMethod = "linkFailed";             const QString LINK_STEAM_PATH = "/api/v1/user/steam/link";             QJsonObject payload;            payload["steam_auth_ticket"] = QJsonValue::fromVariant(QVariant(ticket));             auto accountManager = DependencyManager::get<AccountManager>();            accountManager->sendRequest(LINK_STEAM_PATH, AccountManagerAuth::Required,                                        QNetworkAccessManager::PostOperation, callbackParams,                                        QJsonDocument(payload).toJson());        });    }} void LoginDialog::createAccountFromSteam(QString username) {    qDebug() << "Attempting to create account from Steam info";    if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {        steamClient->requestTicket([this, username](Ticket ticket) {            if (ticket.isNull()) {                emit handleLoginFailed();                return;            }             JSONCallbackParameters callbackParams;            callbackParams.callbackReceiver = this;            callbackParams.jsonCallbackMethod = "createCompleted";            callbackParams.errorCallbackMethod = "createFailed";             const QString CREATE_ACCOUNT_FROM_STEAM_PATH = "/api/v1/user/steam/create";             QJsonObject payload;            payload["steam_auth_ticket"] = QJsonValue::fromVariant(QVariant(ticket));            if (!username.isEmpty()) {                payload["username"] = username;            }             auto accountManager = DependencyManager::get<AccountManager>();            accountManager->sendRequest(CREATE_ACCOUNT_FROM_STEAM_PATH, AccountManagerAuth::None,                                        QNetworkAccessManager::PostOperation, callbackParams,                                        QJsonDocument(payload).toJson());        });    }} void LoginDialog::linkCompleted(QNetworkReply* reply) {    emit handleLinkCompleted();} void LoginDialog::linkFailed(QNetworkReply* reply) {    emit handleLinkFailed(reply->errorString());} void LoginDialog::createCompleted(QNetworkReply* reply) {    emit handleCreateCompleted();} void LoginDialog::createFailed(QNetworkReply* reply) {    if (isOculusRunning()) {        auto replyData = reply->readAll();        QJsonParseError parseError;        auto doc = QJsonDocument::fromJson(replyData, &parseError);        if (parseError.error != QJsonParseError::NoError) {            emit handleCreateFailed(reply->errorString());            return;        }        auto data = doc["data"];        auto error = data["error"];        auto oculusError = data["oculus"];        auto user = error["username"].toArray();        auto uid = error["uid"].toArray();        auto email = error["email"].toArray();        auto password = error["password"].toArray();        QString reply;        if (uid[0].isString()) {            emit handleCreateFailed("Oculus ID " + uid[0].toString() + ".");            return;        }        if (user[0].isString()) {            reply = "Username " + user[0].toString() + ".";        }        if (email[0].isString()) {            reply.append((!reply.isEmpty()) ? "\n" : "");            reply.append("Email " + email[0].toString() + ".");        }        if (password[0].isString()) {            reply.append((!reply.isEmpty()) ? "\n" : "");            reply.append("Password " + password[0].toString() + ".");        }        if (!oculusError.isNull() && !oculusError.isUndefined()) {            emit handleCreateFailed("Could not verify token with Oculus. Please try again.");            return;        } else {            emit handleCreateFailed(reply);            return;        }    }    emit handleCreateFailed(reply->errorString());} void LoginDialog::signup(const QString& email, const QString& username, const QString& password) {    JSONCallbackParameters callbackParams;    callbackParams.callbackReceiver = this;    callbackParams.jsonCallbackMethod = "signupCompleted";    callbackParams.errorCallbackMethod = "signupFailed";     QJsonObject payload;     QJsonObject userObject;    userObject.insert("email", email);    userObject.insert("username", username);    userObject.insert("password", password);     payload.insert("user", userObject);     qDebug() << "Sending a request to create an account for" << username;     auto accountManager = DependencyManager::get<AccountManager>();    accountManager->sendRequest(API_SIGNUP_PATH, AccountManagerAuth::None,                                QNetworkAccessManager::PostOperation, callbackParams,                                QJsonDocument(payload).toJson());} void LoginDialog::signupCompleted(QNetworkReply* reply) {    emit handleSignupCompleted();} bool LoginDialog::getLoginDialogPoppedUp() const {    return qApp->getLoginDialogPoppedUp();} QString errorStringFromAPIObject(const QJsonValue& apiObject) {    if (apiObject.isArray()) {        return apiObject.toArray()[0].toString();    } else if (apiObject.isString()) {        return apiObject.toString();    } else {        return "is invalid";    }} void LoginDialog::signupFailed(QNetworkReply* reply) {    // parse the returned JSON to see what the problem was    auto jsonResponse = QJsonDocument::fromJson(reply->readAll());     static const QString RESPONSE_DATA_KEY = "data";     auto dataJsonValue = jsonResponse.object()[RESPONSE_DATA_KEY];     if (dataJsonValue.isObject()) {        auto dataObject = dataJsonValue.toObject();         static const QString EMAIL_DATA_KEY = "email";        static const QString USERNAME_DATA_KEY = "username";        static const QString PASSWORD_DATA_KEY = "password";         QStringList errorStringList;         if (dataObject.contains(EMAIL_DATA_KEY)) {            errorStringList.append(QString("Email %1.").arg(errorStringFromAPIObject(dataObject[EMAIL_DATA_KEY])));        }         if (dataObject.contains(USERNAME_DATA_KEY)) {            errorStringList.append(QString("Username %1.").arg(errorStringFromAPIObject(dataObject[USERNAME_DATA_KEY])));        }         if (dataObject.contains(PASSWORD_DATA_KEY)) {            errorStringList.append(QString("Password %1.").arg(errorStringFromAPIObject(dataObject[PASSWORD_DATA_KEY])));        }         emit handleSignupFailed(errorStringList.join('\n'));    } else {        static const QString DEFAULT_SIGN_UP_FAILURE_MESSAGE = "There was an unknown error while creating your account. Please try again later.";        emit handleSignupFailed(DEFAULT_SIGN_UP_FAILURE_MESSAGE);    }} bool LoginDialog::getDomainLoginRequested() const {    return DependencyManager::get<DialogsManager>()->getIsDomainLogin();} QString LoginDialog::getDomainLoginDomain() const {    return DependencyManager::get<DialogsManager>()->getDomainLoginDomain();}