From 34408c8144a7797012a705f8fdde9c2de085e8cc Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 25 Feb 2016 17:02:08 -0800 Subject: [PATCH] Revert "verified domain ownership for full automatic networking" --- assignment-client/src/octree/OctreeServer.cpp | 4 + .../resources/web/settings/js/settings.js | 2 +- domain-server/src/DomainGatekeeper.cpp | 1 + domain-server/src/DomainServer.cpp | 167 +++------ domain-server/src/DomainServer.h | 6 +- ice-server/CMakeLists.txt | 14 - ice-server/src/IceServer.cpp | 161 ++------ ice-server/src/IceServer.h | 14 +- interface/src/Application.cpp | 52 ++- interface/src/Application.h | 5 + interface/src/CrashHandler.cpp | 36 +- interface/src/CrashHandler.h | 2 +- .../scripting/WindowScriptingInterface.cpp | 2 +- libraries/networking/src/AccountManager.cpp | 353 +++++------------- libraries/networking/src/AccountManager.h | 29 +- .../networking/src/DataServerAccountInfo.cpp | 102 ++--- .../networking/src/DataServerAccountInfo.h | 20 +- libraries/networking/src/DomainHandler.cpp | 39 +- libraries/networking/src/DomainHandler.h | 7 - libraries/networking/src/LimitedNodeList.cpp | 4 + libraries/networking/src/LimitedNodeList.h | 2 +- libraries/networking/src/NodeList.cpp | 54 +-- .../networking/src/RSAKeypairGenerator.cpp | 6 +- .../networking/src/RSAKeypairGenerator.h | 20 +- .../networking/src/udt/PacketHeaders.cpp | 4 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- 26 files changed, 357 insertions(+), 752 deletions(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index aedf451924..31cab68cdf 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -236,6 +236,10 @@ OctreeServer::OctreeServer(ReceivedMessage& message) : { _averageLoopTime.updateAverage(0); qDebug() << "Octree server starting... [" << this << "]"; + + // make sure the AccountManager has an Auth URL for payment redemptions + + AccountManager::getInstance().setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL); } OctreeServer::~OctreeServer() { diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index fae07ace45..7dc94421be 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -778,7 +778,7 @@ function chooseFromHighFidelityDomains(clickedButton) { function createTemporaryDomain() { swal({ title: 'Create temporary place name', - text: "This will create a temporary place name and domain ID" + text: "This will create a temporary place name and domain ID (valid for 30 days)" + " so other users can easily connect to your domain.

" + "In order to make your domain reachable, this will also enable full automatic networking.", showCancelButton: true, diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index f385f5c489..3e4ee7b758 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -331,6 +331,7 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username, QCryptographicHash::Sha256); if (rsaPublicKey) { + QByteArray decryptedArray(RSA_size(rsaPublicKey), 0); int decryptResult = RSA_verify(NID_sha256, reinterpret_cast(usernameWithToken.constData()), usernameWithToken.size(), diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index f0df67a6f6..9e13c8e6fa 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -96,7 +96,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : // make sure we hear about newly connected nodes from our gatekeeper connect(&_gatekeeper, &DomainGatekeeper::connectedNode, this, &DomainServer::handleConnectedNode); - if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth()) { + if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth() && optionallySetupAssignmentPayment()) { // we either read a certificate and private key or were not passed one // and completed login or did not need to @@ -198,6 +198,7 @@ bool DomainServer::optionallySetupOAuth() { } AccountManager& accountManager = AccountManager::getInstance(); + accountManager.disableSettingsFilePersistence(); accountManager.setAuthURL(_oauthProviderURL); _oauthClientID = settingsMap.value(OAUTH_CLIENT_ID_OPTION).toString(); @@ -371,12 +372,20 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) { packetReceiver.registerListener(PacketType::ICEPing, &_gatekeeper, "processICEPingPacket"); packetReceiver.registerListener(PacketType::ICEPingReply, &_gatekeeper, "processICEPingReplyPacket"); packetReceiver.registerListener(PacketType::ICEServerPeerInformation, &_gatekeeper, "processICEPeerInformationPacket"); - packetReceiver.registerListener(PacketType::ICEServerHeartbeatDenied, this, "processICEServerHeartbeatDenialPacket"); // add whatever static assignments that have been parsed to the queue addStaticAssignmentsToQueue(); } +bool DomainServer::didSetupAccountManagerWithAccessToken() { + if (AccountManager::getInstance().hasValidAccessToken()) { + // we already gave the account manager a valid access token + return true; + } + + return resetAccountManagerAccessToken(); +} + const QString ACCESS_TOKEN_KEY_PATH = "metaverse.access_token"; bool DomainServer::resetAccountManagerAccessToken() { @@ -392,13 +401,9 @@ bool DomainServer::resetAccountManagerAccessToken() { if (accessTokenVariant && accessTokenVariant->canConvert(QMetaType::QString)) { accessToken = accessTokenVariant->toString(); } else { - qDebug() << "A domain-server feature that requires authentication is enabled but no access token is present."; - qDebug() << "Set an access token via the web interface, in your user or master config" + qDebug() << "A domain-server feature that requires authentication is enabled but no access token is present." + << "Set an access token via the web interface, in your user or master config" << "at keypath metaverse.access_token or in your ENV at key DOMAIN_SERVER_ACCESS_TOKEN"; - - // clear any existing access token from AccountManager - AccountManager::getInstance().setAccessTokenForCurrentAuthURL(QString()); - return false; } } else { @@ -424,6 +429,34 @@ bool DomainServer::resetAccountManagerAccessToken() { } } +bool DomainServer::optionallySetupAssignmentPayment() { + const QString PAY_FOR_ASSIGNMENTS_OPTION = "pay-for-assignments"; + const QVariantMap& settingsMap = _settingsManager.getSettingsMap(); + + if (settingsMap.contains(PAY_FOR_ASSIGNMENTS_OPTION) && + settingsMap.value(PAY_FOR_ASSIGNMENTS_OPTION).toBool() && + didSetupAccountManagerWithAccessToken()) { + + qDebug() << "Assignments will be paid for via" << qPrintable(_oauthProviderURL.toString()); + + // assume that the fact we are authing against HF data server means we will pay for assignments + // setup a timer to send transactions to pay assigned nodes every 30 seconds + QTimer* creditSetupTimer = new QTimer(this); + connect(creditSetupTimer, &QTimer::timeout, this, &DomainServer::setupPendingAssignmentCredits); + + const qint64 CREDIT_CHECK_INTERVAL_MSECS = 5 * 1000; + creditSetupTimer->start(CREDIT_CHECK_INTERVAL_MSECS); + + QTimer* nodePaymentTimer = new QTimer(this); + connect(nodePaymentTimer, &QTimer::timeout, this, &DomainServer::sendPendingTransactionsToServer); + + const qint64 TRANSACTION_SEND_INTERVAL_MSECS = 30 * 1000; + nodePaymentTimer->start(TRANSACTION_SEND_INTERVAL_MSECS); + } + + return true; +} + void DomainServer::setupAutomaticNetworking() { auto nodeList = DependencyManager::get(); @@ -434,9 +467,9 @@ void DomainServer::setupAutomaticNetworking() { setupICEHeartbeatForFullNetworking(); } - if (!resetAccountManagerAccessToken()) { - qDebug() << "Will not send heartbeat to Metaverse API without an access token."; - qDebug() << "If this is not a temporary domain add an access token to your config file or via the web interface."; + if (!didSetupAccountManagerWithAccessToken()) { + qDebug() << "Cannot send heartbeat to data server without an access token."; + qDebug() << "Add an access token to your config file or via the web interface."; return; } @@ -493,19 +526,6 @@ void DomainServer::setupICEHeartbeatForFullNetworking() { // we need this DS to know what our public IP is - start trying to figure that out now limitedNodeList->startSTUNPublicSocketUpdate(); - // to send ICE heartbeats we'd better have a private key locally with an uploaded public key - auto& accountManager = AccountManager::getInstance(); - auto domainID = accountManager.getAccountInfo().getDomainID(); - - // if we have an access token and we don't have a private key or the current domain ID has changed - // we should generate a new keypair - if (!accountManager.getAccountInfo().hasPrivateKey() || domainID != limitedNodeList->getSessionUUID()) { - accountManager.generateNewDomainKeypair(limitedNodeList->getSessionUUID()); - } - - // hookup to the signal from account manager that tells us when keypair is available - connect(&accountManager, &AccountManager::newKeypair, this, &DomainServer::handleKeypairChange); - if (!_iceHeartbeatTimer) { // setup a timer to heartbeat with the ice-server every so often _iceHeartbeatTimer = new QTimer { this }; @@ -1062,76 +1082,11 @@ void DomainServer::sendHeartbeatToDataServer(const QString& networkAddress) { domainUpdateJSON.toUtf8()); } +// TODO: have data-web respond with ice-server hostname to use + void DomainServer::sendHeartbeatToIceServer() { if (!_iceServerSocket.getAddress().isNull()) { - - auto& accountManager = AccountManager::getInstance(); - auto limitedNodeList = DependencyManager::get(); - - if (!accountManager.getAccountInfo().hasPrivateKey()) { - qWarning() << "Cannot send an ice-server heartbeat without a private key for signature."; - qWarning() << "Waiting for keypair generation to complete before sending ICE heartbeat."; - - if (!limitedNodeList->getSessionUUID().isNull()) { - accountManager.generateNewDomainKeypair(limitedNodeList->getSessionUUID()); - } else { - qWarning() << "Attempting to send ICE server heartbeat with no domain ID. This is not supported"; - } - - return; - } - - // NOTE: I'd love to specify the correct size for the packet here, but it's a little trickey with - // QDataStream and the possibility of IPv6 address for the sockets. - if (!_iceServerHeartbeatPacket) { - _iceServerHeartbeatPacket = NLPacket::create(PacketType::ICEServerHeartbeat); - } - - bool shouldRecreatePacket = false; - - if (_iceServerHeartbeatPacket->getPayloadSize() > 0) { - // if either of our sockets have changed we need to re-sign the heartbeat - // first read the sockets out from the current packet - _iceServerHeartbeatPacket->seek(0); - QDataStream heartbeatStream(_iceServerHeartbeatPacket.get()); - - QUuid senderUUID; - HifiSockAddr publicSocket, localSocket; - heartbeatStream >> senderUUID >> publicSocket >> localSocket; - - if (senderUUID != limitedNodeList->getSessionUUID() - || publicSocket != limitedNodeList->getPublicSockAddr() - || localSocket != limitedNodeList->getLocalSockAddr()) { - shouldRecreatePacket = true; - } - } else { - shouldRecreatePacket = true; - } - - if (shouldRecreatePacket) { - // either we don't have a heartbeat packet yet or some combination of sockets, ID and keypair have changed - // and we need to make a new one - - // reset the position in the packet before writing - _iceServerHeartbeatPacket->reset(); - - // write our plaintext data to the packet - QDataStream heartbeatDataStream(_iceServerHeartbeatPacket.get()); - heartbeatDataStream << limitedNodeList->getSessionUUID() - << limitedNodeList->getPublicSockAddr() << limitedNodeList->getLocalSockAddr(); - - // setup a QByteArray that points to the plaintext data - auto plaintext = QByteArray::fromRawData(_iceServerHeartbeatPacket->getPayload(), _iceServerHeartbeatPacket->getPayloadSize()); - - // generate a signature for the plaintext data in the packet - auto signature = accountManager.getAccountInfo().signPlaintext(plaintext); - - // pack the signature with the data - heartbeatDataStream << signature; - } - - // send the heartbeat packet to the ice server now - limitedNodeList->sendUnreliablePacket(*_iceServerHeartbeatPacket, _iceServerSocket); + DependencyManager::get()->sendHeartbeatToIceServer(_iceServerSocket); } } @@ -2015,31 +1970,3 @@ void DomainServer::processNodeDisconnectRequestPacket(QSharedPointer message) { - static const int NUM_HEARTBEAT_DENIALS_FOR_KEYPAIR_REGEN = 3; - - static int numHeartbeatDenials = 0; - if (++numHeartbeatDenials > NUM_HEARTBEAT_DENIALS_FOR_KEYPAIR_REGEN) { - qDebug() << "Received" << NUM_HEARTBEAT_DENIALS_FOR_KEYPAIR_REGEN << "heartbeat denials from ice-server" - << "- re-generating keypair now"; - - // we've hit our threshold of heartbeat denials, trigger a keypair re-generation - auto limitedNodeList = DependencyManager::get(); - AccountManager::getInstance().generateNewDomainKeypair(limitedNodeList->getSessionUUID()); - - // reset our number of heartbeat denials - numHeartbeatDenials = 0; - } -} - -void DomainServer::handleKeypairChange() { - if (_iceServerHeartbeatPacket) { - // reset the payload size of the ice-server heartbeat packet - this causes the packet to be re-generated - // the next time we go to send an ice-server heartbeat - _iceServerHeartbeatPacket->setPayloadSize(0); - - // send a heartbeat to the ice server immediately - sendHeartbeatToIceServer(); - } -} diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 3a83e8696b..326ca3e1a8 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -61,7 +61,6 @@ public slots: void processNodeJSONStatsPacket(QSharedPointer packetList, SharedNodePointer sendingNode); void processPathQueryPacket(QSharedPointer packet); void processNodeDisconnectRequestPacket(QSharedPointer message); - void processICEServerHeartbeatDenialPacket(QSharedPointer message); private slots: void aboutToQuit(); @@ -79,16 +78,16 @@ private slots: void handleTempDomainError(QNetworkReply& requestReply); void queuedQuit(QString quitMessage, int exitCode); - - void handleKeypairChange(); private: void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid()); bool optionallySetupOAuth(); bool optionallyReadX509KeyAndCertificate(); + bool optionallySetupAssignmentPayment(); void optionallyGetTemporaryName(const QStringList& arguments); + bool didSetupAccountManagerWithAccessToken(); bool resetAccountManagerAccessToken(); void setupAutomaticNetworking(); @@ -154,7 +153,6 @@ private: DomainServerSettingsManager _settingsManager; HifiSockAddr _iceServerSocket; - std::unique_ptr _iceServerHeartbeatPacket; QTimer* _iceHeartbeatTimer { nullptr }; // this looks like it dangles when created but it's parented to the DomainServer diff --git a/ice-server/CMakeLists.txt b/ice-server/CMakeLists.txt index e5bdffe2e2..cfec3c966c 100644 --- a/ice-server/CMakeLists.txt +++ b/ice-server/CMakeLists.txt @@ -6,17 +6,3 @@ setup_hifi_project(Network) # link the shared hifi libraries link_hifi_libraries(embedded-webserver networking shared) package_libraries_for_deployment() - -# 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 -target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES}) diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp index f38923b873..2baa7a13a7 100644 --- a/ice-server/src/IceServer.cpp +++ b/ice-server/src/IceServer.cpp @@ -9,21 +9,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "IceServer.h" - -#include -#include - -#include -#include -#include -#include +#include #include -#include #include #include +#include "IceServer.h" + const int CLEAR_INACTIVE_PEERS_INTERVAL_MSECS = 1 * 1000; const int PEER_SILENCE_THRESHOLD_MSECS = 5 * 1000; @@ -52,6 +45,7 @@ IceServer::IceServer(int argc, char* argv[]) : QTimer* inactivePeerTimer = new QTimer(this); connect(inactivePeerTimer, &QTimer::timeout, this, &IceServer::clearInactivePeers); inactivePeerTimer->start(CLEAR_INACTIVE_PEERS_INTERVAL_MSECS); + } bool IceServer::packetVersionMatch(const udt::Packet& packet) { @@ -76,14 +70,9 @@ void IceServer::processPacket(std::unique_ptr packet) { if (nlPacket->getType() == PacketType::ICEServerHeartbeat) { SharedNetworkPeer peer = addOrUpdateHeartbeatingPeer(*nlPacket); - if (peer) { - // so that we can send packets to the heartbeating peer when we need, we need to activate a socket now - peer->activateMatchingOrNewSymmetricSocket(nlPacket->getSenderSockAddr()); - } else { - // we couldn't verify this peer - respond back to them so they know they may need to perform keypair re-generation - static auto deniedPacket = NLPacket::create(PacketType::ICEServerHeartbeatDenied); - _serverSocket.writePacket(*deniedPacket, nlPacket->getSenderSockAddr()); - } + + // so that we can send packets to the heartbeating peer when we need, we need to activate a socket now + peer->activateMatchingOrNewSymmetricSocket(nlPacket->getSenderSockAddr()); } else if (nlPacket->getType() == PacketType::ICEServerQuery) { QDataStream heartbeatStream(nlPacket.get()); @@ -125,135 +114,31 @@ SharedNetworkPeer IceServer::addOrUpdateHeartbeatingPeer(NLPacket& packet) { // pull the UUID, public and private sock addrs for this peer QUuid senderUUID; HifiSockAddr publicSocket, localSocket; - QByteArray signature; QDataStream heartbeatStream(&packet); - heartbeatStream >> senderUUID >> publicSocket >> localSocket; + + heartbeatStream >> senderUUID; + heartbeatStream >> publicSocket >> localSocket; - auto signedPlaintext = QByteArray::fromRawData(packet.getPayload(), heartbeatStream.device()->pos()); - heartbeatStream >> signature; + // make sure we have this sender in our peer hash + SharedNetworkPeer matchingPeer = _activePeers.value(senderUUID); - // make sure this is a verified heartbeat before performing any more processing - if (isVerifiedHeartbeat(senderUUID, signedPlaintext, signature)) { - // make sure we have this sender in our peer hash - SharedNetworkPeer matchingPeer = _activePeers.value(senderUUID); + if (!matchingPeer) { + // if we don't have this sender we need to create them now + matchingPeer = QSharedPointer::create(senderUUID, publicSocket, localSocket); + _activePeers.insert(senderUUID, matchingPeer); - if (!matchingPeer) { - // if we don't have this sender we need to create them now - matchingPeer = QSharedPointer::create(senderUUID, publicSocket, localSocket); - _activePeers.insert(senderUUID, matchingPeer); - - qDebug() << "Added a new network peer" << *matchingPeer; - } else { - // we already had the peer so just potentially update their sockets - matchingPeer->setPublicSocket(publicSocket); - matchingPeer->setLocalSocket(localSocket); - } - - // update our last heard microstamp for this network peer to now - matchingPeer->setLastHeardMicrostamp(usecTimestampNow()); - - return matchingPeer; + qDebug() << "Added a new network peer" << *matchingPeer; } else { - // not verified, return the empty peer object - return SharedNetworkPeer(); - } -} - -bool IceServer::isVerifiedHeartbeat(const QUuid& domainID, const QByteArray& plaintext, const QByteArray& signature) { - // check if we have a private key for this domain ID - if we do not then fire off the request for it - auto it = _domainPublicKeys.find(domainID); - if (it != _domainPublicKeys.end()) { - - // attempt to verify the signature for this heartbeat - const unsigned char* publicKeyData = reinterpret_cast(it->second.constData()); - - // first load up the public key into an RSA struct - RSA* rsaPublicKey = d2i_RSA_PUBKEY(NULL, &publicKeyData, it->second.size()); - - if (rsaPublicKey) { - auto hashedPlaintext = QCryptographicHash::hash(plaintext, QCryptographicHash::Sha256); - int verificationResult = RSA_verify(NID_sha256, - reinterpret_cast(hashedPlaintext.constData()), - hashedPlaintext.size(), - reinterpret_cast(signature.constData()), - signature.size(), - rsaPublicKey); - - // free up the public key and remove connection token before we return - RSA_free(rsaPublicKey); - - if (verificationResult == 1) { - // this is the only success case - we return true here to indicate that the heartbeat is verified - return true; - } else { - qDebug() << "Failed to verify heartbeat for" << domainID << "- re-requesting public key from API."; - } - - } else { - // we can't let this user in since we couldn't convert their public key to an RSA key we could use - qWarning() << "Could not convert in-memory public key for" << domainID << "to usable RSA public key."; - qWarning() << "Re-requesting public key from API"; - } + // we already had the peer so just potentially update their sockets + matchingPeer->setPublicSocket(publicSocket); + matchingPeer->setLocalSocket(localSocket); } - // we could not verify this heartbeat (missing public key, could not load public key, bad actor) - // ask the metaverse API for the right public key and return false to indicate that this is not verified - requestDomainPublicKey(domainID); + // update our last heard microstamp for this network peer to now + matchingPeer->setLastHeardMicrostamp(usecTimestampNow()); - return false; -} - -void IceServer::requestDomainPublicKey(const QUuid& domainID) { - // send a request to the metaverse API for the public key for this domain - QNetworkAccessManager* manager = new QNetworkAccessManager { this }; - connect(manager, &QNetworkAccessManager::finished, this, &IceServer::publicKeyReplyFinished); - - QUrl publicKeyURL { NetworkingConstants::METAVERSE_SERVER_URL }; - QString publicKeyPath = QString("/api/v1/domains/%1/public_key").arg(uuidStringWithoutCurlyBraces(domainID)); - publicKeyURL.setPath(publicKeyPath); - - QNetworkRequest publicKeyRequest { publicKeyURL }; - publicKeyRequest.setAttribute(QNetworkRequest::User, domainID); - - qDebug() << "Requesting public key for domain with ID" << domainID; - - manager->get(publicKeyRequest); -} - -void IceServer::publicKeyReplyFinished(QNetworkReply* reply) { - // get the domain ID from the QNetworkReply attribute - QUuid domainID = reply->request().attribute(QNetworkRequest::User).toUuid(); - - if (reply->error() == QNetworkReply::NoError) { - // pull out the public key and store it for this domain - - // the response should be JSON - QJsonDocument responseDocument = QJsonDocument::fromJson(reply->readAll()); - - static const QString DATA_KEY = "data"; - static const QString PUBLIC_KEY_KEY = "public_key"; - static const QString STATUS_KEY = "status"; - static const QString SUCCESS_VALUE = "success"; - - auto responseObject = responseDocument.object(); - if (responseObject[STATUS_KEY].toString() == SUCCESS_VALUE) { - auto dataObject = responseObject[DATA_KEY].toObject(); - if (dataObject.contains(PUBLIC_KEY_KEY)) { - _domainPublicKeys[domainID] = QByteArray::fromBase64(dataObject[PUBLIC_KEY_KEY].toString().toUtf8()); - } else { - qWarning() << "There was no public key present in response for domain with ID" << domainID; - } - } else { - qWarning() << "The metaverse API did not return success for public key request for domain with ID" << domainID; - } - - } else { - // there was a problem getting the public key for the domain - // log it since it will be re-requested on the next heartbeat - - qWarning() << "Error retreiving public key for domain with ID" << domainID << "-" << reply->errorString(); - } + return matchingPeer; } void IceServer::sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr) { diff --git a/ice-server/src/IceServer.h b/ice-server/src/IceServer.h index 81234b2c3c..f1c2c06b65 100644 --- a/ice-server/src/IceServer.h +++ b/ice-server/src/IceServer.h @@ -16,15 +16,13 @@ #include #include -#include - #include #include #include #include #include -class QNetworkReply; +typedef QHash NetworkPeerHash; class IceServer : public QCoreApplication, public HTTPRequestHandler { Q_OBJECT @@ -33,7 +31,6 @@ public: bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false); private slots: void clearInactivePeers(); - void publicKeyReplyFinished(QNetworkReply* reply); private: bool packetVersionMatch(const udt::Packet& packet); void processPacket(std::unique_ptr packet); @@ -41,19 +38,10 @@ private: SharedNetworkPeer addOrUpdateHeartbeatingPeer(NLPacket& incomingPacket); void sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr); - bool isVerifiedHeartbeat(const QUuid& domainID, const QByteArray& plaintext, const QByteArray& signature); - void requestDomainPublicKey(const QUuid& domainID); - QUuid _id; udt::Socket _serverSocket; - - using NetworkPeerHash = QHash; NetworkPeerHash _activePeers; - HTTPManager _httpManager; - - using DomainPublicKeyHash = std::unordered_map; - DomainPublicKeyHash _domainPublicKeys; }; #endif // hifi_IceServer_h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d4f9c32ac1..5bb666bf33 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -553,6 +553,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(updateWindowTitle())); connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle())); connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails())); + connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived); // update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000; @@ -589,7 +590,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : connect(&accountManager, &AccountManager::usernameChanged, this, &Application::updateWindowTitle); // set the account manager's root URL and trigger a login request if we don't have the access token - accountManager.setIsAgent(true); accountManager.setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL); UserActivityLogger::getInstance().launch(applicationVersion()); @@ -903,6 +903,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : SpacemouseManager::getInstance().init(); #endif + auto& packetReceiver = nodeList->getPacketReceiver(); + packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket"); + // If the user clicks an an entity, we will check that it's an unlocked web entity, and if so, set the focus to it auto entityScriptingInterface = DependencyManager::get(); connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity, @@ -3954,10 +3957,30 @@ void Application::clearDomainOctreeDetails() { void Application::domainChanged(const QString& domainHostname) { updateWindowTitle(); clearDomainOctreeDetails(); + _domainConnectionRefusals.clear(); // disable physics until we have enough information about our new location to not cause craziness. _physicsEnabled = false; } +void Application::handleDomainConnectionDeniedPacket(QSharedPointer message) { + // Read deny reason from packet + quint16 reasonSize; + message->readPrimitive(&reasonSize); + QString reason = QString::fromUtf8(message->readWithoutCopy(reasonSize)); + + // 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 + qCDebug(interfaceapp) << "The domain-server denied a connection request: " << reason; + qCDebug(interfaceapp) << "You may need to re-log to generate a keypair so you can provide a username signature."; + + if (!_domainConnectionRefusals.contains(reason)) { + _domainConnectionRefusals.append(reason); + emit domainConnectionRefused(reason); + } + + AccountManager::getInstance().checkAndSignalForAccessToken(); +} + void Application::connectedToDomain(const QString& hostname) { AccountManager& accountManager = AccountManager::getInstance(); const QUuid& domainID = DependencyManager::get()->getDomainHandler().getUUID(); @@ -4501,6 +4524,33 @@ void Application::openUrl(const QUrl& url) { } } +void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject) { + // from the domain-handler, figure out the satoshi cost per voxel and per meter cubed + const QString VOXEL_SETTINGS_KEY = "voxels"; + const QString PER_VOXEL_COST_KEY = "per-voxel-credits"; + const QString PER_METER_CUBED_COST_KEY = "per-meter-cubed-credits"; + const QString VOXEL_WALLET_UUID = "voxel-wallet"; + + const QJsonObject& voxelObject = domainSettingsObject[VOXEL_SETTINGS_KEY].toObject(); + + qint64 satoshisPerVoxel = 0; + qint64 satoshisPerMeterCubed = 0; + QUuid voxelWalletUUID; + + if (!domainSettingsObject.isEmpty()) { + float perVoxelCredits = (float) voxelObject[PER_VOXEL_COST_KEY].toDouble(); + float perMeterCubedCredits = (float) voxelObject[PER_METER_CUBED_COST_KEY].toDouble(); + + satoshisPerVoxel = (qint64) floorf(perVoxelCredits * SATOSHIS_PER_CREDIT); + satoshisPerMeterCubed = (qint64) floorf(perMeterCubedCredits * SATOSHIS_PER_CREDIT); + + voxelWalletUUID = QUuid(voxelObject[VOXEL_WALLET_UUID].toString()); + } + + qCDebug(interfaceapp) << "Octree edits costs are" << satoshisPerVoxel << "per octree cell and" << satoshisPerMeterCubed << "per meter cubed"; + qCDebug(interfaceapp) << "Destination wallet UUID for edit payments is" << voxelWalletUUID; +} + void Application::loadDialog() { auto scriptEngines = DependencyManager::get(); QString fileNameString = OffscreenUi::getOpenFileName( diff --git a/interface/src/Application.h b/interface/src/Application.h index cce968e331..51b90bcb99 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -224,6 +224,7 @@ signals: void svoImportRequested(const QString& url); void checkBackgroundDownloads(); + void domainConnectionRefused(const QString& reason); void fullAvatarURLChanged(const QString& newValue, const QString& modelName); @@ -291,6 +292,9 @@ private slots: void activeChanged(Qt::ApplicationState state); + void domainSettingsReceived(const QJsonObject& domainSettingsObject); + void handleDomainConnectionDeniedPacket(QSharedPointer message); + void notifyPacketVersionMismatch(); void loadSettings(); @@ -469,6 +473,7 @@ private: typedef bool (Application::* AcceptURLMethod)(const QString &); static const QHash _acceptedExtensions; + QList _domainConnectionRefusals; glm::uvec2 _renderResolution; int _maxOctreePPS = DEFAULT_MAX_OCTREE_PPS; diff --git a/interface/src/CrashHandler.cpp b/interface/src/CrashHandler.cpp index f9cd7679ae..ce5facb580 100644 --- a/interface/src/CrashHandler.cpp +++ b/interface/src/CrashHandler.cpp @@ -22,8 +22,11 @@ #include #include +#include "DataServerAccountInfo.h" #include "Menu.h" +Q_DECLARE_METATYPE(DataServerAccountInfo) + static const QString RUNNING_MARKER_FILENAME = "Interface.running"; void CrashHandler::checkForAndHandleCrash() { @@ -54,7 +57,7 @@ CrashHandler::Action CrashHandler::promptUserForAction() { layout->addWidget(label); QRadioButton* option1 = new QRadioButton("Reset all my settings"); - QRadioButton* option2 = new QRadioButton("Reset my settings but retain avatar info."); + QRadioButton* option2 = new QRadioButton("Reset my settings but retain login and avatar info."); QRadioButton* option3 = new QRadioButton("Continue with my current settings"); option3->setChecked(true); layout->addWidget(option1); @@ -76,7 +79,7 @@ CrashHandler::Action CrashHandler::promptUserForAction() { return CrashHandler::DELETE_INTERFACE_INI; } if (option2->isChecked()) { - return CrashHandler::RETAIN_AVATAR_INFO; + return CrashHandler::RETAIN_LOGIN_AND_AVATAR_INFO; } } @@ -85,7 +88,7 @@ CrashHandler::Action CrashHandler::promptUserForAction() { } void CrashHandler::handleCrash(CrashHandler::Action action) { - if (action != CrashHandler::DELETE_INTERFACE_INI && action != CrashHandler::RETAIN_AVATAR_INFO) { + if (action != CrashHandler::DELETE_INTERFACE_INI && action != CrashHandler::RETAIN_LOGIN_AND_AVATAR_INFO) { // CrashHandler::DO_NOTHING or unexpected value return; } @@ -98,13 +101,18 @@ void CrashHandler::handleCrash(CrashHandler::Action action) { const QString DISPLAY_NAME_KEY = "displayName"; const QString FULL_AVATAR_URL_KEY = "fullAvatarURL"; const QString FULL_AVATAR_MODEL_NAME_KEY = "fullAvatarModelName"; + const QString ACCOUNTS_GROUP = "accounts"; QString displayName; QUrl fullAvatarURL; QString fullAvatarModelName; QUrl address; + QMap accounts; - if (action == CrashHandler::RETAIN_AVATAR_INFO) { - // Read avatar info + if (action == CrashHandler::RETAIN_LOGIN_AND_AVATAR_INFO) { + // Read login and avatar info + + qRegisterMetaType("DataServerAccountInfo"); + qRegisterMetaTypeStreamOperators("DataServerAccountInfo"); // Location and orientation settings.beginGroup(ADDRESS_MANAGER_GROUP); @@ -117,6 +125,13 @@ void CrashHandler::handleCrash(CrashHandler::Action action) { fullAvatarURL = settings.value(FULL_AVATAR_URL_KEY).toUrl(); fullAvatarModelName = settings.value(FULL_AVATAR_MODEL_NAME_KEY).toString(); settings.endGroup(); + + // Accounts + settings.beginGroup(ACCOUNTS_GROUP); + foreach(const QString& key, settings.allKeys()) { + accounts.insert(key, settings.value(key).value()); + } + settings.endGroup(); } // Delete Interface.ini @@ -125,8 +140,8 @@ void CrashHandler::handleCrash(CrashHandler::Action action) { settingsFile.remove(); } - if (action == CrashHandler::RETAIN_AVATAR_INFO) { - // Write avatar info + if (action == CrashHandler::RETAIN_LOGIN_AND_AVATAR_INFO) { + // Write login and avatar info // Location and orientation settings.beginGroup(ADDRESS_MANAGER_GROUP); @@ -139,6 +154,13 @@ void CrashHandler::handleCrash(CrashHandler::Action action) { settings.setValue(FULL_AVATAR_URL_KEY, fullAvatarURL); settings.setValue(FULL_AVATAR_MODEL_NAME_KEY, fullAvatarModelName); settings.endGroup(); + + // Accounts + settings.beginGroup(ACCOUNTS_GROUP); + foreach(const QString& key, accounts.keys()) { + settings.setValue(key, QVariant::fromValue(accounts.value(key))); + } + settings.endGroup(); } } diff --git a/interface/src/CrashHandler.h b/interface/src/CrashHandler.h index 61361b6107..fc754cf1ac 100644 --- a/interface/src/CrashHandler.h +++ b/interface/src/CrashHandler.h @@ -25,7 +25,7 @@ public: private: enum Action { DELETE_INTERFACE_INI, - RETAIN_AVATAR_INFO, + RETAIN_LOGIN_AND_AVATAR_INFO, DO_NOTHING }; diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 4c9c0df1cb..b8c82498a1 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -25,7 +25,7 @@ WindowScriptingInterface::WindowScriptingInterface() { const DomainHandler& domainHandler = DependencyManager::get()->getDomainHandler(); connect(&domainHandler, &DomainHandler::connectedToDomain, this, &WindowScriptingInterface::domainChanged); - connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &WindowScriptingInterface::domainConnectionRefused); + connect(qApp, &Application::domainConnectionRefused, this, &WindowScriptingInterface::domainConnectionRefused); connect(qApp, &Application::svoImportRequested, [this](const QString& urlString) { static const QMetaMethod svoImportRequestedSignal = diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index d0838c4a8f..4ded2216d0 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -12,12 +12,10 @@ #include #include -#include #include #include #include #include -#include #include #include #include @@ -62,12 +60,13 @@ JSONCallbackParameters::JSONCallbackParameters(QObject* jsonCallbackReceiver, co updateReciever(updateReceiver), updateSlot(updateSlot) { - } AccountManager::AccountManager() : _authURL(), - _pendingCallbackMap() + _pendingCallbackMap(), + _accountInfo(), + _shouldPersistToSettingsFile(true) { qRegisterMetaType("OAuthAccessToken"); qRegisterMetaTypeStreamOperators("OAuthAccessToken"); @@ -81,6 +80,9 @@ AccountManager::AccountManager() : qRegisterMetaType("QHttpMultiPart*"); connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged); + + // once we have a profile in account manager make sure we generate a new keypair + connect(this, &AccountManager::profileChanged, this, &AccountManager::generateNewKeypair); } const QString DOUBLE_SLASH_SUBSTITUTE = "slashslash"; @@ -91,9 +93,16 @@ void AccountManager::logout() { emit balanceChanged(0); connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged); - - // remove this account from the account settings file - removeAccountFromFile(); + + if (_shouldPersistToSettingsFile) { + QString keyURLString(_authURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE)); + QStringList path = QStringList() << ACCOUNTS_GROUP << keyURLString; + Setting::Handle(path).remove(); + + qCDebug(networking) << "Removed account info for" << _authURL << "from in-memory accounts and .ini file"; + } else { + qCDebug(networking) << "Cleared data server account info in account manager."; + } emit logoutComplete(); // the username has changed to blank @@ -115,83 +124,35 @@ void AccountManager::accountInfoBalanceChanged(qint64 newBalance) { emit balanceChanged(newBalance); } -QString accountFilePath() { - return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/AccountInfo.bin"; -} - -QVariantMap accountMapFromFile(bool& success) { - QFile accountFile { accountFilePath() }; - - if (accountFile.open(QIODevice::ReadOnly)) { - // grab the current QVariantMap from the settings file - QDataStream readStream(&accountFile); - QVariantMap accountMap; - - readStream >> accountMap; - - // close the file now that we have read the data - accountFile.close(); - - success = true; - - return accountMap; - } else { - // failed to open file, return empty QVariantMap - // there was only an error if the account file existed when we tried to load it - success = !accountFile.exists(); - - return QVariantMap(); - } -} - void AccountManager::setAuthURL(const QUrl& authURL) { if (_authURL != authURL) { _authURL = authURL; qCDebug(networking) << "AccountManager URL for authenticated requests has been changed to" << qPrintable(_authURL.toString()); - // check if there are existing access tokens to load from settings - QFile accountsFile { accountFilePath() }; - bool loadedMap = false; - auto accountsMap = accountMapFromFile(loadedMap); - - if (accountsFile.exists() && loadedMap) { - // pull out the stored account info and store it in memory - _accountInfo = accountsMap[_authURL.toString()].value(); - - qCDebug(networking) << "Found metaverse API account information for" << qPrintable(_authURL.toString()); - } else { - // we didn't have a file - see if we can migrate old settings and store them in the new file - + if (_shouldPersistToSettingsFile) { // check if there are existing access tokens to load from settings Settings settings; settings.beginGroup(ACCOUNTS_GROUP); - + foreach(const QString& key, settings.allKeys()) { // take a key copy to perform the double slash replacement QString keyCopy(key); QUrl keyURL(keyCopy.replace(DOUBLE_SLASH_SUBSTITUTE, "//")); - + if (keyURL == _authURL) { // pull out the stored access token and store it in memory _accountInfo = settings.value(key).value(); - - qCDebug(networking) << "Migrated an access token for" << qPrintable(keyURL.toString()) - << "from previous settings file"; + qCDebug(networking) << "Found a data-server access token for" << qPrintable(keyURL.toString()); + + // profile info isn't guaranteed to be saved too + if (_accountInfo.hasProfile()) { + emit profileChanged(); + } else { + requestProfile(); + } } } - - if (_accountInfo.getAccessToken().token.isEmpty()) { - qCWarning(networking) << "Unable to load account file. No existing account settings will be loaded."; - } else { - // persist the migrated settings to file - persistAccountToFile(); - } - } - - if (_isAgent && !_accountInfo.getAccessToken().token.isEmpty() && !_accountInfo.hasProfile()) { - // we are missing profile information, request it now - requestProfile(); } // tell listeners that the auth endpoint has changed @@ -338,11 +299,9 @@ void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) { } else { if (VERBOSE_HTTP_REQUEST_DEBUGGING) { - qCDebug(networking) << "Received JSON response from metaverse API that has no matching callback."; + qCDebug(networking) << "Received JSON response from data-server that has no matching callback."; qCDebug(networking) << QJsonDocument::fromJson(requestReply->readAll()); } - - requestReply->deleteLater(); } } @@ -358,69 +317,22 @@ void AccountManager::passErrorToCallback(QNetworkReply* requestReply) { _pendingCallbackMap.remove(requestReply); } else { if (VERBOSE_HTTP_REQUEST_DEBUGGING) { - qCDebug(networking) << "Received error response from metaverse API that has no matching callback."; + qCDebug(networking) << "Received error response from data-server that has no matching callback."; qCDebug(networking) << "Error" << requestReply->error() << "-" << requestReply->errorString(); qCDebug(networking) << requestReply->readAll(); } - - requestReply->deleteLater(); } } -bool writeAccountMapToFile(const QVariantMap& accountMap) { - // re-open the file and truncate it - QFile accountFile { accountFilePath() }; - if (accountFile.open(QIODevice::WriteOnly)) { - QDataStream writeStream(&accountFile); - - // persist the updated account QVariantMap to file - writeStream << accountMap; - - // close the file with the newly persisted settings - accountFile.close(); - - return true; - } else { - return false; +void AccountManager::persistAccountToSettings() { + if (_shouldPersistToSettingsFile) { + // store this access token into the local settings + QString keyURLString(_authURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE)); + QStringList path = QStringList() << ACCOUNTS_GROUP << keyURLString; + Setting::Handle(path).set(QVariant::fromValue(_accountInfo)); } } -void AccountManager::persistAccountToFile() { - - qCDebug(networking) << "Persisting AccountManager accounts to" << accountFilePath(); - - bool wasLoaded = false; - auto accountMap = accountMapFromFile(wasLoaded); - - if (wasLoaded) { - // replace the current account information for this auth URL in the account map - accountMap[_authURL.toString()] = QVariant::fromValue(_accountInfo); - - // re-open the file and truncate it - if (writeAccountMapToFile(accountMap)) { - return; - } - } - - qCWarning(networking) << "Could not load accounts file - unable to persist account information to file."; -} - -void AccountManager::removeAccountFromFile() { - bool wasLoaded = false; - auto accountMap = accountMapFromFile(wasLoaded); - - if (wasLoaded) { - accountMap.remove(_authURL.toString()); - if (writeAccountMapToFile(accountMap)) { - qCDebug(networking) << "Removed account info for" << _authURL << "from settings file."; - return; - } - } - - qCWarning(networking) << "Count not load accounts file - unable to remove account information for" << _authURL - << "from settings file."; -} - bool AccountManager::hasValidAccessToken() { if (_accountInfo.getAccessToken().token.isEmpty() || _accountInfo.getAccessToken().isExpired()) { @@ -447,19 +359,16 @@ bool AccountManager::checkAndSignalForAccessToken() { } void AccountManager::setAccessTokenForCurrentAuthURL(const QString& accessToken) { - // replace the account info access token with a new OAuthAccessToken + // clear our current DataServerAccountInfo + _accountInfo = DataServerAccountInfo(); + + // start the new account info with a new OAuthAccessToken OAuthAccessToken newOAuthToken; newOAuthToken.token = accessToken; - - if (!accessToken.isEmpty()) { - qCDebug(networking) << "Setting new AccountManager OAuth token. F2C:" << accessToken.left(2) << "L2C:" << accessToken.right(2); - } else if (!_accountInfo.getAccessToken().token.isEmpty()) { - qCDebug(networking) << "Clearing AccountManager OAuth token."; - } + + qCDebug(networking) << "Setting new account manager access token. F2C:" << accessToken.left(2) << "L2C:" << accessToken.right(2); _accountInfo.setAccessToken(newOAuthToken); - - persistAccountToFile(); } void AccountManager::requestAccessToken(const QString& login, const QString& password) { @@ -514,7 +423,7 @@ void AccountManager::requestAccessTokenFinished() { emit loginComplete(rootURL); - persistAccountToFile(); + persistAccountToSettings(); requestProfile(); } @@ -560,7 +469,7 @@ void AccountManager::requestProfileFinished() { emit usernameChanged(_accountInfo.getUsername()); // store the whole profile into the local settings - persistAccountToFile(); + persistAccountToSettings(); } else { // TODO: error handling @@ -573,141 +482,57 @@ void AccountManager::requestProfileError(QNetworkReply::NetworkError error) { qCDebug(networking) << "AccountManager requestProfileError - " << error; } -void AccountManager::generateNewKeypair(bool isUserKeypair, const QUuid& domainID) { - - if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(this, "generateNewKeypair", Q_ARG(bool, isUserKeypair), Q_ARG(QUuid, domainID)); - return; - } - - if (!isUserKeypair && domainID.isNull()) { - qCWarning(networking) << "AccountManager::generateNewKeypair called for domain keypair with no domain ID. Will not generate keypair."; - return; - } - - // make sure we don't already have an outbound keypair generation request - if (!_isWaitingForKeypairResponse) { - _isWaitingForKeypairResponse = true; - - // clear the current private key - qDebug() << "Clearing current private key in DataServerAccountInfo"; - _accountInfo.setPrivateKey(QByteArray()); - - // setup a new QThread to generate the keypair on, in case it takes a while - QThread* generateThread = new QThread(this); - generateThread->setObjectName("Account Manager Generator Thread"); - - // setup a keypair generator - RSAKeypairGenerator* keypairGenerator = new RSAKeypairGenerator; - - if (!isUserKeypair) { - keypairGenerator->setDomainID(domainID); - _accountInfo.setDomainID(domainID); - } - - // start keypair generation when the thread starts - connect(generateThread, &QThread::started, keypairGenerator, &RSAKeypairGenerator::generateKeypair); - - // handle success or failure of keypair generation - 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); - - qCDebug(networking) << "Starting worker thread to generate 2048-bit RSA keypair."; - generateThread->start(); - } -} - -void AccountManager::processGeneratedKeypair() { +void AccountManager::generateNewKeypair() { + // setup a new QThread to generate the keypair on, in case it takes a while + QThread* generateThread = new QThread(this); + generateThread->setObjectName("Account Manager Generator Thread"); - qCDebug(networking) << "Generated 2048-bit RSA keypair. Uploading public key now."; - - RSAKeypairGenerator* keypairGenerator = qobject_cast(sender()); - - if (keypairGenerator) { - // hold the private key to later set our metaverse API account info if upload succeeds - _pendingPrivateKey = keypairGenerator->getPrivateKey(); - - // upload the public key so data-web has an up-to-date key - const QString USER_PUBLIC_KEY_UPDATE_PATH = "api/v1/user/public_key"; - const QString DOMAIN_PUBLIC_KEY_UPDATE_PATH = "api/v1/domains/%1/public_key"; - - QString uploadPath; - if (keypairGenerator->getDomainID().isNull()) { - uploadPath = USER_PUBLIC_KEY_UPDATE_PATH; - } else { - uploadPath = DOMAIN_PUBLIC_KEY_UPDATE_PATH.arg(uuidStringWithoutCurlyBraces(keypairGenerator->getDomainID())); - } - - // 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(keypairGenerator->getPublicKey()); - - requestMultiPart->append(keyPart); - - // setup callback parameters so we know once the keypair upload has succeeded or failed - JSONCallbackParameters callbackParameters; - callbackParameters.jsonCallbackReceiver = this; - callbackParameters.jsonCallbackMethod = "publicKeyUploadSucceeded"; - callbackParameters.errorCallbackReceiver = this; - callbackParameters.errorCallbackMethod = "publicKeyUploadFailed"; - - sendRequest(uploadPath, AccountManagerAuth::Optional, QNetworkAccessManager::PutOperation, - callbackParameters, QByteArray(), requestMultiPart); - - keypairGenerator->deleteLater(); - } else { - qCWarning(networking) << "Expected processGeneratedKeypair to be called by a live RSAKeypairGenerator" - << "but the casted sender is NULL. Will not process generated keypair."; - } + // 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); + + qCDebug(networking) << "Starting worker thread to generate 2048-bit RSA key-pair."; + generateThread->start(); } -void AccountManager::publicKeyUploadSucceeded(QNetworkReply& reply) { - qDebug() << "Uploaded public key to Metaverse API. RSA keypair generation is completed."; - - // public key upload complete - store the matching private key and persist the account to settings - _accountInfo.setPrivateKey(_pendingPrivateKey); - _pendingPrivateKey.clear(); - persistAccountToFile(); - - // clear our waiting state - _isWaitingForKeypairResponse = false; - - emit newKeypair(); - - // delete the reply object now that we are done with it - reply.deleteLater(); -} - -void AccountManager::publicKeyUploadFailed(QNetworkReply& reply) { - // the public key upload has failed - qWarning() << "Public key upload failed from AccountManager" << reply.errorString(); - - // we aren't waiting for a response any longer - _isWaitingForKeypairResponse = false; - - // clear our pending private key - _pendingPrivateKey.clear(); - - // delete the reply object now that we are done with it - reply.deleteLater(); +void AccountManager::processGeneratedKeypair(const QByteArray& publicKey, const QByteArray& privateKey) { + + qCDebug(networking) << "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); + 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"; + + // 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); + + sendRequest(PUBLIC_KEY_UPDATE_PATH, AccountManagerAuth::Required, QNetworkAccessManager::PutOperation, + JSONCallbackParameters(), QByteArray(), requestMultiPart); + + // get rid of the keypair generator now that we don't need it anymore + sender()->deleteLater(); } void AccountManager::handleKeypairGenerationError() { - qCritical() << "Error generating keypair - this is likely to cause authentication issues."; - - // reset our waiting state for keypair response - _isWaitingForKeypairResponse = false; - + // 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 0ebefafbed..719279b0cf 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -62,12 +62,12 @@ public: QHttpMultiPart* dataMultiPart = NULL, const QVariantMap& propertyMap = QVariantMap()); - void setIsAgent(bool isAgent) { _isAgent = isAgent; } - const QUrl& getAuthURL() const { return _authURL; } void setAuthURL(const QUrl& authURL); bool hasAuthEndpoint() { return !_authURL.isEmpty(); } + void disableSettingsFilePersistence() { _shouldPersistToSettingsFile = false; } + bool isLoggedIn() { return !_authURL.isEmpty() && hasValidAccessToken(); } bool hasValidAccessToken(); Q_INVOKABLE bool checkAndSignalForAccessToken(); @@ -87,9 +87,7 @@ public slots: void logout(); void updateBalance(); void accountInfoBalanceChanged(qint64 newBalance); - void generateNewUserKeypair() { generateNewKeypair(); } - void generateNewDomainKeypair(const QUuid& domainID) { generateNewKeypair(false, domainID); } - + void generateNewKeypair(); signals: void authRequired(); void authEndpointChanged(); @@ -99,36 +97,25 @@ signals: void loginFailed(); void logoutComplete(); void balanceChanged(qint64 newBalance); - void newKeypair(); - private slots: void processReply(); void handleKeypairGenerationError(); - void processGeneratedKeypair(); - void publicKeyUploadSucceeded(QNetworkReply& reply); - void publicKeyUploadFailed(QNetworkReply& reply); - void generateNewKeypair(bool isUserKeypair = true, const QUuid& domainID = QUuid()); - + void processGeneratedKeypair(const QByteArray& publicKey, const QByteArray& privateKey); private: AccountManager(); - AccountManager(AccountManager const& other) = delete; - void operator=(AccountManager const& other) = delete; + AccountManager(AccountManager const& other); // not implemented + void operator=(AccountManager const& other); // not implemented - void persistAccountToFile(); - void removeAccountFromFile(); + void persistAccountToSettings(); void passSuccessToCallback(QNetworkReply* reply); void passErrorToCallback(QNetworkReply* reply); QUrl _authURL; - QMap _pendingCallbackMap; DataServerAccountInfo _accountInfo; - bool _isAgent { false }; - - bool _isWaitingForKeypairResponse { false }; - QByteArray _pendingPrivateKey; + bool _shouldPersistToSettingsFile; }; #endif // hifi_AccountManager_h diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index 9455fb1b88..5d633a8df1 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -25,6 +25,19 @@ #pragma clang diagnostic ignored "-Wdeprecated-declarations" #endif +DataServerAccountInfo::DataServerAccountInfo() : + _accessToken(), + _username(), + _xmppPassword(), + _discourseApiKey(), + _walletID(), + _balance(0), + _hasBalance(false), + _privateKey() +{ + +} + DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherInfo) : QObject() { _accessToken = otherInfo._accessToken; _username = otherInfo._username; @@ -34,7 +47,6 @@ DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherI _balance = otherInfo._balance; _hasBalance = otherInfo._hasBalance; _privateKey = otherInfo._privateKey; - _domainID = otherInfo._domainID; } DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountInfo& otherInfo) { @@ -54,7 +66,6 @@ void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) { swap(_balance, otherInfo._balance); swap(_hasBalance, otherInfo._hasBalance); swap(_privateKey, otherInfo._privateKey); - swap(_domainID, otherInfo._domainID); } void DataServerAccountInfo::setAccessTokenFromJSON(const QJsonObject& jsonObject) { @@ -117,62 +128,59 @@ void DataServerAccountInfo::setProfileInfoFromJSON(const QJsonObject& jsonObject } QByteArray DataServerAccountInfo::getUsernameSignature(const QUuid& connectionToken) { - auto lowercaseUsername = _username.toLower().toUtf8(); - auto plaintext = lowercaseUsername.append(connectionToken.toRfc4122()); + + if (!_privateKey.isEmpty()) { + const char* privateKeyData = _privateKey.constData(); + RSA* rsaPrivateKey = d2i_RSAPrivateKey(NULL, + reinterpret_cast(&privateKeyData), + _privateKey.size()); + if (rsaPrivateKey) { + QByteArray lowercaseUsername = _username.toLower().toUtf8(); + QByteArray usernameWithToken = QCryptographicHash::hash(lowercaseUsername.append(connectionToken.toRfc4122()), + QCryptographicHash::Sha256); + + QByteArray usernameSignature(RSA_size(rsaPrivateKey), 0); + unsigned int usernameSignatureSize = 0; + + int encryptReturn = RSA_sign(NID_sha256, + reinterpret_cast(usernameWithToken.constData()), + usernameWithToken.size(), + reinterpret_cast(usernameSignature.data()), + &usernameSignatureSize, + rsaPrivateKey); + + // free the private key RSA struct now that we are done with it + RSA_free(rsaPrivateKey); - auto signature = signPlaintext(plaintext); - if (!signature.isEmpty()) { - qDebug(networking) << "Returning username" << _username - << "signed with connection UUID" << uuidStringWithoutCurlyBraces(connectionToken); - } else { - qCDebug(networking) << "Error signing username with connection token"; - qCDebug(networking) << "Will re-attempt on next domain-server check in."; - } - - return signature; + if (encryptReturn == -1) { + qCDebug(networking) << "Error encrypting username signature."; + qCDebug(networking) << "Will re-attempt on next domain-server check in."; + } else { + qDebug(networking) << "Returning username" << _username << "signed with connection UUID" << uuidStringWithoutCurlyBraces(connectionToken); + return usernameSignature; + } + + } else { + qCDebug(networking) << "Could not create RSA struct from QByteArray private key."; + qCDebug(networking) << "Will re-attempt on next domain-server check in."; + } + } + return QByteArray(); } -QByteArray DataServerAccountInfo::signPlaintext(const QByteArray& plaintext) { - if (!_privateKey.isEmpty()) { - const char* privateKeyData = _privateKey.constData(); - RSA* rsaPrivateKey = d2i_RSAPrivateKey(NULL, - reinterpret_cast(&privateKeyData), - _privateKey.size()); - if (rsaPrivateKey) { - QByteArray signature(RSA_size(rsaPrivateKey), 0); - unsigned int signatureBytes = 0; - - QByteArray hashedPlaintext = QCryptographicHash::hash(plaintext, QCryptographicHash::Sha256); - - int encryptReturn = RSA_sign(NID_sha256, - reinterpret_cast(hashedPlaintext.constData()), - hashedPlaintext.size(), - reinterpret_cast(signature.data()), - &signatureBytes, - rsaPrivateKey); - - // free the private key RSA struct now that we are done with it - RSA_free(rsaPrivateKey); - - if (encryptReturn != -1) { - return signature; - } - } else { - qCDebug(networking) << "Could not create RSA struct from QByteArray private key."; - } - } - return QByteArray(); +void DataServerAccountInfo::setPrivateKey(const QByteArray& privateKey) { + _privateKey = privateKey; + } QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) { out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey - << info._walletID << info._privateKey << info._domainID; - + << info._walletID << info._privateKey; return out; } QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info) { in >> info._accessToken >> info._username >> info._xmppPassword >> info._discourseApiKey - >> info._walletID >> info._privateKey >> info._domainID; + >> info._walletID >> info._privateKey; return in; } diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h index 6223bc008e..9b80de5422 100644 --- a/libraries/networking/src/DataServerAccountInfo.h +++ b/libraries/networking/src/DataServerAccountInfo.h @@ -23,7 +23,7 @@ const float SATOSHIS_PER_CREDIT = 100000000.0f; class DataServerAccountInfo : public QObject { Q_OBJECT public: - DataServerAccountInfo() {}; + DataServerAccountInfo(); DataServerAccountInfo(const DataServerAccountInfo& otherInfo); DataServerAccountInfo& operator=(const DataServerAccountInfo& otherInfo); @@ -42,6 +42,10 @@ public: const QUuid& getWalletID() const { return _walletID; } void setWalletID(const QUuid& walletID); + + QByteArray getUsernameSignature(const QUuid& connectionToken); + bool hasPrivateKey() const { return !_privateKey.isEmpty(); } + void setPrivateKey(const QByteArray& privateKey); qint64 getBalance() const { return _balance; } float getBalanceInSatoshis() const { return _balance / SATOSHIS_PER_CREDIT; } @@ -50,15 +54,6 @@ public: void setHasBalance(bool hasBalance) { _hasBalance = hasBalance; } Q_INVOKABLE void setBalanceFromJSON(QNetworkReply& requestReply); - QByteArray getUsernameSignature(const QUuid& connectionToken); - bool hasPrivateKey() const { return !_privateKey.isEmpty(); } - void setPrivateKey(const QByteArray& privateKey) { _privateKey = privateKey; } - - QByteArray signPlaintext(const QByteArray& plaintext); - - void setDomainID(const QUuid& domainID) { _domainID = domainID; } - const QUuid& getDomainID() const { return _domainID; } - bool hasProfile() const; void setProfileInfoFromJSON(const QJsonObject& jsonObject); @@ -75,9 +70,8 @@ private: QString _xmppPassword; QString _discourseApiKey; QUuid _walletID; - qint64 _balance { 0 }; - bool _hasBalance { false }; - QUuid _domainID; // if this holds account info for a domain, this holds the ID of that domain + qint64 _balance; + bool _hasBalance; QByteArray _privateKey; }; diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 34ca722537..db775983e1 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -92,9 +92,7 @@ void DomainHandler::softReset() { disconnect(); clearSettings(); - - _connectionDenialsSinceKeypairRegen = 0; - + // cancel the failure timeout for any pending requests for settings QMetaObject::invokeMethod(&_settingsTimer, "stop"); } @@ -108,9 +106,6 @@ void DomainHandler::hardReset() { _hostname = QString(); _sockAddr.clear(); - _hasCheckedForAccessToken = false; - _domainConnectionRefusals.clear(); - // clear any pending path we may have wanted to ask the previous DS about _pendingPath.clear(); } @@ -352,35 +347,3 @@ void DomainHandler::processICEResponsePacket(QSharedPointer mes emit icePeerSocketsReceived(); } } - -void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer message) { - // Read deny reason from packet - quint16 reasonSize; - message->readPrimitive(&reasonSize); - QString reason = QString::fromUtf8(message->readWithoutCopy(reasonSize)); - - // 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 - qCWarning(networking) << "The domain-server denied a connection request: " << reason; - qCWarning(networking) << "Make sure you are logged in."; - - if (!_domainConnectionRefusals.contains(reason)) { - _domainConnectionRefusals.append(reason); - emit domainConnectionRefused(reason); - } - - auto& accountManager = AccountManager::getInstance(); - - if (!_hasCheckedForAccessToken) { - accountManager.checkAndSignalForAccessToken(); - _hasCheckedForAccessToken = true; - } - - static const int CONNECTION_DENIALS_FOR_KEYPAIR_REGEN = 3; - - // force a re-generation of key-pair after CONNECTION_DENIALS_FOR_KEYPAIR_REGEN failed connection attempts - if (++_connectionDenialsSinceKeypairRegen >= CONNECTION_DENIALS_FOR_KEYPAIR_REGEN) { - accountManager.generateNewUserKeypair(); - _connectionDenialsSinceKeypairRegen = 0; - } -} diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index b245305a93..f60ac2fbe6 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -92,7 +92,6 @@ public slots: void processICEPingReplyPacket(QSharedPointer message); void processDTLSRequirementPacket(QSharedPointer dtlsRequirementPacket); void processICEResponsePacket(QSharedPointer icePacket); - void processDomainServerConnectionDeniedPacket(QSharedPointer message); private slots: void completedHostnameLookup(const QHostInfo& hostInfo); @@ -114,8 +113,6 @@ signals: void settingsReceived(const QJsonObject& domainSettingsObject); void settingsReceiveFail(); - void domainConnectionRefused(QString reason); - private: void sendDisconnectPacket(); void hardReset(); @@ -133,10 +130,6 @@ private: QJsonObject _settingsObject; QString _pendingPath; QTimer _settingsTimer; - - QStringList _domainConnectionRefusals; - bool _hasCheckedForAccessToken { false }; - int _connectionDenialsSinceKeypairRegen { 0 }; }; #endif // hifi_DomainHandler_h diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index f236f9d596..a3707d19ba 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -902,6 +902,10 @@ void LimitedNodeList::updateLocalSockAddr() { } } +void LimitedNodeList::sendHeartbeatToIceServer(const HifiSockAddr& iceServerSockAddr) { + sendPacketToIceServer(PacketType::ICEServerHeartbeat, iceServerSockAddr, _sessionUUID); +} + void LimitedNodeList::sendPeerQueryToIceServer(const HifiSockAddr& iceServerSockAddr, const QUuid& clientID, const QUuid& peerID) { sendPacketToIceServer(PacketType::ICEServerQuery, iceServerSockAddr, clientID, peerID); diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index de110c7e7f..fcad23da8f 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -143,7 +143,6 @@ public: bool hasCompletedInitialSTUN() const { return _hasCompletedInitialSTUN; } const HifiSockAddr& getLocalSockAddr() const { return _localSockAddr; } - const HifiSockAddr& getPublicSockAddr() const { return _publicSockAddr; } const HifiSockAddr& getSTUNSockAddr() const { return _stunSockAddr; } void processKillNode(ReceivedMessage& message); @@ -162,6 +161,7 @@ public: std::unique_ptr constructICEPingPacket(PingType_t pingType, const QUuid& iceID); std::unique_ptr constructICEPingReplyPacket(ReceivedMessage& message, const QUuid& iceID); + void sendHeartbeatToIceServer(const HifiSockAddr& iceServerSockAddr); void sendPeerQueryToIceServer(const HifiSockAddr& iceServerSockAddr, const QUuid& clientID, const QUuid& peerID); SharedNodePointer findNodeWithAddr(const HifiSockAddr& addr); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 02bb17e870..677a1ad1e6 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -80,16 +80,11 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned // send a ping punch immediately connect(&_domainHandler, &DomainHandler::icePeerSocketsReceived, this, &NodeList::pingPunchForDomainServer); - auto &accountManager = AccountManager::getInstance(); - - // assume that we may need to send a new DS check in anytime a new keypair is generated - connect(&accountManager, &AccountManager::newKeypair, this, &NodeList::sendDomainServerCheckIn); - // clear out NodeList when login is finished - connect(&accountManager, &AccountManager::loginComplete , this, &NodeList::reset); + connect(&AccountManager::getInstance(), &AccountManager::loginComplete , this, &NodeList::reset); // clear our NodeList when logout is requested - connect(&accountManager, &AccountManager::logoutComplete , this, &NodeList::reset); + connect(&AccountManager::getInstance(), &AccountManager::logoutComplete , this, &NodeList::reset); // anytime we get a new node we will want to attempt to punch to it connect(this, &LimitedNodeList::nodeAdded, this, &NodeList::startNodeHolePunch); @@ -110,7 +105,6 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned packetReceiver.registerListener(PacketType::ICEPing, this, "processICEPingPacket"); packetReceiver.registerListener(PacketType::DomainServerAddedNode, this, "processDomainServerAddedNode"); packetReceiver.registerListener(PacketType::DomainServerConnectionToken, this, "processDomainServerConnectionTokenPacket"); - packetReceiver.registerListener(PacketType::DomainConnectionDenied, &_domainHandler, "processDomainServerConnectionDeniedPacket"); packetReceiver.registerListener(PacketType::DomainSettings, &_domainHandler, "processSettingsPacketList"); packetReceiver.registerListener(PacketType::ICEServerPeerInformation, &_domainHandler, "processICEResponsePacket"); packetReceiver.registerListener(PacketType::DomainServerRequireDTLS, &_domainHandler, "processDTLSRequirementPacket"); @@ -271,26 +265,6 @@ void NodeList::sendDomainServerCheckIn() { } - // check if we're missing a keypair we need to verify ourselves with the domain-server - auto& accountManager = AccountManager::getInstance(); - const QUuid& connectionToken = _domainHandler.getConnectionToken(); - - // we assume that we're on the same box as the DS if it has the same local address and - // it didn't present us with a connection token to use for username signature - bool localhostDomain = _domainHandler.getSockAddr().getAddress() == QHostAddress::LocalHost - || (_domainHandler.getSockAddr().getAddress() == _localSockAddr.getAddress() && connectionToken.isNull()); - - bool requiresUsernameSignature = !_domainHandler.isConnected() && !connectionToken.isNull() && !localhostDomain; - - if (requiresUsernameSignature && !accountManager.getAccountInfo().hasPrivateKey()) { - qWarning() << "A keypair is required to present a username signature to the domain-server" - << "but no keypair is present. Waiting for keypair generation to complete."; - accountManager.generateNewUserKeypair(); - - // don't send the check in packet - wait for the keypair first - return; - } - auto domainPacket = NLPacket::create(domainPacketType); QDataStream packetStream(domainPacket.get()); @@ -315,15 +289,23 @@ void NodeList::sendDomainServerCheckIn() { // pack our data to send to the domain-server packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList(); - - if (!_domainHandler.isConnected()) { - DataServerAccountInfo& accountInfo = accountManager.getAccountInfo(); + + // if this is a connect request, and we can present a username signature, send it along + if (!_domainHandler.isConnected() ) { + + DataServerAccountInfo& accountInfo = AccountManager::getInstance().getAccountInfo(); packetStream << accountInfo.getUsername(); - - // if this is a connect request, and we can present a username signature, send it along - if (requiresUsernameSignature && accountManager.getAccountInfo().hasPrivateKey()) { - const QByteArray& usernameSignature = accountManager.getAccountInfo().getUsernameSignature(connectionToken); - packetStream << usernameSignature; + + // get connection token from the domain-server + const QUuid& connectionToken = _domainHandler.getConnectionToken(); + + if (!connectionToken.isNull()) { + + const QByteArray& usernameSignature = AccountManager::getInstance().getAccountInfo().getUsernameSignature(connectionToken); + + if (!usernameSignature.isEmpty()) { + packetStream << usernameSignature; + } } } diff --git a/libraries/networking/src/RSAKeypairGenerator.cpp b/libraries/networking/src/RSAKeypairGenerator.cpp index a98cf74564..53b9b27cc6 100644 --- a/libraries/networking/src/RSAKeypairGenerator.cpp +++ b/libraries/networking/src/RSAKeypairGenerator.cpp @@ -85,12 +85,12 @@ void RSAKeypairGenerator::generateKeypair() { // we can cleanup the RSA struct before we continue on RSA_free(keyPair); - _publicKey = QByteArray { reinterpret_cast(publicKeyDER), publicKeyLength }; - _privateKey = QByteArray { reinterpret_cast(privateKeyDER), privateKeyLength }; + QByteArray publicKeyArray(reinterpret_cast(publicKeyDER), publicKeyLength); + QByteArray privateKeyArray(reinterpret_cast(privateKeyDER), privateKeyLength); // cleanup the publicKeyDER and publicKeyDER data OPENSSL_free(publicKeyDER); OPENSSL_free(privateKeyDER); - emit generatedKeypair(); + emit generatedKeypair(publicKeyArray, privateKeyArray); } diff --git a/libraries/networking/src/RSAKeypairGenerator.h b/libraries/networking/src/RSAKeypairGenerator.h index 36f4a9550b..dd90313625 100644 --- a/libraries/networking/src/RSAKeypairGenerator.h +++ b/libraries/networking/src/RSAKeypairGenerator.h @@ -12,31 +12,17 @@ #ifndef hifi_RSAKeypairGenerator_h #define hifi_RSAKeypairGenerator_h -#include -#include +#include class RSAKeypairGenerator : public QObject { Q_OBJECT public: RSAKeypairGenerator(QObject* parent = 0); - - void setDomainID(const QUuid& domainID) { _domainID = domainID; } - const QUuid& getDomainID() const { return _domainID; } - - const QByteArray& getPublicKey() const { return _publicKey; } - const QByteArray& getPrivateKey() const { return _privateKey; } - public slots: void generateKeypair(); - signals: void errorGeneratingKeypair(); - void generatedKeypair(); - -private: - QUuid _domainID; - QByteArray _publicKey; - QByteArray _privateKey; + void generatedKeypair(const QByteArray& publicKey, const QByteArray& privateKey); }; -#endif // hifi_RSAKeypairGenerator_h +#endif // hifi_RSAKeypairGenerator_h \ No newline at end of file diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index cbe577c14b..88fabf1a5a 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -30,7 +30,7 @@ const QSet NON_SOURCED_PACKETS = QSet() << PacketType::DomainServerAddedNode << PacketType::DomainServerConnectionToken << PacketType::DomainSettingsRequest << PacketType::DomainSettings << PacketType::ICEServerPeerInformation << PacketType::ICEServerQuery << PacketType::ICEServerHeartbeat - << PacketType::ICEPing << PacketType::ICEPingReply << PacketType::ICEServerHeartbeatDenied + << PacketType::ICEPing << PacketType::ICEPingReply << PacketType::AssignmentClientStatus << PacketType::StopNode << PacketType::DomainServerRemovedNode; @@ -45,8 +45,6 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarData: case PacketType::BulkAvatarData: return static_cast(AvatarMixerPacketVersion::SoftAttachmentSupport); - case PacketType::ICEServerHeartbeat: - return 18; // ICE Server Heartbeat signing default: return 17; } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 7840ecb17e..0f586018db 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -90,8 +90,7 @@ public: DomainServerRemovedNode, MessagesData, MessagesSubscribe, - MessagesUnsubscribe, - ICEServerHeartbeatDenied + MessagesUnsubscribe }; };