diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 25233aa024..4ab8e46385 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -70,6 +70,9 @@ DomainServer::DomainServer(int argc, char* argv[]) : NodeList* nodeList = NodeList::createInstance(NodeType::DomainServer, domainServerPort); + // create a random UUID for this session for the domain-server + nodeList->setSessionUUID(QUuid::createUuid()); + connect(nodeList, &NodeList::nodeAdded, this, &DomainServer::nodeAdded); connect(nodeList, &NodeList::nodeKilled, this, &DomainServer::nodeKilled); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ee0f5ec9e0..b49abf9350 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -207,8 +207,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_voxels, SLOT(nodeKilled(SharedNodePointer))); connect(nodeList, &NodeList::uuidChanged, this, &Application::updateWindowTitle); - connect(&AccountManager::getInstance(), SIGNAL(authenticationRequiredForRootURL(const QUrl&)), - Menu::getInstance(), SLOT(showLoginForRootURL(const QUrl&))); + connect(&AccountManager::getInstance(), SIGNAL(authenticationRequired()), + Menu::getInstance(), SLOT(loginForCurrentDomain())); // read the ApplicationInfo.ini file for Name/Version/Domain information QSettings applicationInfo("resources/info/ApplicationInfo.ini", QSettings::IniFormat); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 07d4cc14a8..0cdd859ba6 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -739,10 +739,6 @@ const int QLINE_MINIMUM_WIDTH = 400; const float DIALOG_RATIO_OF_WINDOW = 0.30f; void Menu::loginForCurrentDomain() { - showLoginForRootURL(NodeList::getInstance()->getDomainInfo().getRootAuthenticationURL()); -} - -void Menu::showLoginForRootURL(const QUrl& rootURL) { QDialog loginDialog(Application::getInstance()->getWindow()); loginDialog.setWindowTitle("Login"); @@ -765,14 +761,14 @@ void Menu::showLoginForRootURL(const QUrl& rootURL) { loginDialog.connect(buttons, SIGNAL(accepted()), SLOT(accept())); loginDialog.connect(buttons, SIGNAL(rejected()), SLOT(reject())); layout->addWidget(buttons); - + int dialogReturn = loginDialog.exec(); - + if (dialogReturn == QDialog::Accepted && !usernameLineEdit->text().isEmpty() && !passwordLineEdit->text().isEmpty()) { // attempt to get an access token given this username and password - AccountManager::getInstance().requestAccessToken(rootURL, usernameLineEdit->text(), passwordLineEdit->text()); + AccountManager::getInstance().requestAccessToken(usernameLineEdit->text(), passwordLineEdit->text()); } - + sendFakeEnterEvent(); } diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 4383b0885a..30cd0db857 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -102,7 +102,6 @@ public: public slots: void loginForCurrentDomain(); - void showLoginForRootURL(const QUrl& rootURL); void bandwidthDetails(); void voxelStatsDetails(); void lodTools(); diff --git a/libraries/shared/src/AccountManager.cpp b/libraries/shared/src/AccountManager.cpp index 633590c001..58b8b21c9e 100644 --- a/libraries/shared/src/AccountManager.cpp +++ b/libraries/shared/src/AccountManager.cpp @@ -18,6 +18,9 @@ #include "AccountManager.h" +QMap AccountManager::_accessTokens = QMap(); +QMap AccountManager::_clientIDs = QMap(); + AccountManager& AccountManager::getInstance() { static AccountManager sharedInstance; return sharedInstance; @@ -26,42 +29,67 @@ AccountManager& AccountManager::getInstance() { const QString DEFAULT_NODE_AUTH_OAUTH_CLIENT_ID = "12b7b18e7b8c118707b84ff0735e57a4473b5b0577c2af44734f02e08d02829c"; AccountManager::AccountManager() : + _rootURL(), _username(), - _accessTokens(), - _clientIDs(), _networkAccessManager(NULL) { _clientIDs.insert(DEFAULT_NODE_AUTH_URL, DEFAULT_NODE_AUTH_OAUTH_CLIENT_ID); } -bool AccountManager::hasValidAccessTokenForRootURL(const QUrl &rootURL) { - OAuthAccessToken accessToken = _accessTokens.value(rootURL); +void AccountManager::authenticatedGetRequest(const QString& path, const QObject *successReceiver, const char *successMethod, + const QObject* errorReceiver, const char* errorMethod) { + if (_networkAccessManager && hasValidAccessToken()) { + QNetworkRequest authenticatedRequest; + + QUrl requestURL = _rootURL; + requestURL.setPath(path); + requestURL.setQuery("access_token=" + _accessTokens.value(_rootURL).token); + + authenticatedRequest.setUrl(requestURL); + + qDebug() << "Making an authenticated GET request to" << requestURL; + + QNetworkReply* networkReply = _networkAccessManager->get(authenticatedRequest); + connect(networkReply, SIGNAL(finished()), successReceiver, successMethod); + + if (errorReceiver && errorMethod) { + connect(networkReply, SIGNAL(error()), errorReceiver, errorMethod); + } + } +} + +bool AccountManager::hasValidAccessToken() { + OAuthAccessToken accessToken = _accessTokens.value(_rootURL); if (accessToken.token.isEmpty() || accessToken.isExpired()) { - qDebug() << "An access token is required for requests to" << qPrintable(rootURL.toString()); + qDebug() << "An access token is required for requests to" << qPrintable(_rootURL.toString()); return false; } else { return true; } } -bool AccountManager::checkAndSignalForAccessTokenForRootURL(const QUrl& rootURL) { - if (!hasValidAccessTokenForRootURL(rootURL)) { +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 authenticationRequiredForRootURL(rootURL); + emit authenticationRequired(); } + + return hasToken; } -void AccountManager::requestAccessToken(const QUrl& rootURL, const QString& username, const QString& password) { +void AccountManager::requestAccessToken(const QString& username, const QString& password) { if (_networkAccessManager) { - if (_clientIDs.contains(rootURL)) { + if (_clientIDs.contains(_rootURL)) { QNetworkRequest request; - QUrl grantURL = rootURL; + QUrl grantURL = _rootURL; grantURL.setPath("/oauth/token"); QByteArray postData; - postData.append("client_id=" + _clientIDs.value(rootURL) + "&"); + postData.append("client_id=" + _clientIDs.value(_rootURL) + "&"); postData.append("grant_type=password&"); postData.append("username=" + username + "&"); postData.append("password=" + password); @@ -73,7 +101,7 @@ void AccountManager::requestAccessToken(const QUrl& rootURL, const QString& user connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestFinished); connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError))); } else { - qDebug() << "Client ID for OAuth authorization at" << rootURL.toString() << "is unknown. Cannot authenticate."; + qDebug() << "Client ID for OAuth authorization at" << _rootURL.toString() << "is unknown. Cannot authenticate."; } } } @@ -96,7 +124,9 @@ void AccountManager::requestFinished() { QUrl rootURL = requestReply->url(); rootURL.setPath(""); - _accessTokens.insert(requestReply->url(), OAuthAccessToken(rootObject)); + qDebug() << "Storing an access token for" << rootURL; + + _accessTokens.insert(rootURL, OAuthAccessToken(rootObject)); } } else { // TODO: error handling @@ -105,5 +135,6 @@ void AccountManager::requestFinished() { } void AccountManager::requestError(QNetworkReply::NetworkError error) { - + // TODO: error handling + qDebug() << "AccountManager requestError - " << error; } \ No newline at end of file diff --git a/libraries/shared/src/AccountManager.h b/libraries/shared/src/AccountManager.h index 5863e702b7..32f8a0d9c4 100644 --- a/libraries/shared/src/AccountManager.h +++ b/libraries/shared/src/AccountManager.h @@ -22,10 +22,16 @@ class AccountManager : public QObject { public: static AccountManager& getInstance(); - bool hasValidAccessTokenForRootURL(const QUrl& rootURL); - bool checkAndSignalForAccessTokenForRootURL(const QUrl& rootURL); + void authenticatedGetRequest(const QString& path, + const QObject* successReceiver, const char* successMethod, + const QObject* errorReceiver = 0, const char* errorMethod = NULL); - void requestAccessToken(const QUrl& rootURL, const QString& username, const QString& password); + void setRootURL(const QUrl& rootURL) { _rootURL = rootURL; } + + bool hasValidAccessToken(); + bool checkAndSignalForAccessToken(); + + void requestAccessToken(const QString& username, const QString& password); const QString& getUsername() const { return _username; } void setUsername(const QString& username) { _username = username; } @@ -35,16 +41,18 @@ public slots: void requestFinished(); void requestError(QNetworkReply::NetworkError error); signals: - void authenticationRequiredForRootURL(const QUrl& rootURL); + void authenticationRequired(); private: AccountManager(); AccountManager(AccountManager const& other); // not implemented void operator=(AccountManager const& other); // not implemented + QUrl _rootURL; QString _username; - QMap _accessTokens; - QMap _clientIDs; QNetworkAccessManager* _networkAccessManager; + + static QMap _accessTokens; + static QMap _clientIDs; }; #endif /* defined(__hifi__AccountManager__) */ diff --git a/libraries/shared/src/DomainInfo.cpp b/libraries/shared/src/DomainInfo.cpp index 69651231a5..2feab77ad2 100644 --- a/libraries/shared/src/DomainInfo.cpp +++ b/libraries/shared/src/DomainInfo.cpp @@ -6,23 +6,35 @@ // Copyright (c) 2014 HighFidelity, Inc. All rights reserved. // +#include + #include "DomainInfo.h" DomainInfo::DomainInfo() : + _uuid(), _sockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)), _connectionSecret(), _registrationToken(), - _rootAuthenticationURL() + _rootAuthenticationURL(), + _publicKey() { } void DomainInfo::reset() { + _uuid = QUuid(); _hostname = QString(); _sockAddr.setAddress(QHostAddress::Null); _connectionSecret = QString(); _registrationToken = QString(); _rootAuthenticationURL = QUrl(); + _publicKey = QString(); +} + +void DomainInfo::parseAuthInformationFromJsonObject(const QJsonObject& jsonObject) { + _connectionSecret = QUuid(jsonObject["connection_uuid"].toString()); + _registrationToken = jsonObject["registration_token"].toString(); + _publicKey = jsonObject["public_key"].toString(); } void DomainInfo::setHostname(const QString& hostname) { diff --git a/libraries/shared/src/DomainInfo.h b/libraries/shared/src/DomainInfo.h index b42545f980..6f72d1845f 100644 --- a/libraries/shared/src/DomainInfo.h +++ b/libraries/shared/src/DomainInfo.h @@ -24,6 +24,11 @@ class DomainInfo : public QObject { public: DomainInfo(); + void parseAuthInformationFromJsonObject(const QJsonObject& jsonObject); + + const QUuid& getUUID() const { return _uuid; } + void setUUID(const QUuid& uuid) { _uuid = uuid; } + const QString& getHostname() const { return _hostname; } void setHostname(const QString& hostname); @@ -51,11 +56,13 @@ signals: private: void reset(); + QUuid _uuid; QString _hostname; HifiSockAddr _sockAddr; QUuid _connectionSecret; QString _registrationToken; QUrl _rootAuthenticationURL; + QString _publicKey; }; #endif /* defined(__hifi__DomainInfo__) */ diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index d277ea92dc..1ecee1aca9 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -508,7 +509,7 @@ void NodeList::sendDomainServerCheckIn() { // increment the count of un-replied check-ins _numNoReplyDomainCheckIns++; } else if (!_domainInfo.getRootAuthenticationURL().isEmpty() && _sessionUUID.isNull() - && AccountManager::getInstance().hasValidAccessTokenForRootURL(_domainInfo.getRootAuthenticationURL())) { + && AccountManager::getInstance().hasValidAccessToken()) { // we have an access token we can use for the authentication server the domain-server requested // so ask that server to provide us with information to connect to the domain-server requestAuthForDomainServer(); @@ -571,21 +572,34 @@ int NodeList::processDomainServerList(const QByteArray& packet) { return readNodes; } -void NodeList::requestAuthForDomainServer() { +void NodeList::domainServerAuthReply() { + QNetworkReply* requestReply = reinterpret_cast(sender()); + QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll()); + _domainInfo.parseAuthInformationFromJsonObject(jsonResponse.object()); +} + +void NodeList::requestAuthForDomainServer() { + AccountManager::getInstance().authenticatedGetRequest("/api/v1/domains/" + + uuidStringWithoutCurlyBraces(_domainInfo.getUUID()) + "/auth.json", + this, SLOT(domainServerAuthReply())); } void NodeList::processDomainServerAuthRequest(const QByteArray& packet) { QDataStream authPacketStream(packet); authPacketStream.skipRawData(numBytesForPacketHeader(packet)); + _domainInfo.setUUID(uuidFromPacketHeader(packet)); + AccountManager& accountManager = AccountManager::getInstance(); + // grab the hostname this domain-server wants us to authenticate with QUrl authenticationRootURL; authPacketStream >> authenticationRootURL; + accountManager.setRootURL(authenticationRootURL); _domainInfo.setRootAuthenticationURL(authenticationRootURL); - if (AccountManager::getInstance().checkAndSignalForAccessTokenForRootURL(authenticationRootURL)) { + if (AccountManager::getInstance().checkAndSignalForAccessToken()) { // request a domain-server auth requestAuthForDomainServer(); } diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index 1772a8f3d8..fb59e20ad6 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -130,7 +130,8 @@ signals: void uuidChanged(const QUuid& ownerUUID); void nodeAdded(SharedNodePointer); void nodeKilled(SharedNodePointer); - +private slots: + void domainServerAuthReply(); private: static NodeList* _sharedInstance;