From 260966915a7a03f610ee7f1558681ffe57eba16b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 24 Apr 2014 14:06:24 -0700 Subject: [PATCH 01/27] add HTTPS versions of webserver classes --- domain-server/src/DomainServer.cpp | 6 ++- domain-server/src/DomainServer.h | 4 +- .../embedded-webserver/src/HTTPConnection.cpp | 4 +- .../embedded-webserver/src/HTTPConnection.h | 4 +- .../embedded-webserver/src/HTTPManager.cpp | 12 +++--- .../embedded-webserver/src/HTTPManager.h | 4 +- .../src/HTTPSConnection.cpp | 23 ++++++++++++ .../embedded-webserver/src/HTTPSConnection.h | 26 +++++++++++++ .../embedded-webserver/src/HTTPSManager.cpp | 34 +++++++++++++++++ .../embedded-webserver/src/HTTPSManager.h | 37 +++++++++++++++++++ 10 files changed, 139 insertions(+), 15 deletions(-) create mode 100644 libraries/embedded-webserver/src/HTTPSConnection.cpp create mode 100644 libraries/embedded-webserver/src/HTTPSConnection.h create mode 100644 libraries/embedded-webserver/src/HTTPSManager.cpp create mode 100644 libraries/embedded-webserver/src/HTTPSManager.h diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index e65f3968e0..f169cd26d0 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -31,11 +31,13 @@ #include "DomainServer.h" -const quint16 DOMAIN_SERVER_HTTP_PORT = 8080; +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), + _httpManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this), + _httpsManager(NULL), _staticAssignmentHash(), _assignmentQueue(), _isUsingDTLS(false), diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 1bc9b71064..52aee24682 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -24,6 +24,7 @@ #include #include +#include #include #include "DTLSServerSession.h" @@ -79,7 +80,8 @@ private: QJsonObject jsonForSocket(const HifiSockAddr& socket); QJsonObject jsonObjectForNode(const SharedNodePointer& node); - HTTPManager _HTTPManager; + HTTPManager _httpManager; + HTTPSManager* _httpsManager; QHash _staticAssignmentHash; QQueue _assignmentQueue; diff --git a/libraries/embedded-webserver/src/HTTPConnection.cpp b/libraries/embedded-webserver/src/HTTPConnection.cpp index 276b4e7f64..f8fcc097f2 100755 --- a/libraries/embedded-webserver/src/HTTPConnection.cpp +++ b/libraries/embedded-webserver/src/HTTPConnection.cpp @@ -28,8 +28,8 @@ HTTPConnection::HTTPConnection (QTcpSocket* socket, HTTPManager* parentManager) _parentManager(parentManager), _socket(socket), _stream(socket), - _address(socket->peerAddress()) { - + _address(socket->peerAddress()) +{ // take over ownership of the socket _socket->setParent(this); diff --git a/libraries/embedded-webserver/src/HTTPConnection.h b/libraries/embedded-webserver/src/HTTPConnection.h index a131a22a9e..d5214ee3a8 100644 --- a/libraries/embedded-webserver/src/HTTPConnection.h +++ b/libraries/embedded-webserver/src/HTTPConnection.h @@ -18,10 +18,10 @@ #include #include -#include +#include #include #include -#include +#include #include #include #include diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index 1fc859014a..ff4ea9e6b4 100755 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -138,14 +138,14 @@ HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestH qDebug() << "Failed to open HTTP server socket:" << errorString(); return; } - - // connect the connection signal - connect(this, SIGNAL(newConnection()), SLOT(acceptConnections())); } -void HTTPManager::acceptConnections() { - QTcpSocket* socket; - while ((socket = nextPendingConnection()) != 0) { +void HTTPManager::incomingConnection(qintptr socketDescriptor) { + QTcpSocket* socket = new QTcpSocket(this); + + if (socket->setSocketDescriptor(socketDescriptor)) { new HTTPConnection(socket, this); + } else { + delete socket; } } diff --git a/libraries/embedded-webserver/src/HTTPManager.h b/libraries/embedded-webserver/src/HTTPManager.h index 1e3afca1b5..ea2936f464 100755 --- a/libraries/embedded-webserver/src/HTTPManager.h +++ b/libraries/embedded-webserver/src/HTTPManager.h @@ -35,9 +35,9 @@ public: bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url); -protected slots: +protected: /// Accepts all pending connections - void acceptConnections(); + virtual void incomingConnection(qintptr socketDescriptor); protected: QString _documentRoot; HTTPRequestHandler* _requestHandler; diff --git a/libraries/embedded-webserver/src/HTTPSConnection.cpp b/libraries/embedded-webserver/src/HTTPSConnection.cpp new file mode 100644 index 0000000000..00f95fb0b5 --- /dev/null +++ b/libraries/embedded-webserver/src/HTTPSConnection.cpp @@ -0,0 +1,23 @@ +// +// HTTPSConnection.cpp +// libraries/embedded-webserver/src +// +// Created by Stephen Birarda on 2014-04-24. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "HTTPSConnection.h" + +HTTPSConnection::HTTPSConnection(QSslSocket* sslSocket, HTTPSManager* parentManager) : + HTTPConnection(sslSocket, parentManager) +{ + connect(sslSocket, SIGNAL(sslErrors(const QList&)), this, SLOT(sslErrors(const QList&))); + sslSocket->startServerEncryption(); +} + +void HTTPSConnection::handleSSLErrors(const QList& errors) { + qDebug() << "SSL errors:" << errors; +} \ No newline at end of file diff --git a/libraries/embedded-webserver/src/HTTPSConnection.h b/libraries/embedded-webserver/src/HTTPSConnection.h new file mode 100644 index 0000000000..7b53dc0063 --- /dev/null +++ b/libraries/embedded-webserver/src/HTTPSConnection.h @@ -0,0 +1,26 @@ +// +// HTTPSConnection.h +// libraries/embedded-webserver/src +// +// Created by Stephen Birarda on 2014-04-24. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_HTTPSConnection_h +#define hifi_HTTPSConnection_h + +#include "HTTPConnection.h" +#include "HTTPSManager.h" + +class HTTPSConnection : public HTTPConnection { + Q_OBJECT +public: + HTTPSConnection(QSslSocket* sslSocket, HTTPSManager* parentManager); +protected slots: + void handleSSLErrors(const QList& errors); +}; + +#endif // hifi_HTTPSConnection_h \ No newline at end of file diff --git a/libraries/embedded-webserver/src/HTTPSManager.cpp b/libraries/embedded-webserver/src/HTTPSManager.cpp new file mode 100644 index 0000000000..ed988a6e65 --- /dev/null +++ b/libraries/embedded-webserver/src/HTTPSManager.cpp @@ -0,0 +1,34 @@ +// +// HTTPSManager.cpp +// libraries/embedded-webserver/src +// +// Created by Stephen Birarda on 2014-04-24. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include "HTTPSConnection.h" + +#include "HTTPSManager.h" + +HTTPSManager::HTTPSManager(quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler, QObject* parent) : + HTTPManager(port, documentRoot, requestHandler, parent), + _certificate(), + _privateKey() +{ + +} + +void HTTPSManager::incomingConnection(qintptr socketDescriptor) { + QSslSocket* sslSocket = new QSslSocket(this); + + if (sslSocket->setSocketDescriptor(socketDescriptor)) { + new HTTPSConnection(sslSocket, this); + } else { + delete sslSocket; + } +} \ No newline at end of file diff --git a/libraries/embedded-webserver/src/HTTPSManager.h b/libraries/embedded-webserver/src/HTTPSManager.h new file mode 100644 index 0000000000..e0210c9ee9 --- /dev/null +++ b/libraries/embedded-webserver/src/HTTPSManager.h @@ -0,0 +1,37 @@ +// +// HTTPSManager.h +// libraries/embedded-webserver/src +// +// Created by Stephen Birarda on 2014-04-24. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_HTTPSManager_h +#define hifi_HTTPSManager_h + +#include +#include + +#include "HTTPManager.h" + +class HTTPSManager : public HTTPManager { + Q_OBJECT +public: + HTTPSManager(quint16 port, + const QString& documentRoot, + HTTPRequestHandler* requestHandler = NULL, QObject* parent = 0); + + void setCertificate(const QSslCertificate& certificate) { _certificate = certificate; } + void setPrivateKey(const QSslKey& privateKey) { _privateKey = privateKey; } + +protected: + virtual void incomingConnection(qintptr socketDescriptor); +private: + QSslCertificate _certificate; + QSslKey _privateKey; +}; + +#endif // hifi_HTTPSManager_h \ No newline at end of file From 96de0c1af1d53e54831b14785ba1430cafa1805a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 24 Apr 2014 14:13:11 -0700 Subject: [PATCH 02/27] setup the domain-server HTTPS server --- domain-server/src/DomainServer.cpp | 20 +++++++++++++++++-- domain-server/src/DomainServer.h | 2 +- .../embedded-webserver/src/HTTPSManager.cpp | 7 ++++--- .../embedded-webserver/src/HTTPSManager.h | 2 ++ 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index f169cd26d0..943895a4a0 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -55,7 +55,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : _argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments()); - if (optionallySetupDTLS()) { + if (optionallySetupTLS()) { // 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."; @@ -88,7 +88,7 @@ DomainServer::~DomainServer() { gnutls_global_deinit(); } -bool DomainServer::optionallySetupDTLS() { +bool DomainServer::optionallySetupTLS() { if (readX509KeyAndCertificate()) { if (_x509Credentials) { qDebug() << "Generating Diffie-Hellman parameters."; @@ -159,6 +159,22 @@ bool DomainServer::readX509KeyAndCertificate() { qDebug() << "Successfully read certificate and private key."; + // we need to also pass this certificate and private key to the HTTPS manager + // this is used for Oauth callbacks when authorizing users against a data server + + QFile certFile(certPath); + certFile.open(QIODevice::ReadOnly); + + QFile keyFile(keyPath); + keyFile.open(QIODevice::ReadOnly); + + QSslCertificate sslCertificate(&certFile); + QSslKey privateKey(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, keyPassphraseString.toUtf8()); + + _httpsManager = new HTTPSManager(DOMAIN_SERVER_HTTPS_PORT, sslCertificate, privateKey, QString(), this, this); + + qDebug() << "TCP server listening for HTTPS connections on" << DOMAIN_SERVER_HTTPS_PORT; + } else if (!certPath.isEmpty() || !keyPath.isEmpty()) { qDebug() << "Missing certificate or private key. domain-server will now quit."; QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 52aee24682..5c970748f1 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -53,7 +53,7 @@ private slots: void readAvailableDTLSDatagrams(); private: void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid()); - bool optionallySetupDTLS(); + bool optionallySetupTLS(); bool readX509KeyAndCertificate(); void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr); diff --git a/libraries/embedded-webserver/src/HTTPSManager.cpp b/libraries/embedded-webserver/src/HTTPSManager.cpp index ed988a6e65..575a52fac9 100644 --- a/libraries/embedded-webserver/src/HTTPSManager.cpp +++ b/libraries/embedded-webserver/src/HTTPSManager.cpp @@ -15,10 +15,11 @@ #include "HTTPSManager.h" -HTTPSManager::HTTPSManager(quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler, QObject* parent) : +HTTPSManager::HTTPSManager(quint16 port, const QSslCertificate& certificate, const QSslKey& privateKey, + const QString& documentRoot, HTTPRequestHandler* requestHandler, QObject* parent) : HTTPManager(port, documentRoot, requestHandler, parent), - _certificate(), - _privateKey() + _certificate(certificate), + _privateKey(privateKey) { } diff --git a/libraries/embedded-webserver/src/HTTPSManager.h b/libraries/embedded-webserver/src/HTTPSManager.h index e0210c9ee9..8940b2e79d 100644 --- a/libraries/embedded-webserver/src/HTTPSManager.h +++ b/libraries/embedded-webserver/src/HTTPSManager.h @@ -21,6 +21,8 @@ class HTTPSManager : public HTTPManager { Q_OBJECT public: HTTPSManager(quint16 port, + const QSslCertificate& certificate, + const QSslKey& privateKey, const QString& documentRoot, HTTPRequestHandler* requestHandler = NULL, QObject* parent = 0); From 0bc17b0852c5ba29b6f78ce7b9ee1d7251c2ba42 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 24 Apr 2014 14:35:18 -0700 Subject: [PATCH 03/27] hand certificate and private key to QSslSocket instances --- libraries/embedded-webserver/src/HTTPConnection.cpp | 2 +- libraries/embedded-webserver/src/HTTPSConnection.cpp | 2 +- libraries/embedded-webserver/src/HTTPSManager.cpp | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/embedded-webserver/src/HTTPConnection.cpp b/libraries/embedded-webserver/src/HTTPConnection.cpp index f8fcc097f2..a6eb391138 100755 --- a/libraries/embedded-webserver/src/HTTPConnection.cpp +++ b/libraries/embedded-webserver/src/HTTPConnection.cpp @@ -42,7 +42,7 @@ HTTPConnection::HTTPConnection (QTcpSocket* socket, HTTPManager* parentManager) HTTPConnection::~HTTPConnection() { // log the destruction if (_socket->error() != QAbstractSocket::UnknownSocketError) { - qDebug() << _socket->errorString(); + qDebug() << _socket->errorString() << "-" << _socket->error(); } } diff --git a/libraries/embedded-webserver/src/HTTPSConnection.cpp b/libraries/embedded-webserver/src/HTTPSConnection.cpp index 00f95fb0b5..54893d91c2 100644 --- a/libraries/embedded-webserver/src/HTTPSConnection.cpp +++ b/libraries/embedded-webserver/src/HTTPSConnection.cpp @@ -14,7 +14,7 @@ HTTPSConnection::HTTPSConnection(QSslSocket* sslSocket, HTTPSManager* parentManager) : HTTPConnection(sslSocket, parentManager) { - connect(sslSocket, SIGNAL(sslErrors(const QList&)), this, SLOT(sslErrors(const QList&))); + connect(sslSocket, SIGNAL(sslErrors(const QList&)), this, SLOT(handleSSLErrors(const QList&))); sslSocket->startServerEncryption(); } diff --git a/libraries/embedded-webserver/src/HTTPSManager.cpp b/libraries/embedded-webserver/src/HTTPSManager.cpp index 575a52fac9..0f5b94de64 100644 --- a/libraries/embedded-webserver/src/HTTPSManager.cpp +++ b/libraries/embedded-webserver/src/HTTPSManager.cpp @@ -27,6 +27,9 @@ HTTPSManager::HTTPSManager(quint16 port, const QSslCertificate& certificate, con void HTTPSManager::incomingConnection(qintptr socketDescriptor) { QSslSocket* sslSocket = new QSslSocket(this); + sslSocket->setLocalCertificate(_certificate); + sslSocket->setPrivateKey(_privateKey); + if (sslSocket->setSocketDescriptor(socketDescriptor)) { new HTTPSConnection(sslSocket, this); } else { From 4a68c2e9c4e277ba4a7f703e1385aaaf64008b9b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 24 Apr 2014 14:58:08 -0700 Subject: [PATCH 04/27] allow DS to handle incoming HTTPS requests --- domain-server/src/DomainServer.cpp | 6 + domain-server/src/DomainServer.h | 6 +- .../embedded-webserver/src/HTTPManager.cpp | 227 +++++++++--------- .../embedded-webserver/src/HTTPManager.h | 2 + .../embedded-webserver/src/HTTPSManager.cpp | 17 +- .../embedded-webserver/src/HTTPSManager.h | 17 +- 6 files changed, 157 insertions(+), 118 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 943895a4a0..1bad7d72f5 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -968,6 +968,12 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url return false; } +bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &url) { + qDebug() << "HTTPS request received at" << url; + qDebug() << "not handling"; + return false; +} + void DomainServer::refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment) { QUuid oldUUID = assignment->getUUID(); assignment->resetUUID(); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 5c970748f1..40429e23ea 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -23,21 +23,21 @@ #include #include -#include -#include +#include #include #include "DTLSServerSession.h" typedef QSharedPointer SharedAssignmentPointer; -class DomainServer : public QCoreApplication, public HTTPRequestHandler { +class DomainServer : public QCoreApplication, public HTTPSRequestHandler { Q_OBJECT public: DomainServer(int argc, char* argv[]); ~DomainServer(); bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url); + bool handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url); void exit(int retCode = 0); diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index ff4ea9e6b4..ec5f16012e 100755 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -18,116 +18,6 @@ #include "HTTPConnection.h" #include "HTTPManager.h" -bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QUrl& url) { - if (_requestHandler && _requestHandler->handleHTTPRequest(connection, url)) { - // this request was handled by our _requestHandler object - // so we don't need to attempt to do so in the document root - return true; - } - - // check to see if there is a file to serve from the document root for this path - QString subPath = url.path(); - - // remove any slash at the beginning of the path - if (subPath.startsWith('/')) { - subPath.remove(0, 1); - } - - QString filePath; - - if (QFileInfo(_documentRoot + subPath).isFile()) { - filePath = _documentRoot + subPath; - } else if (subPath.size() > 0 && !subPath.endsWith('/')) { - // this could be a directory with a trailing slash - // send a redirect to the path with a slash so we can - QString redirectLocation = '/' + subPath + '/'; - - if (!url.query().isEmpty()) { - redirectLocation += "?" + url.query(); - } - - QHash redirectHeader; - redirectHeader.insert(QByteArray("Location"), redirectLocation.toUtf8()); - - connection->respond(HTTPConnection::StatusCode301, "", HTTPConnection::DefaultContentType, redirectHeader); - } - - // if the last thing is a trailing slash then we want to look for index file - if (subPath.endsWith('/') || subPath.size() == 0) { - QStringList possibleIndexFiles = QStringList() << "index.html" << "index.shtml"; - - foreach (const QString& possibleIndexFilename, possibleIndexFiles) { - if (QFileInfo(_documentRoot + subPath + possibleIndexFilename).exists()) { - filePath = _documentRoot + subPath + possibleIndexFilename; - break; - } - } - } - - if (!filePath.isEmpty()) { - // file exists, serve it - static QMimeDatabase mimeDatabase; - - QFile localFile(filePath); - localFile.open(QIODevice::ReadOnly); - QByteArray localFileData = localFile.readAll(); - - QFileInfo localFileInfo(filePath); - - if (localFileInfo.completeSuffix() == "shtml") { - // this is a file that may have some SSI statements - // the only thing we support is the include directive, but check the contents for that - - // setup our static QRegExp that will catch and directives - const QString includeRegExpString = ""; - QRegExp includeRegExp(includeRegExpString); - - int matchPosition = 0; - - QString localFileString(localFileData); - - while ((matchPosition = includeRegExp.indexIn(localFileString, matchPosition)) != -1) { - // check if this is a file or vitual include - bool isFileInclude = includeRegExp.cap(1) == "file"; - - // setup the correct file path for the included file - QString includeFilePath = isFileInclude - ? localFileInfo.canonicalPath() + "/" + includeRegExp.cap(2) - : _documentRoot + includeRegExp.cap(2); - - QString replacementString; - - if (QFileInfo(includeFilePath).isFile()) { - - QFile includedFile(includeFilePath); - includedFile.open(QIODevice::ReadOnly); - - replacementString = QString(includedFile.readAll()); - } else { - qDebug() << "SSI include directive referenced a missing file:" << includeFilePath; - } - - // replace the match with the contents of the file, or an empty string if the file was not found - localFileString.replace(matchPosition, includeRegExp.matchedLength(), replacementString); - - // push the match position forward so we can check the next match - matchPosition += includeRegExp.matchedLength(); - } - - localFileData = localFileString.toLocal8Bit(); - } - - connection->respond(HTTPConnection::StatusCode200, localFileData, - qPrintable(mimeDatabase.mimeTypeForFile(filePath).name())); - } else { - - // respond with a 404 - connection->respond(HTTPConnection::StatusCode404, "Resource not found."); - } - - return true; -} - HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler, QObject* parent) : QTcpServer(parent), _documentRoot(documentRoot), @@ -149,3 +39,120 @@ void HTTPManager::incomingConnection(qintptr socketDescriptor) { delete socket; } } + +bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QUrl& url) { + if (requestHandledByRequestHandler(connection, url)) { + // this request was handled by our request handler object + // so we don't need to attempt to do so in the document root + return true; + } + + if (!_documentRoot.isEmpty()) { + // check to see if there is a file to serve from the document root for this path + QString subPath = url.path(); + + // remove any slash at the beginning of the path + if (subPath.startsWith('/')) { + subPath.remove(0, 1); + } + + QString filePath; + + if (QFileInfo(_documentRoot + subPath).isFile()) { + filePath = _documentRoot + subPath; + } else if (subPath.size() > 0 && !subPath.endsWith('/')) { + // this could be a directory with a trailing slash + // send a redirect to the path with a slash so we can + QString redirectLocation = '/' + subPath + '/'; + + if (!url.query().isEmpty()) { + redirectLocation += "?" + url.query(); + } + + QHash redirectHeader; + redirectHeader.insert(QByteArray("Location"), redirectLocation.toUtf8()); + + connection->respond(HTTPConnection::StatusCode301, "", HTTPConnection::DefaultContentType, redirectHeader); + } + + // if the last thing is a trailing slash then we want to look for index file + if (subPath.endsWith('/') || subPath.size() == 0) { + QStringList possibleIndexFiles = QStringList() << "index.html" << "index.shtml"; + + foreach (const QString& possibleIndexFilename, possibleIndexFiles) { + if (QFileInfo(_documentRoot + subPath + possibleIndexFilename).exists()) { + filePath = _documentRoot + subPath + possibleIndexFilename; + break; + } + } + } + + if (!filePath.isEmpty()) { + // file exists, serve it + static QMimeDatabase mimeDatabase; + + QFile localFile(filePath); + localFile.open(QIODevice::ReadOnly); + QByteArray localFileData = localFile.readAll(); + + QFileInfo localFileInfo(filePath); + + if (localFileInfo.completeSuffix() == "shtml") { + // this is a file that may have some SSI statements + // the only thing we support is the include directive, but check the contents for that + + // setup our static QRegExp that will catch and directives + const QString includeRegExpString = ""; + QRegExp includeRegExp(includeRegExpString); + + int matchPosition = 0; + + QString localFileString(localFileData); + + while ((matchPosition = includeRegExp.indexIn(localFileString, matchPosition)) != -1) { + // check if this is a file or vitual include + bool isFileInclude = includeRegExp.cap(1) == "file"; + + // setup the correct file path for the included file + QString includeFilePath = isFileInclude + ? localFileInfo.canonicalPath() + "/" + includeRegExp.cap(2) + : _documentRoot + includeRegExp.cap(2); + + QString replacementString; + + if (QFileInfo(includeFilePath).isFile()) { + + QFile includedFile(includeFilePath); + includedFile.open(QIODevice::ReadOnly); + + replacementString = QString(includedFile.readAll()); + } else { + qDebug() << "SSI include directive referenced a missing file:" << includeFilePath; + } + + // replace the match with the contents of the file, or an empty string if the file was not found + localFileString.replace(matchPosition, includeRegExp.matchedLength(), replacementString); + + // push the match position forward so we can check the next match + matchPosition += includeRegExp.matchedLength(); + } + + localFileData = localFileString.toLocal8Bit(); + } + + connection->respond(HTTPConnection::StatusCode200, localFileData, + qPrintable(mimeDatabase.mimeTypeForFile(filePath).name())); + + return true; + } + } + + // respond with a 404 + connection->respond(HTTPConnection::StatusCode404, "Resource not found."); + + return true; +} + +bool HTTPManager::requestHandledByRequestHandler(HTTPConnection* connection, const QUrl& url) { + return _requestHandler && _requestHandler->handleHTTPRequest(connection, url); +} \ No newline at end of file diff --git a/libraries/embedded-webserver/src/HTTPManager.h b/libraries/embedded-webserver/src/HTTPManager.h index ea2936f464..e8745521dc 100755 --- a/libraries/embedded-webserver/src/HTTPManager.h +++ b/libraries/embedded-webserver/src/HTTPManager.h @@ -19,6 +19,7 @@ #include class HTTPConnection; +class HTTPSConnection; class HTTPRequestHandler { public: @@ -38,6 +39,7 @@ public: protected: /// Accepts all pending connections virtual void incomingConnection(qintptr socketDescriptor); + virtual bool requestHandledByRequestHandler(HTTPConnection* connection, const QUrl& url); protected: QString _documentRoot; HTTPRequestHandler* _requestHandler; diff --git a/libraries/embedded-webserver/src/HTTPSManager.cpp b/libraries/embedded-webserver/src/HTTPSManager.cpp index 0f5b94de64..4e40a0e02c 100644 --- a/libraries/embedded-webserver/src/HTTPSManager.cpp +++ b/libraries/embedded-webserver/src/HTTPSManager.cpp @@ -16,10 +16,11 @@ #include "HTTPSManager.h" HTTPSManager::HTTPSManager(quint16 port, const QSslCertificate& certificate, const QSslKey& privateKey, - const QString& documentRoot, HTTPRequestHandler* requestHandler, QObject* parent) : + const QString& documentRoot, HTTPSRequestHandler* requestHandler, QObject* parent) : HTTPManager(port, documentRoot, requestHandler, parent), _certificate(certificate), - _privateKey(privateKey) + _privateKey(privateKey), + _sslRequestHandler(requestHandler) { } @@ -35,4 +36,16 @@ void HTTPSManager::incomingConnection(qintptr socketDescriptor) { } else { delete sslSocket; } +} + +bool HTTPSManager::handleHTTPRequest(HTTPConnection* connection, const QUrl &url) { + return handleHTTPSRequest(reinterpret_cast(connection), url); +} + +bool HTTPSManager::handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url) { + return HTTPManager::handleHTTPRequest(connection, url); +} + +bool HTTPSManager::requestHandledByRequestHandler(HTTPConnection* connection, const QUrl& url) { + return _sslRequestHandler && _sslRequestHandler->handleHTTPSRequest(reinterpret_cast(connection), url); } \ No newline at end of file diff --git a/libraries/embedded-webserver/src/HTTPSManager.h b/libraries/embedded-webserver/src/HTTPSManager.h index 8940b2e79d..fe7c4dc065 100644 --- a/libraries/embedded-webserver/src/HTTPSManager.h +++ b/libraries/embedded-webserver/src/HTTPSManager.h @@ -17,23 +17,34 @@ #include "HTTPManager.h" -class HTTPSManager : public HTTPManager { +class HTTPSRequestHandler : public HTTPRequestHandler { +public: + /// Handles an HTTPS request + virtual bool handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url) = 0; +}; + +class HTTPSManager : public HTTPManager, public HTTPSRequestHandler { Q_OBJECT public: HTTPSManager(quint16 port, const QSslCertificate& certificate, const QSslKey& privateKey, const QString& documentRoot, - HTTPRequestHandler* requestHandler = NULL, QObject* parent = 0); + HTTPSRequestHandler* requestHandler = NULL, QObject* parent = 0); void setCertificate(const QSslCertificate& certificate) { _certificate = certificate; } void setPrivateKey(const QSslKey& privateKey) { _privateKey = privateKey; } + bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url); + bool handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url); + protected: - virtual void incomingConnection(qintptr socketDescriptor); + void incomingConnection(qintptr socketDescriptor); + bool requestHandledByRequestHandler(HTTPConnection* connection, const QUrl& url); private: QSslCertificate _certificate; QSslKey _privateKey; + HTTPSRequestHandler* _sslRequestHandler; }; #endif // hifi_HTTPSManager_h \ No newline at end of file From c26fc7a8385ffa9f36bfd9c21b78781238609b96 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 24 Apr 2014 15:05:40 -0700 Subject: [PATCH 05/27] pull OAuth code from URL in domain-server --- domain-server/src/DomainServer.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 1bad7d72f5..f267769ac0 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -969,9 +970,24 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url } bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &url) { - qDebug() << "HTTPS request received at" << url; - qDebug() << "not handling"; - return false; + const QString URI_OAUTH = "/oauth"; + if (url.path() == URI_OAUTH) { + qDebug() << "Handling an OAuth authorization."; + + const QString CODE_QUERY_KEY = "code"; + QString authorizationCode = QUrlQuery(url).queryItemValue(CODE_QUERY_KEY); + + if (!authorizationCode.isEmpty()) { + + } + + // respond with a 200 code indicating that login is complete + connection->respond(HTTPConnection::StatusCode200); + + return true; + } else { + return false; + } } void DomainServer::refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment) { From 86f3cf2a75f2eea80df6e53788242bad83644eab Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 25 Apr 2014 16:39:45 -0700 Subject: [PATCH 06/27] allow passing of oauth info to DS --- domain-server/src/DomainServer.cpp | 101 +++++++++++++++++------------ domain-server/src/DomainServer.h | 8 ++- 2 files changed, 67 insertions(+), 42 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index f267769ac0..650c79c5c7 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -45,7 +45,9 @@ DomainServer::DomainServer(int argc, char* argv[]) : _x509Credentials(NULL), _dhParams(NULL), _priorityCache(NULL), - _dtlsSessions() + _dtlsSessions(), + _oauthProviderURL(), + _oauthClientID() { gnutls_global_init(); @@ -56,7 +58,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : _argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments()); - if (optionallySetupTLS()) { + if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth() && optionallySetupDTLS()) { // 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."; @@ -89,44 +91,7 @@ DomainServer::~DomainServer() { gnutls_global_deinit(); } -bool DomainServer::optionallySetupTLS() { - if (readX509KeyAndCertificate()) { - if (_x509Credentials) { - qDebug() << "Generating Diffie-Hellman parameters."; - - // generate Diffie-Hellman parameters - // When short bit length is used, it might be wise to regenerate parameters often. - int dhBits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_LEGACY); - - _dhParams = new gnutls_dh_params_t; - gnutls_dh_params_init(_dhParams); - gnutls_dh_params_generate2(*_dhParams, dhBits); - - qDebug() << "Successfully generated Diffie-Hellman parameters."; - - // set the D-H paramters on the X509 credentials - gnutls_certificate_set_dh_params(*_x509Credentials, *_dhParams); - - // setup the key used for cookie verification - _cookieKey = new gnutls_datum_t; - gnutls_key_generate(_cookieKey, GNUTLS_COOKIE_KEY_SIZE); - - _priorityCache = new gnutls_priority_t; - const char DTLS_PRIORITY_STRING[] = "PERFORMANCE:-VERS-TLS-ALL:+VERS-DTLS1.2:%SERVER_PRECEDENCE"; - gnutls_priority_init(_priorityCache, DTLS_PRIORITY_STRING, NULL); - - _isUsingDTLS = true; - - qDebug() << "Initial DTLS setup complete."; - } - - return true; - } else { - return false; - } -} - -bool DomainServer::readX509KeyAndCertificate() { +bool DomainServer::optionallyReadX509KeyAndCertificate() { const QString X509_CERTIFICATE_OPTION = "cert"; const QString X509_PRIVATE_KEY_OPTION = "key"; const QString X509_KEY_PASSPHRASE_ENV = "DOMAIN_SERVER_KEY_PASSPHRASE"; @@ -185,6 +150,62 @@ bool DomainServer::readX509KeyAndCertificate() { return true; } +bool DomainServer::optionallySetupOAuth() { + const QString OAUTH_PROVIDER_URL_OPTION = "oauth-provider"; + const QString OAUTH_CLIENT_ID_OPTION = "oauth-client-id"; + const QString REDIRECT_HOSTNAME_OPTION = "hostname"; + + _oauthProviderURL = QUrl(_argumentVariantMap.value(OAUTH_PROVIDER_URL_OPTION).toString()); + _oauthClientID = _argumentVariantMap.value(OAUTH_CLIENT_ID_OPTION).toString(); + QString oauthRedirectHostname = _argumentVariantMap.value(REDIRECT_HOSTNAME_OPTION).toString(); + + if (!_oauthProviderURL.isEmpty() || !oauthRedirectHostname.isEmpty() || !_oauthClientID.isEmpty()) { + if (_oauthProviderURL.isEmpty() || oauthRedirectHostname.isEmpty() || _oauthClientID.isEmpty()) { + qDebug() << "Missing OAuth provider URL or hostname. domain-server will now quit."; + QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); + return false; + } else { + qDebug() << "OAuth will be used to identify clients using provider at" << _oauthProviderURL.toString(); + qDebug() << "OAuth Client ID is" << _oauthClientID; + } + } + + return true; +} + +bool DomainServer::optionallySetupDTLS() { + if (_x509Credentials) { + qDebug() << "Generating Diffie-Hellman parameters."; + + // generate Diffie-Hellman parameters + // When short bit length is used, it might be wise to regenerate parameters often. + int dhBits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_LEGACY); + + _dhParams = new gnutls_dh_params_t; + gnutls_dh_params_init(_dhParams); + gnutls_dh_params_generate2(*_dhParams, dhBits); + + qDebug() << "Successfully generated Diffie-Hellman parameters."; + + // set the D-H paramters on the X509 credentials + gnutls_certificate_set_dh_params(*_x509Credentials, *_dhParams); + + // setup the key used for cookie verification + _cookieKey = new gnutls_datum_t; + gnutls_key_generate(_cookieKey, GNUTLS_COOKIE_KEY_SIZE); + + _priorityCache = new gnutls_priority_t; + const char DTLS_PRIORITY_STRING[] = "PERFORMANCE:-VERS-TLS-ALL:+VERS-DTLS1.2:%SERVER_PRECEDENCE"; + gnutls_priority_init(_priorityCache, DTLS_PRIORITY_STRING, NULL); + + _isUsingDTLS = true; + + qDebug() << "Initial DTLS setup complete."; + } + + return true; +} + void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) { const QString CUSTOM_PORT_OPTION = "port"; diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 40429e23ea..2a81c2921d 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -53,8 +53,9 @@ private slots: void readAvailableDTLSDatagrams(); private: void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid()); - bool optionallySetupTLS(); - bool readX509KeyAndCertificate(); + bool optionallySetupOAuth(); + bool optionallySetupDTLS(); + bool optionallyReadX509KeyAndCertificate(); void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr); @@ -95,6 +96,9 @@ private: gnutls_priority_t* _priorityCache; QHash _dtlsSessions; + + QUrl _oauthProviderURL; + QString _oauthClientID; }; #endif // hifi_DomainServer_h From 1c2032790a156c81f9513bd58dc67eefc42376d1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 25 Apr 2014 17:02:19 -0700 Subject: [PATCH 07/27] allow passing of OAuth client secret to DS on command-line --- domain-server/src/DomainServer.cpp | 9 +++++++-- domain-server/src/DomainServer.h | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 650c79c5c7..75411ef936 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -153,15 +153,20 @@ bool DomainServer::optionallyReadX509KeyAndCertificate() { bool DomainServer::optionallySetupOAuth() { const QString OAUTH_PROVIDER_URL_OPTION = "oauth-provider"; const QString OAUTH_CLIENT_ID_OPTION = "oauth-client-id"; + const QString OAUTH_CLIENT_SECRET_ENV = "DOMAIN_SERVER_CLIENT_SECRET"; const QString REDIRECT_HOSTNAME_OPTION = "hostname"; _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(); if (!_oauthProviderURL.isEmpty() || !oauthRedirectHostname.isEmpty() || !_oauthClientID.isEmpty()) { - if (_oauthProviderURL.isEmpty() || oauthRedirectHostname.isEmpty() || _oauthClientID.isEmpty()) { - qDebug() << "Missing OAuth provider URL or hostname. domain-server will now quit."; + if (_oauthProviderURL.isEmpty() + || oauthRedirectHostname.isEmpty() + || _oauthClientID.isEmpty() + || _oauthClientSecret.isEmpty()) { + qDebug() << "Missing OAuth provider URL, hostname, client ID, or client secret. domain-server will now quit."; QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); return false; } else { diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 2a81c2921d..da43e76d60 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -99,6 +99,7 @@ private: QUrl _oauthProviderURL; QString _oauthClientID; + QString _oauthClientSecret; }; #endif // hifi_DomainServer_h From 2f0e311a9923cc411201c1742626249a4f355b70 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 1 May 2014 10:19:05 -0700 Subject: [PATCH 08/27] 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; From 56c24ce8b750d871a5c4455ce354279b5e29f38f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 1 May 2014 11:09:35 -0700 Subject: [PATCH 09/27] present QWebView for OAuth authorization in interface --- domain-server/src/DomainServer.cpp | 1 - interface/src/Application.cpp | 6 +++- interface/src/DatagramProcessor.cpp | 4 ++- interface/src/ui/OAuthWebViewHandler.cpp | 36 ++++++++++++++++++++++++ interface/src/ui/OAuthWebViewHandler.h | 32 +++++++++++++++++++++ 5 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 interface/src/ui/OAuthWebViewHandler.cpp create mode 100644 interface/src/ui/OAuthWebViewHandler.h diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 684db8e959..045436affa 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -454,7 +454,6 @@ QUrl DomainServer::oauthAuthorizationURL() { 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); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 97b5c05f25..e052ff8cf7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -79,9 +79,10 @@ #include "scripting/SettingsScriptingInterface.h" #include "ui/InfoView.h" +#include "ui/OAuthWebviewHandler.h" #include "ui/Snapshot.h" -#include "ui/TextRenderer.h" #include "ui/Stats.h" +#include "ui/TextRenderer.h" using namespace std; @@ -354,6 +355,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : } //When -url in command line, teleport to location urlGoTo(argc, constArgv); + + // call the OAuthWebviewHandler static getter so that its instance lives in our thread + OAuthWebViewHandler::getInstance(); } Application::~Application() { diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index 9b34b3b651..70e791f59d 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -15,6 +15,7 @@ #include "Application.h" #include "Menu.h" +#include "ui/OAuthWebviewHandler.h" #include "DatagramProcessor.h" @@ -118,7 +119,8 @@ void DatagramProcessor::processDatagrams() { QUrl authorizationURL; readStream >> authorizationURL; - qDebug() << "the authorization URL sent from the server is" << authorizationURL; + QMetaObject::invokeMethod(&OAuthWebViewHandler::getInstance(), "displayWebviewForAuthorizationURL", + Q_ARG(const QUrl&, authorizationURL)); break; } diff --git a/interface/src/ui/OAuthWebViewHandler.cpp b/interface/src/ui/OAuthWebViewHandler.cpp new file mode 100644 index 0000000000..bd33482d41 --- /dev/null +++ b/interface/src/ui/OAuthWebViewHandler.cpp @@ -0,0 +1,36 @@ +// +// OAuthWebViewHandler.cpp +// interface/src/ui +// +// Created by Stephen Birarda on 2014-05-01. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include "Application.h" + +#include "OAuthWebviewHandler.h" + +OAuthWebViewHandler& OAuthWebViewHandler::getInstance() { + static OAuthWebViewHandler sharedInstance; + return sharedInstance; +} + +OAuthWebViewHandler::OAuthWebViewHandler() : + _activeWebView(NULL) +{ + +} + +void OAuthWebViewHandler::displayWebviewForAuthorizationURL(const QUrl& authorizationURL) { + if (!_activeWebView) { + _activeWebView = new QWebView(); + _activeWebView->setWindowFlags(Qt::WindowStaysOnTopHint); + _activeWebView->load(authorizationURL); + _activeWebView->show(); + } +} diff --git a/interface/src/ui/OAuthWebViewHandler.h b/interface/src/ui/OAuthWebViewHandler.h new file mode 100644 index 0000000000..6f8d140d87 --- /dev/null +++ b/interface/src/ui/OAuthWebViewHandler.h @@ -0,0 +1,32 @@ +// +// OAuthWebviewHandler.h +// interface/src/ui +// +// Created by Stephen Birarda on 2014-05-01. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_OAuthWebviewHandler_h +#define hifi_OAuthWebviewHandler_h + +#include + +class QWebView; + +class OAuthWebViewHandler : public QObject { + Q_OBJECT +public: + OAuthWebViewHandler(); + static OAuthWebViewHandler& getInstance(); + +public slots: + void displayWebviewForAuthorizationURL(const QUrl& authorizationURL); + +private: + QWebView* _activeWebView; +}; + +#endif // hifi_OAuthWebviewHandler_h \ No newline at end of file From d9033a8074a1fb3f4a1c0cec021440a2bb53e441 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 1 May 2014 11:13:46 -0700 Subject: [PATCH 10/27] output SSL errors from OAuthWebViewHandler --- interface/src/ui/OAuthWebViewHandler.cpp | 7 +++++++ interface/src/ui/OAuthWebViewHandler.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/interface/src/ui/OAuthWebViewHandler.cpp b/interface/src/ui/OAuthWebViewHandler.cpp index bd33482d41..222edcb0aa 100644 --- a/interface/src/ui/OAuthWebViewHandler.cpp +++ b/interface/src/ui/OAuthWebViewHandler.cpp @@ -32,5 +32,12 @@ void OAuthWebViewHandler::displayWebviewForAuthorizationURL(const QUrl& authoriz _activeWebView->setWindowFlags(Qt::WindowStaysOnTopHint); _activeWebView->load(authorizationURL); _activeWebView->show(); + + connect(_activeWebView->page()->networkAccessManager(), &QNetworkAccessManager::sslErrors, + this, &OAuthWebViewHandler::handleSSLErrors); } } + +void OAuthWebViewHandler::handleSSLErrors(QNetworkReply* networkReply, const QList& errorList) { + qDebug() << "SSL Errors:" << errorList; +} diff --git a/interface/src/ui/OAuthWebViewHandler.h b/interface/src/ui/OAuthWebViewHandler.h index 6f8d140d87..8a9fb8520f 100644 --- a/interface/src/ui/OAuthWebViewHandler.h +++ b/interface/src/ui/OAuthWebViewHandler.h @@ -25,6 +25,8 @@ public: public slots: void displayWebviewForAuthorizationURL(const QUrl& authorizationURL); +private slots: + void handleSSLErrors(QNetworkReply* networkReply, const QList& errorList); private: QWebView* _activeWebView; }; From 7d3157b1a09c47f7e9262ce20229c2e0052c2da8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 1 May 2014 11:54:51 -0700 Subject: [PATCH 11/27] trust high fidelity root CA cert for OAuth redirect --- interface/src/Application.cpp | 2 ++ interface/src/ui/OAuthWebViewHandler.cpp | 14 ++++++++++++++ interface/src/ui/OAuthWebViewHandler.h | 1 + libraries/networking/src/DTLSSession.cpp | 4 ++-- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e052ff8cf7..d2ef9c1122 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -358,6 +358,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // call the OAuthWebviewHandler static getter so that its instance lives in our thread OAuthWebViewHandler::getInstance(); + // make sure the High Fidelity root CA is in our list of trusted certs + OAuthWebViewHandler::addHighFidelityRootCAToSSLConfig(); } Application::~Application() { diff --git a/interface/src/ui/OAuthWebViewHandler.cpp b/interface/src/ui/OAuthWebViewHandler.cpp index 222edcb0aa..3b9cf9f125 100644 --- a/interface/src/ui/OAuthWebViewHandler.cpp +++ b/interface/src/ui/OAuthWebViewHandler.cpp @@ -26,10 +26,24 @@ OAuthWebViewHandler::OAuthWebViewHandler() : } +void OAuthWebViewHandler::addHighFidelityRootCAToSSLConfig() { + QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration(); + + // add the High Fidelity root CA to the list of trusted CA certificates + QByteArray highFidelityCACertificate(reinterpret_cast(DTLSSession::highFidelityCADatum()->data), + DTLSSession::highFidelityCADatum()->size); + sslConfig.setCaCertificates(sslConfig.caCertificates() + QSslCertificate::fromData(highFidelityCACertificate)); + + // set the modified configuration + QSslConfiguration::setDefaultConfiguration(sslConfig); +} + void OAuthWebViewHandler::displayWebviewForAuthorizationURL(const QUrl& authorizationURL) { if (!_activeWebView) { _activeWebView = new QWebView(); _activeWebView->setWindowFlags(Qt::WindowStaysOnTopHint); + + qDebug() << "Displaying QWebView for OAuth authorization at" << authorizationURL.toString(); _activeWebView->load(authorizationURL); _activeWebView->show(); diff --git a/interface/src/ui/OAuthWebViewHandler.h b/interface/src/ui/OAuthWebViewHandler.h index 8a9fb8520f..e411bda579 100644 --- a/interface/src/ui/OAuthWebViewHandler.h +++ b/interface/src/ui/OAuthWebViewHandler.h @@ -21,6 +21,7 @@ class OAuthWebViewHandler : public QObject { public: OAuthWebViewHandler(); static OAuthWebViewHandler& getInstance(); + static void addHighFidelityRootCAToSSLConfig(); public slots: void displayWebviewForAuthorizationURL(const QUrl& authorizationURL); diff --git a/libraries/networking/src/DTLSSession.cpp b/libraries/networking/src/DTLSSession.cpp index 7d375ec327..f0649e4fc8 100644 --- a/libraries/networking/src/DTLSSession.cpp +++ b/libraries/networking/src/DTLSSession.cpp @@ -78,7 +78,7 @@ gnutls_datum_t* DTLSSession::highFidelityCADatum() { static bool datumInitialized = false; static unsigned char HIGHFIDELITY_ROOT_CA_CERT[] = - "-----BEGIN CERTIFICATE-----" + "-----BEGIN CERTIFICATE-----\n" "MIID6TCCA1KgAwIBAgIJANlfRkRD9A8bMA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD\n" "VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5j\n" "aXNjbzEbMBkGA1UEChMSSGlnaCBGaWRlbGl0eSwgSW5jMRMwEQYDVQQLEwpPcGVy\n" @@ -100,7 +100,7 @@ gnutls_datum_t* DTLSSession::highFidelityCADatum() { "SIb3DQEBBQUAA4GBAEkQl3p+lH5vuoCNgyfa67nL0MsBEt+5RSBOgjwCjjASjzou\n" "FTv5w0he2OypgMQb8i/BYtS1lJSFqjPJcSM1Salzrm3xDOK5pOXJ7h6SQLPDVEyf\n" "Hy2/9d/to+99+SOUlvfzfgycgjOc+s/AV7Y+GBd7uzGxUdrN4egCZW1F6/mH\n" - "-----END CERTIFICATE-----"; + "-----END CERTIFICATE-----\n"; if (!datumInitialized) { hifiCADatum.data = HIGHFIDELITY_ROOT_CA_CERT; From ca2148178d2cfd74838c2e26941e74e6307590f8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 1 May 2014 12:01:08 -0700 Subject: [PATCH 12/27] handle cleanup of OAuth web view when force closed --- interface/src/ui/OAuthWebViewHandler.cpp | 5 ++++- interface/src/ui/OAuthWebViewHandler.h | 2 +- libraries/networking/src/NodeList.cpp | 17 +++++++++-------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/interface/src/ui/OAuthWebViewHandler.cpp b/interface/src/ui/OAuthWebViewHandler.cpp index 3b9cf9f125..6ef7bebdce 100644 --- a/interface/src/ui/OAuthWebViewHandler.cpp +++ b/interface/src/ui/OAuthWebViewHandler.cpp @@ -40,8 +40,11 @@ void OAuthWebViewHandler::addHighFidelityRootCAToSSLConfig() { void OAuthWebViewHandler::displayWebviewForAuthorizationURL(const QUrl& authorizationURL) { if (!_activeWebView) { - _activeWebView = new QWebView(); + _activeWebView = new QWebView; + + // keep the window on top and delete it when it closes _activeWebView->setWindowFlags(Qt::WindowStaysOnTopHint); + _activeWebView->setAttribute(Qt::WA_DeleteOnClose); qDebug() << "Displaying QWebView for OAuth authorization at" << authorizationURL.toString(); _activeWebView->load(authorizationURL); diff --git a/interface/src/ui/OAuthWebViewHandler.h b/interface/src/ui/OAuthWebViewHandler.h index e411bda579..692a31dc75 100644 --- a/interface/src/ui/OAuthWebViewHandler.h +++ b/interface/src/ui/OAuthWebViewHandler.h @@ -29,7 +29,7 @@ public slots: private slots: void handleSSLErrors(QNetworkReply* networkReply, const QList& errorList); private: - QWebView* _activeWebView; + QPointer _activeWebView; }; #endif // hifi_OAuthWebviewHandler_h \ No newline at end of file diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index fbf2655269..9714bf11e5 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -404,7 +404,6 @@ void NodeList::sendDomainServerCheckIn() { dtlsSession->writeDatagram(domainServerPacket); } - const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5; static unsigned int numDomainCheckins = 0; @@ -413,14 +412,16 @@ void NodeList::sendDomainServerCheckIn() { sendSTUNRequest(); } - if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { - // we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS - // so emit our signal that indicates that - emit limitOfSilentDomainCheckInsReached(); + if (_domainHandler.isConnected()) { + if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { + // we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS + // so emit our signal that indicates that + emit limitOfSilentDomainCheckInsReached(); + } + + // increment the count of un-replied check-ins + _numNoReplyDomainCheckIns++; } - - // increment the count of un-replied check-ins - _numNoReplyDomainCheckIns++; } } From 66077d5616709ab836b2e07f88b3a7aefe41c8b7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 1 May 2014 12:18:40 -0700 Subject: [PATCH 13/27] present the OAuth webview only every 5 seconds --- interface/src/ui/OAuthWebViewHandler.cpp | 19 ++++++++++++++++++- interface/src/ui/OAuthWebViewHandler.h | 3 +++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/OAuthWebViewHandler.cpp b/interface/src/ui/OAuthWebViewHandler.cpp index 6ef7bebdce..3ba2a1115a 100644 --- a/interface/src/ui/OAuthWebViewHandler.cpp +++ b/interface/src/ui/OAuthWebViewHandler.cpp @@ -38,8 +38,10 @@ void OAuthWebViewHandler::addHighFidelityRootCAToSSLConfig() { QSslConfiguration::setDefaultConfiguration(sslConfig); } +const int WEB_VIEW_REDISPLAY_ELAPSED_MSECS = 5 * 1000; + void OAuthWebViewHandler::displayWebviewForAuthorizationURL(const QUrl& authorizationURL) { - if (!_activeWebView) { + if (!_activeWebView && _webViewRedisplayTimer.elapsed() >= WEB_VIEW_REDISPLAY_ELAPSED_MSECS) { _activeWebView = new QWebView; // keep the window on top and delete it when it closes @@ -52,9 +54,24 @@ void OAuthWebViewHandler::displayWebviewForAuthorizationURL(const QUrl& authoriz connect(_activeWebView->page()->networkAccessManager(), &QNetworkAccessManager::sslErrors, this, &OAuthWebViewHandler::handleSSLErrors); + connect(_activeWebView, &QWebView::loadFinished, this, &OAuthWebViewHandler::handleLoadFinished); + + // connect to the destroyed signal so after the web view closes we can start a timer + connect(_activeWebView, &QWebView::destroyed, this, &OAuthWebViewHandler::handleWebViewDestroyed); } } void OAuthWebViewHandler::handleSSLErrors(QNetworkReply* networkReply, const QList& errorList) { qDebug() << "SSL Errors:" << errorList; } + +void OAuthWebViewHandler::handleLoadFinished(bool success) { + if (success && _activeWebView->url().host() == NodeList::getInstance()->getDomainHandler().getHostname()) { + qDebug() << "OAuth authorization code passed successfully to domain-server."; + _activeWebView->close(); + } +} + +void OAuthWebViewHandler::handleWebViewDestroyed(QObject* destroyedObject) { + _webViewRedisplayTimer.restart(); +} diff --git a/interface/src/ui/OAuthWebViewHandler.h b/interface/src/ui/OAuthWebViewHandler.h index 692a31dc75..b3a865ce07 100644 --- a/interface/src/ui/OAuthWebViewHandler.h +++ b/interface/src/ui/OAuthWebViewHandler.h @@ -28,8 +28,11 @@ public slots: private slots: void handleSSLErrors(QNetworkReply* networkReply, const QList& errorList); + void handleLoadFinished(bool success); + void handleWebViewDestroyed(QObject* destroyedObject); private: QPointer _activeWebView; + QElapsedTimer _webViewRedisplayTimer; }; #endif // hifi_OAuthWebviewHandler_h \ No newline at end of file From d942054015cc0f3287d7891aeeaf4c091ccf0dc8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 1 May 2014 12:44:42 -0700 Subject: [PATCH 14/27] cleanup re-display of OAuthWebView --- domain-server/src/DomainServer.cpp | 4 ++++ interface/src/Application.cpp | 3 +++ interface/src/ui/OAuthWebViewHandler.cpp | 18 ++++++++++++++++-- interface/src/ui/OAuthWebViewHandler.h | 3 +++ libraries/networking/src/NodeList.cpp | 10 +++++++--- 5 files changed, 33 insertions(+), 5 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 045436affa..932b45cb6b 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -453,6 +453,10 @@ QUrl DomainServer::oauthAuthorizationURL() { const QString OAUTH_REPSONSE_TYPE_QUERY_VALUE = "code"; authorizationQuery.addQueryItem(OAUTH_RESPONSE_TYPE_QUERY_KEY, OAUTH_REPSONSE_TYPE_QUERY_VALUE); + const QString OAUTH_STATE_QUERY_KEY = "state"; + // create a new UUID that will be the state parameter for oauth authorization AND the new session UUID for that node + authorizationQuery.addQueryItem(OAUTH_STATE_QUERY_KEY, uuidStringWithoutCurlyBraces(QUuid::createUuid())); + QString redirectURL = QString("https://%1:%2/oauth").arg(_hostname).arg(_httpsManager->serverPort()); const QString OAUTH_REDIRECT_URI_QUERY_KEY = "redirect_uri"; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d2ef9c1122..7350a23b9a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3094,6 +3094,9 @@ void Application::domainChanged(const QString& domainHostname) { // reset the voxels renderer _voxels.killLocalVoxels(); + + // reset the auth URL for OAuth web view handler + OAuthWebViewHandler::getInstance().clearLastAuthorizationURL(); } void Application::connectedToDomain(const QString& hostname) { diff --git a/interface/src/ui/OAuthWebViewHandler.cpp b/interface/src/ui/OAuthWebViewHandler.cpp index 3ba2a1115a..00202d9a89 100644 --- a/interface/src/ui/OAuthWebViewHandler.cpp +++ b/interface/src/ui/OAuthWebViewHandler.cpp @@ -21,7 +21,9 @@ OAuthWebViewHandler& OAuthWebViewHandler::getInstance() { } OAuthWebViewHandler::OAuthWebViewHandler() : - _activeWebView(NULL) + _activeWebView(NULL), + _webViewRedisplayTimer(), + _lastAuthorizationURL() { } @@ -41,7 +43,19 @@ void OAuthWebViewHandler::addHighFidelityRootCAToSSLConfig() { const int WEB_VIEW_REDISPLAY_ELAPSED_MSECS = 5 * 1000; void OAuthWebViewHandler::displayWebviewForAuthorizationURL(const QUrl& authorizationURL) { - if (!_activeWebView && _webViewRedisplayTimer.elapsed() >= WEB_VIEW_REDISPLAY_ELAPSED_MSECS) { + if (!_activeWebView) { + + if (!_lastAuthorizationURL.isEmpty()) { + if (_lastAuthorizationURL.host() == authorizationURL.host() + && _webViewRedisplayTimer.elapsed() < WEB_VIEW_REDISPLAY_ELAPSED_MSECS) { + // this would be re-displaying an OAuth dialog for the same auth URL inside of the redisplay ms + // so return instead + return; + } + } + + _lastAuthorizationURL = authorizationURL; + _activeWebView = new QWebView; // keep the window on top and delete it when it closes diff --git a/interface/src/ui/OAuthWebViewHandler.h b/interface/src/ui/OAuthWebViewHandler.h index b3a865ce07..3a30705f88 100644 --- a/interface/src/ui/OAuthWebViewHandler.h +++ b/interface/src/ui/OAuthWebViewHandler.h @@ -23,6 +23,8 @@ public: static OAuthWebViewHandler& getInstance(); static void addHighFidelityRootCAToSSLConfig(); + void clearLastAuthorizationURL() { _lastAuthorizationURL = QUrl(); } + public slots: void displayWebviewForAuthorizationURL(const QUrl& authorizationURL); @@ -33,6 +35,7 @@ private slots: private: QPointer _activeWebView; QElapsedTimer _webViewRedisplayTimer; + QUrl _lastAuthorizationURL; }; #endif // hifi_OAuthWebviewHandler_h \ No newline at end of file diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 9714bf11e5..00422f7be9 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -378,12 +378,16 @@ void NodeList::sendDomainServerCheckIn() { } } - PacketType domainPacketType = _sessionUUID.isNull() + PacketType domainPacketType = !_domainHandler.isConnected() ? PacketTypeDomainConnectRequest : PacketTypeDomainListRequest; // construct the DS check in packet - QUuid packetUUID = (domainPacketType == PacketTypeDomainListRequest - ? _sessionUUID : _domainHandler.getAssignmentUUID()); + QUuid packetUUID = _sessionUUID; + + if (!_domainHandler.getAssignmentUUID().isNull() && domainPacketType == PacketTypeDomainConnectRequest) { + // for assigned nodes who have not yet connected, send the assignment UUID as this packet UUID + packetUUID = _domainHandler.getAssignmentUUID(); + } QByteArray domainServerPacket = byteArrayWithPopulatedHeader(domainPacketType, packetUUID); QDataStream packetStream(&domainServerPacket, QIODevice::Append); From 5d5292b4b557018129044e88997f10e825bc79d1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 1 May 2014 12:50:44 -0700 Subject: [PATCH 15/27] pull state as session UUID from OAuth URL --- interface/src/ui/OAuthWebViewHandler.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/interface/src/ui/OAuthWebViewHandler.cpp b/interface/src/ui/OAuthWebViewHandler.cpp index 00202d9a89..d8aed59bdf 100644 --- a/interface/src/ui/OAuthWebViewHandler.cpp +++ b/interface/src/ui/OAuthWebViewHandler.cpp @@ -82,6 +82,14 @@ void OAuthWebViewHandler::handleSSLErrors(QNetworkReply* networkReply, const QLi void OAuthWebViewHandler::handleLoadFinished(bool success) { if (success && _activeWebView->url().host() == NodeList::getInstance()->getDomainHandler().getHostname()) { qDebug() << "OAuth authorization code passed successfully to domain-server."; + + // grab the UUID that is set as the state parameter in the auth URL + // since that is our new session UUID + QUrlQuery authQuery(_activeWebView->url()); + + const QString AUTH_STATE_QUERY_KEY = "state"; + NodeList::getInstance()->setSessionUUID(QUuid(authQuery.queryItemValue(AUTH_STATE_QUERY_KEY))); + _activeWebView->close(); } } From 7a1aa7ec89a14fddb25a27c22becf919b80696fc Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 1 May 2014 13:50:39 -0700 Subject: [PATCH 16/27] keep track of all assignments to stop double agents --- domain-server/src/DomainServer.cpp | 132 ++++++++++----------- domain-server/src/DomainServer.h | 6 +- domain-server/src/DomainServerNodeData.cpp | 2 +- domain-server/src/DomainServerNodeData.h | 6 +- libraries/networking/src/Assignment.cpp | 6 +- libraries/networking/src/Assignment.h | 4 + libraries/networking/src/NodeList.cpp | 19 ++- 7 files changed, 85 insertions(+), 90 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 932b45cb6b..9266934c3c 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -36,8 +36,8 @@ DomainServer::DomainServer(int argc, char* argv[]) : QCoreApplication(argc, argv), _httpManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this), _httpsManager(NULL), - _staticAssignmentHash(), - _assignmentQueue(), + _allAssignments(), + _unfulfilledAssignments(), _isUsingDTLS(false), _x509Credentials(NULL), _dhParams(NULL), @@ -285,7 +285,8 @@ void DomainServer::parseAssignmentConfigs(QSet& excludedTypes) void DomainServer::addStaticAssignmentToAssignmentHash(Assignment* newAssignment) { qDebug() << "Inserting assignment" << *newAssignment << "to static assignment hash."; - _staticAssignmentHash.insert(newAssignment->getUUID(), SharedAssignmentPointer(newAssignment)); + newAssignment->setIsStatic(true); + _allAssignments.insert(newAssignment->getUUID(), SharedAssignmentPointer(newAssignment)); } void DomainServer::createScriptedAssignmentsFromArray(const QJsonArray &configArray) { @@ -318,7 +319,9 @@ void DomainServer::createScriptedAssignmentsFromArray(const QJsonArray &configAr qDebug() << "URL for script is" << assignmentURL; // scripts passed on CL or via JSON are static - so they are added back to the queue if the node dies - _assignmentQueue.enqueue(SharedAssignmentPointer(scriptAssignment)); + SharedAssignmentPointer sharedScriptAssignment(scriptAssignment); + _unfulfilledAssignments.enqueue(sharedScriptAssignment); + _allAssignments.insert(sharedScriptAssignment->getUUID(), sharedScriptAssignment); } } } @@ -383,18 +386,23 @@ const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer << NodeType::VoxelServer << NodeType::ParticleServer << NodeType::MetavoxelServer; -void DomainServer::addNodeToNodeListAndConfirmConnection(const QByteArray& packet, const HifiSockAddr& senderSockAddr) { +void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSockAddr& senderSockAddr) { NodeType_t nodeType; HifiSockAddr publicSockAddr, localSockAddr; int numPreInterestBytes = parseNodeDataFromByteArray(nodeType, publicSockAddr, localSockAddr, packet, senderSockAddr); - QUuid assignmentUUID = uuidFromPacketHeader(packet); - bool isStaticAssignment = _staticAssignmentHash.contains(assignmentUUID); - SharedAssignmentPointer matchingAssignment = SharedAssignmentPointer(); + QUuid packetUUID = uuidFromPacketHeader(packet); + + // check if this connect request matches an assignment in the queue + bool isFulfilledOrUnfulfilledAssignment = _allAssignments.contains(packetUUID); + SharedAssignmentPointer matchingQueuedAssignment = SharedAssignmentPointer(); + if (isFulfilledOrUnfulfilledAssignment) { + matchingQueuedAssignment = matchingQueuedAssignmentForCheckIn(packetUUID, nodeType); + } - if (assignmentUUID.isNull() && !_oauthProviderURL.isEmpty()) { + if (!matchingQueuedAssignment && !_oauthProviderURL.isEmpty()) { // we have an OAuth provider, ask this interface client to auth against it QByteArray oauthRequestByteArray = byteArrayWithPopulatedHeader(PacketTypeDomainOAuthRequest); @@ -403,36 +411,27 @@ void DomainServer::addNodeToNodeListAndConfirmConnection(const QByteArray& packe // send this oauth request datagram back to the client LimitedNodeList::getInstance()->writeUnverifiedDatagram(oauthRequestByteArray, senderSockAddr); - } else { - 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); + } else if ((!isFulfilledOrUnfulfilledAssignment && !STATICALLY_ASSIGNED_NODES.contains(nodeType)) + || (isFulfilledOrUnfulfilledAssignment && matchingQueuedAssignment)) { + // this was either not a static assignment or it was and we had a matching one in the queue + + // create a new session UUID for this node + QUuid nodeUUID = QUuid::createUuid(); - if (matchingAssignment) { - // remove the matching assignment from the assignment queue so we don't take the next check in - // (if it exists) - removeMatchingAssignmentFromQueue(matchingAssignment); - } + 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()); + + if (isFulfilledOrUnfulfilledAssignment) { + nodeData->setAssignmentUUID(packetUUID); } - // 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)); - } + nodeData->setSendingSockAddr(senderSockAddr); + + // reply back to the user with a PacketTypeDomainList + sendDomainListToNode(newNode, senderSockAddr, nodeInterestListFromPacket(packet, numPreInterestBytes)); } } @@ -751,9 +750,7 @@ void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiS PacketType requestType = packetTypeForPacket(receivedPacket); if (requestType == PacketTypeDomainConnectRequest) { - // add this node to our NodeList - // and send back session UUID right away - addNodeToNodeListAndConfirmConnection(receivedPacket, senderSockAddr); + handleConnectRequest(receivedPacket, senderSockAddr); } else if (requestType == PacketTypeDomainListRequest) { QUuid nodeUUID = uuidFromPacketHeader(receivedPacket); @@ -819,7 +816,7 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { nodeJson[JSON_KEY_WAKE_TIMESTAMP] = QString::number(node->getWakeTimestamp()); // if the node has pool information, add it - SharedAssignmentPointer matchingAssignment = _staticAssignmentHash.value(node->getUUID()); + SharedAssignmentPointer matchingAssignment = _allAssignments.value(node->getUUID()); if (matchingAssignment) { nodeJson[JSON_KEY_POOL] = matchingAssignment->getPool(); } @@ -855,7 +852,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url // enumerate the NodeList to find the assigned nodes foreach (const SharedNodePointer& node, LimitedNodeList::getInstance()->getNodeHash()) { - if (_staticAssignmentHash.value(node->getUUID())) { + if (_allAssignments.value(node->getUUID())) { // add the node using the UUID as the key QString uuidString = uuidStringWithoutCurlyBraces(node->getUUID()); assignedNodesJSON[uuidString] = jsonObjectForNode(node); @@ -867,7 +864,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url QJsonObject queuedAssignmentsJSON; // add the queued but unfilled assignments to the json - foreach(const SharedAssignmentPointer& assignment, _assignmentQueue) { + foreach(const SharedAssignmentPointer& assignment, _unfulfilledAssignments) { QJsonObject queuedAssignmentJSON; QString uuidString = uuidStringWithoutCurlyBraces(assignment->getUUID()); @@ -984,7 +981,9 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url .arg(newPath).arg(assignmentPool == emptyPool ? "" : " - pool is " + assignmentPool)); // add the script assigment to the assignment queue - _assignmentQueue.enqueue(SharedAssignmentPointer(scriptAssignment)); + SharedAssignmentPointer sharedScriptedAssignment(scriptAssignment); + _unfulfilledAssignments.enqueue(sharedScriptedAssignment); + _allAssignments.insert(sharedScriptedAssignment->getUUID(), sharedScriptedAssignment); } // respond with a 200 code for successful upload @@ -1065,13 +1064,8 @@ void DomainServer::refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& } // add the static assignment back under the right UUID, and to the queue - _staticAssignmentHash.insert(assignment->getUUID(), assignment); - - _assignmentQueue.enqueue(assignment); - - // remove the old assignment from the _staticAssignmentHash - // this must be done last so copies are created before the assignment passed by reference is killed - _staticAssignmentHash.remove(oldUUID); + _allAssignments.insert(assignment->getUUID(), assignment); + _unfulfilledAssignments.enqueue(assignment); } void DomainServer::nodeAdded(SharedNodePointer node) { @@ -1085,10 +1079,10 @@ void DomainServer::nodeKilled(SharedNodePointer node) { if (nodeData) { // if this node's UUID matches a static assignment we need to throw it back in the assignment queue - if (!nodeData->getStaticAssignmentUUID().isNull()) { - SharedAssignmentPointer matchedAssignment = _staticAssignmentHash.value(nodeData->getStaticAssignmentUUID()); + if (!nodeData->getAssignmentUUID().isNull()) { + SharedAssignmentPointer matchedAssignment = _allAssignments.take(nodeData->getAssignmentUUID()); - if (matchedAssignment) { + if (matchedAssignment && matchedAssignment->isStatic()) { refreshStaticAssignmentAndAddToQueue(matchedAssignment); } } @@ -1112,11 +1106,11 @@ void DomainServer::nodeKilled(SharedNodePointer node) { } SharedAssignmentPointer DomainServer::matchingQueuedAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType) { - QQueue::iterator i = _assignmentQueue.begin(); + QQueue::iterator i = _unfulfilledAssignments.begin(); - while (i != _assignmentQueue.end()) { + while (i != _unfulfilledAssignments.end()) { if (i->data()->getType() == Assignment::typeForNodeType(nodeType) && i->data()->getUUID() == checkInUUID) { - return _assignmentQueue.takeAt(i - _assignmentQueue.begin()); + return _unfulfilledAssignments.takeAt(i - _unfulfilledAssignments.begin()); } else { ++i; } @@ -1128,9 +1122,9 @@ SharedAssignmentPointer DomainServer::matchingQueuedAssignmentForCheckIn(const Q SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assignment& requestAssignment) { // this is an unassigned client talking to us directly for an assignment // go through our queue and see if there are any assignments to give out - QQueue::iterator sharedAssignment = _assignmentQueue.begin(); + QQueue::iterator sharedAssignment = _unfulfilledAssignments.begin(); - while (sharedAssignment != _assignmentQueue.end()) { + while (sharedAssignment != _unfulfilledAssignments.end()) { Assignment* assignment = sharedAssignment->data(); bool requestIsAllTypes = requestAssignment.getType() == Assignment::AllTypes; bool assignmentTypesMatch = assignment->getType() == requestAssignment.getType(); @@ -1140,16 +1134,12 @@ SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assig if ((requestIsAllTypes || assignmentTypesMatch) && (nietherHasPool || assignmentPoolsMatch)) { // remove the assignment from the queue - SharedAssignmentPointer deployableAssignment = _assignmentQueue.takeAt(sharedAssignment - - _assignmentQueue.begin()); + SharedAssignmentPointer deployableAssignment = _unfulfilledAssignments.takeAt(sharedAssignment + - _unfulfilledAssignments.begin()); - if (deployableAssignment->getType() != Assignment::AgentType - || _staticAssignmentHash.contains(deployableAssignment->getUUID())) { - // this is a static assignment - // until we get a check-in from that GUID - // put assignment back in queue but stick it at the back so the others have a chance to go out - _assignmentQueue.enqueue(deployableAssignment); - } + // until we get a connection for this assignment + // put assignment back in queue but stick it at the back so the others have a chance to go out + _unfulfilledAssignments.enqueue(deployableAssignment); // stop looping, we've handed out an assignment return deployableAssignment; @@ -1163,10 +1153,10 @@ SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assig } void DomainServer::removeMatchingAssignmentFromQueue(const SharedAssignmentPointer& removableAssignment) { - QQueue::iterator potentialMatchingAssignment = _assignmentQueue.begin(); - while (potentialMatchingAssignment != _assignmentQueue.end()) { + QQueue::iterator potentialMatchingAssignment = _unfulfilledAssignments.begin(); + while (potentialMatchingAssignment != _unfulfilledAssignments.end()) { if (potentialMatchingAssignment->data()->getUUID() == removableAssignment->getUUID()) { - _assignmentQueue.erase(potentialMatchingAssignment); + _unfulfilledAssignments.erase(potentialMatchingAssignment); // we matched and removed an assignment, bail out break; @@ -1180,7 +1170,7 @@ void DomainServer::addStaticAssignmentsToQueue() { // if the domain-server has just restarted, // check if there are static assignments that we need to throw into the assignment queue - QHash staticHashCopy = _staticAssignmentHash; + QHash staticHashCopy = _allAssignments; QHash::iterator staticAssignment = staticHashCopy.begin(); while (staticAssignment != staticHashCopy.end()) { // add any of the un-matched static assignments to the queue diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 7cb5861727..fa01459554 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -59,7 +59,7 @@ private: void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr); - void addNodeToNodeListAndConfirmConnection(const QByteArray& packet, const HifiSockAddr& senderSockAddr); + void handleConnectRequest(const QByteArray& packet, const HifiSockAddr& senderSockAddr); int parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr, HifiSockAddr& localSockAddr, const QByteArray& packet, const HifiSockAddr& senderSockAddr); NodeSet nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes); @@ -86,8 +86,8 @@ private: HTTPManager _httpManager; HTTPSManager* _httpsManager; - QHash _staticAssignmentHash; - QQueue _assignmentQueue; + QHash _allAssignments; + QQueue _unfulfilledAssignments; QVariantMap _argumentVariantMap; diff --git a/domain-server/src/DomainServerNodeData.cpp b/domain-server/src/DomainServerNodeData.cpp index eb2ddb3200..a43e17ae60 100644 --- a/domain-server/src/DomainServerNodeData.cpp +++ b/domain-server/src/DomainServerNodeData.cpp @@ -19,7 +19,7 @@ DomainServerNodeData::DomainServerNodeData() : _sessionSecretHash(), - _staticAssignmentUUID(), + _assignmentUUID(), _statsJSONObject(), _sendingSockAddr(), _isAuthenticated(true) diff --git a/domain-server/src/DomainServerNodeData.h b/domain-server/src/DomainServerNodeData.h index f079244c4a..011bee57c1 100644 --- a/domain-server/src/DomainServerNodeData.h +++ b/domain-server/src/DomainServerNodeData.h @@ -27,8 +27,8 @@ public: void parseJSONStatsPacket(const QByteArray& statsPacket); - void setStaticAssignmentUUID(const QUuid& staticAssignmentUUID) { _staticAssignmentUUID = staticAssignmentUUID; } - const QUuid& getStaticAssignmentUUID() const { return _staticAssignmentUUID; } + void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; } + const QUuid& getAssignmentUUID() const { return _assignmentUUID; } void setSendingSockAddr(const HifiSockAddr& sendingSockAddr) { _sendingSockAddr = sendingSockAddr; } const HifiSockAddr& getSendingSockAddr() { return _sendingSockAddr; } @@ -41,7 +41,7 @@ private: QJsonObject mergeJSONStatsFromNewObject(const QJsonObject& newObject, QJsonObject destinationObject); QHash _sessionSecretHash; - QUuid _staticAssignmentUUID; + QUuid _assignmentUUID; QJsonObject _statsJSONObject; HifiSockAddr _sendingSockAddr; bool _isAuthenticated; diff --git a/libraries/networking/src/Assignment.cpp b/libraries/networking/src/Assignment.cpp index dd318aad8e..f6f3208822 100644 --- a/libraries/networking/src/Assignment.cpp +++ b/libraries/networking/src/Assignment.cpp @@ -48,7 +48,8 @@ Assignment::Assignment() : _type(Assignment::AllTypes), _pool(), _location(Assignment::LocalLocation), - _payload() + _payload(), + _isStatic(false) { } @@ -59,7 +60,8 @@ Assignment::Assignment(Assignment::Command command, Assignment::Type type, const _type(type), _pool(pool), _location(location), - _payload() + _payload(), + _isStatic(false) { if (_command == Assignment::CreateCommand) { // this is a newly created assignment, generate a random UUID diff --git a/libraries/networking/src/Assignment.h b/libraries/networking/src/Assignment.h index f0f7e8db1a..8180c56985 100644 --- a/libraries/networking/src/Assignment.h +++ b/libraries/networking/src/Assignment.h @@ -77,6 +77,9 @@ public: void setPool(const QString& pool) { _pool = pool; }; const QString& getPool() const { return _pool; } + void setIsStatic(bool isStatic) { _isStatic = isStatic; } + bool isStatic() const { return _isStatic; } + const char* getTypeName() const; // implement parseData to return 0 so we can be a subclass of NodeData @@ -93,6 +96,7 @@ protected: QString _pool; /// the destination pool for this assignment Assignment::Location _location; /// the location of the assignment, allows a domain to preferentially use local ACs QByteArray _payload; /// an optional payload attached to this assignment, a maximum for 1024 bytes will be packed + bool _isStatic; /// defines if this assignment needs to be re-queued in the domain-server if it stops being fulfilled }; #endif // hifi_Assignment_h diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 00422f7be9..d5c02f8eed 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -385,7 +385,8 @@ void NodeList::sendDomainServerCheckIn() { QUuid packetUUID = _sessionUUID; if (!_domainHandler.getAssignmentUUID().isNull() && domainPacketType == PacketTypeDomainConnectRequest) { - // for assigned nodes who have not yet connected, send the assignment UUID as this packet UUID + // this is a connect request and we're an assigned node + // so set our packetUUID as the assignment UUID packetUUID = _domainHandler.getAssignmentUUID(); } @@ -416,16 +417,14 @@ void NodeList::sendDomainServerCheckIn() { sendSTUNRequest(); } - if (_domainHandler.isConnected()) { - if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { - // we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS - // so emit our signal that indicates that - emit limitOfSilentDomainCheckInsReached(); - } - - // increment the count of un-replied check-ins - _numNoReplyDomainCheckIns++; + if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { + // we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS + // so emit our signal that indicates that + emit limitOfSilentDomainCheckInsReached(); } + + // increment the count of un-replied check-ins + _numNoReplyDomainCheckIns++; } } From a826f3817b5ad9cf5a5ae9a3b5cb78a6f6bf8285 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 1 May 2014 13:56:45 -0700 Subject: [PATCH 17/27] fix assignment json for fulfilled assignments --- domain-server/src/DomainServer.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 9266934c3c..caca8d74c1 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -816,7 +816,8 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { nodeJson[JSON_KEY_WAKE_TIMESTAMP] = QString::number(node->getWakeTimestamp()); // if the node has pool information, add it - SharedAssignmentPointer matchingAssignment = _allAssignments.value(node->getUUID()); + DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); + SharedAssignmentPointer matchingAssignment = _allAssignments.value(nodeData->getAssignmentUUID()); if (matchingAssignment) { nodeJson[JSON_KEY_POOL] = matchingAssignment->getPool(); } @@ -852,9 +853,11 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url // enumerate the NodeList to find the assigned nodes foreach (const SharedNodePointer& node, LimitedNodeList::getInstance()->getNodeHash()) { - if (_allAssignments.value(node->getUUID())) { + DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); + + if (!nodeData->getAssignmentUUID().isNull()) { // add the node using the UUID as the key - QString uuidString = uuidStringWithoutCurlyBraces(node->getUUID()); + QString uuidString = uuidStringWithoutCurlyBraces(nodeData->getAssignmentUUID()); assignedNodesJSON[uuidString] = jsonObjectForNode(node); } } From a49668031d47786762eb81822d60cafcb9ef89b7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 1 May 2014 14:49:58 -0700 Subject: [PATCH 18/27] send a request for an access token after receiving auth code --- domain-server/src/DomainServer.cpp | 37 +++++++++++++++++++++++++----- domain-server/src/DomainServer.h | 3 +++ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index caca8d74c1..5f5c67e82f 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -435,6 +435,13 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock } } +QUrl DomainServer::oauthRedirectURL() { + return QString("https://%1:%2/oauth").arg(_hostname).arg(_httpsManager->serverPort()); +} + +const QString OAUTH_CLIENT_ID_QUERY_KEY = "client_id"; +const QString OAUTH_REDIRECT_URI_QUERY_KEY = "redirect_uri"; + QUrl DomainServer::oauthAuthorizationURL() { // for now these are all interface clients that have a GUI // so just send them back the full authorization URL @@ -445,7 +452,6 @@ QUrl DomainServer::oauthAuthorizationURL() { 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"; @@ -456,10 +462,7 @@ QUrl DomainServer::oauthAuthorizationURL() { // create a new UUID that will be the state parameter for oauth authorization AND the new session UUID for that node authorizationQuery.addQueryItem(OAUTH_STATE_QUERY_KEY, uuidStringWithoutCurlyBraces(QUuid::createUuid())); - QString redirectURL = QString("https://%1:%2/oauth").arg(_hostname).arg(_httpsManager->serverPort()); - - const QString OAUTH_REDIRECT_URI_QUERY_KEY = "redirect_uri"; - authorizationQuery.addQueryItem(OAUTH_REDIRECT_URI_QUERY_KEY, redirectURL); + authorizationQuery.addQueryItem(OAUTH_REDIRECT_URI_QUERY_KEY, oauthRedirectURL().toString()); authorizationURL.setQuery(authorizationQuery); @@ -1036,13 +1039,31 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &url) { const QString URI_OAUTH = "/oauth"; if (url.path() == URI_OAUTH) { - qDebug() << "Handling an OAuth authorization."; const QString CODE_QUERY_KEY = "code"; QString authorizationCode = QUrlQuery(url).queryItemValue(CODE_QUERY_KEY); if (!authorizationCode.isEmpty()) { + // fire off a request with this code and state to get an access token for the user + static QNetworkAccessManager* networkAccessManager = new QNetworkAccessManager(this); + const QString OAUTH_TOKEN_REQUEST_PATH = "/oauth/token"; + QUrl tokenRequestUrl = _oauthProviderURL; + tokenRequestUrl.setPath(OAUTH_TOKEN_REQUEST_PATH); + + const QString OAUTH_GRANT_TYPE_POST_STRING = "grant_type=authorization_code"; + QString tokenPostBody = OAUTH_GRANT_TYPE_POST_STRING; + tokenPostBody += QString("&code=%1&redirect_uri=%2&client_id=%3&client_secret=%4") + .arg(authorizationCode, oauthRedirectURL().toString(), _oauthClientID, _oauthClientSecret); + tokenPostBody += "&state=MOTHERFUKCINGSTATE"; + + QNetworkRequest tokenRequest(tokenRequestUrl); + tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + + networkAccessManager->post(QNetworkRequest(tokenRequestUrl), tokenPostBody.toLocal8Bit()); + + connect(networkAccessManager, &QNetworkAccessManager::finished, + this, &DomainServer::handleAuthCodeRequestFinished); } // respond with a 200 code indicating that login is complete @@ -1054,6 +1075,10 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u } } +void DomainServer::handleAuthCodeRequestFinished(QNetworkReply* networkReply) { + qDebug() << "response for auth code request" << networkReply->readAll(); +} + void DomainServer::refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment) { QUuid oldUUID = assignment->getUUID(); assignment->resetUUID(); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index fa01459554..f19e7cf3c1 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -78,8 +78,11 @@ private: void refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment); void addStaticAssignmentsToQueue(); + QUrl oauthRedirectURL(); QUrl oauthAuthorizationURL(); + void handleAuthCodeRequestFinished(QNetworkReply* networkReply); + QJsonObject jsonForSocket(const HifiSockAddr& socket); QJsonObject jsonObjectForNode(const SharedNodePointer& node); From b14d543701c34aab4f0188ad6a60e2ccb2e78e4c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 1 May 2014 15:46:09 -0700 Subject: [PATCH 19/27] retreive user profile and enumerate roles --- domain-server/src/DomainServer.cpp | 70 +++++++++++++++++++++++++----- domain-server/src/DomainServer.h | 6 ++- 2 files changed, 64 insertions(+), 12 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 5f5c67e82f..1e88da9460 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -45,7 +45,8 @@ DomainServer::DomainServer(int argc, char* argv[]) : _dtlsSessions(), _oauthProviderURL(), _oauthClientID(), - _hostname() + _hostname(), + _networkReplyUUIDMap() { gnutls_global_init(); @@ -68,6 +69,8 @@ DomainServer::DomainServer(int argc, char* argv[]) : // connect our socket to read datagrams received on the DTLS socket connect(&nodeList->getDTLSSocket(), &QUdpSocket::readyRead, this, &DomainServer::readAvailableDTLSDatagrams); } + + _networkAccessManager = new QNetworkAccessManager(this); } } @@ -1040,12 +1043,16 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u const QString URI_OAUTH = "/oauth"; if (url.path() == URI_OAUTH) { - const QString CODE_QUERY_KEY = "code"; - QString authorizationCode = QUrlQuery(url).queryItemValue(CODE_QUERY_KEY); + QUrlQuery codeURLQuery(url); - if (!authorizationCode.isEmpty()) { + const QString CODE_QUERY_KEY = "code"; + QString authorizationCode = codeURLQuery.queryItemValue(CODE_QUERY_KEY); + + const QString STATE_QUERY_KEY = "state"; + QUuid stateUUID = QUuid(codeURLQuery.queryItemValue(STATE_QUERY_KEY)); + + if (!authorizationCode.isEmpty() && !stateUUID.isNull()) { // fire off a request with this code and state to get an access token for the user - static QNetworkAccessManager* networkAccessManager = new QNetworkAccessManager(this); const QString OAUTH_TOKEN_REQUEST_PATH = "/oauth/token"; QUrl tokenRequestUrl = _oauthProviderURL; @@ -1055,15 +1062,16 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u QString tokenPostBody = OAUTH_GRANT_TYPE_POST_STRING; tokenPostBody += QString("&code=%1&redirect_uri=%2&client_id=%3&client_secret=%4") .arg(authorizationCode, oauthRedirectURL().toString(), _oauthClientID, _oauthClientSecret); - tokenPostBody += "&state=MOTHERFUKCINGSTATE"; QNetworkRequest tokenRequest(tokenRequestUrl); tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - networkAccessManager->post(QNetworkRequest(tokenRequestUrl), tokenPostBody.toLocal8Bit()); + QNetworkReply* tokenReply = _networkAccessManager->post(tokenRequest, tokenPostBody.toLocal8Bit()); - connect(networkAccessManager, &QNetworkAccessManager::finished, - this, &DomainServer::handleAuthCodeRequestFinished); + // insert this to our pending token replies so we can associate the returned access token with the right UUID + _networkReplyUUIDMap.insert(tokenReply, stateUUID); + + connect(tokenReply, &QNetworkReply::finished, this, &DomainServer::handleAuthCodeRequestFinished); } // respond with a 200 code indicating that login is complete @@ -1075,8 +1083,48 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u } } -void DomainServer::handleAuthCodeRequestFinished(QNetworkReply* networkReply) { - qDebug() << "response for auth code request" << networkReply->readAll(); +const QString OAUTH_JSON_ACCESS_TOKEN_KEY = "access_token"; + +void DomainServer::handleAuthCodeRequestFinished() { + QNetworkReply* networkReply = reinterpret_cast(sender()); + QUuid matchingSessionUUID = _networkReplyUUIDMap.take(networkReply); + + if (!matchingSessionUUID.isNull() && networkReply->error() == QNetworkReply::NoError) { + // pull the access token from the returned JSON and store it with the matching session UUID + QJsonDocument returnedJSON = QJsonDocument::fromJson(networkReply->readAll()); + QString accessToken = returnedJSON.object()[OAUTH_JSON_ACCESS_TOKEN_KEY].toString(); + + qDebug() << "Received access token for user with UUID" << uuidStringWithoutCurlyBraces(matchingSessionUUID); + + // fire off a request to get this user's identity so we can see if we will let them in + QUrl profileURL = _oauthProviderURL; + profileURL.setPath("/api/v1/users/profile"); + profileURL.setQuery(QString("%1=%2").arg(OAUTH_JSON_ACCESS_TOKEN_KEY, accessToken)); + + QNetworkReply* profileReply = _networkAccessManager->get(QNetworkRequest(profileURL)); + + connect(profileReply, &QNetworkReply::finished, this, &DomainServer::handleProfileRequestFinished); + + _networkReplyUUIDMap.insert(profileReply, matchingSessionUUID); + } +} + +void DomainServer::handleProfileRequestFinished() { + QNetworkReply* networkReply = reinterpret_cast(sender()); + QUuid matchingSessionUUID = _networkReplyUUIDMap.take(networkReply); + + if (!matchingSessionUUID.isNull() && networkReply->error() == QNetworkReply::NoError) { + QJsonDocument profileJSON = QJsonDocument::fromJson(networkReply->readAll()); + + if (profileJSON.object()["status"].toString() == "success") { + // pull the user roles from the response + QJsonArray rolesArray = profileJSON.object()["data"].toObject()["user"].toObject()["roles"].toArray(); + + foreach(const QJsonValue& roleValue, rolesArray) { + + } + } + } } void DomainServer::refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment) { diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index f19e7cf3c1..ab491c25c5 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -81,7 +81,8 @@ private: QUrl oauthRedirectURL(); QUrl oauthAuthorizationURL(); - void handleAuthCodeRequestFinished(QNetworkReply* networkReply); + void handleAuthCodeRequestFinished(); + void handleProfileRequestFinished(); QJsonObject jsonForSocket(const HifiSockAddr& socket); QJsonObject jsonObjectForNode(const SharedNodePointer& node); @@ -102,10 +103,13 @@ private: QHash _dtlsSessions; + QNetworkAccessManager* _networkAccessManager; + QUrl _oauthProviderURL; QString _oauthClientID; QString _oauthClientSecret; QString _hostname; + QMap _networkReplyUUIDMap; }; #endif // hifi_DomainServer_h From 709bcdb1481a1c03eecea58d334fe019055a9f67 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 1 May 2014 16:18:07 -0700 Subject: [PATCH 20/27] handle interface client connection to DS with user roles --- domain-server/src/DomainServer.cpp | 75 +++++++++++++++++++++++------- domain-server/src/DomainServer.h | 5 +- 2 files changed, 60 insertions(+), 20 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 1e88da9460..03d4e4eb81 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -46,7 +46,8 @@ DomainServer::DomainServer(int argc, char* argv[]) : _oauthProviderURL(), _oauthClientID(), _hostname(), - _networkReplyUUIDMap() + _networkReplyUUIDMap(), + _sessionAuthenticationHash() { gnutls_global_init(); @@ -406,21 +407,38 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock } if (!matchingQueuedAssignment && !_oauthProviderURL.isEmpty()) { - // we have an OAuth provider, ask this interface client to auth against it + // this is an Agent, and we require authentication + if (_sessionAuthenticationHash.contains(packetUUID)) { + if (!_sessionAuthenticationHash.value(packetUUID)) { + // we've decided this is a user that isn't allowed in, return out + // TODO: provide information to the user so they know why they can't connect + return; + } else { + // we're letting this user in, don't return and remove their UUID from the hash + _sessionAuthenticationHash.remove(packetUUID); + } + } else { + // we don't know anything about this client + // we have an OAuth provider, ask this interface client to auth against it + QByteArray oauthRequestByteArray = byteArrayWithPopulatedHeader(PacketTypeDomainOAuthRequest); + QDataStream oauthRequestStream(&oauthRequestByteArray, QIODevice::Append); + QUrl authorizationURL = packetUUID.isNull() ? oauthAuthorizationURL() : oauthAuthorizationURL(packetUUID); + oauthRequestStream << authorizationURL; + + // send this oauth request datagram back to the client + LimitedNodeList::getInstance()->writeUnverifiedDatagram(oauthRequestByteArray, senderSockAddr); + + return; + } + } - 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 if ((!isFulfilledOrUnfulfilledAssignment && !STATICALLY_ASSIGNED_NODES.contains(nodeType)) - || (isFulfilledOrUnfulfilledAssignment && matchingQueuedAssignment)) { + if ((!isFulfilledOrUnfulfilledAssignment && !STATICALLY_ASSIGNED_NODES.contains(nodeType)) + || (isFulfilledOrUnfulfilledAssignment && matchingQueuedAssignment)) { // this was either not a static assignment or it was and we had a matching one in the queue // 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 @@ -445,7 +463,7 @@ QUrl DomainServer::oauthRedirectURL() { const QString OAUTH_CLIENT_ID_QUERY_KEY = "client_id"; const QString OAUTH_REDIRECT_URI_QUERY_KEY = "redirect_uri"; -QUrl DomainServer::oauthAuthorizationURL() { +QUrl DomainServer::oauthAuthorizationURL(const QUuid& stateUUID) { // for now these are all interface clients that have a GUI // so just send them back the full authorization URL QUrl authorizationURL = _oauthProviderURL; @@ -463,7 +481,7 @@ QUrl DomainServer::oauthAuthorizationURL() { const QString OAUTH_STATE_QUERY_KEY = "state"; // create a new UUID that will be the state parameter for oauth authorization AND the new session UUID for that node - authorizationQuery.addQueryItem(OAUTH_STATE_QUERY_KEY, uuidStringWithoutCurlyBraces(QUuid::createUuid())); + authorizationQuery.addQueryItem(OAUTH_STATE_QUERY_KEY, uuidStringWithoutCurlyBraces(stateUUID)); authorizationQuery.addQueryItem(OAUTH_REDIRECT_URI_QUERY_KEY, oauthRedirectURL().toString()); @@ -1051,6 +1069,7 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u const QString STATE_QUERY_KEY = "state"; QUuid stateUUID = QUuid(codeURLQuery.queryItemValue(STATE_QUERY_KEY)); + if (!authorizationCode.isEmpty() && !stateUUID.isNull()) { // fire off a request with this code and state to get an access token for the user @@ -1068,10 +1087,12 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u QNetworkReply* tokenReply = _networkAccessManager->post(tokenRequest, tokenPostBody.toLocal8Bit()); + qDebug() << "Requesting a token for user with session UUID" << uuidStringWithoutCurlyBraces(stateUUID); + // insert this to our pending token replies so we can associate the returned access token with the right UUID _networkReplyUUIDMap.insert(tokenReply, stateUUID); - connect(tokenReply, &QNetworkReply::finished, this, &DomainServer::handleAuthCodeRequestFinished); + connect(tokenReply, &QNetworkReply::finished, this, &DomainServer::handleTokenRequestFinished); } // respond with a 200 code indicating that login is complete @@ -1085,7 +1106,7 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u const QString OAUTH_JSON_ACCESS_TOKEN_KEY = "access_token"; -void DomainServer::handleAuthCodeRequestFinished() { +void DomainServer::handleTokenRequestFinished() { QNetworkReply* networkReply = reinterpret_cast(sender()); QUuid matchingSessionUUID = _networkReplyUUIDMap.take(networkReply); @@ -1103,6 +1124,8 @@ void DomainServer::handleAuthCodeRequestFinished() { QNetworkReply* profileReply = _networkAccessManager->get(QNetworkRequest(profileURL)); + qDebug() << "Requesting access token for user with session UUID" << uuidStringWithoutCurlyBraces(matchingSessionUUID); + connect(profileReply, &QNetworkReply::finished, this, &DomainServer::handleProfileRequestFinished); _networkReplyUUIDMap.insert(profileReply, matchingSessionUUID); @@ -1118,11 +1141,27 @@ void DomainServer::handleProfileRequestFinished() { if (profileJSON.object()["status"].toString() == "success") { // pull the user roles from the response - QJsonArray rolesArray = profileJSON.object()["data"].toObject()["user"].toObject()["roles"].toArray(); + QJsonArray userRolesArray = profileJSON.object()["data"].toObject()["user"].toObject()["roles"].toArray(); - foreach(const QJsonValue& roleValue, rolesArray) { - + const QString ALLOWED_ROLES_CONFIG_KEY = "allowed-roles"; + QJsonArray allowedRolesArray = _argumentVariantMap.value(ALLOWED_ROLES_CONFIG_KEY).toJsonValue().toArray(); + + bool shouldAllowUserToConnect = false; + + foreach(const QJsonValue& roleValue, userRolesArray) { + if (allowedRolesArray.contains(roleValue)) { + // the user has a role that lets them in + // set the bool to true and break + shouldAllowUserToConnect = true; + break; + } } + + qDebug() << "Confirmed authentication state for user" << uuidStringWithoutCurlyBraces(matchingSessionUUID) + << "-" << shouldAllowUserToConnect; + + // insert this UUID and a flag that indicates if they are allowed to connect + _sessionAuthenticationHash.insert(matchingSessionUUID, shouldAllowUserToConnect); } } } diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index ab491c25c5..4c0f9447ea 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -79,9 +79,9 @@ private: void addStaticAssignmentsToQueue(); QUrl oauthRedirectURL(); - QUrl oauthAuthorizationURL(); + QUrl oauthAuthorizationURL(const QUuid& stateUUID = QUuid::createUuid()); - void handleAuthCodeRequestFinished(); + void handleTokenRequestFinished(); void handleProfileRequestFinished(); QJsonObject jsonForSocket(const HifiSockAddr& socket); @@ -110,6 +110,7 @@ private: QString _oauthClientSecret; QString _hostname; QMap _networkReplyUUIDMap; + QHash _sessionAuthenticationHash; }; #endif // hifi_DomainServer_h From 4936fb38570ae9697ea9efb4be5af04b45ac289a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 2 May 2014 10:34:29 -0700 Subject: [PATCH 21/27] expose access token from AccountManager --- interface/src/Application.cpp | 2 +- interface/src/Menu.cpp | 2 +- interface/src/XmppClient.cpp | 9 +++++---- interface/src/ui/ChatWindow.cpp | 2 +- interface/src/ui/OAuthWebViewHandler.cpp | 19 ++++++++++++++++++- interface/src/ui/Snapshot.cpp | 2 +- libraries/networking/src/AccountManager.h | 4 +--- 7 files changed, 28 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7350a23b9a..1f2b74680b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3071,7 +3071,7 @@ void Application::updateWindowTitle(){ QString buildVersion = " (build " + applicationVersion() + ")"; NodeList* nodeList = NodeList::getInstance(); - QString username = AccountManager::getInstance().getUsername(); + QString username = AccountManager::getInstance().getAccountInfo().getUsername(); QString title = QString() + (!username.isEmpty() ? username + " " : QString()) + nodeList->getSessionUUID().toString() + " @ " + nodeList->getDomainHandler().getHostname() + buildVersion; qDebug("Application title set to: %s", title.toStdString().c_str()); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 44117df55c..14fa0f60e4 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1119,7 +1119,7 @@ void Menu::toggleLoginMenuItem() { if (accountManager.isLoggedIn()) { // change the menu item to logout - _loginAction->setText("Logout " + accountManager.getUsername()); + _loginAction->setText("Logout " + accountManager.getAccountInfo().getUsername()); connect(_loginAction, &QAction::triggered, &accountManager, &AccountManager::logout); } else { // change the menu item to login diff --git a/interface/src/XmppClient.cpp b/interface/src/XmppClient.cpp index d930c16b53..48c1f5d976 100644 --- a/interface/src/XmppClient.cpp +++ b/interface/src/XmppClient.cpp @@ -34,12 +34,13 @@ XmppClient& XmppClient::getInstance() { void XmppClient::xmppConnected() { _publicChatRoom = _xmppMUCManager.addRoom(DEFAULT_CHAT_ROOM); - _publicChatRoom->setNickName(AccountManager::getInstance().getUsername()); + _publicChatRoom->setNickName(AccountManager::getInstance().getAccountInfo().getUsername()); _publicChatRoom->join(); } void XmppClient::xmppError(QXmppClient::Error error) { - qDebug() << "Error connnecting to XMPP for user " << AccountManager::getInstance().getUsername() << ": " << error; + qDebug() << "Error connnecting to XMPP for user " + << AccountManager::getInstance().getAccountInfo().getUsername() << ": " << error; } void XmppClient::connectToServer() { @@ -50,8 +51,8 @@ void XmppClient::connectToServer() { connect(&_xmppClient, SIGNAL(error(QXmppClient::Error)), this, SLOT(xmppError(QXmppClient::Error))); } AccountManager& accountManager = AccountManager::getInstance(); - QString user = accountManager.getUsername(); - const QString& password = accountManager.getXMPPPassword(); + QString user = accountManager.getAccountInfo().getUsername(); + const QString& password = accountManager.getAccountInfo().getXMPPPassword(); _xmppClient.connectToServer(user + "@" + DEFAULT_XMPP_SERVER, password); } diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index e0802c6bc5..3f2df5593b 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -254,7 +254,7 @@ void ChatWindow::messageReceived(const QXmppMessage& message) { } // Update background if this is a message from the current user - bool fromSelf = getParticipantName(message.from()) == AccountManager::getInstance().getUsername(); + bool fromSelf = getParticipantName(message.from()) == AccountManager::getInstance().getAccountInfo().getUsername(); // Create message area ChatMessageArea* messageArea = new ChatMessageArea(true); diff --git a/interface/src/ui/OAuthWebViewHandler.cpp b/interface/src/ui/OAuthWebViewHandler.cpp index d8aed59bdf..b72d2df273 100644 --- a/interface/src/ui/OAuthWebViewHandler.cpp +++ b/interface/src/ui/OAuthWebViewHandler.cpp @@ -63,7 +63,24 @@ void OAuthWebViewHandler::displayWebviewForAuthorizationURL(const QUrl& authoriz _activeWebView->setAttribute(Qt::WA_DeleteOnClose); qDebug() << "Displaying QWebView for OAuth authorization at" << authorizationURL.toString(); - _activeWebView->load(authorizationURL); + + AccountManager& accountManager = AccountManager::getInstance(); + + QUrl codedAuthorizationURL = authorizationURL; + + // check if we have an access token for this host - if so we can bypass login by adding it to the URL + if (accountManager.getAuthURL().host() == authorizationURL.host() + && accountManager.hasValidAccessToken()) { + + const QString ACCESS_TOKEN_QUERY_STRING_KEY = "access_token"; + + QUrlQuery authQuery(codedAuthorizationURL); + authQuery.addQueryItem(ACCESS_TOKEN_QUERY_STRING_KEY, accountManager.getAccountInfo().getAccessToken().token); + + codedAuthorizationURL.setQuery(authQuery); + } + + _activeWebView->load(codedAuthorizationURL); _activeWebView->show(); connect(_activeWebView->page()->networkAccessManager(), &QNetworkAccessManager::sslErrors, diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index 29c2d3c90f..243acdfb10 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -86,7 +86,7 @@ void Snapshot::saveSnapshot(QGLWidget* widget, Avatar* avatar) { // replace decimal . with '-' formattedLocation.replace('.', '-'); - QString username = AccountManager::getInstance().getUsername(); + QString username = AccountManager::getInstance().getAccountInfo().getUsername(); // normalize username, replace all non alphanumeric with '-' username.replace(QRegExp("[^A-Za-z0-9_]"), "-"); diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index cb76786f4e..5362e30383 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -55,9 +55,7 @@ public: void requestAccessToken(const QString& login, const QString& password); - QString getUsername() const { return _accountInfo.getUsername(); } - - const QString& getXMPPPassword() const { return _accountInfo.getXMPPPassword(); } + const DataServerAccountInfo& getAccountInfo() const { return _accountInfo; } void destroy() { delete _networkAccessManager; } From 72dd26acbee99248548fc22595fcb198a9590223 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 2 May 2014 10:47:59 -0700 Subject: [PATCH 22/27] only require OAuth authentication with a list of allowed roles --- domain-server/src/DomainServer.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 40b1c79a16..d9927f3833 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -386,6 +386,8 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet Date: Fri, 2 May 2014 15:52:50 -0700 Subject: [PATCH 23/27] fix typo for OAuth header include --- interface/src/Application.cpp | 2 +- interface/src/DatagramProcessor.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 98f2783f19..b3bc3f7263 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -81,7 +81,7 @@ #include "scripting/LocationScriptingInterface.h" #include "ui/InfoView.h" -#include "ui/OAuthWebviewHandler.h" +#include "ui/OAuthWebViewHandler.h" #include "ui/Snapshot.h" #include "ui/Stats.h" #include "ui/TextRenderer.h" diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index 3acbbd4d1e..287744eba2 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -15,7 +15,7 @@ #include "Application.h" #include "Menu.h" -#include "ui/OAuthWebviewHandler.h" +#include "ui/OAuthWebViewHandler.h" #include "DatagramProcessor.h" From 1fda012364d1a93b118e010e141df63b16df3745 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 2 May 2014 16:22:12 -0700 Subject: [PATCH 24/27] fix another casing issue in OAuthWebViewHandler --- interface/src/ui/OAuthWebViewHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/OAuthWebViewHandler.cpp b/interface/src/ui/OAuthWebViewHandler.cpp index b72d2df273..0dc238d332 100644 --- a/interface/src/ui/OAuthWebViewHandler.cpp +++ b/interface/src/ui/OAuthWebViewHandler.cpp @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include #include "Application.h" From 59ad09bfda6b9538732c2ff2c992075b793e3e02 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 2 May 2014 16:34:06 -0700 Subject: [PATCH 25/27] fix another missed casing issue --- interface/src/ui/OAuthWebViewHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/OAuthWebViewHandler.cpp b/interface/src/ui/OAuthWebViewHandler.cpp index 0dc238d332..26f873b5cb 100644 --- a/interface/src/ui/OAuthWebViewHandler.cpp +++ b/interface/src/ui/OAuthWebViewHandler.cpp @@ -13,7 +13,7 @@ #include "Application.h" -#include "OAuthWebviewHandler.h" +#include "OAuthWebViewHandler.h" OAuthWebViewHandler& OAuthWebViewHandler::getInstance() { static OAuthWebViewHandler sharedInstance; From 2dacc81960930a8b3911a958c4ddb810c3e8ed5b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 2 May 2014 16:47:49 -0700 Subject: [PATCH 26/27] use old signal/slot notation in OAuthWebViewHandler --- interface/src/ui/OAuthWebViewHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/OAuthWebViewHandler.cpp b/interface/src/ui/OAuthWebViewHandler.cpp index 26f873b5cb..91731c16a4 100644 --- a/interface/src/ui/OAuthWebViewHandler.cpp +++ b/interface/src/ui/OAuthWebViewHandler.cpp @@ -88,7 +88,7 @@ void OAuthWebViewHandler::displayWebviewForAuthorizationURL(const QUrl& authoriz connect(_activeWebView, &QWebView::loadFinished, this, &OAuthWebViewHandler::handleLoadFinished); // connect to the destroyed signal so after the web view closes we can start a timer - connect(_activeWebView, &QWebView::destroyed, this, &OAuthWebViewHandler::handleWebViewDestroyed); + connect(_activeWebView, SIGNAL(destroyed(QObject*)), this, SLOT(handleWebViewDestroyed(QObject*))); } } From 1f35c07420bfefb910759f36343332a4ae521b06 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 2 May 2014 16:58:06 -0700 Subject: [PATCH 27/27] more repairs to OAuth signal/slot connection --- interface/src/ui/OAuthWebViewHandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/OAuthWebViewHandler.cpp b/interface/src/ui/OAuthWebViewHandler.cpp index 91731c16a4..b3b914bd64 100644 --- a/interface/src/ui/OAuthWebViewHandler.cpp +++ b/interface/src/ui/OAuthWebViewHandler.cpp @@ -85,10 +85,10 @@ void OAuthWebViewHandler::displayWebviewForAuthorizationURL(const QUrl& authoriz connect(_activeWebView->page()->networkAccessManager(), &QNetworkAccessManager::sslErrors, this, &OAuthWebViewHandler::handleSSLErrors); - connect(_activeWebView, &QWebView::loadFinished, this, &OAuthWebViewHandler::handleLoadFinished); + connect(_activeWebView.data(), &QWebView::loadFinished, this, &OAuthWebViewHandler::handleLoadFinished); // connect to the destroyed signal so after the web view closes we can start a timer - connect(_activeWebView, SIGNAL(destroyed(QObject*)), this, SLOT(handleWebViewDestroyed(QObject*))); + connect(_activeWebView.data(), &QWebView::destroyed, this, &OAuthWebViewHandler::handleWebViewDestroyed); } }