Merge branch 'master' of https://github.com/highfidelity/hifi into yellow

This commit is contained in:
Sam Gateau 2019-09-23 18:59:20 -07:00
commit 69ccc47bda
4 changed files with 234 additions and 32 deletions

View file

@ -47,6 +47,9 @@ Q_DECLARE_METATYPE(JSONCallbackParameters)
const QString ACCOUNTS_GROUP = "accounts";
const int POST_SETTINGS_INTERVAL = 10 * MSECS_PER_SECOND;
const int PULL_SETTINGS_RETRY_INTERVAL = 1 * MSECS_PER_SECOND;
JSONCallbackParameters::JSONCallbackParameters(QObject* callbackReceiver,
const QString& jsonCallbackMethod,
const QString& errorCallbackMethod) :
@ -87,12 +90,20 @@ AccountManager::AccountManager(UserAgentGetter userAgentGetter) :
qRegisterMetaType<AccountManagerAuth::Type>();
connect(this, &AccountManager::loginComplete, this, &AccountManager::uploadPublicKey);
connect(this, &AccountManager::loginComplete, this, &AccountManager::requestAccountSettings);
_postSettingsTimer = new QTimer(this);
_postSettingsTimer->setInterval(POST_SETTINGS_INTERVAL);
connect(this, SIGNAL(loginComplete(QUrl)), _postSettingsTimer, SLOT(start()));
connect(this, &AccountManager::logoutComplete, _postSettingsTimer, &QTimer::stop);
connect(_postSettingsTimer, &QTimer::timeout, this, &AccountManager::postAccountSettings);
connect(qApp, &QCoreApplication::aboutToQuit, this, &AccountManager::postAccountSettings);
}
const QString DOUBLE_SLASH_SUBSTITUTE = "slashslash";
const QString ACCOUNT_MANAGER_REQUESTED_SCOPE = "owner";
void AccountManager::logout() {
postAccountSettings();
// a logout means we want to delete the DataServerAccountInfo we currently have for this URL, in-memory and in file
_accountInfo = DataServerAccountInfo();
@ -104,6 +115,8 @@ void AccountManager::logout() {
emit logoutComplete();
// the username has changed to blank
emit usernameChanged(QString());
_settings.loggedOut();
}
QString accountFileDir() {
@ -160,33 +173,7 @@ void AccountManager::setAuthURL(const QUrl& authURL) {
qCDebug(networking) << "Found metaverse API account information for" << qPrintable(_authURL.toString());
} else {
// we didn't have a file - see if we can migrate old settings and store them in the new file
// check if there are existing access tokens to load from settings
Settings settings;
settings.beginGroup(ACCOUNTS_GROUP);
foreach(const QString& key, settings.allKeys()) {
// take a key copy to perform the double slash replacement
QString keyCopy(key);
QUrl keyURL(keyCopy.replace(DOUBLE_SLASH_SUBSTITUTE, "//"));
if (keyURL == _authURL) {
// pull out the stored access token and store it in memory
_accountInfo = settings.value(key).value<DataServerAccountInfo>();
qCDebug(networking) << "Migrated an access token for" << qPrintable(keyURL.toString())
<< "from previous settings file";
}
}
settings.endGroup();
if (_accountInfo.getAccessToken().token.isEmpty()) {
qCWarning(networking) << "Unable to load account file. No existing account settings will be loaded.";
} else {
// persist the migrated settings to file
persistAccountToFile();
}
qCWarning(networking) << "Unable to load account file. No existing account settings will be loaded.";
}
if (_isAgent && !_accountInfo.getAccessToken().token.isEmpty() && !_accountInfo.hasProfile()) {
@ -199,6 +186,10 @@ void AccountManager::setAuthURL(const QUrl& authURL) {
refreshAccessToken();
}
if (isLoggedIn()) {
emit loginComplete(_authURL);
}
// tell listeners that the auth endpoint has changed
emit authEndpointChanged();
}
@ -804,6 +795,99 @@ void AccountManager::requestProfileError(QNetworkReply::NetworkError error) {
qCDebug(networking) << "AccountManager requestProfileError - " << error;
}
void AccountManager::requestAccountSettings() {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QUrl lockerURL = _authURL;
lockerURL.setPath("/api/v1/user/locker");
QNetworkRequest lockerRequest(lockerURL);
lockerRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
lockerRequest.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter());
lockerRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER, _accountInfo.getAccessToken().authorizationHeaderValue());
QNetworkReply* lockerReply = networkAccessManager.get(lockerRequest);
connect(lockerReply, &QNetworkReply::finished, this, &AccountManager::requestAccountSettingsFinished);
connect(lockerReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccountSettingsError(QNetworkReply::NetworkError)));
_settings.startedLoading();
}
void AccountManager::requestAccountSettingsFinished() {
QNetworkReply* lockerReply = reinterpret_cast<QNetworkReply*>(sender());
QJsonDocument jsonResponse = QJsonDocument::fromJson(lockerReply->readAll());
const QJsonObject& rootObject = jsonResponse.object();
if (rootObject.contains("status") && rootObject["status"].toString() == "success") {
if (rootObject.contains("data") && rootObject["data"].isObject()) {
_settings.unpack(rootObject["data"].toObject());
emit accountSettingsLoaded();
} else {
qCDebug(networking) << "Error in response for account settings: no data object";
QTimer::singleShot(PULL_SETTINGS_RETRY_INTERVAL, this, &AccountManager::requestAccountSettings);
}
} else {
qCDebug(networking) << "Error in response for account settings" << lockerReply->errorString();
QTimer::singleShot(PULL_SETTINGS_RETRY_INTERVAL, this, &AccountManager::requestAccountSettings);
}
}
void AccountManager::requestAccountSettingsError(QNetworkReply::NetworkError error) {
qCWarning(networking) << "Account settings request encountered an error" << error;
QTimer::singleShot(PULL_SETTINGS_RETRY_INTERVAL, this, &AccountManager::requestAccountSettings);
}
void AccountManager::postAccountSettings() {
if (_settings.lastChangeTimestamp() <= _lastSuccessfulSyncTimestamp && _lastSuccessfulSyncTimestamp != 0) {
// Nothing changed, skipping settings post
return;
}
if (!isLoggedIn()) {
qCWarning(networking) << "Can't post account settings: Not logged in";
return;
}
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QUrl lockerURL = _authURL;
lockerURL.setPath("/api/v1/user/locker");
QNetworkRequest lockerRequest(lockerURL);
lockerRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
lockerRequest.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter());
lockerRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
lockerRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER, _accountInfo.getAccessToken().authorizationHeaderValue());
_currentSyncTimestamp = _settings.lastChangeTimestamp();
QJsonObject dataObj;
dataObj.insert("locker", _settings.pack());
auto postData = QJsonDocument(dataObj).toJson(QJsonDocument::Compact);
QNetworkReply* lockerReply = networkAccessManager.put(lockerRequest, postData);
connect(lockerReply, &QNetworkReply::finished, this, &AccountManager::postAccountSettingsFinished);
connect(lockerReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(postAccountSettingsError(QNetworkReply::NetworkError)));
}
void AccountManager::postAccountSettingsFinished() {
QNetworkReply* lockerReply = reinterpret_cast<QNetworkReply*>(sender());
QJsonDocument jsonResponse = QJsonDocument::fromJson(lockerReply->readAll());
const QJsonObject& rootObject = jsonResponse.object();
if (rootObject.contains("status") && rootObject["status"].toString() == "success") {
_lastSuccessfulSyncTimestamp = _currentSyncTimestamp;
} else {
qCDebug(networking) << "Error in response for account settings post" << lockerReply->errorString();
}
}
void AccountManager::postAccountSettingsError(QNetworkReply::NetworkError error) {
qCWarning(networking) << "Post encountered an error" << error;
}
void AccountManager::generateNewKeypair(bool isUserKeypair, const QUuid& domainID) {
if (thread() != QThread::currentThread()) {

View file

@ -14,18 +14,19 @@
#include <QtCore/QByteArray>
#include <QtCore/QObject>
#include <QtCore/QTimer>
#include <QtCore/QUrl>
#include <QtNetwork/QNetworkReply>
#include <QUrlQuery>
#include <DependencyManager.h>
#include "AccountSettings.h"
#include "DataServerAccountInfo.h"
#include "NetworkingConstants.h"
#include "NetworkAccessManager.h"
#include "DataServerAccountInfo.h"
#include "SharedUtil.h"
#include <DependencyManager.h>
class JSONCallbackParameters {
public:
JSONCallbackParameters(QObject* callbackReceiver = nullptr,
@ -107,6 +108,8 @@ public:
void setConfigFileURL(const QString& fileURL) { _configFileURL = fileURL; }
void saveLoginStatus(bool isLoggedIn);
AccountSettings& getAccountSettings() { return _settings; }
public slots:
void requestAccessToken(const QString& login, const QString& password);
void requestAccessTokenWithSteam(QByteArray authSessionTicket);
@ -136,6 +139,7 @@ signals:
void logoutComplete();
void newKeypair();
void limitedCommerceChanged();
void accountSettingsLoaded();
private slots:
void handleKeypairGenerationError();
@ -145,6 +149,13 @@ private slots:
void publicKeyUploadFailed(QNetworkReply* reply);
void generateNewKeypair(bool isUserKeypair = true, const QUuid& domainID = QUuid());
void requestAccountSettings();
void requestAccountSettingsFinished();
void requestAccountSettingsError(QNetworkReply::NetworkError error);
void postAccountSettings();
void postAccountSettingsFinished();
void postAccountSettingsError(QNetworkReply::NetworkError error);
private:
AccountManager(AccountManager const& other) = delete;
void operator=(AccountManager const& other) = delete;
@ -170,6 +181,11 @@ private:
bool _limitedCommerce { false };
QString _configFileURL;
AccountSettings _settings;
quint64 _currentSyncTimestamp { 0 };
quint64 _lastSuccessfulSyncTimestamp { 0 };
QTimer* _postSettingsTimer { nullptr };
};
#endif // hifi_AccountManager_h

View file

@ -0,0 +1,55 @@
//
// AccountSettings.cpp
// libraries/networking/src
//
// Created by Clement Brisset on 9/12/19.
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AccountSettings.h"
#include <QJsonDocument>
#include <QJsonObject>
#include "NetworkLogging.h"
#include "SharedUtil.h"
static QString HOME_LOCATION_KEY { "home_location" };
QJsonObject AccountSettings::pack() {
QJsonObject data;
QReadLocker lock(&_settingsLock);
data.insert(HOME_LOCATION_KEY, _homeLocation);
return data;
}
void AccountSettings::unpack(QJsonObject data) {
QWriteLocker lock(&_settingsLock);
_lastChangeTimestamp = usecTimestampNow();
auto it = data.find(HOME_LOCATION_KEY);
_homeLocationState = it != data.end() && it->isString() ? Loaded : NotPresent;
_homeLocation = _homeLocationState == Loaded ? it->toString() : "";
}
void AccountSettings::setHomeLocation(QString homeLocation) {
QWriteLocker lock(&_settingsLock);
if (homeLocation != _homeLocation) {
_lastChangeTimestamp = usecTimestampNow();
}
_homeLocation = homeLocation;
}
void AccountSettings::startedLoading() {
_homeLocationState = Loading;
}
void AccountSettings::loggedOut() {
_homeLocationState = LoggedOut;
}

View file

@ -0,0 +1,47 @@
//
// AccountSettings.h
// libraries/networking/src
//
// Created by Clement Brisset on 9/12/19.
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_AccountSettings_h
#define hifi_AccountSettings_h
#include <QJsonObject>
#include <QReadWriteLock>
#include <QString>
class AccountSettings {
public:
enum State {
LoggedOut,
Loading,
Loaded,
NotPresent
};
void loggedOut();
void startedLoading();
quint64 lastChangeTimestamp() const { return _lastChangeTimestamp; }
QJsonObject pack();
void unpack(QJsonObject data);
State homeLocationState() const { QReadLocker lock(&_settingsLock); return _homeLocationState; }
QString getHomeLocation() const { QReadLocker lock(&_settingsLock); return _homeLocation; }
void setHomeLocation(QString homeLocation);
private:
mutable QReadWriteLock _settingsLock;
quint64 _lastChangeTimestamp { 0 };
State _homeLocationState { LoggedOut };
QString _homeLocation;
};
#endif /* hifi_AccountSettings_h */