From b368e866fb08a0ad1d0b5c9f3b45c1fc333396e2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 8 Oct 2014 14:26:23 -0700 Subject: [PATCH 01/31] change oauth request packet type to username request --- domain-server/src/DomainServer.cpp | 2 +- libraries/networking/src/PacketHeaders.cpp | 2 +- libraries/networking/src/PacketHeaders.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index fa7a0fe012..ed027be26d 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -568,7 +568,7 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock } else { // we don't know anything about this client // we have an OAuth provider, ask this interface client to auth against it - QByteArray oauthRequestByteArray = byteArrayWithPopulatedHeader(PacketTypeDomainOAuthRequest); + QByteArray oauthRequestByteArray = byteArrayWithPopulatedHeader(PacketTypeDomainUsernameRequest); QDataStream oauthRequestStream(&oauthRequestByteArray, QIODevice::Append); QUrl authorizationURL = packetUUID.isNull() ? oauthAuthorizationURL() : oauthAuthorizationURL(packetUUID); oauthRequestStream << authorizationURL; diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 14bcd7c3ce..03b919afcc 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -114,7 +114,7 @@ QString nameForPacketType(PacketType type) { PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainListRequest); PACKET_TYPE_NAME_LOOKUP(PacketTypeRequestAssignment); PACKET_TYPE_NAME_LOOKUP(PacketTypeCreateAssignment); - PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainOAuthRequest); + PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainUsernameRequest); PACKET_TYPE_NAME_LOOKUP(PacketTypeMuteEnvironment); PACKET_TYPE_NAME_LOOKUP(PacketTypeAudioStreamStats); PACKET_TYPE_NAME_LOOKUP(PacketTypeDataServerConfirm); diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 2d298d253d..59959071c6 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -38,7 +38,7 @@ enum PacketType { PacketTypeDomainListRequest, PacketTypeRequestAssignment, PacketTypeCreateAssignment, - PacketTypeDomainOAuthRequest, + PacketTypeDomainUsernameRequest, PacketTypeMuteEnvironment, PacketTypeAudioStreamStats, PacketTypeDataServerConfirm, @@ -81,7 +81,7 @@ typedef char PacketVersion; const QSet NON_VERIFIED_PACKETS = QSet() << PacketTypeDomainServerRequireDTLS << PacketTypeDomainConnectRequest - << PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainOAuthRequest + << PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainUsernameRequest << PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse << PacketTypeNodeJsonStats << PacketTypeVoxelQuery << PacketTypeParticleQuery << PacketTypeEntityQuery << PacketTypeOctreeDataNack << PacketTypeVoxelEditNack << PacketTypeParticleEditNack << PacketTypeEntityEditNack From d741b860f82b7f18768054717e22c9b0006ba18f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 15:58:26 -0700 Subject: [PATCH 02/31] fix for delete of object table row --- domain-server/resources/web/js/settings.js | 27 ++++++++++++---------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index 923a01a8a9..38baf223b3 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -474,21 +474,24 @@ function deleteTableRow(delete_glyphicon) { if (!isArray) { // this is a hash row, so we empty it but leave the hidden input blank so it is cleared when we save row.empty() - row.html(""); - } else if (table.find('.' + Settings.DATA_ROW_CLASS).length > 1) { - updateDataChangedForSiblingRows(row) - - // this isn't the last row - we can just remove it - row.remove() + row.html(""); } else { - // this is the last row, we can't remove it completely since we need to post an empty array - row.empty() + if (table.find('.' + Settings.DATA_ROW_CLASS).length) { + updateDataChangedForSiblingRows(row) - row.removeClass(Settings.DATA_ROW_CLASS).removeClass(Settings.NEW_ROW_CLASS) - row.addClass('empty-array-row') + // this isn't the last row - we can just remove it + row.remove() + } else { + // this is the last row, we can't remove it completely since we need to post an empty array + row.empty() - row.html(""); + row.removeClass(Settings.DATA_ROW_CLASS).removeClass(Settings.NEW_ROW_CLASS) + row.addClass('empty-array-row') + + row.html(""); + } } // we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated From d70bf7fc5058e79b8418a07da5354e91ce16dab1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 16:15:59 -0700 Subject: [PATCH 03/31] add allowed users to describe, remove +/- from table header --- domain-server/resources/describe-settings.json | 14 ++++++++++++++ domain-server/resources/web/js/settings.js | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index ee2c9ae796..e9df7f264d 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -59,6 +59,20 @@ "type": "password", "help": "Password used for basic HTTP authentication. Leave this blank if you do not want to change it.", "value-hidden": true + }, + { + "name": "allowed_users", + "type": "table", + "label": "Allowed Users", + "help": "A list of usernames for the High Fidelity users you want to allow into your domain. Users not found in this list will not be allowed to connect.", + "numbered": false, + "columns": [ + { + "name": "username", + "label": "Username", + "can_set": true + } + ] } ] }, diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index 38baf223b3..ccc254dbc3 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -256,7 +256,7 @@ function makeTable(setting, setting_name, setting_value) { html += "" + col.label + "" // Data }) - html += "+/-" + html += "" // populate rows in the table from existing values var row_num = 1 From c342e1b2e8b092ed3fa4eda20c47fdd02f53390a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 16:19:16 -0700 Subject: [PATCH 04/31] cleanup help text for allowed users for domain --- domain-server/resources/describe-settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index e9df7f264d..3f4e69a061 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -64,7 +64,7 @@ "name": "allowed_users", "type": "table", "label": "Allowed Users", - "help": "A list of usernames for the High Fidelity users you want to allow into your domain. Users not found in this list will not be allowed to connect.", + "help": "A list of usernames for the High Fidelity users you want to allow into your domain.
If the list is blank, all users are allowed to connect.
You are always allowed to connect to your domain directly from your local machine.", "numbered": false, "columns": [ { From 0d2b1e361c96aa68895c18734ef3404f46600f55 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 16:29:52 -0700 Subject: [PATCH 05/31] have domain-server reply back with username request is allowed_users is not empty --- domain-server/src/DomainServer.cpp | 109 ++++------------------------- domain-server/src/DomainServer.h | 2 - 2 files changed, 15 insertions(+), 96 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index ed027be26d..25428ca4b3 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -44,8 +44,6 @@ DomainServer::DomainServer(int argc, char* argv[]) : _oauthProviderURL(), _oauthClientID(), _hostname(), - _networkReplyUUIDMap(), - _sessionAuthenticationHash(), _webAuthenticationStateSet(), _cookieSessionHash(), _settingsManager() @@ -507,7 +505,7 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSetwriteUnverifiedDatagram(oauthRequestByteArray, senderSockAddr); - - return; - } + + static const QVariant* allowedUsersVariant = valueForKeyPath(_settingsManager.getSettingsMap(), + ALLOWED_USERS_SETTINGS_KEYPATH); + static QVariantList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toList() : QVariantList(); + + if (!isAssignment && allowedUsers.count() > 0) { + // this is an agent, we need to ask them to provide us with their signed username to see if they are allowed in + + QByteArray usernameRequestByteArray = byteArrayWithPopulatedHeader(PacketTypeDomainUsernameRequest); + + // send this oauth request datagram back to the client + LimitedNodeList::getInstance()->writeUnverifiedDatagram(usernameRequestByteArray, senderSockAddr); + + return; } if ((!isAssignment && !STATICALLY_ASSIGNED_NODES.contains(nodeType)) @@ -1545,13 +1531,6 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u // we've redirected the user back to our homepage return true; - } else { - qDebug() << "Requesting a token for user with session UUID" << uuidStringWithoutCurlyBraces(stateUUID); - - // insert this to our pending token replies so we can associate the returned access token with the right UUID - _networkReplyUUIDMap.insert(tokenReply, stateUUID); - - connect(tokenReply, &QNetworkReply::finished, this, &DomainServer::handleTokenRequestFinished); } } @@ -1695,22 +1674,6 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl const QString OAUTH_JSON_ACCESS_TOKEN_KEY = "access_token"; -void DomainServer::handleTokenRequestFinished() { - QNetworkReply* networkReply = reinterpret_cast(sender()); - QUuid matchingSessionUUID = _networkReplyUUIDMap.take(networkReply); - - if (!matchingSessionUUID.isNull() && networkReply->error() == QNetworkReply::NoError) { - - qDebug() << "Received access token for user with UUID" << uuidStringWithoutCurlyBraces(matchingSessionUUID) - << "-" << "requesting profile."; - - QNetworkReply* profileReply = profileRequestGivenTokenReply(networkReply); - - connect(profileReply, &QNetworkReply::finished, this, &DomainServer::handleProfileRequestFinished); - - _networkReplyUUIDMap.insert(profileReply, matchingSessionUUID); - } -} QNetworkReply* DomainServer::profileRequestGivenTokenReply(QNetworkReply* tokenReply) { // pull the access token from the returned JSON and store it with the matching session UUID @@ -1725,48 +1688,6 @@ QNetworkReply* DomainServer::profileRequestGivenTokenReply(QNetworkReply* tokenR return NetworkAccessManager::getInstance().get(QNetworkRequest(profileURL)); } -void DomainServer::handleProfileRequestFinished() { - QNetworkReply* networkReply = reinterpret_cast(sender()); - QUuid matchingSessionUUID = _networkReplyUUIDMap.take(networkReply); - - if (!matchingSessionUUID.isNull() && networkReply->error() == QNetworkReply::NoError) { - QJsonDocument profileJSON = QJsonDocument::fromJson(networkReply->readAll()); - - if (profileJSON.object()["status"].toString() == "success") { - // pull the user roles from the response - QJsonArray userRolesArray = profileJSON.object()["data"].toObject()["user"].toObject()["roles"].toArray(); - - QStringList allowedRolesArray = _settingsManager.getSettingsMap().value(ALLOWED_ROLES_CONFIG_KEY).toStringList(); - - QString connectableUsername; - QString profileUsername = profileJSON.object()["data"].toObject()["user"].toObject()["username"].toString(); - - foreach(const QJsonValue& roleValue, userRolesArray) { - if (allowedRolesArray.contains(roleValue.toString())) { - // the user has a role that lets them in - // set the bool to true and break - connectableUsername = profileUsername; - break; - } - } - - if (connectableUsername.isEmpty()) { - qDebug() << "User" << profileUsername << "with session UUID" - << uuidStringWithoutCurlyBraces(matchingSessionUUID) - << "does not have an allowable role. Refusing connection."; - } else { - qDebug() << "User" << profileUsername << "with session UUID" - << uuidStringWithoutCurlyBraces(matchingSessionUUID) - << "has an allowable role. Can connect."; - } - - // insert this UUID and a flag that indicates if they are allowed to connect - _sessionAuthenticationHash.insert(matchingSessionUUID, connectableUsername); - } - } -} - - const QString DS_SETTINGS_SESSIONS_GROUP = "web-sessions"; Headers DomainServer::setupCookieHeadersFromProfileReply(QNetworkReply* profileReply) { diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 0ad2aae8a8..913d66cc32 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -131,8 +131,6 @@ private: QString _oauthClientID; QString _oauthClientSecret; QString _hostname; - QMap _networkReplyUUIDMap; - QHash _sessionAuthenticationHash; QSet _webAuthenticationStateSet; QHash _cookieSessionHash; From 31df339565c09bfcbc56d3e4164cd3e51d4d7a30 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 16:53:22 -0700 Subject: [PATCH 06/31] pipe username signature requirement through to DomainHandler --- .../resources/describe-settings.json | 2 +- domain-server/resources/web/js/settings.js | 8 +- interface/src/Application.cpp | 8 - interface/src/DatagramProcessor.cpp | 16 +- interface/src/ui/OAuthWebViewHandler.cpp | 168 ------------------ interface/src/ui/OAuthWebViewHandler.h | 43 ----- libraries/networking/src/DomainHandler.cpp | 2 + libraries/networking/src/DomainHandler.h | 4 + 8 files changed, 17 insertions(+), 234 deletions(-) delete mode 100644 interface/src/ui/OAuthWebViewHandler.cpp delete mode 100644 interface/src/ui/OAuthWebViewHandler.h diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 3f4e69a061..74db2c1e52 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -64,7 +64,7 @@ "name": "allowed_users", "type": "table", "label": "Allowed Users", - "help": "A list of usernames for the High Fidelity users you want to allow into your domain.
If the list is blank, all users are allowed to connect.
You are always allowed to connect to your domain directly from your local machine.", + "help": "List the High Fidelity names for people you want to be able to connect to this domain.
An empty list means everyone.
You can always connect from this machine.", "numbered": false, "columns": [ { diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index ccc254dbc3..56e451abf6 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -276,13 +276,15 @@ function makeTable(setting, setting_name, setting_value) { html += "" if (isArray) { - colIsArray = _.isArray(row) - colValue = colIsArray ? row : row[col.name] + rowIsObject = setting.columns.length > 1 + colValue = rowIsObject ? row[col.name] : row html += colValue // for arrays we add a hidden input to this td so that values can be posted appropriately html += "" + + (rowIsObject ? "." + col.name : "") + "' value='" + colValue + "'/>" + + console.log(html) } else if (row.hasOwnProperty(col.name)) { html += row[col.name] } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 718360864a..da5c858fff 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -90,7 +90,6 @@ #include "scripting/WindowScriptingInterface.h" #include "ui/InfoView.h" -#include "ui/OAuthWebViewHandler.h" #include "ui/Snapshot.h" #include "ui/Stats.h" #include "ui/TextRenderer.h" @@ -219,10 +218,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : listenPort = atoi(portStr); } - // call the OAuthWebviewHandler static getter so that its instance lives in our thread - // make sure it is ready before the NodeList might need it - OAuthWebViewHandler::getInstance(); - // start the nodeThread so its event loop is running _nodeThread->start(); @@ -3522,9 +3517,6 @@ void Application::domainChanged(const QString& domainHostname) { // reset the voxels renderer _voxels.killLocalVoxels(); - - // reset the auth URL for OAuth web view handler - OAuthWebViewHandler::getInstance().clearLastAuthorizationURL(); } void Application::connectedToDomain(const QString& hostname) { diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index 0889007c76..a5ad4a4c59 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -15,7 +15,6 @@ #include "Application.h" #include "Menu.h" -#include "ui/OAuthWebViewHandler.h" #include "DatagramProcessor.h" @@ -136,16 +135,11 @@ void DatagramProcessor::processDatagrams() { application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(incomingPacket.size()); break; } - case PacketTypeDomainOAuthRequest: { - QDataStream readStream(incomingPacket); - readStream.skipRawData(numBytesForPacketHeader(incomingPacket)); - - QUrl authorizationURL; - readStream >> authorizationURL; - - QMetaObject::invokeMethod(&OAuthWebViewHandler::getInstance(), "displayWebviewForAuthorizationURL", - Q_ARG(const QUrl&, authorizationURL)); - + case PacketTypeDomainUsernameRequest: { + // flag the domain handler so it knows to send a username signature on next check-in + // and then make it send that next check in + nodeList->getDomainHandler().setRequiresUsernameSignature(true); + nodeList->sendDomainServerCheckIn(); break; } case PacketTypeMuteEnvironment: { diff --git a/interface/src/ui/OAuthWebViewHandler.cpp b/interface/src/ui/OAuthWebViewHandler.cpp deleted file mode 100644 index 86db54afb4..0000000000 --- a/interface/src/ui/OAuthWebViewHandler.cpp +++ /dev/null @@ -1,168 +0,0 @@ -// -// OAuthWebViewHandler.cpp -// interface/src/ui -// -// Created by Stephen Birarda on 2014-05-01. -// Copyright 2014 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 -#include - -#include - -#include "Application.h" - -#include "OAuthWebViewHandler.h" - -OAuthWebViewHandler& OAuthWebViewHandler::getInstance() { - static OAuthWebViewHandler sharedInstance; - return sharedInstance; -} - -OAuthWebViewHandler::OAuthWebViewHandler() : - _activeWebView(NULL), - _webViewRedisplayTimer(), - _lastAuthorizationURL() -{ - addHighFidelityRootCAToSSLConfig(); -} - -const char HIGH_FIDELITY_CA[] = "-----BEGIN CERTIFICATE-----\n" - "MIID6TCCA1KgAwIBAgIJANlfRkRD9A8bMA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD\n" - "VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5j\n" - "aXNjbzEbMBkGA1UEChMSSGlnaCBGaWRlbGl0eSwgSW5jMRMwEQYDVQQLEwpPcGVy\n" - "YXRpb25zMRgwFgYDVQQDEw9oaWdoZmlkZWxpdHkuaW8xIjAgBgkqhkiG9w0BCQEW\n" - "E29wc0BoaWdoZmlkZWxpdHkuaW8wHhcNMTQwMzI4MjIzMzM1WhcNMjQwMzI1MjIz\n" - "MzM1WjCBqjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNV\n" - "BAcTDVNhbiBGcmFuY2lzY28xGzAZBgNVBAoTEkhpZ2ggRmlkZWxpdHksIEluYzET\n" - "MBEGA1UECxMKT3BlcmF0aW9uczEYMBYGA1UEAxMPaGlnaGZpZGVsaXR5LmlvMSIw\n" - "IAYJKoZIhvcNAQkBFhNvcHNAaGlnaGZpZGVsaXR5LmlvMIGfMA0GCSqGSIb3DQEB\n" - "AQUAA4GNADCBiQKBgQDyo1euYiPPEdnvDZnIjWrrP230qUKMSj8SWoIkbTJF2hE8\n" - "2eP3YOgbgSGBzZ8EJBxIOuNmj9g9Eg6691hIKFqy5W0BXO38P04Gg+pVBvpHFGBi\n" - "wpqGbfsjaUDuYmBeJRcMO0XYkLCRQG+lAQNHoFDdItWAJfC3FwtP3OCDnz8cNwID\n" - "AQABo4IBEzCCAQ8wHQYDVR0OBBYEFCSv2kmiGg6VFMnxXzLDNP304cPAMIHfBgNV\n" - "HSMEgdcwgdSAFCSv2kmiGg6VFMnxXzLDNP304cPAoYGwpIGtMIGqMQswCQYDVQQG\n" - "EwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj\n" - "bzEbMBkGA1UEChMSSGlnaCBGaWRlbGl0eSwgSW5jMRMwEQYDVQQLEwpPcGVyYXRp\n" - "b25zMRgwFgYDVQQDEw9oaWdoZmlkZWxpdHkuaW8xIjAgBgkqhkiG9w0BCQEWE29w\n" - "c0BoaWdoZmlkZWxpdHkuaW+CCQDZX0ZEQ/QPGzAMBgNVHRMEBTADAQH/MA0GCSqG\n" - "SIb3DQEBBQUAA4GBAEkQl3p+lH5vuoCNgyfa67nL0MsBEt+5RSBOgjwCjjASjzou\n" - "FTv5w0he2OypgMQb8i/BYtS1lJSFqjPJcSM1Salzrm3xDOK5pOXJ7h6SQLPDVEyf\n" - "Hy2/9d/to+99+SOUlvfzfgycgjOc+s/AV7Y+GBd7uzGxUdrN4egCZW1F6/mH\n" - "-----END CERTIFICATE-----\n"; - -void OAuthWebViewHandler::addHighFidelityRootCAToSSLConfig() { - QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration(); - - // add the High Fidelity root CA to the list of trusted CA certificates - QByteArray highFidelityCACertificate(HIGH_FIDELITY_CA, sizeof(HIGH_FIDELITY_CA)); - sslConfig.setCaCertificates(sslConfig.caCertificates() + QSslCertificate::fromData(highFidelityCACertificate)); - - // set the modified configuration - QSslConfiguration::setDefaultConfiguration(sslConfig); -} - -const int WEB_VIEW_REDISPLAY_ELAPSED_MSECS = 5 * 1000; - -void OAuthWebViewHandler::displayWebviewForAuthorizationURL(const QUrl& authorizationURL) { - if (!_activeWebView) { - - if (!_lastAuthorizationURL.isEmpty()) { - if (_lastAuthorizationURL.host() == authorizationURL.host() - && _webViewRedisplayTimer.elapsed() < WEB_VIEW_REDISPLAY_ELAPSED_MSECS) { - // this would be re-displaying an OAuth dialog for the same auth URL inside of the redisplay ms - // so return instead - return; - } - } - - _lastAuthorizationURL = authorizationURL; - - _activeWebView = new QWebView; - - // keep the window on top and delete it when it closes - _activeWebView->setWindowFlags(Qt::Sheet); - _activeWebView->setAttribute(Qt::WA_DeleteOnClose); - - qDebug() << "Displaying QWebView for OAuth authorization at" << authorizationURL.toString(); - - AccountManager& accountManager = AccountManager::getInstance(); - - QUrl codedAuthorizationURL = authorizationURL; - - // check if we have an access token for this host - if so we can bypass login by adding it to the URL - if (accountManager.getAuthURL().host() == authorizationURL.host() - && accountManager.hasValidAccessToken()) { - - const QString ACCESS_TOKEN_QUERY_STRING_KEY = "access_token"; - - QUrlQuery authQuery(codedAuthorizationURL); - authQuery.addQueryItem(ACCESS_TOKEN_QUERY_STRING_KEY, accountManager.getAccountInfo().getAccessToken().token); - - codedAuthorizationURL.setQuery(authQuery); - } - - connect(_activeWebView.data(), &QWebView::urlChanged, this, &OAuthWebViewHandler::handleURLChanged); - - _activeWebView->load(codedAuthorizationURL); - - connect(_activeWebView->page()->networkAccessManager(), &QNetworkAccessManager::sslErrors, - this, &OAuthWebViewHandler::handleSSLErrors); - connect(_activeWebView->page()->networkAccessManager(), &QNetworkAccessManager::finished, - this, &OAuthWebViewHandler::handleReplyFinished); - connect(_activeWebView.data(), &QWebView::loadFinished, this, &OAuthWebViewHandler::handleLoadFinished); - - // connect to the destroyed signal so after the web view closes we can start a timer - connect(_activeWebView.data(), &QWebView::destroyed, this, &OAuthWebViewHandler::handleWebViewDestroyed); - } -} - -void OAuthWebViewHandler::handleSSLErrors(QNetworkReply* networkReply, const QList& errorList) { - qDebug() << "SSL Errors:" << errorList; -} - -void OAuthWebViewHandler::handleLoadFinished(bool success) { - if (success && _activeWebView->url().host() == NodeList::getInstance()->getDomainHandler().getHostname()) { - qDebug() << "OAuth authorization code passed successfully to domain-server."; - - // grab the UUID that is set as the state parameter in the auth URL - // since that is our new session UUID - QUrlQuery authQuery(_activeWebView->url()); - - const QString AUTH_STATE_QUERY_KEY = "state"; - NodeList::getInstance()->setSessionUUID(QUuid(authQuery.queryItemValue(AUTH_STATE_QUERY_KEY))); - - _activeWebView->close(); - _activeWebView = NULL; - } -} - -void OAuthWebViewHandler::handleReplyFinished(QNetworkReply* reply) { - if (_activeWebView && reply->error() != QNetworkReply::NoError) { - qDebug() << "Error loading" << reply->url() << "-" << reply->errorString(); - _activeWebView->close(); - } -} - -void OAuthWebViewHandler::handleWebViewDestroyed(QObject* destroyedObject) { - _webViewRedisplayTimer.restart(); -} - -void OAuthWebViewHandler::handleURLChanged(const QUrl& url) { - // check if this is the authorization screen - if it is then we need to show the OAuthWebViewHandler - const QString ACCESS_TOKEN_URL_REGEX_STRING = "redirect_uri=[\\w:\\/\\.]+&access_token="; - QRegExp accessTokenRegex(ACCESS_TOKEN_URL_REGEX_STRING); - - if (accessTokenRegex.indexIn(url.toString()) != -1) { - _activeWebView->show(); - } else if (url.toString() == DEFAULT_NODE_AUTH_URL.toString() + "/login") { - // this is a login request - we're going to close the webview and signal the AccountManager that we need a login - qDebug() << "data-server replied with login request. Signalling that login is required to proceed with OAuth."; - _activeWebView->close(); - AccountManager::getInstance().checkAndSignalForAccessToken(); - } -} diff --git a/interface/src/ui/OAuthWebViewHandler.h b/interface/src/ui/OAuthWebViewHandler.h deleted file mode 100644 index 1a95f17dfd..0000000000 --- a/interface/src/ui/OAuthWebViewHandler.h +++ /dev/null @@ -1,43 +0,0 @@ -// -// OAuthWebviewHandler.h -// interface/src/ui -// -// Created by Stephen Birarda on 2014-05-01. -// Copyright 2014 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_OAuthWebviewHandler_h -#define hifi_OAuthWebviewHandler_h - -#include - -class QWebView; - -class OAuthWebViewHandler : public QObject { - Q_OBJECT -public: - OAuthWebViewHandler(); - static OAuthWebViewHandler& getInstance(); - static void addHighFidelityRootCAToSSLConfig(); - - void clearLastAuthorizationURL() { _lastAuthorizationURL = QUrl(); } - -public slots: - void displayWebviewForAuthorizationURL(const QUrl& authorizationURL); - -private slots: - void handleSSLErrors(QNetworkReply* networkReply, const QList& errorList); - void handleLoadFinished(bool success); - void handleReplyFinished(QNetworkReply* reply); - void handleWebViewDestroyed(QObject* destroyedObject); - void handleURLChanged(const QUrl& url); -private: - QPointer _activeWebView; - QElapsedTimer _webViewRedisplayTimer; - QUrl _lastAuthorizationURL; -}; - -#endif // hifi_OAuthWebviewHandler_h \ No newline at end of file diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 760c9f4c04..48f0112ef6 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -30,6 +30,7 @@ DomainHandler::DomainHandler(QObject* parent) : _iceServerSockAddr(), _icePeer(), _isConnected(false), + _requiresUsernameSignature(false), _handshakeTimer(NULL), _settingsObject(), _failedSettingsRequests(0) @@ -48,6 +49,7 @@ void DomainHandler::clearConnectionInfo() { } _isConnected = false; + _requiresUsernameSignature = false; emit disconnectedFromDomain(); diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 86c6e6bc57..ecee505569 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -68,6 +68,9 @@ public: bool isConnected() const { return _isConnected; } void setIsConnected(bool isConnected); + bool requiresUsernameSignature() const { return _requiresUsernameSignature; } + void setRequiresUsernameSignature(bool requiresUsernameSignature) { _requiresUsernameSignature = requiresUsernameSignature; } + bool hasSettings() const { return !_settingsObject.isEmpty(); } void requestDomainSettings(); const QJsonObject& getSettingsObject() const { return _settingsObject; } @@ -104,6 +107,7 @@ private: HifiSockAddr _iceServerSockAddr; NetworkPeer _icePeer; bool _isConnected; + bool _requiresUsernameSignature; QTimer* _handshakeTimer; QJsonObject _settingsObject; int _failedSettingsRequests; From e40aff48648eef661a61280e411dca7d5544f766 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 16:54:08 -0700 Subject: [PATCH 07/31] remove a missed console log --- domain-server/resources/web/js/settings.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index 56e451abf6..8a1165a523 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -283,8 +283,6 @@ function makeTable(setting, setting_name, setting_value) { // for arrays we add a hidden input to this td so that values can be posted appropriately html += "" - - console.log(html) } else if (row.hasOwnProperty(col.name)) { html += row[col.name] } From f637f5a4a61025f023670cab103a53a653a25e5d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 17:07:32 -0700 Subject: [PATCH 08/31] =?UTF-8?q?always=C2=A0allow=20connection=20from=20l?= =?UTF-8?q?ocalhost=20user?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- domain-server/src/DomainServer.cpp | 19 +++++++++++-------- domain-server/src/DomainServer.h | 2 -- libraries/networking/src/LimitedNodeList.cpp | 1 - libraries/networking/src/LimitedNodeList.h | 2 ++ 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 25428ca4b3..d8e381c58c 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -556,14 +556,17 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock static QVariantList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toList() : QVariantList(); if (!isAssignment && allowedUsers.count() > 0) { - // this is an agent, we need to ask them to provide us with their signed username to see if they are allowed in - - QByteArray usernameRequestByteArray = byteArrayWithPopulatedHeader(PacketTypeDomainUsernameRequest); - - // send this oauth request datagram back to the client - LimitedNodeList::getInstance()->writeUnverifiedDatagram(usernameRequestByteArray, senderSockAddr); - - return; + // this is an agent, we need to ask them to provide us with their signed username to see if they are allowed in + // we always let in a user who is sending a packet from our local socket or from the localhost address + if (senderSockAddr.getAddress() != LimitedNodeList::getInstance()->getLocalSockAddr().getAddress() + && senderSockAddr.getAddress() != QHostAddress::LocalHost) { + QByteArray usernameRequestByteArray = byteArrayWithPopulatedHeader(PacketTypeDomainUsernameRequest); + + // send this oauth request datagram back to the client + LimitedNodeList::getInstance()->writeUnverifiedDatagram(usernameRequestByteArray, senderSockAddr); + + return; + } } if ((!isAssignment && !STATICALLY_ASSIGNED_NODES.contains(nodeType)) diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 913d66cc32..a06f5fa35b 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -135,8 +135,6 @@ private: QSet _webAuthenticationStateSet; QHash _cookieSessionHash; - HifiSockAddr _localSockAddr; - QHash _connectingICEPeers; QHash _connectedICEPeers; diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index dd33c96d74..919dc75c23 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -679,7 +679,6 @@ void LimitedNodeList::updateLocalSockAddr() { qDebug() << "Local socket has changed from" << _localSockAddr << "to" << newSockAddr; } - _localSockAddr = newSockAddr; emit localSockAddrChanged(_localSockAddr); diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index c416773201..73381d01a5 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -101,6 +101,8 @@ public: const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket); SharedNodePointer updateSocketsForNode(const QUuid& uuid, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket); + + const HifiSockAddr& getLocalSockAddr() const { return _localSockAddr; } void processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet); void processKillNode(const QByteArray& datagram); From cca8b68d2ee90b42484a55af82d73f1cbb17ded8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Oct 2014 10:40:58 -0700 Subject: [PATCH 09/31] update location and profile paths for new format --- domain-server/src/DomainServer.cpp | 2 +- interface/src/Application.cpp | 2 +- libraries/networking/src/AccountManager.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index d8e381c58c..2e90609b0c 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1685,7 +1685,7 @@ QNetworkReply* DomainServer::profileRequestGivenTokenReply(QNetworkReply* tokenR // fire off a request to get this user's identity so we can see if we will let them in QUrl profileURL = _oauthProviderURL; - profileURL.setPath("/api/v1/users/profile"); + profileURL.setPath("/api/v1/user/profile"); profileURL.setQuery(QString("%1=%2").arg(OAUTH_JSON_ACCESS_TOKEN_KEY, accessToken)); return NetworkAccessManager::getInstance().get(QNetworkRequest(profileURL)); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c44a4a4548..f14edec313 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3468,7 +3468,7 @@ void Application::updateLocationInServer() { rootObject.insert(LOCATION_KEY_IN_ROOT, locationObject); - accountManager.authenticatedRequest("/api/v1/users/location", QNetworkAccessManager::PutOperation, + accountManager.authenticatedRequest("/api/v1/user/location", QNetworkAccessManager::PutOperation, JSONCallbackParameters(), QJsonDocument(rootObject).toJson()); } } diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index bb471442ea..7b49aebfe9 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -434,7 +434,7 @@ void AccountManager::requestProfile() { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QUrl profileURL = _authURL; - profileURL.setPath("/api/v1/users/profile"); + profileURL.setPath("/api/v1/user/profile"); QNetworkRequest profileRequest(profileURL); profileRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER, _accountInfo.getAccessToken().authorizationHeaderValue()); From 14d4dd1f4b42861e43f3b04e9185332b155a5b8f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Oct 2014 10:43:13 -0700 Subject: [PATCH 10/31] add a private key string to DataServerAccountInfo --- libraries/networking/src/DataServerAccountInfo.cpp | 11 ++++++++--- libraries/networking/src/DataServerAccountInfo.h | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index 9bdb012f70..e6c800a1a7 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -20,7 +20,8 @@ DataServerAccountInfo::DataServerAccountInfo() : _discourseApiKey(), _walletID(), _balance(0), - _hasBalance(false) + _hasBalance(false), + _privateKey() { } @@ -33,6 +34,7 @@ DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherI _walletID = otherInfo._walletID; _balance = otherInfo._balance; _hasBalance = otherInfo._hasBalance; + _privateKey = otherInfo._privateKey; } DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountInfo& otherInfo) { @@ -51,6 +53,7 @@ void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) { swap(_walletID, otherInfo._walletID); swap(_balance, otherInfo._balance); swap(_hasBalance, otherInfo._hasBalance); + swap(_privateKey, otherInfo._privateKey); } void DataServerAccountInfo::setAccessTokenFromJSON(const QJsonObject& jsonObject) { @@ -112,11 +115,13 @@ void DataServerAccountInfo::setProfileInfoFromJSON(const QJsonObject& jsonObject } QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) { - out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey << info._walletID; + out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey + << info._walletID << info._privateKey; return out; } QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info) { - in >> info._accessToken >> info._username >> info._xmppPassword >> info._discourseApiKey >> info._walletID; + in >> info._accessToken >> info._username >> info._xmppPassword >> info._discourseApiKey + >> info._walletID >> info._privateKey; return in; } diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h index 91675594ad..225227d16a 100644 --- a/libraries/networking/src/DataServerAccountInfo.h +++ b/libraries/networking/src/DataServerAccountInfo.h @@ -67,6 +67,7 @@ private: QUuid _walletID; qint64 _balance; bool _hasBalance; + QString _privateKey; }; #endif // hifi_DataServerAccountInfo_h From 912512936ed1ab841e4e6e6e9b86082310efb050 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Oct 2014 11:36:40 -0700 Subject: [PATCH 11/31] add RSAKeypairGenerator to generate a 2048-bit keypair --- interface/CMakeLists.txt | 4 +- interface/src/Application.cpp | 1 + libraries/networking/CMakeLists.txt | 8 ++ .../networking/src/DataServerAccountInfo.h | 2 +- .../networking/src/RSAKeypairGenerator.cpp | 83 +++++++++++++++++++ .../networking/src/RSAKeypairGenerator.h | 26 ++++++ 6 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 libraries/networking/src/RSAKeypairGenerator.cpp create mode 100644 libraries/networking/src/RSAKeypairGenerator.h diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 16ca977bae..e6e49750d1 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -105,7 +105,6 @@ link_hifi_libraries(shared octree voxels fbx metavoxels networking particles ent # find any optional and required libraries find_package(ZLIB REQUIRED) -find_package(OpenSSL REQUIRED) # perform standard include and linking for found externals foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) @@ -169,10 +168,9 @@ endif () # include headers for interface and InterfaceConfig. include_directories("${PROJECT_SOURCE_DIR}/src" "${PROJECT_BINARY_DIR}/includes") -include_directories("${OPENSSL_INCLUDE_DIR}") target_link_libraries( - ${TARGET_NAME} ${ZLIB_LIBRARIES} ${OPENSSL_LIBRARIES} + ${TARGET_NAME} ${ZLIB_LIBRARIES} Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL Qt5::Script Qt5::Svg Qt5::WebKitWidgets ) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f14edec313..af108dc947 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -64,6 +64,7 @@ #include #include #include +#include #include #include diff --git a/libraries/networking/CMakeLists.txt b/libraries/networking/CMakeLists.txt index 501437fab2..b86f53105c 100644 --- a/libraries/networking/CMakeLists.txt +++ b/libraries/networking/CMakeLists.txt @@ -10,5 +10,13 @@ if (WIN32) target_link_libraries(${TARGET_NAME} ws2_32.lib) endif () +# find OpenSSL +find_package(OpenSSL REQUIRED) + +include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") + +# append OpenSSL to our list of libraries to link +list(APPEND ${TARGET_NAME}_LIBRARIES_TO_LINK "${OPENSSL_LIBRARIES}") + # call macro to link our dependencies and bubble them up via a property on our target link_shared_dependencies() \ No newline at end of file diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h index 225227d16a..ae7699584b 100644 --- a/libraries/networking/src/DataServerAccountInfo.h +++ b/libraries/networking/src/DataServerAccountInfo.h @@ -67,7 +67,7 @@ private: QUuid _walletID; qint64 _balance; bool _hasBalance; - QString _privateKey; + QByteArray _privateKey; }; #endif // hifi_DataServerAccountInfo_h diff --git a/libraries/networking/src/RSAKeypairGenerator.cpp b/libraries/networking/src/RSAKeypairGenerator.cpp new file mode 100644 index 0000000000..032bd62f06 --- /dev/null +++ b/libraries/networking/src/RSAKeypairGenerator.cpp @@ -0,0 +1,83 @@ +// +// RSAKeypairGenerator.cpp +// libraries/networking/src +// +// Created by Stephen Birarda on 2014-10-14. +// Copyright 2014 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 +#include +#include + +#include + +#include "RSAKeypairGenerator.h" + +void RSAKeypairGenerator::generateKeypair() { + + RSA* keyPair = RSA_new(); + BIGNUM* exponent = BN_new(); + + const unsigned long RSA_KEY_EXPONENT = 65537; + BN_set_word(exponent, RSA_KEY_EXPONENT); + + // seed the random number generator before we call RSA_generate_key_ex + srand(time(NULL)); + + const int RSA_KEY_BITS = 2048; + + if (!RSA_generate_key_ex(keyPair, RSA_KEY_BITS, exponent, NULL)) { + qDebug() << "Error generating 2048-bit RSA Keypair -" << ERR_get_error(); + + emit errorGeneratingKeypair(); + + // we're going to bust out of here but first we cleanup the BIGNUM + BN_free(exponent); + return; + } + + // we don't need the BIGNUM anymore so clean that up + BN_free(exponent); + + // grab the public key and private key from the file + BIO *privateKeyBIO = BIO_new(BIO_s_mem()); + int privateWrite = PEM_write_bio_RSAPrivateKey(privateKeyBIO, keyPair, NULL, NULL, 0, NULL, NULL); + + BIO *publicKeyBIO = BIO_new(BIO_s_mem()); + int publicWrite = PEM_write_bio_RSAPublicKey(publicKeyBIO, keyPair); + + if (privateWrite == 0 || publicWrite == 0) { + // we had a error grabbing either the private or public key from the RSA + + // bubble up our error + emit errorGeneratingKeypair(); + + // cleanup the RSA struct + RSA_free(keyPair); + + // cleanup the BIOs + BIO_free(privateKeyBIO); + BIO_free(publicKeyBIO); + + return; + } + + // we have the public key and private key in memory + // we can cleanup the RSA struct before we continue on + RSA_free(keyPair); + + char* publicKeyData; + int publicKeyLength = BIO_get_mem_data(publicKeyBIO, &publicKeyData); + + char* privateKeyData; + int privateKeyLength = BIO_get_mem_data(privateKeyBIO, &privateKeyData); + + QByteArray publicKeyArray(publicKeyData, publicKeyLength); + QByteArray privateKeyArray(privateKeyData, privateKeyLength); + + emit generatedKeypair(publicKeyArray, privateKeyArray); +} \ No newline at end of file diff --git a/libraries/networking/src/RSAKeypairGenerator.h b/libraries/networking/src/RSAKeypairGenerator.h new file mode 100644 index 0000000000..f98daddb1e --- /dev/null +++ b/libraries/networking/src/RSAKeypairGenerator.h @@ -0,0 +1,26 @@ +// +// RSAKeypairGenerator.h +// libraries/networking/src +// +// Created by Stephen Birarda on 2014-10-14. +// Copyright 2014 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_RSAKeypairGenerator_h +#define hifi_RSAKeypairGenerator_h + +#include + +class RSAKeypairGenerator : public QObject { + Q_OBJECT +public slots: + void generateKeypair(); +signals: + void errorGeneratingKeypair(); + void generatedKeypair(const QByteArray& publicKey, const QByteArray& privateKey); +}; + +#endif // hifi_RSAKeypairGenerator_h \ No newline at end of file From d0f91c5c2e67a2f2ce1fac43681ef993652d67b2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Oct 2014 11:38:50 -0700 Subject: [PATCH 12/31] remove old mechanism for octree payments --- interface/src/Application.cpp | 6 -- interface/src/PaymentManager.cpp | 64 ----------------- interface/src/PaymentManager.h | 25 ------- interface/src/SignedWalletTransaction.cpp | 86 ----------------------- interface/src/SignedWalletTransaction.h | 32 --------- 5 files changed, 213 deletions(-) delete mode 100644 interface/src/PaymentManager.cpp delete mode 100644 interface/src/PaymentManager.h delete mode 100644 interface/src/SignedWalletTransaction.cpp delete mode 100644 interface/src/SignedWalletTransaction.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index af108dc947..4e5d3c9e96 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -73,7 +73,6 @@ #include "InterfaceVersion.h" #include "Menu.h" #include "ModelUploader.h" -#include "PaymentManager.h" #include "Util.h" #include "devices/MIDIManager.h" #include "devices/OculusManager.h" @@ -251,11 +250,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived); connect(&domainHandler, &DomainHandler::hostnameChanged, Menu::getInstance(), &Menu::clearLoginDialogDisplayedFlag); - // hookup VoxelEditSender to PaymentManager so we can pay for octree edits - const PaymentManager& paymentManager = PaymentManager::getInstance(); - connect(&_voxelEditSender, &VoxelEditPacketSender::octreePaymentRequired, - &paymentManager, &PaymentManager::sendSignedPayment); - // update our location every 5 seconds in the data-server, assuming that we are authenticated with one const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000; diff --git a/interface/src/PaymentManager.cpp b/interface/src/PaymentManager.cpp deleted file mode 100644 index a0c05f34b3..0000000000 --- a/interface/src/PaymentManager.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// -// PaymentManager.cpp -// interface/src -// -// Created by Stephen Birarda on 2014-07-30. -// Copyright 2014 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 -#include -#include - -#include -#include -#include - -#include "SignedWalletTransaction.h" - -#include "PaymentManager.h" - -PaymentManager& PaymentManager::getInstance() { - static PaymentManager sharedInstance; - return sharedInstance; -} - -void PaymentManager::sendSignedPayment(qint64 satoshiAmount, const QUuid& nodeUUID, const QUuid& destinationWalletUUID) { - - QByteArray walletPrivateKeyByteArray = Menu::getInstance()->getWalletPrivateKey(); - - if (!walletPrivateKeyByteArray.isEmpty()) { - // setup a signed wallet transaction - const qint64 DEFAULT_TRANSACTION_EXPIRY_SECONDS = 60; - qint64 currentTimestamp = QDateTime::currentDateTimeUtc().toTime_t(); - SignedWalletTransaction newTransaction(destinationWalletUUID, satoshiAmount, - currentTimestamp, DEFAULT_TRANSACTION_EXPIRY_SECONDS); - - // send the signed transaction to the redeeming node - QByteArray transactionByteArray = byteArrayWithPopulatedHeader(PacketTypeSignedTransactionPayment); - - // append the binary message and the signed message digest - transactionByteArray.append(newTransaction.binaryMessage()); - QByteArray signedMessageDigest = newTransaction.signedMessageDigest(); - - if (!signedMessageDigest.isEmpty()) { - transactionByteArray.append(signedMessageDigest); - - qDebug() << "Paying" << satoshiAmount << "satoshis to" << destinationWalletUUID << "via" << nodeUUID; - - // use the NodeList to send that to the right node - NodeList* nodeList = NodeList::getInstance(); - nodeList->writeDatagram(transactionByteArray, nodeList->nodeWithUUID(nodeUUID)); - } else { - qDebug() << "Payment of" << satoshiAmount << "satoshis to" << destinationWalletUUID << "via" << nodeUUID << - "cannot be sent because there was an error signing the transaction."; - } - - } else { - qDebug() << "Payment of" << satoshiAmount << "satoshis to" << destinationWalletUUID << "via" << nodeUUID << - "cannot be sent because there is no stored wallet private key."; - } -} \ No newline at end of file diff --git a/interface/src/PaymentManager.h b/interface/src/PaymentManager.h deleted file mode 100644 index 67419a39a4..0000000000 --- a/interface/src/PaymentManager.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// PaymentManager.h -// interface/src -// -// Created by Stephen Birarda on 2014-07-30. -// Copyright 2014 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_PaymentManager_h -#define hifi_PaymentManager_h - -#include - -class PaymentManager : public QObject { - Q_OBJECT -public: - static PaymentManager& getInstance(); -public slots: - void sendSignedPayment(qint64 satoshiAmount, const QUuid& nodeUUID, const QUuid& destinationWalletUUID); -}; - -#endif // hifi_PaymentManager_h \ No newline at end of file diff --git a/interface/src/SignedWalletTransaction.cpp b/interface/src/SignedWalletTransaction.cpp deleted file mode 100644 index 8a0d4e6fdb..0000000000 --- a/interface/src/SignedWalletTransaction.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// -// SignedWalletTransaction.cpp -// interface/src -// -// Created by Stephen Birarda on 2014-07-11. -// Copyright 2014 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 -#include -#include - -#include -#include -#include - -#include - -#include "Menu.h" - -#include "SignedWalletTransaction.h" - -SignedWalletTransaction::SignedWalletTransaction(const QUuid& destinationUUID, qint64 amount, - qint64 messageTimestamp, qint64 expiryDelta) : - WalletTransaction(destinationUUID, amount), - _messageTimestamp(messageTimestamp), - _expiryDelta(expiryDelta) -{ - -} - -QByteArray SignedWalletTransaction::binaryMessage() { - // build the message using the components of this transaction - - // UUID, source UUID, destination UUID, message timestamp, expiry delta, amount - QByteArray messageBinary; - - messageBinary.append(_uuid.toRfc4122()); - - messageBinary.append(reinterpret_cast(&_messageTimestamp), sizeof(_messageTimestamp)); - messageBinary.append(reinterpret_cast(&_expiryDelta), sizeof(_expiryDelta)); - - messageBinary.append(AccountManager::getInstance().getAccountInfo().getWalletID().toRfc4122()); - - messageBinary.append(_destinationUUID.toRfc4122()); - - messageBinary.append(reinterpret_cast(&_amount), sizeof(_amount)); - - return messageBinary; -} - -QByteArray SignedWalletTransaction::hexMessage() { - return binaryMessage().toHex(); -} - -QByteArray SignedWalletTransaction::messageDigest() { - return QCryptographicHash::hash(hexMessage(), QCryptographicHash::Sha256).toHex(); -} - -QByteArray SignedWalletTransaction::signedMessageDigest() { - // pull the current private key from menu into RSA structure in memory - QByteArray privateKeyByteArray = Menu::getInstance()->getWalletPrivateKey(); - - BIO* privateKeyBIO = NULL; - RSA* rsaPrivateKey = NULL; - - privateKeyBIO = BIO_new_mem_buf(privateKeyByteArray.data(), privateKeyByteArray.size()); - PEM_read_bio_RSAPrivateKey(privateKeyBIO, &rsaPrivateKey, NULL, NULL); - - QByteArray digestToEncrypt = messageDigest(); - QByteArray encryptedDigest(RSA_size(rsaPrivateKey), 0); - - int encryptReturn = RSA_private_encrypt(digestToEncrypt.size(), - reinterpret_cast(digestToEncrypt.constData()), - reinterpret_cast(encryptedDigest.data()), - rsaPrivateKey, RSA_PKCS1_PADDING); - - // free the two structures used - BIO_free(privateKeyBIO); - RSA_free(rsaPrivateKey); - - return (encryptReturn != -1) ? encryptedDigest : QByteArray(); -} \ No newline at end of file diff --git a/interface/src/SignedWalletTransaction.h b/interface/src/SignedWalletTransaction.h deleted file mode 100644 index 6bc66a535e..0000000000 --- a/interface/src/SignedWalletTransaction.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// SignedWalletTransaction.h -// interfac/src -// -// Created by Stephen Birarda on 2014-07-11. -// Copyright 2014 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_SignedWalletTransaction_h -#define hifi_SignedWalletTransaction_h - -#include - -class SignedWalletTransaction : public WalletTransaction { - Q_OBJECT -public: - SignedWalletTransaction(const QUuid& destinationUUID, qint64 amount, qint64 messageTimestamp, qint64 expiryDelta); - - QByteArray binaryMessage(); - QByteArray hexMessage(); - QByteArray messageDigest(); - QByteArray signedMessageDigest(); - -private: - qint64 _messageTimestamp; - qint64 _expiryDelta; -}; - -#endif // hifi_SignedWalletTransaction_h \ No newline at end of file From 5f6719456447f7a40d33c8040715576d9af9936e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Oct 2014 11:39:28 -0700 Subject: [PATCH 13/31] fix member variable order in Joystick class --- interface/src/devices/Joystick.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/devices/Joystick.cpp b/interface/src/devices/Joystick.cpp index e110027e1a..5ca2925ba8 100644 --- a/interface/src/devices/Joystick.cpp +++ b/interface/src/devices/Joystick.cpp @@ -20,10 +20,10 @@ const float MAX_AXIS = 32768.0f; #ifdef HAVE_SDL2 Joystick::Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController) : - _instanceId(instanceId), - _name(name), _sdlGameController(sdlGameController), _sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)), + _instanceId(instanceId), + _name(name), _axes(QVector(SDL_JoystickNumAxes(_sdlJoystick))), _buttons(QVector(SDL_JoystickNumButtons(_sdlJoystick))) { From 69402e275cd3715dee6fd4e0bd11fd111dc54a66 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Oct 2014 11:51:58 -0700 Subject: [PATCH 14/31] use DER format for generated keys in RSAKeypairGenerator --- interface/src/Application.cpp | 1 - .../networking/src/RSAKeypairGenerator.cpp | 38 ++++++++++--------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4e5d3c9e96..9dc478b9ba 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -64,7 +64,6 @@ #include #include #include -#include #include #include diff --git a/libraries/networking/src/RSAKeypairGenerator.cpp b/libraries/networking/src/RSAKeypairGenerator.cpp index 032bd62f06..ae61ac3eda 100644 --- a/libraries/networking/src/RSAKeypairGenerator.cpp +++ b/libraries/networking/src/RSAKeypairGenerator.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include @@ -44,24 +44,28 @@ void RSAKeypairGenerator::generateKeypair() { BN_free(exponent); // grab the public key and private key from the file - BIO *privateKeyBIO = BIO_new(BIO_s_mem()); - int privateWrite = PEM_write_bio_RSAPrivateKey(privateKeyBIO, keyPair, NULL, NULL, 0, NULL, NULL); + unsigned char* publicKeyDER = NULL; + int publicKeyLength = i2d_RSA_PUBKEY(keyPair, &publicKeyDER); - BIO *publicKeyBIO = BIO_new(BIO_s_mem()); - int publicWrite = PEM_write_bio_RSAPublicKey(publicKeyBIO, keyPair); + unsigned char* privateKeyDER = NULL; + int privateKeyLength = i2d_RSAPrivateKey(keyPair, &privateKeyDER); - if (privateWrite == 0 || publicWrite == 0) { - // we had a error grabbing either the private or public key from the RSA + if (publicKeyLength <= 0 || privateKeyLength <= 0) { + qDebug() << "Error getting DER public or private key from RSA struct -" << ERR_get_error(); - // bubble up our error emit errorGeneratingKeypair(); // cleanup the RSA struct RSA_free(keyPair); - // cleanup the BIOs - BIO_free(privateKeyBIO); - BIO_free(publicKeyBIO); + // cleanup the public and private key DER data, if required + if (publicKeyLength > 0) { + delete publicKeyDER; + } + + if (privateKeyLength > 0) { + delete privateKeyDER; + } return; } @@ -70,14 +74,12 @@ void RSAKeypairGenerator::generateKeypair() { // we can cleanup the RSA struct before we continue on RSA_free(keyPair); - char* publicKeyData; - int publicKeyLength = BIO_get_mem_data(publicKeyBIO, &publicKeyData); + QByteArray publicKeyArray(reinterpret_cast(publicKeyDER), publicKeyLength); + QByteArray privateKeyArray(reinterpret_cast(privateKeyDER), privateKeyLength); - char* privateKeyData; - int privateKeyLength = BIO_get_mem_data(privateKeyBIO, &privateKeyData); - - QByteArray publicKeyArray(publicKeyData, publicKeyLength); - QByteArray privateKeyArray(privateKeyData, privateKeyLength); + // cleanup the publicKeyDER and publicKeyDER data + delete publicKeyDER; + delete privateKeyDER; emit generatedKeypair(publicKeyArray, privateKeyArray); } \ No newline at end of file From 7ce806b7464b6ee7365ae1917cf965920f1992a2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Oct 2014 12:12:32 -0700 Subject: [PATCH 15/31] fire off generation of key-pair in conjunction with login, store private key --- libraries/networking/src/AccountManager.cpp | 39 +++++++++++++++++++ libraries/networking/src/AccountManager.h | 4 ++ .../networking/src/DataServerAccountInfo.h | 2 + .../networking/src/RSAKeypairGenerator.cpp | 6 +++ .../networking/src/RSAKeypairGenerator.h | 2 + 5 files changed, 53 insertions(+) diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 7b49aebfe9..a8b88133d6 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -19,9 +19,11 @@ #include #include #include +#include #include "NodeList.h" #include "PacketHeaders.h" +#include "RSAKeypairGenerator.h" #include "AccountManager.h" @@ -417,6 +419,7 @@ void AccountManager::requestAccessTokenFinished() { } requestProfile(); + generateNewKeypair(); } } else { // TODO: error handling @@ -478,3 +481,39 @@ void AccountManager::requestProfileError(QNetworkReply::NetworkError error) { // TODO: error handling qDebug() << "AccountManager requestProfileError - " << error; } + +void AccountManager::generateNewKeypair() { + // setup a new QThread to generate the keypair on, in case it takes a while + QThread* generateThread = new QThread(this); + + // setup a keypair generator + RSAKeypairGenerator* keypairGenerator = new RSAKeypairGenerator(); + + connect(generateThread, &QThread::started, keypairGenerator, &RSAKeypairGenerator::generateKeypair); + connect(keypairGenerator, &RSAKeypairGenerator::generatedKeypair, this, &AccountManager::processGeneratedKeypair); + connect(keypairGenerator, &RSAKeypairGenerator::errorGeneratingKeypair, + this, &AccountManager::handleKeypairGenerationError); + connect(keypairGenerator, &QObject::destroyed, generateThread, &QThread::quit); + connect(generateThread, &QThread::finished, generateThread, &QThread::deleteLater); + + keypairGenerator->moveToThread(generateThread); + + qDebug() << "Starting worker thread to generate 2048-bit RSA key-pair."; + generateThread->start(); +} + +void AccountManager::processGeneratedKeypair(const QByteArray& publicKey, const QByteArray& privateKey) { + + qDebug() << "Generated 2048-bit RSA key-pair. Storing private key and uploading public key."; + + // set the private key on our data-server account info + _accountInfo.setPrivateKey(privateKey); + + // get rid of the keypair generator now that we don't need it anymore + sender()->deleteLater(); +} + +void AccountManager::handleKeypairGenerationError() { + // for now there isn't anything we do with this except get the worker thread to clean up + sender()->deleteLater(); +} diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index 3628b5a865..1dc0506338 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -91,6 +91,8 @@ signals: void balanceChanged(qint64 newBalance); private slots: void processReply(); + void handleKeypairGenerationError(); + void processGeneratedKeypair(const QByteArray& publicKey, const QByteArray& privateKey); private: AccountManager(); AccountManager(AccountManager const& other); // not implemented @@ -98,6 +100,8 @@ private: void passSuccessToCallback(QNetworkReply* reply); void passErrorToCallback(QNetworkReply* reply); + + void generateNewKeypair(); Q_INVOKABLE void invokedRequest(const QString& path, bool requiresAuthentication, diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h index ae7699584b..8dc321bb02 100644 --- a/libraries/networking/src/DataServerAccountInfo.h +++ b/libraries/networking/src/DataServerAccountInfo.h @@ -41,6 +41,8 @@ public: const QUuid& getWalletID() const { return _walletID; } void setWalletID(const QUuid& walletID); + + void setPrivateKey(const QByteArray& privateKey) { _privateKey = privateKey; } qint64 getBalance() const { return _balance; } float getBalanceInSatoshis() const { return _balance / SATOSHIS_PER_CREDIT; } diff --git a/libraries/networking/src/RSAKeypairGenerator.cpp b/libraries/networking/src/RSAKeypairGenerator.cpp index ae61ac3eda..91a7fe8df6 100644 --- a/libraries/networking/src/RSAKeypairGenerator.cpp +++ b/libraries/networking/src/RSAKeypairGenerator.cpp @@ -17,6 +17,12 @@ #include "RSAKeypairGenerator.h" +RSAKeypairGenerator::RSAKeypairGenerator(QObject* parent) : + QObject(parent) +{ + +} + void RSAKeypairGenerator::generateKeypair() { RSA* keyPair = RSA_new(); diff --git a/libraries/networking/src/RSAKeypairGenerator.h b/libraries/networking/src/RSAKeypairGenerator.h index f98daddb1e..dd90313625 100644 --- a/libraries/networking/src/RSAKeypairGenerator.h +++ b/libraries/networking/src/RSAKeypairGenerator.h @@ -16,6 +16,8 @@ class RSAKeypairGenerator : public QObject { Q_OBJECT +public: + RSAKeypairGenerator(QObject* parent = 0); public slots: void generateKeypair(); signals: From f8c89d07906fcef2bace34a94931f02dfe562788 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Oct 2014 13:51:41 -0700 Subject: [PATCH 16/31] upload the generated public key to data-web, add getter for private key --- libraries/networking/src/AccountManager.cpp | 17 +++++++++++++++++ .../networking/src/DataServerAccountInfo.h | 1 + libraries/networking/src/LimitedNodeList.cpp | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index a8b88133d6..4d23bf5d39 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -509,6 +509,23 @@ void AccountManager::processGeneratedKeypair(const QByteArray& publicKey, const // set the private key on our data-server account info _accountInfo.setPrivateKey(privateKey); + // upload the public key so data-web has an up-to-date key + const QString PUBLIC_KEY_UPDATE_PATH = "api/v1/user/public_key"; + + // setup a multipart upload to send up the public key + QHttpMultiPart* requestMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + + QHttpPart keyPart; + keyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream")); + keyPart.setHeader(QNetworkRequest::ContentDispositionHeader, + QVariant("form-data; name=\"public_key\"; filename=\"public_key\"")); + keyPart.setBody(publicKey); + + requestMultiPart->append(keyPart); + + authenticatedRequest(PUBLIC_KEY_UPDATE_PATH, QNetworkAccessManager::PutOperation, + JSONCallbackParameters(), QByteArray(), requestMultiPart); + // get rid of the keypair generator now that we don't need it anymore sender()->deleteLater(); } diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h index 8dc321bb02..14419b929c 100644 --- a/libraries/networking/src/DataServerAccountInfo.h +++ b/libraries/networking/src/DataServerAccountInfo.h @@ -42,6 +42,7 @@ public: const QUuid& getWalletID() const { return _walletID; } void setWalletID(const QUuid& walletID); + const QByteArray& getPrivateKey() const { return _privateKey; } void setPrivateKey(const QByteArray& privateKey) { _privateKey = privateKey; } qint64 getBalance() const { return _balance; } diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 919dc75c23..c33b20a8e7 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -33,7 +33,7 @@ const char SOLO_NODE_TYPES[2] = { NodeType::AudioMixer }; -const QUrl DEFAULT_NODE_AUTH_URL = QUrl("https://data.highfidelity.io"); +const QUrl DEFAULT_NODE_AUTH_URL = QUrl("http://data.highfidelity.io"); std::auto_ptr LimitedNodeList::_sharedInstance; From bac6961235cad8a941e94102ec0dff63263290e1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Oct 2014 14:01:41 -0700 Subject: [PATCH 17/31] fix node auth url to be HTTPS --- libraries/networking/src/LimitedNodeList.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index c33b20a8e7..919dc75c23 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -33,7 +33,7 @@ const char SOLO_NODE_TYPES[2] = { NodeType::AudioMixer }; -const QUrl DEFAULT_NODE_AUTH_URL = QUrl("http://data.highfidelity.io"); +const QUrl DEFAULT_NODE_AUTH_URL = QUrl("https://data.highfidelity.io"); std::auto_ptr LimitedNodeList::_sharedInstance; From 22b599b8cc9cbe5d0f12b546bca40bd8f97e1d24 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Oct 2014 14:21:18 -0700 Subject: [PATCH 18/31] repair for persisting of Account to file after keypair generation --- interface/src/DatagramProcessor.cpp | 3 +-- libraries/networking/src/AccountManager.cpp | 30 ++++++++++----------- libraries/networking/src/AccountManager.h | 2 ++ libraries/networking/src/DomainHandler.cpp | 2 -- libraries/networking/src/DomainHandler.h | 4 --- libraries/networking/src/NodeList.cpp | 11 ++++++++ 6 files changed, 28 insertions(+), 24 deletions(-) diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index a5ad4a4c59..ba81f37920 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -138,8 +138,7 @@ void DatagramProcessor::processDatagrams() { case PacketTypeDomainUsernameRequest: { // flag the domain handler so it knows to send a username signature on next check-in // and then make it send that next check in - nodeList->getDomainHandler().setRequiresUsernameSignature(true); - nodeList->sendDomainServerCheckIn(); + qDebug() << "domain-server is requesting a connection with a username signature"; break; } case PacketTypeMuteEnvironment: { diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 4d23bf5d39..8217bb0b61 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -321,6 +321,16 @@ void AccountManager::passErrorToCallback(QNetworkReply* requestReply) { } } +void AccountManager::persistAccountToSettings() { + if (_shouldPersistToSettingsFile) { + // store this access token into the local settings + QSettings localSettings; + localSettings.beginGroup(ACCOUNTS_GROUP); + localSettings.setValue(_authURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE), + QVariant::fromValue(_accountInfo)); + } +} + bool AccountManager::hasValidAccessToken() { if (_accountInfo.getAccessToken().token.isEmpty() || _accountInfo.getAccessToken().isExpired()) { @@ -410,13 +420,7 @@ void AccountManager::requestAccessTokenFinished() { emit loginComplete(rootURL); - if (_shouldPersistToSettingsFile) { - // 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)); - } + persistAccountToSettings(); requestProfile(); generateNewKeypair(); @@ -461,15 +465,8 @@ void AccountManager::requestProfileFinished() { // the username has changed to whatever came back emit usernameChanged(_accountInfo.getUsername()); - if (_shouldPersistToSettingsFile) { - // 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)); - } + // store the whole profile into the local settings + persistAccountToSettings(); } else { // TODO: error handling @@ -508,6 +505,7 @@ void AccountManager::processGeneratedKeypair(const QByteArray& publicKey, const // set the private key on our data-server account info _accountInfo.setPrivateKey(privateKey); + persistAccountToSettings(); // upload the public key so data-web has an up-to-date key const QString PUBLIC_KEY_UPDATE_PATH = "api/v1/user/public_key"; diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index 1dc0506338..9c111c8d7b 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -97,6 +97,8 @@ private: AccountManager(); AccountManager(AccountManager const& other); // not implemented void operator=(AccountManager const& other); // not implemented + + void persistAccountToSettings(); void passSuccessToCallback(QNetworkReply* reply); void passErrorToCallback(QNetworkReply* reply); diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 48f0112ef6..760c9f4c04 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -30,7 +30,6 @@ DomainHandler::DomainHandler(QObject* parent) : _iceServerSockAddr(), _icePeer(), _isConnected(false), - _requiresUsernameSignature(false), _handshakeTimer(NULL), _settingsObject(), _failedSettingsRequests(0) @@ -49,7 +48,6 @@ void DomainHandler::clearConnectionInfo() { } _isConnected = false; - _requiresUsernameSignature = false; emit disconnectedFromDomain(); diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index ecee505569..86c6e6bc57 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -68,9 +68,6 @@ public: bool isConnected() const { return _isConnected; } void setIsConnected(bool isConnected); - bool requiresUsernameSignature() const { return _requiresUsernameSignature; } - void setRequiresUsernameSignature(bool requiresUsernameSignature) { _requiresUsernameSignature = requiresUsernameSignature; } - bool hasSettings() const { return !_settingsObject.isEmpty(); } void requestDomainSettings(); const QJsonObject& getSettingsObject() const { return _settingsObject; } @@ -107,7 +104,6 @@ private: HifiSockAddr _iceServerSockAddr; NetworkPeer _icePeer; bool _isConnected; - bool _requiresUsernameSignature; QTimer* _handshakeTimer; QJsonObject _settingsObject; int _failedSettingsRequests; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 25531861ca..b6983e17e3 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -309,6 +309,17 @@ void NodeList::sendDomainServerCheckIn() { packetStream << nodeTypeOfInterest; } + // if this is a connect request, and we can present a username signature, send it along + AccountManager& accountManager = AccountManager::getInstance(); + const QByteArray& privateKey = accountManager.getAccountInfo().getPrivateKey(); + if (!_domainHandler.isConnected()) { + if (!privateKey.isEmpty()) { + qDebug() << "Including username signature in domain connect request."; + } else { + qDebug() << "Private key not present - domain connect request cannot include username signature"; + } + } + if (!isUsingDTLS) { writeDatagram(domainServerPacket, _domainHandler.getSockAddr(), QUuid()); } From 970f2c7fad0121634fbb6266630f187e8403e075 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Oct 2014 15:15:16 -0700 Subject: [PATCH 19/31] have DSAI generate a username signature upon request, when it has private key --- libraries/networking/src/AccountManager.h | 2 +- .../networking/src/DataServerAccountInfo.cpp | 51 ++++++++++++++++++- .../networking/src/DataServerAccountInfo.h | 5 +- libraries/networking/src/NodeList.cpp | 7 +-- 4 files changed, 58 insertions(+), 7 deletions(-) diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index 9c111c8d7b..7b5ea8de8b 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -70,7 +70,7 @@ public: void requestAccessToken(const QString& login, const QString& password); void requestProfile(); - const DataServerAccountInfo& getAccountInfo() const { return _accountInfo; } + DataServerAccountInfo& getAccountInfo() { return _accountInfo; } public slots: void requestAccessTokenFinished(); diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index e6c800a1a7..27860c32d7 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include #include "DataServerAccountInfo.h" @@ -21,7 +23,8 @@ DataServerAccountInfo::DataServerAccountInfo() : _walletID(), _balance(0), _hasBalance(false), - _privateKey() + _privateKey(), + _usernameSignature() { } @@ -64,6 +67,9 @@ void DataServerAccountInfo::setUsername(const QString& username) { if (_username != username) { _username = username; + // clear our username signature so it has to be re-created + _usernameSignature = QByteArray(); + qDebug() << "Username changed to" << username; } } @@ -114,6 +120,49 @@ void DataServerAccountInfo::setProfileInfoFromJSON(const QJsonObject& jsonObject setWalletID(QUuid(user["wallet_id"].toString())); } +const QByteArray& DataServerAccountInfo::usernameSignature() { + if (_usernameSignature.isEmpty()) { + if (!_privateKey.isEmpty()) { + const char* privateKeyData = _privateKey.constData(); + RSA* rsaPrivateKey = d2i_RSAPrivateKey(NULL, + reinterpret_cast(&privateKeyData), + _privateKey.size()); + if (rsaPrivateKey) { + QByteArray usernameByteArray = _username.toUtf8(); + QByteArray encryptedUsername(RSA_size(rsaPrivateKey), 0); + + int encryptReturn = RSA_private_encrypt(usernameByteArray.size(), + reinterpret_cast(usernameByteArray.constData()), + reinterpret_cast(encryptedUsername.data()), + rsaPrivateKey, RSA_PKCS1_PADDING); + + if (encryptReturn != -1) { + _usernameSignature = usernameByteArray; + _usernameSignature.append(encryptedUsername); + } else { + qDebug() << "Error encrypting username signature."; + qDebug() << "Will re-attempt on next domain-server check in."; + } + } else { + qDebug() << "Could not create RSA struct from QByteArray private key."; + qDebug() << "Will re-attempt on next domain-server check in."; + } + } else { + qDebug() << "No private key present in DataServerAccountInfo. Re-log to generate new key."; + qDebug() << "Returning empty username signature."; + } + } + + return _usernameSignature; +} + +void DataServerAccountInfo::setPrivateKey(const QByteArray& privateKey) { + _privateKey = privateKey; + + // clear our username signature so it has to be re-created + _usernameSignature = QByteArray(); +} + QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) { out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey << info._walletID << info._privateKey; diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h index 14419b929c..2a569ada14 100644 --- a/libraries/networking/src/DataServerAccountInfo.h +++ b/libraries/networking/src/DataServerAccountInfo.h @@ -42,8 +42,8 @@ public: const QUuid& getWalletID() const { return _walletID; } void setWalletID(const QUuid& walletID); - const QByteArray& getPrivateKey() const { return _privateKey; } - void setPrivateKey(const QByteArray& privateKey) { _privateKey = privateKey; } + const QByteArray& usernameSignature(); + void setPrivateKey(const QByteArray& privateKey); qint64 getBalance() const { return _balance; } float getBalanceInSatoshis() const { return _balance / SATOSHIS_PER_CREDIT; } @@ -71,6 +71,7 @@ private: qint64 _balance; bool _hasBalance; QByteArray _privateKey; + QByteArray _usernameSignature; }; #endif // hifi_DataServerAccountInfo_h diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index b6983e17e3..1cfe9c21f3 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -310,11 +310,12 @@ void NodeList::sendDomainServerCheckIn() { } // if this is a connect request, and we can present a username signature, send it along - AccountManager& accountManager = AccountManager::getInstance(); - const QByteArray& privateKey = accountManager.getAccountInfo().getPrivateKey(); if (!_domainHandler.isConnected()) { - if (!privateKey.isEmpty()) { + const QByteArray& usernameSignature = AccountManager::getInstance().getAccountInfo().usernameSignature(); + + if (!usernameSignature.isEmpty()) { qDebug() << "Including username signature in domain connect request."; + packetStream << usernameSignature; } else { qDebug() << "Private key not present - domain connect request cannot include username signature"; } From c4b0dc9dccc551e97639529056f3bf4926f6f635 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Oct 2014 16:03:35 -0700 Subject: [PATCH 20/31] initial handling of username signature in domain-server --- domain-server/src/DomainServer.cpp | 70 ++++++++++++++----- domain-server/src/DomainServer.h | 10 ++- interface/src/Application.cpp | 11 +-- interface/src/DatagramProcessor.cpp | 10 +-- .../networking/src/DataServerAccountInfo.cpp | 12 ++-- .../networking/src/DataServerAccountInfo.h | 2 +- libraries/networking/src/NodeList.cpp | 11 ++- libraries/networking/src/PacketHeaders.cpp | 2 +- libraries/networking/src/PacketHeaders.h | 4 +- 9 files changed, 86 insertions(+), 46 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 2e90609b0c..70aab5ccd1 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -515,8 +515,11 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock NodeType_t nodeType; HifiSockAddr publicSockAddr, localSockAddr; + + QDataStream packetStream(packet); + packetStream.skipRawData(numBytesForPacketHeader(packet)); - int numPreInterestBytes = parseNodeDataFromByteArray(nodeType, publicSockAddr, localSockAddr, packet, senderSockAddr); + parseNodeDataFromByteArray(packetStream, nodeType, publicSockAddr, localSockAddr, senderSockAddr); QUuid packetUUID = uuidFromPacketHeader(packet); @@ -549,23 +552,50 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock } - QString connectedUsername; + QList nodeInterestList; + QString username; + QByteArray usernameSignature; + + packetStream >> nodeInterestList >> username >> usernameSignature; static const QVariant* allowedUsersVariant = valueForKeyPath(_settingsManager.getSettingsMap(), ALLOWED_USERS_SETTINGS_KEYPATH); - static QVariantList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toList() : QVariantList(); + static QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList(); if (!isAssignment && allowedUsers.count() > 0) { // this is an agent, we need to ask them to provide us with their signed username to see if they are allowed in // we always let in a user who is sending a packet from our local socket or from the localhost address if (senderSockAddr.getAddress() != LimitedNodeList::getInstance()->getLocalSockAddr().getAddress() && senderSockAddr.getAddress() != QHostAddress::LocalHost) { - QByteArray usernameRequestByteArray = byteArrayWithPopulatedHeader(PacketTypeDomainUsernameRequest); - // send this oauth request datagram back to the client - LimitedNodeList::getInstance()->writeUnverifiedDatagram(usernameRequestByteArray, senderSockAddr); + bool canConnect = false; - return; + if (allowedUsers.contains(username)) { + // it's possible this user can be allowed to connect, but we need to check their username signature + + // even if we have a public key for them right now, request a new one in case it has just changed + JSONCallbackParameters callbackParams; + callbackParams.jsonCallbackReceiver = this; + callbackParams.jsonCallbackMethod = "publicKeyJSONCallback"; + + const QString USER_PUBLIC_KEY_PATH = "api/v1/users/%1/public_key"; + + AccountManager::getInstance().unauthenticatedRequest(USER_PUBLIC_KEY_PATH.arg(username), + QNetworkAccessManager::GetOperation, callbackParams); + + if (_userPublicKeys.contains(username)) { + // if we do have a public key for the user, check for a signature match + } + } + + if (!canConnect) { + QByteArray usernameRequestByteArray = byteArrayWithPopulatedHeader(PacketTypeDomainConnectionDenied); + + // send this oauth request datagram back to the client + LimitedNodeList::getInstance()->writeUnverifiedDatagram(usernameRequestByteArray, senderSockAddr); + + return; + } } } @@ -599,12 +629,11 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock } // if we have a username from an OAuth connect request, set it on the DomainServerNodeData - nodeData->setUsername(connectedUsername); - + nodeData->setUsername(username); nodeData->setSendingSockAddr(senderSockAddr); // reply back to the user with a PacketTypeDomainList - sendDomainListToNode(newNode, senderSockAddr, nodeInterestListFromPacket(packet, numPreInterestBytes)); + sendDomainListToNode(newNode, senderSockAddr, nodeInterestList.toSet()); } } @@ -642,12 +671,9 @@ QUrl DomainServer::oauthAuthorizationURL(const QUuid& stateUUID) { return authorizationURL; } -int DomainServer::parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr, - HifiSockAddr& localSockAddr, const QByteArray& packet, - const HifiSockAddr& senderSockAddr) { - QDataStream packetStream(packet); - packetStream.skipRawData(numBytesForPacketHeader(packet)); - +int DomainServer::parseNodeDataFromByteArray(QDataStream& packetStream, NodeType_t& nodeType, + HifiSockAddr& publicSockAddr, HifiSockAddr& localSockAddr, + const HifiSockAddr& senderSockAddr) { packetStream >> nodeType; packetStream >> publicSockAddr >> localSockAddr; @@ -917,6 +943,10 @@ void DomainServer::sendPendingTransactionsToServer() { } +void DomainServer::publicKeyJSONCallback(const QJsonObject& data) { + qDebug() << data; +} + void DomainServer::transactionJSONCallback(const QJsonObject& data) { // check if this was successful - if so we can remove it from our list of pending if (data.value("status").toString() == "success") { @@ -1084,9 +1114,13 @@ void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiS if (!nodeUUID.isNull() && nodeList->nodeWithUUID(nodeUUID)) { NodeType_t throwawayNodeType; HifiSockAddr nodePublicAddress, nodeLocalAddress; + + QDataStream packetStream(receivedPacket); + packetStream.skipRawData(numBytesForPacketHeader(receivedPacket)); - int numNodeInfoBytes = parseNodeDataFromByteArray(throwawayNodeType, nodePublicAddress, nodeLocalAddress, - receivedPacket, senderSockAddr); + int numNodeInfoBytes = parseNodeDataFromByteArray(packetStream, throwawayNodeType, + nodePublicAddress, nodeLocalAddress, + senderSockAddr); SharedNodePointer checkInNode = nodeList->updateSocketsForNode(nodeUUID, nodePublicAddress, nodeLocalAddress); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index a06f5fa35b..9c1be03339 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -52,6 +52,7 @@ public slots: /// Called by NodeList to inform us a node has been killed void nodeKilled(SharedNodePointer node); + void publicKeyJSONCallback(const QJsonObject& data); void transactionJSONCallback(const QJsonObject& data); void restart(); @@ -82,8 +83,11 @@ private: void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr); void handleConnectRequest(const QByteArray& packet, const HifiSockAddr& senderSockAddr); - int parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr, - HifiSockAddr& localSockAddr, const QByteArray& packet, const HifiSockAddr& senderSockAddr); + int parseNodeDataFromByteArray(QDataStream& packetStream, + NodeType_t& nodeType, + HifiSockAddr& publicSockAddr, + HifiSockAddr& localSockAddr, + const HifiSockAddr& senderSockAddr); NodeSet nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes); void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr, const NodeSet& nodeInterestList); @@ -135,6 +139,8 @@ private: QSet _webAuthenticationStateSet; QHash _cookieSessionHash; + QHash _userPublicKeys; + QHash _connectingICEPeers; QHash _connectedICEPeers; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9dc478b9ba..0079631943 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3509,13 +3509,14 @@ void Application::domainChanged(const QString& domainHostname) { void Application::connectedToDomain(const QString& hostname) { AccountManager& accountManager = AccountManager::getInstance(); + const QUuid& domainID = NodeList::getInstance()->getDomainHandler().getUUID(); + + if (accountManager.isLoggedIn() && !domainID.isNull()) { + // update our data-server with the domain-server we're logged in with - if (accountManager.isLoggedIn()) { - // update our domain-server with the data-server we're logged in with + QString domainPutJsonString = "{\"location\":{\"domain_id\":\"" + uuidStringWithoutCurlyBraces(domainID) + "\"}}"; - QString domainPutJsonString = "{\"address\":{\"domain\":\"" + hostname + "\"}}"; - - accountManager.authenticatedRequest("/api/v1/users/address", QNetworkAccessManager::PutOperation, + accountManager.authenticatedRequest("/api/v1/user/location", QNetworkAccessManager::PutOperation, JSONCallbackParameters(), domainPutJsonString.toUtf8()); } } diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index ba81f37920..0232e0e71d 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -11,6 +11,7 @@ #include +#include #include #include "Application.h" @@ -135,10 +136,11 @@ void DatagramProcessor::processDatagrams() { application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(incomingPacket.size()); break; } - case PacketTypeDomainUsernameRequest: { - // flag the domain handler so it knows to send a username signature on next check-in - // and then make it send that next check in - qDebug() << "domain-server is requesting a connection with a username signature"; + case PacketTypeDomainConnectionDenied: { + // output to the log so the user knows they got a denied connection request + // and check and signal for an access token so that we can make sure they are logged in + qDebug() << "The domain-server denied a connection request."; + AccountManager::getInstance().checkAndSignalForAccessToken(); break; } case PacketTypeMuteEnvironment: { diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index 27860c32d7..7bb186ad7c 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -120,7 +120,7 @@ void DataServerAccountInfo::setProfileInfoFromJSON(const QJsonObject& jsonObject setWalletID(QUuid(user["wallet_id"].toString())); } -const QByteArray& DataServerAccountInfo::usernameSignature() { +const QByteArray& DataServerAccountInfo::getUsernameSignature() { if (_usernameSignature.isEmpty()) { if (!_privateKey.isEmpty()) { const char* privateKeyData = _privateKey.constData(); @@ -129,19 +129,17 @@ const QByteArray& DataServerAccountInfo::usernameSignature() { _privateKey.size()); if (rsaPrivateKey) { QByteArray usernameByteArray = _username.toUtf8(); - QByteArray encryptedUsername(RSA_size(rsaPrivateKey), 0); + _usernameSignature.resize(RSA_size(rsaPrivateKey)); int encryptReturn = RSA_private_encrypt(usernameByteArray.size(), reinterpret_cast(usernameByteArray.constData()), - reinterpret_cast(encryptedUsername.data()), + reinterpret_cast(_usernameSignature.data()), rsaPrivateKey, RSA_PKCS1_PADDING); - if (encryptReturn != -1) { - _usernameSignature = usernameByteArray; - _usernameSignature.append(encryptedUsername); - } else { + if (encryptReturn == -1) { qDebug() << "Error encrypting username signature."; qDebug() << "Will re-attempt on next domain-server check in."; + _usernameSignature = QByteArray(); } } else { qDebug() << "Could not create RSA struct from QByteArray private key."; diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h index 2a569ada14..20bdb11b31 100644 --- a/libraries/networking/src/DataServerAccountInfo.h +++ b/libraries/networking/src/DataServerAccountInfo.h @@ -42,7 +42,7 @@ public: const QUuid& getWalletID() const { return _walletID; } void setWalletID(const QUuid& walletID); - const QByteArray& usernameSignature(); + const QByteArray& getUsernameSignature(); void setPrivateKey(const QByteArray& privateKey); qint64 getBalance() const { return _balance; } diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 1cfe9c21f3..13ebfb59b8 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -302,16 +302,15 @@ void NodeList::sendDomainServerCheckIn() { QDataStream packetStream(&domainServerPacket, QIODevice::Append); // pack our data to send to the domain-server - packetStream << _ownerType << _publicSockAddr << _localSockAddr << (quint8) _nodeTypesOfInterest.size(); + packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList(); - // copy over the bytes for node types of interest, if required - foreach (NodeType_t nodeTypeOfInterest, _nodeTypesOfInterest) { - packetStream << nodeTypeOfInterest; - } // if this is a connect request, and we can present a username signature, send it along if (!_domainHandler.isConnected()) { - const QByteArray& usernameSignature = AccountManager::getInstance().getAccountInfo().usernameSignature(); + DataServerAccountInfo& accountInfo = AccountManager::getInstance().getAccountInfo(); + packetStream << accountInfo.getUsername(); + + const QByteArray& usernameSignature = AccountManager::getInstance().getAccountInfo().getUsernameSignature(); if (!usernameSignature.isEmpty()) { qDebug() << "Including username signature in domain connect request."; diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 1ddc8150f3..21f0755aa0 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -114,7 +114,7 @@ QString nameForPacketType(PacketType type) { PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainListRequest); PACKET_TYPE_NAME_LOOKUP(PacketTypeRequestAssignment); PACKET_TYPE_NAME_LOOKUP(PacketTypeCreateAssignment); - PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainUsernameRequest); + PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainConnectionDenied); PACKET_TYPE_NAME_LOOKUP(PacketTypeMuteEnvironment); PACKET_TYPE_NAME_LOOKUP(PacketTypeAudioStreamStats); PACKET_TYPE_NAME_LOOKUP(PacketTypeDataServerConfirm); diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 59959071c6..ab596673f5 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -38,7 +38,7 @@ enum PacketType { PacketTypeDomainListRequest, PacketTypeRequestAssignment, PacketTypeCreateAssignment, - PacketTypeDomainUsernameRequest, + PacketTypeDomainConnectionDenied, PacketTypeMuteEnvironment, PacketTypeAudioStreamStats, PacketTypeDataServerConfirm, @@ -81,7 +81,7 @@ typedef char PacketVersion; const QSet NON_VERIFIED_PACKETS = QSet() << PacketTypeDomainServerRequireDTLS << PacketTypeDomainConnectRequest - << PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainUsernameRequest + << PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainConnectionDenied << PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse << PacketTypeNodeJsonStats << PacketTypeVoxelQuery << PacketTypeParticleQuery << PacketTypeEntityQuery << PacketTypeOctreeDataNack << PacketTypeVoxelEditNack << PacketTypeParticleEditNack << PacketTypeEntityEditNack From 46fb7fb4a9bf12095cdb52227fe13563da668967 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Oct 2014 16:12:04 -0700 Subject: [PATCH 21/31] return QNetworkReply for AccountManager callbacks --- libraries/networking/src/AccountManager.cpp | 6 ++---- libraries/networking/src/AddressManager.cpp | 6 ++++-- libraries/networking/src/AddressManager.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 8217bb0b61..356f369ec6 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -282,14 +282,12 @@ 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())); + Q_ARG(QNetworkReply&, *requestReply)); // remove the related reply-callback group from the map _pendingCallbackMap.remove(requestReply); @@ -297,7 +295,7 @@ void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) { } else { if (VERBOSE_HTTP_REQUEST_DEBUGGING) { qDebug() << "Received JSON response from data-server that has no matching callback."; - qDebug() << jsonResponse; + qDebug() << QJsonDocument::fromJson(requestReply->readAll()); } } } diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 3d7617e17b..3d20cf7696 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -10,6 +10,7 @@ // #include +#include #include #include @@ -134,8 +135,9 @@ void AddressManager::handleLookupString(const QString& lookupString) { } } -void AddressManager::handleAPIResponse(const QJsonObject &jsonObject) { - QJsonObject dataObject = jsonObject["data"].toObject(); +void AddressManager::handleAPIResponse(QNetworkReply& requestReply) { + QJsonObject responseObject = QJsonDocument::fromJson(requestReply.readAll()).object(); + QJsonObject dataObject = responseObject["data"].toObject(); const QString ADDRESS_API_DOMAIN_KEY = "domain"; const QString ADDRESS_API_ONLINE_KEY = "online"; diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index fe6219b3f7..bc0f2716dd 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -42,7 +42,7 @@ public: public slots: void handleLookupString(const QString& lookupString); - void handleAPIResponse(const QJsonObject& jsonObject); + void handleAPIResponse(QNetworkReply& requestReply); void handleAPIError(QNetworkReply& errorReply); void goToUser(const QString& username); signals: From e9bcc901663217ab93d32e27edda6ba34ed2292d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Oct 2014 16:16:54 -0700 Subject: [PATCH 22/31] handle remaining conversions for AccountManager passing QNetworkReply --- assignment-client/src/octree/OctreeServer.cpp | 47 ------------------- assignment-client/src/octree/OctreeServer.h | 3 -- domain-server/src/DomainServer.cpp | 11 +++-- domain-server/src/DomainServer.h | 2 +- interface/src/ModelUploader.cpp | 6 ++- interface/src/ModelUploader.h | 4 +- libraries/networking/src/AddressManager.cpp | 2 +- .../networking/src/UserActivityLogger.cpp | 2 +- libraries/networking/src/UserActivityLogger.h | 2 +- 9 files changed, 16 insertions(+), 63 deletions(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 42af8ff1b9..d0a17287cb 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -861,8 +861,6 @@ void OctreeServer::readPendingDatagram(const QByteArray& receivedPacket, const H } } else if (packetType == PacketTypeJurisdictionRequest) { _jurisdictionSender->queueReceivedPacket(matchingNode, receivedPacket); - } else if (packetType == PacketTypeSignedTransactionPayment) { - handleSignedTransactionPayment(packetType, receivedPacket); } else if (_octreeInboundPacketProcessor && getOctree()->handlesEditPacketType(packetType)) { _octreeInboundPacketProcessor->queueReceivedPacket(matchingNode, receivedPacket); } else { @@ -1245,51 +1243,6 @@ QString OctreeServer::getStatusLink() { return result; } -void OctreeServer::handleSignedTransactionPayment(PacketType packetType, const QByteArray& datagram) { - // for now we're not verifying that this is actual payment for any octree edits - // just use the AccountManager to send it up to the data server and have it redeemed - AccountManager& accountManager = AccountManager::getInstance(); - - const int NUM_BYTES_SIGNED_TRANSACTION_BINARY_MESSAGE = 72; - const int NUM_BYTES_SIGNED_TRANSACTION_BINARY_SIGNATURE = 256; - - int numBytesPacketHeader = numBytesForPacketHeaderGivenPacketType(packetType); - - // pull out the transaction message in binary - QByteArray messageHex = datagram.mid(numBytesPacketHeader, NUM_BYTES_SIGNED_TRANSACTION_BINARY_MESSAGE).toHex(); - // pull out the binary signed message digest - QByteArray signatureHex = datagram.mid(numBytesPacketHeader + NUM_BYTES_SIGNED_TRANSACTION_BINARY_MESSAGE, - NUM_BYTES_SIGNED_TRANSACTION_BINARY_SIGNATURE).toHex(); - - // setup the QJSONObject we are posting - QJsonObject postObject; - - const QString TRANSACTION_OBJECT_MESSAGE_KEY = "message"; - const QString TRANSACTION_OBJECT_SIGNATURE_KEY = "signature"; - const QString POST_OBJECT_TRANSACTION_KEY = "transaction"; - - QJsonObject transactionObject; - transactionObject.insert(TRANSACTION_OBJECT_MESSAGE_KEY, QString(messageHex)); - transactionObject.insert(TRANSACTION_OBJECT_SIGNATURE_KEY, QString(signatureHex)); - - postObject.insert(POST_OBJECT_TRANSACTION_KEY, transactionObject); - - // setup our callback params - JSONCallbackParameters callbackParameters; - callbackParameters.jsonCallbackReceiver = this; - callbackParameters.jsonCallbackMethod = "handleSignedTransactionPaymentResponse"; - - accountManager.unauthenticatedRequest("/api/v1/transactions/redeem", QNetworkAccessManager::PostOperation, - callbackParameters, QJsonDocument(postObject).toJson()); - -} - -void OctreeServer::handleSignedTransactionPaymentResponse(const QJsonObject& jsonObject) { - // pull the ID to debug the transaction - QString transactionIDString = jsonObject["data"].toObject()["transaction"].toObject()["id"].toString(); - qDebug() << "Redeemed transaction with ID" << transactionIDString << "successfully."; -} - void OctreeServer::sendStatsPacket() { // TODO: we have too many stats to fit in a single MTU... so for now, we break it into multiple JSON objects and // send them separately. What we really should do is change the NodeList::sendStatsToDomainServer() to handle the diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index aa0c419dd4..2fcaae8c78 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -127,8 +127,6 @@ public slots: void nodeKilled(SharedNodePointer node); void sendStatsPacket(); - void handleSignedTransactionPaymentResponse(const QJsonObject& jsonObject); - void readPendingDatagrams() { }; // this will not be called since our datagram processing thread will handle void readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr); @@ -141,7 +139,6 @@ protected: QString getConfiguration(); QString getStatusLink(); - void handleSignedTransactionPayment(PacketType packetType, const QByteArray& datagram); void setupDatagramProcessingThread(); int _argc; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 70aab5ccd1..acc0f20328 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -565,9 +565,10 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock if (!isAssignment && allowedUsers.count() > 0) { // this is an agent, we need to ask them to provide us with their signed username to see if they are allowed in // we always let in a user who is sending a packet from our local socket or from the localhost address - if (senderSockAddr.getAddress() != LimitedNodeList::getInstance()->getLocalSockAddr().getAddress() - && senderSockAddr.getAddress() != QHostAddress::LocalHost) { - + +// if (senderSockAddr.getAddress() != LimitedNodeList::getInstance()->getLocalSockAddr().getAddress() +// && senderSockAddr.getAddress() != QHostAddress::LocalHost) { + if (true) { bool canConnect = false; if (allowedUsers.contains(username)) { @@ -943,8 +944,8 @@ void DomainServer::sendPendingTransactionsToServer() { } -void DomainServer::publicKeyJSONCallback(const QJsonObject& data) { - qDebug() << data; +void DomainServer::publicKeyJSONCallback(QNetworkReply& requestReply) { + } void DomainServer::transactionJSONCallback(const QJsonObject& data) { diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 9c1be03339..d94c7157d1 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -52,7 +52,7 @@ public slots: /// Called by NodeList to inform us a node has been killed void nodeKilled(SharedNodePointer node); - void publicKeyJSONCallback(const QJsonObject& data); + void publicKeyJSONCallback(QNetworkReply& requestReply); void transactionJSONCallback(const QJsonObject& data); void restart(); diff --git a/interface/src/ModelUploader.cpp b/interface/src/ModelUploader.cpp index f11d035ebe..7077c44287 100644 --- a/interface/src/ModelUploader.cpp +++ b/interface/src/ModelUploader.cpp @@ -364,7 +364,9 @@ void ModelUploader::send() { _progressBar = NULL; } -void ModelUploader::checkJSON(const QJsonObject& jsonResponse) { +void ModelUploader::checkJSON(QNetworkReply& requestReply) { + QJsonObject jsonResponse = QJsonDocument::fromJson(requestReply.readAll()).object(); + if (jsonResponse.contains("status") && jsonResponse.value("status").toString() == "success") { qDebug() << "status : success"; JSONCallbackParameters callbackParams; @@ -426,7 +428,7 @@ void ModelUploader::uploadUpdate(qint64 bytesSent, qint64 bytesTotal) { } } -void ModelUploader::uploadSuccess(const QJsonObject& jsonResponse) { +void ModelUploader::uploadSuccess(QNetworkReply& requestReply) { if (_progressDialog) { _progressDialog->accept(); } diff --git a/interface/src/ModelUploader.h b/interface/src/ModelUploader.h index a1c7a27393..7d8ad2b526 100644 --- a/interface/src/ModelUploader.h +++ b/interface/src/ModelUploader.h @@ -40,9 +40,9 @@ public slots: void send(); private slots: - void checkJSON(const QJsonObject& jsonResponse); + void checkJSON(QNetworkReply& requestReply); void uploadUpdate(qint64 bytesSent, qint64 bytesTotal); - void uploadSuccess(const QJsonObject& jsonResponse); + void uploadSuccess(QNetworkReply& requestReply); void uploadFailed(QNetworkReply& errorReply); void checkS3(); void processCheck(); diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 3d20cf7696..1a7f6d3dbc 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -192,7 +192,7 @@ void AddressManager::handleAPIResponse(QNetworkReply& requestReply) { } else { qDebug() << "Received an address manager API response with no domain key. Cannot parse."; - qDebug() << jsonObject; + qDebug() << responseObject; } } else { // we've been told that this result exists but is offline, emit our signal so the application can handle diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 549f02ae3c..4fc6905e3a 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -69,7 +69,7 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall multipart); } -void UserActivityLogger::requestFinished(const QJsonObject& object) { +void UserActivityLogger::requestFinished(QNetworkReply& requestReply) { // qDebug() << object; } diff --git a/libraries/networking/src/UserActivityLogger.h b/libraries/networking/src/UserActivityLogger.h index 1bd966d632..9b100461ee 100644 --- a/libraries/networking/src/UserActivityLogger.h +++ b/libraries/networking/src/UserActivityLogger.h @@ -39,7 +39,7 @@ public slots: void wentTo(QString destinationType, QString destinationName); private slots: - void requestFinished(const QJsonObject& object); + void requestFinished(QNetworkReply& requestReply); void requestError(QNetworkReply& errorReply); private: From 13f43dfc70654d89498079cb6e5932fff83cd674 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Oct 2014 16:20:17 -0700 Subject: [PATCH 23/31] fix for remaining AccountManager converted callbacks --- libraries/networking/src/DataServerAccountInfo.cpp | 4 +++- libraries/networking/src/DataServerAccountInfo.h | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index 7bb186ad7c..4a88186c96 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -11,6 +11,7 @@ #include +#include #include #include "DataServerAccountInfo.h" @@ -101,7 +102,8 @@ void DataServerAccountInfo::setBalance(qint64 balance) { } } -void DataServerAccountInfo::setBalanceFromJSON(const QJsonObject& jsonObject) { +void DataServerAccountInfo::setBalanceFromJSON(QNetworkReply& requestReply) { + QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); if (jsonObject["status"].toString() == "success") { qint64 balanceInSatoshis = jsonObject["data"].toObject()["wallet"].toObject()["balance"].toDouble(); setBalance(balanceInSatoshis); diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h index 20bdb11b31..2f02a7aee3 100644 --- a/libraries/networking/src/DataServerAccountInfo.h +++ b/libraries/networking/src/DataServerAccountInfo.h @@ -13,6 +13,7 @@ #define hifi_DataServerAccountInfo_h #include +#include #include #include "OAuthAccessToken.h" @@ -50,7 +51,7 @@ public: void setBalance(qint64 balance); bool hasBalance() const { return _hasBalance; } void setHasBalance(bool hasBalance) { _hasBalance = hasBalance; } - Q_INVOKABLE void setBalanceFromJSON(const QJsonObject& jsonObject); + Q_INVOKABLE void setBalanceFromJSON(QNetworkReply& requestReply); bool hasProfile() const; From e3ba8cddbb88b6b3d97ab8a82ca4af99901cabab Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Oct 2014 16:35:07 -0700 Subject: [PATCH 24/31] store returned public keys in domain-server --- domain-server/src/DomainServer.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index acc0f20328..74c4ad097d 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -581,6 +581,8 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock const QString USER_PUBLIC_KEY_PATH = "api/v1/users/%1/public_key"; + qDebug() << "Requesting public key for user" << username; + AccountManager::getInstance().unauthenticatedRequest(USER_PUBLIC_KEY_PATH.arg(username), QNetworkAccessManager::GetOperation, callbackParams); @@ -945,7 +947,27 @@ void DomainServer::sendPendingTransactionsToServer() { } void DomainServer::publicKeyJSONCallback(QNetworkReply& requestReply) { + QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); + if (jsonObject["status"] == "success") { + // figure out which user this is for + + const QString PUBLIC_KEY_URL_REGEX_STRING = "api\\/v1\\/users\\/([A-Za-z0-9_\\.]+)\\/public_key"; + QRegExp usernameRegex(PUBLIC_KEY_URL_REGEX_STRING); + + if (usernameRegex.indexIn(requestReply.url().toString()) != -1) { + QString username = usernameRegex.cap(1); + + qDebug() << "Storing a public key for user" << username; + + // pull the public key as a QByteArray from this response + const QString JSON_DATA_KEY = "data"; + const QString JSON_PUBLIC_KEY_KEY = "public_key"; + + _userPublicKeys[username] = + QByteArray::fromBase64(jsonObject[JSON_DATA_KEY].toObject()[JSON_PUBLIC_KEY_KEY].toString().toUtf8()); + } + } } void DomainServer::transactionJSONCallback(const QJsonObject& data) { From 50f27d3e16fad908e0c32d68b11280f2ffa72f6c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 14 Oct 2014 18:02:22 -0700 Subject: [PATCH 25/31] handle verification of username signature during connection --- domain-server/CMakeLists.txt | 8 ++ domain-server/src/DomainServer.cpp | 130 ++++++++++++------ domain-server/src/DomainServer.h | 2 + .../networking/src/DataServerAccountInfo.cpp | 3 + .../networking/src/RSAKeypairGenerator.cpp | 2 +- 5 files changed, 102 insertions(+), 43 deletions(-) diff --git a/domain-server/CMakeLists.txt b/domain-server/CMakeLists.txt index 17d8b61b11..24cb6e3fdc 100644 --- a/domain-server/CMakeLists.txt +++ b/domain-server/CMakeLists.txt @@ -38,4 +38,12 @@ endif () # link the shared hifi libraries link_hifi_libraries(embedded-webserver networking shared) +# find OpenSSL +find_package(OpenSSL REQUIRED) + +include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") + +# append OpenSSL to our list of libraries to link +list(APPEND ${TARGET_NAME}_LIBRARIES_TO_LINK "${OPENSSL_LIBRARIES}") + link_shared_dependencies() \ No newline at end of file diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 74c4ad097d..f65f196cf0 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -9,6 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include +#include + #include #include #include @@ -558,48 +561,14 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock packetStream >> nodeInterestList >> username >> usernameSignature; - static const QVariant* allowedUsersVariant = valueForKeyPath(_settingsManager.getSettingsMap(), - ALLOWED_USERS_SETTINGS_KEYPATH); - static QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList(); - - if (!isAssignment && allowedUsers.count() > 0) { - // this is an agent, we need to ask them to provide us with their signed username to see if they are allowed in - // we always let in a user who is sending a packet from our local socket or from the localhost address - -// if (senderSockAddr.getAddress() != LimitedNodeList::getInstance()->getLocalSockAddr().getAddress() -// && senderSockAddr.getAddress() != QHostAddress::LocalHost) { - if (true) { - bool canConnect = false; - - if (allowedUsers.contains(username)) { - // it's possible this user can be allowed to connect, but we need to check their username signature - - // even if we have a public key for them right now, request a new one in case it has just changed - JSONCallbackParameters callbackParams; - callbackParams.jsonCallbackReceiver = this; - callbackParams.jsonCallbackMethod = "publicKeyJSONCallback"; - - const QString USER_PUBLIC_KEY_PATH = "api/v1/users/%1/public_key"; - - qDebug() << "Requesting public key for user" << username; - - AccountManager::getInstance().unauthenticatedRequest(USER_PUBLIC_KEY_PATH.arg(username), - QNetworkAccessManager::GetOperation, callbackParams); - - if (_userPublicKeys.contains(username)) { - // if we do have a public key for the user, check for a signature match - } - } - - if (!canConnect) { - QByteArray usernameRequestByteArray = byteArrayWithPopulatedHeader(PacketTypeDomainConnectionDenied); - - // send this oauth request datagram back to the client - LimitedNodeList::getInstance()->writeUnverifiedDatagram(usernameRequestByteArray, senderSockAddr); - - return; - } - } + if (!isAssignment && !shouldAllowConnectionFromNode(username, usernameSignature, senderSockAddr)) { + // this is an agent and we've decided we won't let them connect - send them a packet to deny connection + QByteArray usernameRequestByteArray = byteArrayWithPopulatedHeader(PacketTypeDomainConnectionDenied); + + // send this oauth request datagram back to the client + LimitedNodeList::getInstance()->writeUnverifiedDatagram(usernameRequestByteArray, senderSockAddr); + + return; } if ((!isAssignment && !STATICALLY_ASSIGNED_NODES.contains(nodeType)) @@ -640,6 +609,83 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock } } +bool DomainServer::shouldAllowConnectionFromNode(const QString& username, + const QByteArray& usernameSignature, + const HifiSockAddr& senderSockAddr) { + static const QVariant* allowedUsersVariant = valueForKeyPath(_settingsManager.getSettingsMap(), + ALLOWED_USERS_SETTINGS_KEYPATH); + static QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList(); + + if (allowedUsers.count() > 0) { + // this is an agent, we need to ask them to provide us with their signed username to see if they are allowed in + // we always let in a user who is sending a packet from our local socket or from the localhost address + + if (senderSockAddr.getAddress() != LimitedNodeList::getInstance()->getLocalSockAddr().getAddress() + && senderSockAddr.getAddress() != QHostAddress::LocalHost) { + if (allowedUsers.contains(username)) { + // it's possible this user can be allowed to connect, but we need to check their username signature + + QByteArray publicKeyArray = _userPublicKeys.value(username); + if (!publicKeyArray.isEmpty()) { + // if we do have a public key for the user, check for a signature match + + const unsigned char* publicKeyData = reinterpret_cast(publicKeyArray.constData()); + + // first load up the public key into an RSA struct + RSA* rsaPublicKey = d2i_RSAPublicKey(NULL, &publicKeyData, publicKeyArray.size()); + + if (rsaPublicKey) { + QByteArray decryptedArray(RSA_size(rsaPublicKey), 0); + int decryptResult = RSA_public_decrypt(usernameSignature.size(), + reinterpret_cast(usernameSignature.constData()), + reinterpret_cast(decryptedArray.data()), + rsaPublicKey, RSA_PKCS1_PADDING); + + if (decryptResult != -1) { + if (username == decryptedArray) { + qDebug() << "Username signature matches for" << username << "- allowing connection."; + + // free up the public key before we return + RSA_free(rsaPublicKey); + + return true; + } else { + qDebug() << "Username signature did not match for" << username << "- denying connection."; + } + } else { + qDebug() << "Couldn't decrypt user signature for" << username << "- denying connection."; + } + + // free up the public key, we don't need it anymore + RSA_free(rsaPublicKey); + } else { + // we can't let this user in since we couldn't convert their public key to an RSA key we could use + qDebug() << "Couldn't convert data to RSA key for" << username << "- denying connection."; + } + } + + // even if we have a public key for them right now, request a new one in case it has just changed + JSONCallbackParameters callbackParams; + callbackParams.jsonCallbackReceiver = this; + callbackParams.jsonCallbackMethod = "publicKeyJSONCallback"; + + const QString USER_PUBLIC_KEY_PATH = "api/v1/users/%1/public_key"; + + qDebug() << "Requesting public key for user" << username; + + AccountManager::getInstance().unauthenticatedRequest(USER_PUBLIC_KEY_PATH.arg(username), + QNetworkAccessManager::GetOperation, callbackParams); + + } + } + } else { + // since we have no allowed user list, let them all in + return true; + } + + return false; +} + QUrl DomainServer::oauthRedirectURL() { return QString("https://%1:%2/oauth").arg(_hostname).arg(_httpsManager->serverPort()); } diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index d94c7157d1..1a402abada 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -83,6 +83,8 @@ private: void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr); void handleConnectRequest(const QByteArray& packet, const HifiSockAddr& senderSockAddr); + bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature, + const HifiSockAddr& senderSockAddr); int parseNodeDataFromByteArray(QDataStream& packetStream, NodeType_t& nodeType, HifiSockAddr& publicSockAddr, diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index 4a88186c96..61b1bbf418 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -143,6 +143,9 @@ const QByteArray& DataServerAccountInfo::getUsernameSignature() { qDebug() << "Will re-attempt on next domain-server check in."; _usernameSignature = QByteArray(); } + + // free the private key RSA struct now that we are done with it + RSA_free(rsaPrivateKey); } else { qDebug() << "Could not create RSA struct from QByteArray private key."; qDebug() << "Will re-attempt on next domain-server check in."; diff --git a/libraries/networking/src/RSAKeypairGenerator.cpp b/libraries/networking/src/RSAKeypairGenerator.cpp index 91a7fe8df6..5b53a84dc4 100644 --- a/libraries/networking/src/RSAKeypairGenerator.cpp +++ b/libraries/networking/src/RSAKeypairGenerator.cpp @@ -51,7 +51,7 @@ void RSAKeypairGenerator::generateKeypair() { // grab the public key and private key from the file unsigned char* publicKeyDER = NULL; - int publicKeyLength = i2d_RSA_PUBKEY(keyPair, &publicKeyDER); + int publicKeyLength = i2d_RSAPublicKey(keyPair, &publicKeyDER); unsigned char* privateKeyDER = NULL; int privateKeyLength = i2d_RSAPrivateKey(keyPair, &privateKeyDER); From 4d8e44afd1581e79992f6637b4f5c7a07703c70d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 15 Oct 2014 09:19:02 -0700 Subject: [PATCH 26/31] remove an extra space --- domain-server/src/DomainServer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index f65f196cf0..297c25ec9f 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -675,7 +675,6 @@ bool DomainServer::shouldAllowConnectionFromNode(const QString& username, AccountManager::getInstance().unauthenticatedRequest(USER_PUBLIC_KEY_PATH.arg(username), QNetworkAccessManager::GetOperation, callbackParams); - } } } else { From 05728681a5522f8fd04cbf9ae740f1fa1e9a6a46 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 15 Oct 2014 09:49:03 -0700 Subject: [PATCH 27/31] add a check to warn if OpenSSL is OS X system version --- domain-server/CMakeLists.txt | 6 ++++++ libraries/networking/CMakeLists.txt | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/domain-server/CMakeLists.txt b/domain-server/CMakeLists.txt index 24cb6e3fdc..9090a1b637 100644 --- a/domain-server/CMakeLists.txt +++ b/domain-server/CMakeLists.txt @@ -41,6 +41,12 @@ link_hifi_libraries(embedded-webserver networking shared) # find OpenSSL find_package(OpenSSL REQUIRED) +if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include") + # this is a user on OS X using system OpenSSL, which is going to throw warnings since they're deprecating for their common crypto + message(WARNING "The found version of OpenSSL is the OS X system version. This will produce deprecation warnings." + "\nWe recommend you install a newer version (at least 1.0.1h) in a different directory and set OPENSSL_ROOT_DIR in your env so Cmake can find it.") +endif () + include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") # append OpenSSL to our list of libraries to link diff --git a/libraries/networking/CMakeLists.txt b/libraries/networking/CMakeLists.txt index b86f53105c..e4dd37234b 100644 --- a/libraries/networking/CMakeLists.txt +++ b/libraries/networking/CMakeLists.txt @@ -13,6 +13,12 @@ endif () # find OpenSSL find_package(OpenSSL REQUIRED) +if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include") + # this is a user on OS X using system OpenSSL, which is going to throw warnings since they're deprecating for their common crypto + message(WARNING "The found version of OpenSSL is the OS X system version. This will produce deprecation warnings." + "\nWe recommend you install a newer version (at least 1.0.1h) in a different directory and set OPENSSL_ROOT_DIR in your env so Cmake can find it.") +endif () + include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") # append OpenSSL to our list of libraries to link From 3800389eb450e3f12043366eec878294db4a91bd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 15 Oct 2014 09:51:40 -0700 Subject: [PATCH 28/31] fix a bad equality check on QJsonObject --- domain-server/src/DomainServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 297c25ec9f..bc240ac1aa 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -994,7 +994,7 @@ void DomainServer::sendPendingTransactionsToServer() { void DomainServer::publicKeyJSONCallback(QNetworkReply& requestReply) { QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); - if (jsonObject["status"] == "success") { + if (jsonObject["status"].toString() == "success") { // figure out which user this is for const QString PUBLIC_KEY_URL_REGEX_STRING = "api\\/v1\\/users\\/([A-Za-z0-9_\\.]+)\\/public_key"; From 1613b16a9337d32f44b4bee02fa1598d4fab3746 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 15 Oct 2014 09:51:52 -0700 Subject: [PATCH 29/31] remove another extra space --- domain-server/src/DomainServer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index bc240ac1aa..ac68e6cf30 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -988,7 +988,6 @@ void DomainServer::sendPendingTransactionsToServer() { ++i; } } - } void DomainServer::publicKeyJSONCallback(QNetworkReply& requestReply) { From 9a3ba972e1cde8e42c02276694e4b866eec2dc1d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 15 Oct 2014 10:21:11 -0700 Subject: [PATCH 30/31] preload the public keys for allowed users to allow immediate connection --- domain-server/src/DomainServer.cpp | 46 +++++++++++++++++++++--------- domain-server/src/DomainServer.h | 4 +++ 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index ac68e6cf30..bbfa01c709 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -81,6 +81,9 @@ DomainServer::DomainServer(int argc, char* argv[]) : // setup automatic networking settings with data server setupAutomaticNetworking(); + + // preload some user public keys so they can connect on first request + preloadAllowedUserPublicKeys(); } } @@ -508,8 +511,6 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSettoStringList() : QStringList(); + + if (allowedUsers.size() > 0) { + // in the future we may need to limit how many requests here - for now assume that lists of allowed users are not + // going to create > 100 requests + foreach(const QString& username, allowedUsers) { + requestUserPublicKey(username); + } + } +} + +void DomainServer::requestUserPublicKey(const QString& username) { + // even if we have a public key for them right now, request a new one in case it has just changed + JSONCallbackParameters callbackParams; + callbackParams.jsonCallbackReceiver = this; + callbackParams.jsonCallbackMethod = "publicKeyJSONCallback"; + + const QString USER_PUBLIC_KEY_PATH = "api/v1/users/%1/public_key"; + + qDebug() << "Requesting public key for user" << username; + + AccountManager::getInstance().unauthenticatedRequest(USER_PUBLIC_KEY_PATH.arg(username), + QNetworkAccessManager::GetOperation, callbackParams); +} + QUrl DomainServer::oauthRedirectURL() { return QString("https://%1:%2/oauth").arg(_hostname).arg(_httpsManager->serverPort()); } diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 1a402abada..5e4da00601 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -85,6 +85,10 @@ private: void handleConnectRequest(const QByteArray& packet, const HifiSockAddr& senderSockAddr); bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature, const HifiSockAddr& senderSockAddr); + + void preloadAllowedUserPublicKeys(); + void requestUserPublicKey(const QString& username); + int parseNodeDataFromByteArray(QDataStream& packetStream, NodeType_t& nodeType, HifiSockAddr& publicSockAddr, From e4b19545b00dfcf94ec1eadb45ee3f912c03538f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 15 Oct 2014 10:36:21 -0700 Subject: [PATCH 31/31] generate a new keypair if one isn't found in settings --- libraries/networking/src/AccountManager.cpp | 6 ++++++ libraries/networking/src/DataServerAccountInfo.h | 1 + 2 files changed, 7 insertions(+) diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 356f369ec6..650e0e3321 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -146,6 +146,12 @@ void AccountManager::setAuthURL(const QUrl& authURL) { } else { requestProfile(); } + + // if we don't have a private key in settings we should generate a new keypair + if (!_accountInfo.hasPrivateKey()) { + qDebug() << "No private key present - generating a new key-pair."; + generateNewKeypair(); + } } } } diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h index 2f02a7aee3..9804bd755c 100644 --- a/libraries/networking/src/DataServerAccountInfo.h +++ b/libraries/networking/src/DataServerAccountInfo.h @@ -44,6 +44,7 @@ public: void setWalletID(const QUuid& walletID); const QByteArray& getUsernameSignature(); + bool hasPrivateKey() const { return !_privateKey.isEmpty(); } void setPrivateKey(const QByteArray& privateKey); qint64 getBalance() const { return _balance; }