From 2f0e311a9923cc411201c1742626249a4f355b70 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 1 May 2014 10:19:05 -0700 Subject: [PATCH] return OAuth URL to interface clients when OAuth enabled --- assignment-client/src/Agent.cpp | 7 +- domain-server/src/DomainServer.cpp | 187 +++++++++++-------- domain-server/src/DomainServer.h | 3 + domain-server/src/DomainServerNodeData.cpp | 3 +- domain-server/src/DomainServerNodeData.h | 4 + interface/src/DatagramProcessor.cpp | 12 +- libraries/networking/src/DomainHandler.h | 3 + libraries/networking/src/LimitedNodeList.cpp | 4 + libraries/networking/src/PacketHeaders.h | 4 +- 9 files changed, 143 insertions(+), 84 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index e39cb39307..953cbe6270 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -164,9 +164,10 @@ void Agent::run() { // figure out the URL for the script for this agent assignment QUrl scriptURL; if (_payload.isEmpty()) { - scriptURL = QUrl(QString("http://%1:8080/assignment/%2") - .arg(NodeList::getInstance()->getDomainHandler().getIP().toString(), - uuidStringWithoutCurlyBraces(_uuid))); + scriptURL = QUrl(QString("http://%1:%2/assignment/%3") + .arg(NodeList::getInstance()->getDomainHandler().getIP().toString()) + .arg(DOMAIN_SERVER_HTTP_PORT) + .arg(uuidStringWithoutCurlyBraces(_uuid))); } else { scriptURL = QUrl(_payload); } diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 75411ef936..684db8e959 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -32,9 +32,6 @@ #include "DomainServer.h" -const quint16 DOMAIN_SERVER_HTTP_PORT = 40100; -const quint16 DOMAIN_SERVER_HTTPS_PORT = 40101; - DomainServer::DomainServer(int argc, char* argv[]) : QCoreApplication(argc, argv), _httpManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this), @@ -47,7 +44,8 @@ DomainServer::DomainServer(int argc, char* argv[]) : _priorityCache(NULL), _dtlsSessions(), _oauthProviderURL(), - _oauthClientID() + _oauthClientID(), + _hostname() { gnutls_global_init(); @@ -58,17 +56,13 @@ DomainServer::DomainServer(int argc, char* argv[]) : _argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments()); - if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth() && optionallySetupDTLS()) { + if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth()) { // we either read a certificate and private key or were not passed one, good to load assignments // and set up the node list qDebug() << "Setting up LimitedNodeList and assignments."; setupNodeListAndAssignments(); if (_isUsingDTLS) { - // we're using DTLS and our NodeList socket is good to go, so make the required DTLS changes - // DTLS requires that IP_DONTFRAG be set - // This is not accessible on some platforms (OS X) so we need to make sure DTLS still works without it - LimitedNodeList* nodeList = LimitedNodeList::getInstance(); // connect our socket to read datagrams received on the DTLS socket @@ -159,11 +153,11 @@ bool DomainServer::optionallySetupOAuth() { _oauthProviderURL = QUrl(_argumentVariantMap.value(OAUTH_PROVIDER_URL_OPTION).toString()); _oauthClientID = _argumentVariantMap.value(OAUTH_CLIENT_ID_OPTION).toString(); _oauthClientSecret = QProcessEnvironment::systemEnvironment().value(OAUTH_CLIENT_SECRET_ENV); - QString oauthRedirectHostname = _argumentVariantMap.value(REDIRECT_HOSTNAME_OPTION).toString(); + _hostname = _argumentVariantMap.value(REDIRECT_HOSTNAME_OPTION).toString(); - if (!_oauthProviderURL.isEmpty() || !oauthRedirectHostname.isEmpty() || !_oauthClientID.isEmpty()) { + if (!_oauthProviderURL.isEmpty() || !_hostname.isEmpty() || !_oauthClientID.isEmpty()) { if (_oauthProviderURL.isEmpty() - || oauthRedirectHostname.isEmpty() + || _hostname.isEmpty() || _oauthClientID.isEmpty() || _oauthClientSecret.isEmpty()) { qDebug() << "Missing OAuth provider URL, hostname, client ID, or client secret. domain-server will now quit."; @@ -400,37 +394,74 @@ void DomainServer::addNodeToNodeListAndConfirmConnection(const QByteArray& packe bool isStaticAssignment = _staticAssignmentHash.contains(assignmentUUID); SharedAssignmentPointer matchingAssignment = SharedAssignmentPointer(); - if (isStaticAssignment) { - // this is a static assignment, make sure the UUID sent is for an assignment we're actually trying to give out - matchingAssignment = matchingQueuedAssignmentForCheckIn(assignmentUUID, nodeType); + if (assignmentUUID.isNull() && !_oauthProviderURL.isEmpty()) { + // we have an OAuth provider, ask this interface client to auth against it - if (matchingAssignment) { - // remove the matching assignment from the assignment queue so we don't take the next check in - // (if it exists) - removeMatchingAssignmentFromQueue(matchingAssignment); - } + QByteArray oauthRequestByteArray = byteArrayWithPopulatedHeader(PacketTypeDomainOAuthRequest); + QDataStream oauthRequestStream(&oauthRequestByteArray, QIODevice::Append); + oauthRequestStream << oauthAuthorizationURL(); + + // send this oauth request datagram back to the client + LimitedNodeList::getInstance()->writeUnverifiedDatagram(oauthRequestByteArray, senderSockAddr); } else { - assignmentUUID = QUuid(); + if (isStaticAssignment) { + // this is a static assignment, make sure the UUID sent is for an assignment we're actually trying to give out + matchingAssignment = matchingQueuedAssignmentForCheckIn(assignmentUUID, nodeType); + + if (matchingAssignment) { + // remove the matching assignment from the assignment queue so we don't take the next check in + // (if it exists) + removeMatchingAssignmentFromQueue(matchingAssignment); + } + } + + // make sure this was either not a static assignment or it was and we had a matching one in teh queue + if ((!isStaticAssignment && !STATICALLY_ASSIGNED_NODES.contains(nodeType)) || (isStaticAssignment && matchingAssignment)) { + // create a new session UUID for this node + QUuid nodeUUID = QUuid::createUuid(); + + SharedNodePointer newNode = LimitedNodeList::getInstance()->addOrUpdateNode(nodeUUID, nodeType, + publicSockAddr, localSockAddr); + + // when the newNode is created the linked data is also created + // if this was a static assignment set the UUID, set the sendingSockAddr + DomainServerNodeData* nodeData = reinterpret_cast(newNode->getLinkedData()); + + nodeData->setStaticAssignmentUUID(assignmentUUID); + nodeData->setSendingSockAddr(senderSockAddr); + + // reply back to the user with a PacketTypeDomainList + sendDomainListToNode(newNode, senderSockAddr, nodeInterestListFromPacket(packet, numPreInterestBytes)); + } } +} + +QUrl DomainServer::oauthAuthorizationURL() { + // for now these are all interface clients that have a GUI + // so just send them back the full authorization URL + QUrl authorizationURL = _oauthProviderURL; - // make sure this was either not a static assignment or it was and we had a matching one in teh queue - if ((!isStaticAssignment && !STATICALLY_ASSIGNED_NODES.contains(nodeType)) || (isStaticAssignment && matchingAssignment)) { - // create a new session UUID for this node - QUuid nodeUUID = QUuid::createUuid(); - - SharedNodePointer newNode = LimitedNodeList::getInstance()->addOrUpdateNode(nodeUUID, nodeType, - publicSockAddr, localSockAddr); - - // when the newNode is created the linked data is also created - // if this was a static assignment set the UUID, set the sendingSockAddr - DomainServerNodeData* nodeData = reinterpret_cast(newNode->getLinkedData()); - - nodeData->setStaticAssignmentUUID(assignmentUUID); - nodeData->setSendingSockAddr(senderSockAddr); - - // reply back to the user with a PacketTypeDomainList - sendDomainListToNode(newNode, senderSockAddr, nodeInterestListFromPacket(packet, numPreInterestBytes)); - } + const QString OAUTH_AUTHORIZATION_PATH = "/oauth/authorize"; + authorizationURL.setPath(OAUTH_AUTHORIZATION_PATH); + + QUrlQuery authorizationQuery; + + const QString OAUTH_CLIENT_ID_QUERY_KEY = "client_id"; + authorizationQuery.addQueryItem(OAUTH_CLIENT_ID_QUERY_KEY, _oauthClientID); + + const QString OAUTH_RESPONSE_TYPE_QUERY_KEY = "response_type"; + const QString OAUTH_REPSONSE_TYPE_QUERY_VALUE = "code"; + authorizationQuery.addQueryItem(OAUTH_RESPONSE_TYPE_QUERY_KEY, OAUTH_REPSONSE_TYPE_QUERY_VALUE); + + QString redirectURL = QString("https://%1:%2/oauth").arg(_hostname).arg(_httpsManager->serverPort()); + qDebug() << "redirect URL is" << redirectURL; + + const QString OAUTH_REDIRECT_URI_QUERY_KEY = "redirect_uri"; + authorizationQuery.addQueryItem(OAUTH_REDIRECT_URI_QUERY_KEY, redirectURL); + + authorizationURL.setQuery(authorizationQuery); + + return authorizationURL; } int DomainServer::parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr, @@ -496,52 +527,54 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL; int dataMTU = dtlsSession ? (int)gnutls_dtls_get_data_mtu(*dtlsSession->getGnuTLSSession()) : MAX_PACKET_SIZE; - // if the node has any interest types, send back those nodes as well - foreach (const SharedNodePointer& otherNode, nodeList->getNodeHash()) { - - // reset our nodeByteArray and nodeDataStream - QByteArray nodeByteArray; - QDataStream nodeDataStream(&nodeByteArray, QIODevice::Append); - - if (otherNode->getUUID() != node->getUUID() && nodeInterestList.contains(otherNode->getType())) { + if (nodeData->isAuthenticated()) { + // if this authenticated node has any interest types, send back those nodes as well + foreach (const SharedNodePointer& otherNode, nodeList->getNodeHash()) { - // don't send avatar nodes to other avatars, that will come from avatar mixer - nodeDataStream << *otherNode.data(); + // reset our nodeByteArray and nodeDataStream + QByteArray nodeByteArray; + QDataStream nodeDataStream(&nodeByteArray, QIODevice::Append); - // pack the secret that these two nodes will use to communicate with each other - QUuid secretUUID = nodeData->getSessionSecretHash().value(otherNode->getUUID()); - if (secretUUID.isNull()) { - // generate a new secret UUID these two nodes can use - secretUUID = QUuid::createUuid(); + if (otherNode->getUUID() != node->getUUID() && nodeInterestList.contains(otherNode->getType())) { - // set that on the current Node's sessionSecretHash - nodeData->getSessionSecretHash().insert(otherNode->getUUID(), secretUUID); + // don't send avatar nodes to other avatars, that will come from avatar mixer + nodeDataStream << *otherNode.data(); - // set it on the other Node's sessionSecretHash - reinterpret_cast(otherNode->getLinkedData()) + // pack the secret that these two nodes will use to communicate with each other + QUuid secretUUID = nodeData->getSessionSecretHash().value(otherNode->getUUID()); + if (secretUUID.isNull()) { + // generate a new secret UUID these two nodes can use + secretUUID = QUuid::createUuid(); + + // set that on the current Node's sessionSecretHash + nodeData->getSessionSecretHash().insert(otherNode->getUUID(), secretUUID); + + // set it on the other Node's sessionSecretHash + reinterpret_cast(otherNode->getLinkedData()) ->getSessionSecretHash().insert(node->getUUID(), secretUUID); - - } - - nodeDataStream << secretUUID; - - if (broadcastPacket.size() + nodeByteArray.size() > dataMTU) { - // we need to break here and start a new packet - // so send the current one - - if (!dtlsSession) { - nodeList->writeDatagram(broadcastPacket, node, senderSockAddr); - } else { - dtlsSession->writeDatagram(broadcastPacket); + } - // reset the broadcastPacket structure - broadcastPacket.resize(numBroadcastPacketLeadBytes); - broadcastDataStream.device()->seek(numBroadcastPacketLeadBytes); + nodeDataStream << secretUUID; + + if (broadcastPacket.size() + nodeByteArray.size() > dataMTU) { + // we need to break here and start a new packet + // so send the current one + + if (!dtlsSession) { + nodeList->writeDatagram(broadcastPacket, node, senderSockAddr); + } else { + dtlsSession->writeDatagram(broadcastPacket); + } + + // reset the broadcastPacket structure + broadcastPacket.resize(numBroadcastPacketLeadBytes); + broadcastDataStream.device()->seek(numBroadcastPacketLeadBytes); + } + + // append the nodeByteArray to the current state of broadcastDataStream + broadcastPacket.append(nodeByteArray); } - - // append the nodeByteArray to the current state of broadcastDataStream - broadcastPacket.append(nodeByteArray); } } diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index da43e76d60..7cb5861727 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -78,6 +78,8 @@ private: void refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment); void addStaticAssignmentsToQueue(); + QUrl oauthAuthorizationURL(); + QJsonObject jsonForSocket(const HifiSockAddr& socket); QJsonObject jsonObjectForNode(const SharedNodePointer& node); @@ -100,6 +102,7 @@ private: QUrl _oauthProviderURL; QString _oauthClientID; QString _oauthClientSecret; + QString _hostname; }; #endif // hifi_DomainServer_h diff --git a/domain-server/src/DomainServerNodeData.cpp b/domain-server/src/DomainServerNodeData.cpp index 59d60659de..eb2ddb3200 100644 --- a/domain-server/src/DomainServerNodeData.cpp +++ b/domain-server/src/DomainServerNodeData.cpp @@ -21,7 +21,8 @@ DomainServerNodeData::DomainServerNodeData() : _sessionSecretHash(), _staticAssignmentUUID(), _statsJSONObject(), - _sendingSockAddr() + _sendingSockAddr(), + _isAuthenticated(true) { } diff --git a/domain-server/src/DomainServerNodeData.h b/domain-server/src/DomainServerNodeData.h index 6026e65f25..f079244c4a 100644 --- a/domain-server/src/DomainServerNodeData.h +++ b/domain-server/src/DomainServerNodeData.h @@ -33,6 +33,9 @@ public: void setSendingSockAddr(const HifiSockAddr& sendingSockAddr) { _sendingSockAddr = sendingSockAddr; } const HifiSockAddr& getSendingSockAddr() { return _sendingSockAddr; } + void setIsAuthenticated(bool isAuthenticated) { _isAuthenticated = isAuthenticated; } + bool isAuthenticated() const { return _isAuthenticated; } + QHash& getSessionSecretHash() { return _sessionSecretHash; } private: QJsonObject mergeJSONStatsFromNewObject(const QJsonObject& newObject, QJsonObject destinationObject); @@ -41,6 +44,7 @@ private: QUuid _staticAssignmentUUID; QJsonObject _statsJSONObject; HifiSockAddr _sendingSockAddr; + bool _isAuthenticated; }; #endif // hifi_DomainServerNodeData_h diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index 6525e79cf2..9b34b3b651 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -56,7 +56,6 @@ void DatagramProcessor::processDatagrams() { Particle::handleAddParticleResponse(incomingPacket); application->getParticles()->getTree()->handleAddParticleResponse(incomingPacket); break; - case PacketTypeParticleData: case PacketTypeParticleErase: case PacketTypeVoxelData: @@ -112,6 +111,17 @@ 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; + + qDebug() << "the authorization URL sent from the server is" << authorizationURL; + + break; + } default: nodeList->processNodeData(senderSockAddr, incomingPacket); break; diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 2cc520991c..b78b8875c4 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -22,8 +22,11 @@ #include "HifiSockAddr.h" const QString DEFAULT_DOMAIN_HOSTNAME = "alpha.highfidelity.io"; + const unsigned short DEFAULT_DOMAIN_SERVER_PORT = 40102; const unsigned short DEFAULT_DOMAIN_SERVER_DTLS_PORT = 40103; +const quint16 DOMAIN_SERVER_HTTP_PORT = 40100; +const quint16 DOMAIN_SERVER_HTTPS_PORT = 40101; class DomainHandler : public QObject { Q_OBJECT diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index ce78ec2d10..5692023ab1 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -106,6 +106,10 @@ QUdpSocket& LimitedNodeList::getDTLSSocket() { _dtlsSocket->bind(QHostAddress::AnyIPv4, 0, QAbstractSocket::DontShareAddress); + // we're using DTLS and our socket is good to go, so make the required DTLS changes + // DTLS requires that IP_DONTFRAG be set + // This is not accessible on some platforms (OS X) so we need to make sure DTLS still works without it + #if defined(IP_DONTFRAG) || defined(IP_MTU_DISCOVER) qDebug() << "Making required DTLS changes to LimitedNodeList DTLS socket."; diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index b7535e5064..3cef7750b1 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -38,7 +38,7 @@ enum PacketType { PacketTypeDomainListRequest, PacketTypeRequestAssignment, PacketTypeCreateAssignment, - PacketTypeDataServerPut, // reusable + PacketTypeDomainOAuthRequest, PacketTypeDataServerGet, // reusable PacketTypeDataServerSend, // reusable PacketTypeDataServerConfirm, @@ -67,7 +67,7 @@ typedef char PacketVersion; const QSet NON_VERIFIED_PACKETS = QSet() << PacketTypeDomainServerRequireDTLS << PacketTypeDomainConnectRequest - << PacketTypeDomainList << PacketTypeDomainListRequest + << PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainOAuthRequest << PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse << PacketTypeNodeJsonStats << PacketTypeVoxelQuery << PacketTypeParticleQuery;