Add account settings to the AccountManager

This commit is contained in:
Clement 2019-09-17 18:39:31 -07:00
parent 610e0c3d82
commit 86196cfd62
4 changed files with 261 additions and 31 deletions

View file

@ -87,6 +87,14 @@ AccountManager::AccountManager(UserAgentGetter userAgentGetter) :
qRegisterMetaType<AccountManagerAuth::Type>();
connect(this, &AccountManager::loginComplete, this, &AccountManager::uploadPublicKey);
connect(this, &AccountManager::loginComplete, this, &AccountManager::requestAccountSettings);
static int POST_SETTINGS_INTERVAL = 10 * MSECS_PER_SECOND;
_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);
}
const QString DOUBLE_SLASH_SUBSTITUTE = "slashslash";
@ -160,33 +168,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 +181,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 +790,94 @@ 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)));
}
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";
}
} else {
// TODO: error handling
qCDebug(networking) << "Error in response for account settings" << lockerReply->errorString();
}
}
void AccountManager::requestAccountSettingsError(QNetworkReply::NetworkError error) {
// TODO: error handling
qCWarning(networking) << "Account settings request encountered an error" << error;
}
void AccountManager::postAccountSettings() {
if (!_settings.somethingChanged()) {
// Nothing changed, skipping settings post
return;
}
if (!isLoggedIn()) {
qCWarning(networking) << "Can't post account settings: Not logged in";
}
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());
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") {
// TODO: error handling
qCDebug(networking) << "Error in response for account settings post" << lockerReply->errorString();
}
}
void AccountManager::postAccountSettingsError(QNetworkReply::NetworkError error) {
// TODO: error handling
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,9 @@ private:
bool _limitedCommerce { false };
QString _configFileURL;
AccountSettings _settings;
QTimer* _postSettingsTimer;
};
#endif // hifi_AccountManager_h

View file

@ -0,0 +1,83 @@
//
// 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"
// Examples:
//static QString SOME_STRING_KEY { "some_string" };
//static QString SOME_INT_KEY { "some_int" };
//static QString SOME_BOOL_KEY { "some_bool" };
// Examples:
//QString AccountSettings::DEFAULT_SOME_STRING { "" };
//int AccountSettings::DEFAULT_SOME_INT { 17 };
//bool AccountSettings::DEFAULT_SOME_BOOL { true };
QJsonObject AccountSettings::pack() {
QJsonObject data;
QReadLocker lock(&_settingsLock);
// Examples:
// data.insert(SOME_STRING_KEY, _someString);
// data.insert(SOME_INT_KEY, _someInt);
// data.insert(SOME_BOOL_KEY, _someBool);
return data;
}
void AccountSettings::unpack(QJsonObject data) {
QWriteLocker lock(&_settingsLock);
// Examples:
// auto it = data.find(SOME_STRING_KEY);
// _hasSomeString = it != data.end() && it->isString();
// _someString = _hasSomeString ? it->toString() : DEFAULT_SOME_STRING;
//
// it = data.find(SOME_INT_KEY);
// _hasSomeInt = it != data.end() && it->isDouble();
// _someInt = _hasSomeInt ? it->toInt() : DEFAULT_SOME_INT;
//
// it = data.find(SOME_BOOL_KEY);
// _hasSomeBool = it != data.end() && it->isBool();
// _someBool = _hasSomeBool ? it->toBool() : DEFAULT_SOME_BOOL;
_somethingChanged = false;
}
// Examples:
//void AccountSettings::setSomeString(QString someString) {
// QWriteLocker lock(&_settingsLock);
// if (someString != _someString) {
// _somethingChanged = true;
// }
// _someString = someString;
//}
//
//void AccountSettings::setSomeInt(int someInt) {
// QWriteLocker lock(&_settingsLock);
// if (someInt != _someInt) {
// _somethingChanged = true;
// }
// _someInt = someInt;
//}
//
//void AccountSettings::setSomeBool(bool someBool) {
// QWriteLocker lock(&_settingsLock);
// if (someBool != _someBool) {
// _somethingChanged = true;
// }
// _someBool = someBool;
//}

View file

@ -0,0 +1,59 @@
//
// 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:
// Examples:
// static QString DEFAULT_SOME_STRING;
// static int DEFAULT_SOME_INT;
// static bool DEFAULT_SOME_BOOL;
bool somethingChanged() const { return _somethingChanged; }
QJsonObject pack();
void unpack(QJsonObject data);
// Examples:
// bool hasSomeString() const { QReadLocker lock(&_settingsLock); return _hasSomeString; }
// QString getSomeString() const { QReadLocker lock(&_settingsLock); return _someString; }
// void setSomeString(QString someString);
//
// bool hasSomeInt() const { QReadLocker lock(&_settingsLock); return _hasSomeInt; }
// int getSomeInt() const { QReadLocker lock(&_settingsLock); return _someInt; }
// void setSomeInt(int someInt);
//
// bool hasSomeBool() const { QReadLocker lock(&_settingsLock); return _hasSomeBool; }
// bool getSomeBool() const { QReadLocker lock(&_settingsLock); return _someBool; }
// void setSomeBool(bool someBool);
private:
mutable QReadWriteLock _settingsLock;
bool _somethingChanged { false };
// Examples:
// bool _hasSomeString { false };
// bool _hasSomeInt { false };
// bool _hasSomeBool { false };
// Examples:
// QString _someString { DEFAULT_SOME_STRING };
// int _someInt { DEFAULT_SOME_INT };
// bool _someBool { DEFAULT_SOME_BOOL };
};
#endif /* hifi_AccountSettings_h */