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

This commit is contained in:
barnold1953 2014-06-13 15:06:11 -07:00
commit 7e4317d5d2
6 changed files with 163 additions and 106 deletions

View file

@ -132,7 +132,7 @@ Menu::Menu() :
toggleLoginMenuItem();
// connect to the appropriate slots of the AccountManager so that we can change the Login/Logout menu item
connect(&accountManager, &AccountManager::accessTokenChanged, this, &Menu::toggleLoginMenuItem);
connect(&accountManager, &AccountManager::profileChanged, this, &Menu::toggleLoginMenuItem);
connect(&accountManager, &AccountManager::logoutComplete, this, &Menu::toggleLoginMenuItem);
addDisabledActionAndSeparator(fileMenu, "Scripts");
@ -331,7 +331,7 @@ Menu::Menu() :
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, "None", 0, true));
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::SimpleShadows, 0, false));
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::CascadedShadows, 0, false));
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, true);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::BuckyBalls, 0, false);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Particles, 0, true);
@ -411,7 +411,7 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::DisableNackPackets, 0, false);
addDisabledActionAndSeparator(developerMenu, "Testing");
QMenu* timingMenu = developerMenu->addMenu("Timing and Statistics Tools");
QMenu* perfTimerMenu = timingMenu->addMenu("Performance Timer");
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayTimingDetails, 0, true);
@ -466,7 +466,7 @@ Menu::Menu() :
false,
appInstance->getAudio(),
SLOT(toggleToneInjection()));
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScope,
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScope,
Qt::CTRL | Qt::Key_P, false,
appInstance->getAudio(),
SLOT(toggleScope()));
@ -1779,4 +1779,3 @@ QString Menu::getSnapshotsLocation() const {
}
return _snapshotsLocation;
}

View file

@ -23,7 +23,7 @@ XmppClient::XmppClient() :
_xmppMUCManager()
{
AccountManager& accountManager = AccountManager::getInstance();
connect(&accountManager, SIGNAL(accessTokenChanged()), this, SLOT(connectToServer()));
connect(&accountManager, SIGNAL(profileChanged()), this, SLOT(connectToServer()));
connect(&accountManager, SIGNAL(logoutComplete()), this, SLOT(disconnectFromServer()));
}

View file

@ -55,13 +55,13 @@ AccountManager::AccountManager() :
{
qRegisterMetaType<OAuthAccessToken>("OAuthAccessToken");
qRegisterMetaTypeStreamOperators<OAuthAccessToken>("OAuthAccessToken");
qRegisterMetaType<DataServerAccountInfo>("DataServerAccountInfo");
qRegisterMetaTypeStreamOperators<DataServerAccountInfo>("DataServerAccountInfo");
qRegisterMetaType<QNetworkAccessManager::Operation>("QNetworkAccessManager::Operation");
qRegisterMetaType<JSONCallbackParameters>("JSONCallbackParameters");
connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged);
}
@ -70,18 +70,18 @@ const QString DOUBLE_SLASH_SUBSTITUTE = "slashslash";
void AccountManager::logout() {
// a logout means we want to delete the DataServerAccountInfo we currently have for this URL, in-memory and in file
_accountInfo = DataServerAccountInfo();
emit balanceChanged(0);
connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged);
QSettings settings;
settings.beginGroup(ACCOUNTS_GROUP);
QString keyURLString(_authURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE));
settings.remove(keyURLString);
qDebug() << "Removed account info for" << _authURL << "from in-memory accounts and .ini file";
emit logoutComplete();
// the username has changed to blank
emit usernameChanged(QString());
@ -93,7 +93,7 @@ void AccountManager::updateBalance() {
JSONCallbackParameters callbackParameters;
callbackParameters.jsonCallbackReceiver = &_accountInfo;
callbackParameters.jsonCallbackMethod = "setBalanceFromJSON";
authenticatedRequest("/api/v1/wallets/mine", QNetworkAccessManager::GetOperation, callbackParameters);
}
}
@ -105,28 +105,33 @@ void AccountManager::accountInfoBalanceChanged(qint64 newBalance) {
void AccountManager::setAuthURL(const QUrl& authURL) {
if (_authURL != authURL) {
_authURL = authURL;
qDebug() << "URL for node authentication has been changed to" << qPrintable(_authURL.toString());
qDebug() << "Re-setting authentication flow.";
// check if there are existing access tokens to load from settings
QSettings 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("slashslash", "//"));
if (keyURL == _authURL) {
// pull out the stored access token and store it in memory
_accountInfo = settings.value(key).value<DataServerAccountInfo>();
qDebug() << "Found a data-server access token for" << qPrintable(keyURL.toString());
emit accessTokenChanged();
// profile info isn't guaranteed to be saved too
if (_accountInfo.hasProfile()) {
emit profileChanged();
} else {
requestProfile();
}
}
}
// tell listeners that the auth endpoint has changed
emit authEndpointChanged();
}
@ -147,36 +152,36 @@ void AccountManager::authenticatedRequest(const QString& path, QNetworkAccessMan
void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager::Operation operation,
const JSONCallbackParameters& callbackParams,
const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart) {
if (!_networkAccessManager) {
_networkAccessManager = new QNetworkAccessManager(this);
}
if (hasValidAccessToken()) {
QNetworkRequest authenticatedRequest;
QUrl requestURL = _authURL;
if (path.startsWith("/")) {
requestURL.setPath(path);
} else {
requestURL.setPath("/" + path);
}
requestURL.setQuery("access_token=" + _accountInfo.getAccessToken().token);
authenticatedRequest.setUrl(requestURL);
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
qDebug() << "Making an authenticated request to" << qPrintable(requestURL.toString());
if (!dataByteArray.isEmpty()) {
qDebug() << "The POST/PUT body -" << QString(dataByteArray);
}
}
QNetworkReply* networkReply = NULL;
switch (operation) {
case QNetworkAccessManager::GetOperation:
networkReply = _networkAccessManager->get(authenticatedRequest);
@ -198,24 +203,24 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager::
networkReply = _networkAccessManager->put(authenticatedRequest, dataByteArray);
}
}
break;
default:
// other methods not yet handled
break;
}
if (networkReply) {
if (!callbackParams.isEmpty()) {
// if we have information for a callback, insert the callbackParams into our local map
_pendingCallbackMap.insert(networkReply, callbackParams);
if (callbackParams.updateReciever && !callbackParams.updateSlot.isEmpty()) {
callbackParams.updateReciever->connect(networkReply, SIGNAL(uploadProgress(qint64, qint64)),
callbackParams.updateSlot.toStdString().c_str());
}
}
// if we ended up firing of a request, hook up to it now
connect(networkReply, SIGNAL(finished()), SLOT(processReply()));
}
@ -224,7 +229,7 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager::
void AccountManager::processReply() {
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
if (requestReply->error() == QNetworkReply::NoError) {
passSuccessToCallback(requestReply);
} else {
@ -235,17 +240,17 @@ void AccountManager::processReply() {
void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) {
QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());
JSONCallbackParameters callbackParams = _pendingCallbackMap.value(requestReply);
if (callbackParams.jsonCallbackReceiver) {
// invoke the right method on the callback receiver
QMetaObject::invokeMethod(callbackParams.jsonCallbackReceiver, qPrintable(callbackParams.jsonCallbackMethod),
Q_ARG(const QJsonObject&, jsonResponse.object()));
// remove the related reply-callback group from the map
_pendingCallbackMap.remove(requestReply);
} else {
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
qDebug() << "Received JSON response from data-server that has no matching callback.";
@ -256,13 +261,13 @@ void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) {
void AccountManager::passErrorToCallback(QNetworkReply* requestReply) {
JSONCallbackParameters callbackParams = _pendingCallbackMap.value(requestReply);
if (callbackParams.errorCallbackReceiver) {
// invoke the right method on the callback receiver
QMetaObject::invokeMethod(callbackParams.errorCallbackReceiver, qPrintable(callbackParams.errorCallbackMethod),
Q_ARG(QNetworkReply::NetworkError, requestReply->error()),
Q_ARG(const QString&, requestReply->errorString()));
// remove the related reply-callback group from the map
_pendingCallbackMap.remove(requestReply);
} else {
@ -274,12 +279,12 @@ void AccountManager::passErrorToCallback(QNetworkReply* requestReply) {
}
bool AccountManager::hasValidAccessToken() {
if (_accountInfo.getAccessToken().token.isEmpty() || _accountInfo.getAccessToken().isExpired()) {
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
qDebug() << "An access token is required for requests to" << qPrintable(_authURL.toString());
}
return false;
} else {
return true;
@ -288,12 +293,12 @@ bool AccountManager::hasValidAccessToken() {
bool AccountManager::checkAndSignalForAccessToken() {
bool hasToken = hasValidAccessToken();
if (!hasToken) {
// emit a signal so somebody can call back to us and request an access token given a username and password
emit authRequired();
}
return hasToken;
}
@ -304,36 +309,36 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas
}
QNetworkRequest request;
QUrl grantURL = _authURL;
grantURL.setPath("/oauth/token");
const QString ACCOUNT_MANAGER_REQUESTED_SCOPE = "owner";
QByteArray postData;
postData.append("grant_type=password&");
postData.append("username=" + login + "&");
postData.append("password=" + QUrl::toPercentEncoding(password) + "&");
postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE);
request.setUrl(grantURL);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QNetworkReply* requestReply = _networkAccessManager->post(request, postData);
connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestFinished);
connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError)));
connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished);
connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError)));
}
void AccountManager::requestFinished() {
void AccountManager::requestAccessTokenFinished() {
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());
const QJsonObject& rootObject = jsonResponse.object();
if (!rootObject.contains("error")) {
// construct an OAuthAccessToken from the json object
if (!rootObject.contains("access_token") || !rootObject.contains("expires_in")
|| !rootObject.contains("token_type")) {
// TODO: error handling - malformed token response
@ -342,23 +347,21 @@ void AccountManager::requestFinished() {
// clear the path from the response URL so we have the right root URL for this access token
QUrl rootURL = requestReply->url();
rootURL.setPath("");
qDebug() << "Storing an account with access-token for" << qPrintable(rootURL.toString());
_accountInfo = DataServerAccountInfo(rootObject);
_accountInfo = DataServerAccountInfo();
_accountInfo.setAccessTokenFromJSON(rootObject);
emit loginComplete(rootURL);
// the username has changed to whatever came back
emit usernameChanged(_accountInfo.getUsername());
// we have found or requested an access token
emit accessTokenChanged();
// store this access token into the local settings
QSettings localSettings;
localSettings.beginGroup(ACCOUNTS_GROUP);
localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE),
QVariant::fromValue(_accountInfo));
requestProfile();
}
} else {
// TODO: error handling
@ -367,7 +370,53 @@ void AccountManager::requestFinished() {
}
}
void AccountManager::requestError(QNetworkReply::NetworkError error) {
void AccountManager::requestAccessTokenError(QNetworkReply::NetworkError error) {
// TODO: error handling
qDebug() << "AccountManager requestError - " << error;
}
void AccountManager::requestProfile() {
if (!_networkAccessManager) {
_networkAccessManager = new QNetworkAccessManager(this);
}
QUrl profileURL = _authURL;
profileURL.setPath("/api/v1/users/profile");
profileURL.setQuery("access_token=" + _accountInfo.getAccessToken().token);
QNetworkReply* profileReply = _networkAccessManager->get(QNetworkRequest(profileURL));
connect(profileReply, &QNetworkReply::finished, this, &AccountManager::requestProfileFinished);
connect(profileReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestProfileError(QNetworkReply::NetworkError)));
}
void AccountManager::requestProfileFinished() {
QNetworkReply* profileReply = reinterpret_cast<QNetworkReply*>(sender());
QJsonDocument jsonResponse = QJsonDocument::fromJson(profileReply->readAll());
const QJsonObject& rootObject = jsonResponse.object();
if (rootObject.contains("status") && rootObject["status"].toString() == "success") {
_accountInfo.setProfileInfoFromJSON(rootObject);
emit profileChanged();
// the username has changed to whatever came back
emit usernameChanged(_accountInfo.getUsername());
// store the whole profile into the local settings
QUrl rootURL = profileReply->url();
rootURL.setPath("");
QSettings localSettings;
localSettings.beginGroup(ACCOUNTS_GROUP);
localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE),
QVariant::fromValue(_accountInfo));
} else {
// TODO: error handling
qDebug() << "Error in response for profile";
}
}
void AccountManager::requestProfileError(QNetworkReply::NetworkError error) {
// TODO: error handling
qDebug() << "AccountManager requestProfileError - " << error;
}

View file

@ -23,9 +23,9 @@
class JSONCallbackParameters {
public:
JSONCallbackParameters();
bool isEmpty() const { return !jsonCallbackReceiver && !errorCallbackReceiver; }
QObject* jsonCallbackReceiver;
QString jsonCallbackMethod;
QObject* errorCallbackReceiver;
@ -38,30 +38,33 @@ class AccountManager : public QObject {
Q_OBJECT
public:
static AccountManager& getInstance();
void authenticatedRequest(const QString& path,
QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
const JSONCallbackParameters& callbackParams = JSONCallbackParameters(),
const QByteArray& dataByteArray = QByteArray(),
QHttpMultiPart* dataMultiPart = NULL);
const QUrl& getAuthURL() const { return _authURL; }
void setAuthURL(const QUrl& authURL);
bool hasAuthEndpoint() { return !_authURL.isEmpty(); }
bool isLoggedIn() { return !_authURL.isEmpty() && hasValidAccessToken(); }
bool hasValidAccessToken();
Q_INVOKABLE bool checkAndSignalForAccessToken();
void requestAccessToken(const QString& login, const QString& password);
void requestProfile();
const DataServerAccountInfo& getAccountInfo() const { return _accountInfo; }
void destroy() { delete _networkAccessManager; }
public slots:
void requestFinished();
void requestError(QNetworkReply::NetworkError error);
void requestAccessTokenFinished();
void requestProfileFinished();
void requestAccessTokenError(QNetworkReply::NetworkError error);
void requestProfileError(QNetworkReply::NetworkError error);
void logout();
void updateBalance();
void accountInfoBalanceChanged(qint64 newBalance);
@ -69,7 +72,7 @@ signals:
void authRequired();
void authEndpointChanged();
void usernameChanged(const QString& username);
void accessTokenChanged();
void profileChanged();
void loginComplete(const QUrl& authURL);
void loginFailed();
void logoutComplete();
@ -80,19 +83,19 @@ private:
AccountManager();
AccountManager(AccountManager const& other); // not implemented
void operator=(AccountManager const& other); // not implemented
void passSuccessToCallback(QNetworkReply* reply);
void passErrorToCallback(QNetworkReply* reply);
Q_INVOKABLE void invokedRequest(const QString& path, QNetworkAccessManager::Operation operation,
const JSONCallbackParameters& callbackParams,
const QByteArray& dataByteArray,
QHttpMultiPart* dataMultiPart);
QUrl _authURL;
QNetworkAccessManager* _networkAccessManager;
QMap<QNetworkReply*, JSONCallbackParameters> _pendingCallbackMap;
DataServerAccountInfo _accountInfo;
};

View file

@ -21,20 +21,7 @@ DataServerAccountInfo::DataServerAccountInfo() :
_balance(0),
_hasBalance(false)
{
}
DataServerAccountInfo::DataServerAccountInfo(const QJsonObject& jsonObject) :
_accessToken(jsonObject),
_username(),
_xmppPassword(),
_balance(0),
_hasBalance(false)
{
QJsonObject userJSONObject = jsonObject["user"].toObject();
setUsername(userJSONObject["username"].toString());
setXMPPPassword(userJSONObject["xmpp_password"].toString());
setDiscourseApiKey(userJSONObject["discourse_api_key"].toString());
}
DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherInfo) {
@ -54,7 +41,7 @@ DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountI
void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) {
using std::swap;
swap(_accessToken, otherInfo._accessToken);
swap(_username, otherInfo._username);
swap(_xmppPassword, otherInfo._xmppPassword);
@ -63,10 +50,14 @@ void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) {
swap(_hasBalance, otherInfo._hasBalance);
}
void DataServerAccountInfo::setAccessTokenFromJSON(const QJsonObject& jsonObject) {
_accessToken = OAuthAccessToken(jsonObject);
}
void DataServerAccountInfo::setUsername(const QString& username) {
if (_username != username) {
_username = username;
qDebug() << "Username changed to" << username;
}
}
@ -87,7 +78,7 @@ void DataServerAccountInfo::setBalance(qint64 balance) {
if (!_hasBalance || _balance != balance) {
_balance = balance;
_hasBalance = true;
emit balanceChanged(_balance);
}
}
@ -99,6 +90,17 @@ void DataServerAccountInfo::setBalanceFromJSON(const QJsonObject& jsonObject) {
}
}
bool DataServerAccountInfo::hasProfile() const {
return _username.length() > 0;
}
void DataServerAccountInfo::setProfileInfoFromJSON(const QJsonObject& jsonObject) {
QJsonObject user = jsonObject["data"].toObject()["user"].toObject();
setUsername(user["username"].toString());
setXMPPPassword(user["xmpp_password"].toString());
setDiscourseApiKey(user["discourse_api_key"].toString());
}
QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) {
out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey;
return out;

View file

@ -22,12 +22,12 @@ class DataServerAccountInfo : public QObject {
Q_OBJECT
public:
DataServerAccountInfo();
DataServerAccountInfo(const QJsonObject& jsonObject);
DataServerAccountInfo(const DataServerAccountInfo& otherInfo);
DataServerAccountInfo& operator=(const DataServerAccountInfo& otherInfo);
const OAuthAccessToken& getAccessToken() const { return _accessToken; }
void setAccessTokenFromJSON(const QJsonObject& jsonObject);
const QString& getUsername() const { return _username; }
void setUsername(const QString& username);
@ -36,20 +36,24 @@ public:
const QString& getDiscourseApiKey() const { return _discourseApiKey; }
void setDiscourseApiKey(const QString& discourseApiKey);
qint64 getBalance() const { return _balance; }
void setBalance(qint64 balance);
bool hasBalance() const { return _hasBalance; }
void setHasBalance(bool hasBalance) { _hasBalance = hasBalance; }
Q_INVOKABLE void setBalanceFromJSON(const QJsonObject& jsonObject);
bool hasProfile() const;
void setProfileInfoFromJSON(const QJsonObject& jsonObject);
friend QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info);
friend QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info);
signals:
qint64 balanceChanged(qint64 newBalance);
private:
void swap(DataServerAccountInfo& otherInfo);
OAuthAccessToken _accessToken;
QString _username;
QString _xmppPassword;