From 260966915a7a03f610ee7f1558681ffe57eba16b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 24 Apr 2014 14:06:24 -0700 Subject: [PATCH 01/33] 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/33] 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/33] 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/33] 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/33] 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 4927a77382c6147702f7021d7c1431b9d033d182 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 24 Apr 2014 18:15:39 -0700 Subject: [PATCH 06/33] Some testing with editModels.js --- examples/editModels.js | 293 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 293 insertions(+) create mode 100644 examples/editModels.js diff --git a/examples/editModels.js b/examples/editModels.js new file mode 100644 index 0000000000..2724a17373 --- /dev/null +++ b/examples/editModels.js @@ -0,0 +1,293 @@ +// +// editModels.js +// examples +// +// Created by Clément Brisset on 4/24/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +var LEFT_BUTTON_FWD = 5; +var LEFT_BUTTON_3 = 3; +var RIGHT_BUTTON_FWD = 11; +var RIGHT_BUTTON_3 = 9; +var leftBallAlreadyInHand = false; +var rightBallAlreadyInHand = false; +var leftHandParticle; +var rightHandParticle; +var targetRadius = 0.25; + + + + +var LEFT_PALM = 0; +var LEFT_TIP = 1; +var LEFT_TRIGGER = 0; + +var RIGHT_PALM = 2; +var RIGHT_TIP = 3; +var RIGHT_TRIGGER = 1; + +var previewLineWidth = 4; +var leftLaser = Overlays.addOverlay("line3d", { + position: { x: 0, y: 0, z: 0}, + end: { x: 0, y: 0, z: 0}, + color: { red: 255, green: 0, blue: 0}, + alpha: 1, + visible: false, + lineWidth: previewLineWidth + }); +var rightLaser = Overlays.addOverlay("line3d", { + position: { x: 0, y: 0, z: 0}, + end: { x: 0, y: 0, z: 0}, + color: { red: 255, green: 0, blue: 0}, + alpha: 1, + visible: false, + lineWidth: previewLineWidth + }); + +var particle = Particles.addParticle({ position: { x: 0, y: 0, z:0 }, + velocity: { x: 0, y: 0, z: 0}, + gravity: { x: 0, y: 0, z: 0}, + radius: 0.05, + damping: 0.999, + color: { red: 255, green: 0, blue: 0 }, + modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/models/heads/defaultAvatar_head.fst", + }) + + +var wantDebugging = false; +function debugPrint(message) { + if (wantDebugging) { + print(message); + } +} + +function getBallHoldPosition(whichSide) { + var normal; + var tipPosition; + if (whichSide == LEFT_PALM) { + normal = Controller.getSpatialControlNormal(LEFT_PALM); + tipPosition = Controller.getSpatialControlPosition(LEFT_TIP); + } else { + normal = Controller.getSpatialControlNormal(RIGHT_PALM); + tipPosition = Controller.getSpatialControlPosition(RIGHT_TIP); + } + + var BALL_FORWARD_OFFSET = 0.08; // put the ball a bit forward of fingers + position = { x: BALL_FORWARD_OFFSET * normal.x, + y: BALL_FORWARD_OFFSET * normal.y, + z: BALL_FORWARD_OFFSET * normal.z }; + + position.x += tipPosition.x; + position.y += tipPosition.y; + position.z += tipPosition.z; + + return position; +} + +function checkControllerSide(whichSide) { + var palmPosition; + var tipPosition; + var overlay; + var triggerValue; + var BUTTON_3; + + if (whichSide == LEFT_PALM) { + palmPosition = Controller.getSpatialControlPosition(LEFT_PALM); + tipPosition = Controller.getSpatialControlPosition(LEFT_TIP); + overlay = leftLaser; + triggerValue = Controller.getTriggerValue(LEFT_TRIGGER); + BUTTON_3 = LEFT_BUTTON_3; + } else { + palmPosition = Controller.getSpatialControlPosition(RIGHT_PALM); + tipPosition = Controller.getSpatialControlPosition(RIGHT_TIP); + overlay = rightLaser; + triggerValue = Controller.getTriggerValue(RIGHT_TRIGGER); + BUTTON_3 = RIGHT_BUTTON_3; + } + + var createButtonPressed = (Controller.isButtonPressed(BUTTON_3)); + if (createButtonPressed) { + Particles.editParticle(particle, { + position: { x: 0, y: 0, z:0 }, + velocity: { x: 0, y: 0, z: 0}, + gravity: { x: 0, y: 0, z: 0}, + radius: 0.05, + damping: 0.999, + color: { red: 255, green: 0, blue: 0 }, + modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/models/heads/defaultAvatar_head.fst", + }); + } + + + var vector = Vec3.subtract(tipPosition, palmPosition); + var endPosition = Vec3.sum(palmPosition, Vec3.multiply(vector, 100)); + + Overlays.editOverlay(overlay, { + position: palmPosition, + end: endPosition, + visible: true + }); + + + if (triggerValue > 0.9) { + + } + + return; + var BUTTON_FWD; + var BUTTON_3; + var palmPosition; + var ballAlreadyInHand; + var handMessage; + + if (whichSide == LEFT_PALM) { + BUTTON_FWD = LEFT_BUTTON_FWD; + BUTTON_3 = LEFT_BUTTON_3; + palmPosition = Controller.getSpatialControlPosition(LEFT_PALM); + ballAlreadyInHand = leftBallAlreadyInHand; + handMessage = "LEFT"; + } else { + BUTTON_FWD = RIGHT_BUTTON_FWD; + BUTTON_3 = RIGHT_BUTTON_3; + palmPosition = Controller.getSpatialControlPosition(RIGHT_PALM); + ballAlreadyInHand = rightBallAlreadyInHand; + handMessage = "RIGHT"; + } + + var grabButtonPressed = (Controller.isButtonPressed(BUTTON_FWD) || Controller.isButtonPressed(BUTTON_3)); + + // If I don't currently have a ball in my hand, then try to catch closest one + if (!ballAlreadyInHand && grabButtonPressed) { + var closestParticle = Particles.findClosestParticle(palmPosition, targetRadius); + + if (closestParticle.isKnownID) { + + debugPrint(handMessage + " HAND- CAUGHT SOMETHING!!"); + + if (whichSide == LEFT_PALM) { + leftBallAlreadyInHand = true; + leftHandParticle = closestParticle; + } else { + rightBallAlreadyInHand = true; + rightHandParticle = closestParticle; + } + var ballPosition = getBallHoldPosition(whichSide); + var properties = { position: { x: ballPosition.x, + y: ballPosition.y, + z: ballPosition.z }, + velocity : { x: 0, y: 0, z: 0}, inHand: true }; + Particles.editParticle(closestParticle, properties); + + + return; // exit early + } + } + + // change ball color logic... + // + //if (wasButtonJustPressed()) { + // rotateColor(); + //} + + // If '3' is pressed, and not holding a ball, make a new one + if (Controller.isButtonPressed(BUTTON_3) && !ballAlreadyInHand) { + var ballPosition = getBallHoldPosition(whichSide); + var properties = { position: ballPosition, + velocity: { x: 0, y: 0, z: 0}, + gravity: { x: 0, y: 0, z: 0}, + inHand: true, + radius: 0.05, + damping: 0.999, + color: { red: 255, green: 0, blue: 0 }, + modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/models/heads/defaultAvatar_head.fst", + }; + + newParticle = Particles.addParticle(properties); + if (whichSide == LEFT_PALM) { + leftBallAlreadyInHand = true; + leftHandParticle = newParticle; + } else { + rightBallAlreadyInHand = true; + rightHandParticle = newParticle; + } + + return; // exit early + } + + if (ballAlreadyInHand) { + if (whichSide == LEFT_PALM) { + handParticle = leftHandParticle; + whichTip = LEFT_TIP; + } else { + handParticle = rightHandParticle; + whichTip = RIGHT_TIP; + } + + // If holding the ball keep it in the palm + if (grabButtonPressed) { + debugPrint(">>>>> " + handMessage + "-BALL IN HAND, grabbing, hold and move"); + var ballPosition = getBallHoldPosition(whichSide); + var properties = { position: { x: ballPosition.x, + y: ballPosition.y, + z: ballPosition.z }, + }; + Particles.editParticle(handParticle, properties); + } else { + debugPrint(">>>>> " + handMessage + "-BALL IN HAND, not grabbing, THROW!!!"); + // If toy ball just released, add velocity to it! + var tipVelocity = Controller.getSpatialControlVelocity(whichTip); + var THROWN_VELOCITY_SCALING = 1.5; + var properties = { + velocity: { x: tipVelocity.x * THROWN_VELOCITY_SCALING, + y: tipVelocity.y * THROWN_VELOCITY_SCALING, + z: tipVelocity.z * THROWN_VELOCITY_SCALING } , + inHand: false, + gravity: { x: 0, y: -2, z: 0}, + }; + + Particles.editParticle(handParticle, properties); + + if (whichSide == LEFT_PALM) { + leftBallAlreadyInHand = false; + leftHandParticle = false; + } else { + rightBallAlreadyInHand = false; + rightHandParticle = false; + } + } + } +} + + +function checkController(deltaTime) { + var numberOfButtons = Controller.getNumberOfButtons(); + var numberOfTriggers = Controller.getNumberOfTriggers(); + var numberOfSpatialControls = Controller.getNumberOfSpatialControls(); + var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers; + + // this is expected for hydras + if (!(numberOfButtons==12 && numberOfTriggers == 2 && controllersPerTrigger == 2)) { + debugPrint("no hydra connected?"); + return; // bail if no hydra + } + + checkControllerSide(LEFT_PALM); + checkControllerSide(RIGHT_PALM); +} + +function scriptEnding() { + Overlays.deleteOverlay(leftLaser); + Overlays.deleteOverlay(rightLaser); +} +Script.scriptEnding.connect(scriptEnding); + +// register the call back so it fires before each data send +Script.update.connect(checkController); + + + From 86f3cf2a75f2eea80df6e53788242bad83644eab Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 25 Apr 2014 16:39:45 -0700 Subject: [PATCH 07/33] 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 08/33] 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 0457f2035de829492f0d5bd1ec9af13e614b7848 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 28 Apr 2014 14:02:09 -0700 Subject: [PATCH 09/33] more editModels work --- examples/editModels.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 2724a17373..e0412122cc 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -42,7 +42,7 @@ var leftLaser = Overlays.addOverlay("line3d", { }); var rightLaser = Overlays.addOverlay("line3d", { position: { x: 0, y: 0, z: 0}, - end: { x: 0, y: 0, z: 0}, + end: { x: 0, y: 0, z: 0}, color: { red: 255, green: 0, blue: 0}, alpha: 1, visible: false, @@ -112,20 +112,24 @@ function checkControllerSide(whichSide) { var createButtonPressed = (Controller.isButtonPressed(BUTTON_3)); if (createButtonPressed) { + var position = MyAvatar.position; + var forwardVector = Quat.getFront(MyAvatar.orientation); + position = Vec3.sum(position, Vec3.multiply(forwardVector, 2)); Particles.editParticle(particle, { - position: { x: 0, y: 0, z:0 }, + position: position, velocity: { x: 0, y: 0, z: 0}, gravity: { x: 0, y: 0, z: 0}, radius: 0.05, damping: 0.999, color: { red: 255, green: 0, blue: 0 }, + lifetime: 1000, modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/models/heads/defaultAvatar_head.fst", }); } var vector = Vec3.subtract(tipPosition, palmPosition); - var endPosition = Vec3.sum(palmPosition, Vec3.multiply(vector, 100)); + var endPosition = Vec3.sum(palmPosition, Vec3.multiply(vector, 4)); Overlays.editOverlay(overlay, { position: palmPosition, @@ -136,6 +140,7 @@ function checkControllerSide(whichSide) { if (triggerValue > 0.9) { + } return; From 8068a915aac103640bdb82080efc3e2e757fc9b1 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 30 Apr 2014 16:50:40 -0700 Subject: [PATCH 10/33] More work on editModels --- examples/editModels.js | 427 +++++++++++---------------- libraries/script-engine/src/Vec3.cpp | 4 + libraries/script-engine/src/Vec3.h | 3 +- 3 files changed, 182 insertions(+), 252 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index e0412122cc..f9fc5fc31e 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -9,266 +9,175 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +var LASER_WIDTH = 4; +var LASER_COLOR = { red: 255, green: 0, blue: 0 }; +var LASER_LENGTH_FACTOR = 4; -var LEFT_BUTTON_FWD = 5; -var LEFT_BUTTON_3 = 3; -var RIGHT_BUTTON_FWD = 11; -var RIGHT_BUTTON_3 = 9; -var leftBallAlreadyInHand = false; -var rightBallAlreadyInHand = false; -var leftHandParticle; -var rightHandParticle; -var targetRadius = 0.25; +var LEFT = 0; +var RIGHT = 1; - - -var LEFT_PALM = 0; -var LEFT_TIP = 1; -var LEFT_TRIGGER = 0; - -var RIGHT_PALM = 2; -var RIGHT_TIP = 3; -var RIGHT_TRIGGER = 1; - -var previewLineWidth = 4; -var leftLaser = Overlays.addOverlay("line3d", { - position: { x: 0, y: 0, z: 0}, - end: { x: 0, y: 0, z: 0}, - color: { red: 255, green: 0, blue: 0}, - alpha: 1, - visible: false, - lineWidth: previewLineWidth - }); -var rightLaser = Overlays.addOverlay("line3d", { - position: { x: 0, y: 0, z: 0}, - end: { x: 0, y: 0, z: 0}, - color: { red: 255, green: 0, blue: 0}, +function controller(wichSide) { + this.side = wichSide; + this.palm = 2 * wichSide; + this.tip = 2 * wichSide + 1; + this.trigger = wichSide; + + this.oldPalmPosition = Controller.getSpatialControlPosition(this.palm); + this.palmPosition = Controller.getSpatialControlPosition(this.palm); + + this.oldTipPosition = Controller.getSpatialControlPosition(this.tip); + this.tipPosition = Controller.getSpatialControlPosition(this.tip); + + this.triggerValue = Controller.getTriggerValue(this.trigger); + + this.pressed = false; // is trigger pressed + this.pressing = false; // is trigger being pressed (is pressed now but wasn't previously) + + this.grabbing = false; + this.particleID = 0; + this.oldParticlePosition = { x: 0, y: 0, z: 0 }; + + this.laser = Overlays.addOverlay("line3d", { + position: this.palmPosition, + end: this.tipPosition, + color: LASER_COLOR, alpha: 1, visible: false, - lineWidth: previewLineWidth + lineWidth: LASER_WIDTH }); + + this.grab = function (particle) { + if (!particle.isKnownID) { + var identify = Particles.identifyParticle(particle); + if (!identify.isKnownID) { + return; + } + } + this.grabbing = true; + this.particle = identify; + + var properties = Particles.getParticleProperties(this.particle); + + this.oldParticlePosition = properties.position; + + print("Grabbed: " + this.oldParticlePosition); + } + + this.release = function () { + this.grabbing = false; + this.particleID = -1; + this.oldParticlePosition = { x: 0, y: 0, z: 0 }; + } + + this.checkTrigger = function () { + if (this.triggerValue > 0.9) { + if (this.pressed) { + this.pressing = false; + } else { + this.pressing = true; + } + this.pressed = true; + } else { + this.pressing = false; + this.pressed = false; + } + } + + this.moveLaser = function () { + var vector = Vec3.subtract(this.tipPosition, this.palmPosition); + var endPosition = Vec3.sum(this.palmPosition, Vec3.multiply(vector, LASER_LENGTH_FACTOR)); + + Overlays.editOverlay(this.laser, { + position: this.palmPosition, + end: endPosition, + visible: true + }); + } + + this.moveParticle = function () { + if (this.grabbing) { + + + + } + } + + this.update = function () { + this.oldPalmPosition = this.palmPosition; + this.oldTipPosition = this.tipPosition; + this.palmPosition = Controller.getSpatialControlPosition(this.palm); + this.tipPosition = Controller.getSpatialControlPosition(this.tip); + this.triggerValue = Controller.getTriggerValue(this.trigger); + + this.checkTrigger(); + + if (this.pressing) { + var particle = -1; + +// P P - Particle +// /| A - Palm +// / | d B - unit vector toward tip +// / | X - base of the perpendicular line +// A---X----->B d - distance fom axis +// x x - distance from A +// +// |X-A| = (P-A).B, in B unit +// X == A + ((P-A).B)B +// d = |P-X| in standard unit + + var A = this.palmPosition; + var B = Vec3.subtract(this.tipPosition, A); + var P = particlePosition; + + var x = Vec3.dot(Vec3.subtract(P, A), B); + var X = Vec3.sum(A, Vec3.multiply(B, x)); + var d = Vec3.length(Vec3.subtract(P, X)); + + + if (d < 0.5 && 0 < x && x < LASER_LENGTH_FACTOR) { + particle = particleTest; + } + Vec3.print("A = ", A); + Vec3.print("B = ", B); + Vec3.print("P = ", P); + Vec3.print("X = ", X); + print("d = " + d + ", x = " + x); + this.grab(particle); + } + + if (!this.pressed && this.grabbing) { + this.release(); + } + + + this.moveLaser(); + this.moveParticle(); + } + + this.cleanup = function () { + Overlays.deleteOverlay(this.laser); + } +} -var particle = Particles.addParticle({ position: { x: 0, y: 0, z:0 }, +var leftController = new controller(LEFT); +var rightController = new controller(RIGHT); + + + +var particlePosition = MyAvatar.position; +var particleRadius = 0.05; +var particleTest = Particles.addParticle({ position: particlePosition, velocity: { x: 0, y: 0, z: 0}, gravity: { x: 0, y: 0, z: 0}, - radius: 0.05, + radius: particleRadius, damping: 0.999, color: { red: 255, green: 0, blue: 0 }, - modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/models/heads/defaultAvatar_head.fst", + lifetime: 100, + modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/models/heads/defaultAvatar_head.fst" }) -var wantDebugging = false; -function debugPrint(message) { - if (wantDebugging) { - print(message); - } -} - -function getBallHoldPosition(whichSide) { - var normal; - var tipPosition; - if (whichSide == LEFT_PALM) { - normal = Controller.getSpatialControlNormal(LEFT_PALM); - tipPosition = Controller.getSpatialControlPosition(LEFT_TIP); - } else { - normal = Controller.getSpatialControlNormal(RIGHT_PALM); - tipPosition = Controller.getSpatialControlPosition(RIGHT_TIP); - } - - var BALL_FORWARD_OFFSET = 0.08; // put the ball a bit forward of fingers - position = { x: BALL_FORWARD_OFFSET * normal.x, - y: BALL_FORWARD_OFFSET * normal.y, - z: BALL_FORWARD_OFFSET * normal.z }; - - position.x += tipPosition.x; - position.y += tipPosition.y; - position.z += tipPosition.z; - - return position; -} - -function checkControllerSide(whichSide) { - var palmPosition; - var tipPosition; - var overlay; - var triggerValue; - var BUTTON_3; - - if (whichSide == LEFT_PALM) { - palmPosition = Controller.getSpatialControlPosition(LEFT_PALM); - tipPosition = Controller.getSpatialControlPosition(LEFT_TIP); - overlay = leftLaser; - triggerValue = Controller.getTriggerValue(LEFT_TRIGGER); - BUTTON_3 = LEFT_BUTTON_3; - } else { - palmPosition = Controller.getSpatialControlPosition(RIGHT_PALM); - tipPosition = Controller.getSpatialControlPosition(RIGHT_TIP); - overlay = rightLaser; - triggerValue = Controller.getTriggerValue(RIGHT_TRIGGER); - BUTTON_3 = RIGHT_BUTTON_3; - } - - var createButtonPressed = (Controller.isButtonPressed(BUTTON_3)); - if (createButtonPressed) { - var position = MyAvatar.position; - var forwardVector = Quat.getFront(MyAvatar.orientation); - position = Vec3.sum(position, Vec3.multiply(forwardVector, 2)); - Particles.editParticle(particle, { - position: position, - velocity: { x: 0, y: 0, z: 0}, - gravity: { x: 0, y: 0, z: 0}, - radius: 0.05, - damping: 0.999, - color: { red: 255, green: 0, blue: 0 }, - lifetime: 1000, - modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/models/heads/defaultAvatar_head.fst", - }); - } - - - var vector = Vec3.subtract(tipPosition, palmPosition); - var endPosition = Vec3.sum(palmPosition, Vec3.multiply(vector, 4)); - - Overlays.editOverlay(overlay, { - position: palmPosition, - end: endPosition, - visible: true - }); - - - if (triggerValue > 0.9) { - - - } - - return; - var BUTTON_FWD; - var BUTTON_3; - var palmPosition; - var ballAlreadyInHand; - var handMessage; - - if (whichSide == LEFT_PALM) { - BUTTON_FWD = LEFT_BUTTON_FWD; - BUTTON_3 = LEFT_BUTTON_3; - palmPosition = Controller.getSpatialControlPosition(LEFT_PALM); - ballAlreadyInHand = leftBallAlreadyInHand; - handMessage = "LEFT"; - } else { - BUTTON_FWD = RIGHT_BUTTON_FWD; - BUTTON_3 = RIGHT_BUTTON_3; - palmPosition = Controller.getSpatialControlPosition(RIGHT_PALM); - ballAlreadyInHand = rightBallAlreadyInHand; - handMessage = "RIGHT"; - } - - var grabButtonPressed = (Controller.isButtonPressed(BUTTON_FWD) || Controller.isButtonPressed(BUTTON_3)); - - // If I don't currently have a ball in my hand, then try to catch closest one - if (!ballAlreadyInHand && grabButtonPressed) { - var closestParticle = Particles.findClosestParticle(palmPosition, targetRadius); - - if (closestParticle.isKnownID) { - - debugPrint(handMessage + " HAND- CAUGHT SOMETHING!!"); - - if (whichSide == LEFT_PALM) { - leftBallAlreadyInHand = true; - leftHandParticle = closestParticle; - } else { - rightBallAlreadyInHand = true; - rightHandParticle = closestParticle; - } - var ballPosition = getBallHoldPosition(whichSide); - var properties = { position: { x: ballPosition.x, - y: ballPosition.y, - z: ballPosition.z }, - velocity : { x: 0, y: 0, z: 0}, inHand: true }; - Particles.editParticle(closestParticle, properties); - - - return; // exit early - } - } - - // change ball color logic... - // - //if (wasButtonJustPressed()) { - // rotateColor(); - //} - - // If '3' is pressed, and not holding a ball, make a new one - if (Controller.isButtonPressed(BUTTON_3) && !ballAlreadyInHand) { - var ballPosition = getBallHoldPosition(whichSide); - var properties = { position: ballPosition, - velocity: { x: 0, y: 0, z: 0}, - gravity: { x: 0, y: 0, z: 0}, - inHand: true, - radius: 0.05, - damping: 0.999, - color: { red: 255, green: 0, blue: 0 }, - modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/models/heads/defaultAvatar_head.fst", - }; - - newParticle = Particles.addParticle(properties); - if (whichSide == LEFT_PALM) { - leftBallAlreadyInHand = true; - leftHandParticle = newParticle; - } else { - rightBallAlreadyInHand = true; - rightHandParticle = newParticle; - } - - return; // exit early - } - - if (ballAlreadyInHand) { - if (whichSide == LEFT_PALM) { - handParticle = leftHandParticle; - whichTip = LEFT_TIP; - } else { - handParticle = rightHandParticle; - whichTip = RIGHT_TIP; - } - - // If holding the ball keep it in the palm - if (grabButtonPressed) { - debugPrint(">>>>> " + handMessage + "-BALL IN HAND, grabbing, hold and move"); - var ballPosition = getBallHoldPosition(whichSide); - var properties = { position: { x: ballPosition.x, - y: ballPosition.y, - z: ballPosition.z }, - }; - Particles.editParticle(handParticle, properties); - } else { - debugPrint(">>>>> " + handMessage + "-BALL IN HAND, not grabbing, THROW!!!"); - // If toy ball just released, add velocity to it! - var tipVelocity = Controller.getSpatialControlVelocity(whichTip); - var THROWN_VELOCITY_SCALING = 1.5; - var properties = { - velocity: { x: tipVelocity.x * THROWN_VELOCITY_SCALING, - y: tipVelocity.y * THROWN_VELOCITY_SCALING, - z: tipVelocity.z * THROWN_VELOCITY_SCALING } , - inHand: false, - gravity: { x: 0, y: -2, z: 0}, - }; - - Particles.editParticle(handParticle, properties); - - if (whichSide == LEFT_PALM) { - leftBallAlreadyInHand = false; - leftHandParticle = false; - } else { - rightBallAlreadyInHand = false; - rightHandParticle = false; - } - } - } -} - - function checkController(deltaTime) { var numberOfButtons = Controller.getNumberOfButtons(); var numberOfTriggers = Controller.getNumberOfTriggers(); @@ -277,17 +186,33 @@ function checkController(deltaTime) { // this is expected for hydras if (!(numberOfButtons==12 && numberOfTriggers == 2 && controllersPerTrigger == 2)) { - debugPrint("no hydra connected?"); + print("no hydra connected?"); return; // bail if no hydra } + + leftController.update(); + rightController.update(); - checkControllerSide(LEFT_PALM); - checkControllerSide(RIGHT_PALM); + + + ///// TEMP /////// + var createButtonPressed = Controller.isButtonPressed(3) || Controller.isButtonPressed(9); + if (createButtonPressed) { + particlePosition = MyAvatar.position; + var forwardVector = Quat.getFront(MyAvatar.orientation); + particlePosition = Vec3.sum(particlePosition, Vec3.multiply(forwardVector, 2)); + Particles.editParticle(particleTest, { + position: particlePosition, + lifetime: 100000 + }); + } + ////////////////////////////////// } function scriptEnding() { - Overlays.deleteOverlay(leftLaser); - Overlays.deleteOverlay(rightLaser); + leftController.cleanup(); + rightController.cleanup(); + Particles.deleteParticle(particleTest); } Script.scriptEnding.connect(scriptEnding); diff --git a/libraries/script-engine/src/Vec3.cpp b/libraries/script-engine/src/Vec3.cpp index badc980913..0cbb43f89a 100644 --- a/libraries/script-engine/src/Vec3.cpp +++ b/libraries/script-engine/src/Vec3.cpp @@ -17,6 +17,10 @@ glm::vec3 Vec3::cross(const glm::vec3& v1, const glm::vec3& v2) { return glm::cross(v1,v2); } +float Vec3::dot(const glm::vec3& v1, const glm::vec3& v2) { + return glm::dot(v1,v2); +} + glm::vec3 Vec3::multiply(const glm::vec3& v1, float f) { return v1 * f; } diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index e401cd71bd..3dce565fa1 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -24,8 +24,9 @@ class Vec3 : public QObject { Q_OBJECT -public slots: + public slots: glm::vec3 cross(const glm::vec3& v1, const glm::vec3& v2); + float dot(const glm::vec3& v1, const glm::vec3& v2); glm::vec3 multiply(const glm::vec3& v1, float f); glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v); glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2); From 2f0e311a9923cc411201c1742626249a4f355b70 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 1 May 2014 10:19:05 -0700 Subject: [PATCH 11/33] 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 12/33] 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 13/33] 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 14/33] 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 15/33] 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 16/33] 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 17/33] 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 18/33] 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 19/33] 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 20/33] 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 21/33] 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 22/33] 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 23/33] 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 24/33] 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 25/33] 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 11:19:12 -0700 Subject: [PATCH 26/33] Working version of editModels.js --- examples/editModels.js | 348 ++++++++++++++---- interface/src/ui/overlays/Sphere3DOverlay.cpp | 6 +- 2 files changed, 284 insertions(+), 70 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index f9fc5fc31e..5918818800 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -11,7 +11,7 @@ var LASER_WIDTH = 4; var LASER_COLOR = { red: 255, green: 0, blue: 0 }; -var LASER_LENGTH_FACTOR = 4; +var LASER_LENGTH_FACTOR = 1; var LEFT = 0; var RIGHT = 1; @@ -29,6 +29,18 @@ function controller(wichSide) { this.oldTipPosition = Controller.getSpatialControlPosition(this.tip); this.tipPosition = Controller.getSpatialControlPosition(this.tip); + this.oldUp = Controller.getSpatialControlNormal(this.palm); + this.up = this.oldUp; + + this.oldFront = Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)); + this.front = this.oldFront; + + this.oldRight = Vec3.cross(this.front, this.up); + this.right = this.oldRight; + + this.oldRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm)); + this.rotation = this.oldRotation; + this.triggerValue = Controller.getTriggerValue(this.trigger); this.pressed = false; // is trigger pressed @@ -36,7 +48,7 @@ function controller(wichSide) { this.grabbing = false; this.particleID = 0; - this.oldParticlePosition = { x: 0, y: 0, z: 0 }; + this.oldParticleProperties; this.laser = Overlays.addOverlay("line3d", { position: this.palmPosition, @@ -47,6 +59,34 @@ function controller(wichSide) { lineWidth: LASER_WIDTH }); + this.guideScale = 0.02; + this.ball = Overlays.addOverlay("sphere", { + position: this.palmPosition, + size: this.guideScale, + solid: true, + color: { red: 0, green: 255, blue: 0 }, + alpha: 1, + visible: false, + }); + this.leftRight = Overlays.addOverlay("line3d", { + position: this.palmPosition, + end: this.tipPosition, + color: { red: 0, green: 0, blue: 255 }, + alpha: 1, + visible: false, + lineWidth: LASER_WIDTH + }); + this.topDown = Overlays.addOverlay("line3d", { + position: this.palmPosition, + end: this.tipPosition, + color: { red: 0, green: 0, blue: 255 }, + alpha: 1, + visible: false, + lineWidth: LASER_WIDTH + }); + + + this.grab = function (particle) { if (!particle.isKnownID) { var identify = Particles.identifyParticle(particle); @@ -54,20 +94,17 @@ function controller(wichSide) { return; } } + print("Grabbing"); this.grabbing = true; - this.particle = identify; + this.particleID = identify; - var properties = Particles.getParticleProperties(this.particle); - - this.oldParticlePosition = properties.position; - - print("Grabbed: " + this.oldParticlePosition); + this.oldParticleProperties = Particles.getParticleProperties(this.particleID); } this.release = function () { this.grabbing = false; this.particleID = -1; - this.oldParticlePosition = { x: 0, y: 0, z: 0 }; + this.oldParticleProperties = { }; } this.checkTrigger = function () { @@ -85,22 +122,68 @@ function controller(wichSide) { } this.moveLaser = function () { - var vector = Vec3.subtract(this.tipPosition, this.palmPosition); - var endPosition = Vec3.sum(this.palmPosition, Vec3.multiply(vector, LASER_LENGTH_FACTOR)); + var endPosition = Vec3.sum(this.palmPosition, Vec3.multiply(this.front, LASER_LENGTH_FACTOR)); Overlays.editOverlay(this.laser, { position: this.palmPosition, end: endPosition, visible: true }); + + + Overlays.editOverlay(this.ball, { + position: endPosition, + visible: true + }); + Overlays.editOverlay(this.leftRight, { + position: Vec3.sum(endPosition, Vec3.multiply(this.right, 2 * this.guideScale)), + end: Vec3.sum(endPosition, Vec3.multiply(this.right, -2 * this.guideScale)), + visible: true + }); + Overlays.editOverlay(this.topDown, {position: Vec3.sum(endPosition, Vec3.multiply(this.up, 2 * this.guideScale)), + end: Vec3.sum(endPosition, Vec3.multiply(this.up, -2 * this.guideScale)), + visible: true + }); } - this.moveParticle = function () { - if (this.grabbing) { - - - + this.checkParticle = function (particle) { + if (!particle.isKnownID) { + var identify = Particles.identifyParticle(particle); + if (!identify.isKnownID) { + print("Unknown ID (checkParticle)"); + return; + } } + // P P - Particle + // /| A - Palm + // / | d B - unit vector toward tip + // / | X - base of the perpendicular line + // A---X----->B d - distance fom axis + // x x - distance from A + // + // |X-A| = (P-A).B + // X == A + ((P-A).B)B + // d = |P-X| + + var A = this.palmPosition; + var B = this.front; + var P = Particles.getParticleProperties(particle).position; + + this.x = Vec3.dot(Vec3.subtract(P, A), B); + this.y = Vec3.dot(Vec3.subtract(P, A), this.up); + this.z = Vec3.dot(Vec3.subtract(P, A), this.right); + var X = Vec3.sum(A, Vec3.multiply(B, this.x)); + var d = Vec3.length(Vec3.subtract(P, X)); + +// Vec3.print("A: ", A); +// Vec3.print("B: ", B); +// Vec3.print("Particle pos: ", P); +// print("d: " + d + ", x: " + this.x); + if (d < 0.5 && 0 < this.x && this.x < LASER_LENGTH_FACTOR) { + return true; + } + + return false; } this.update = function () { @@ -108,55 +191,45 @@ function controller(wichSide) { this.oldTipPosition = this.tipPosition; this.palmPosition = Controller.getSpatialControlPosition(this.palm); this.tipPosition = Controller.getSpatialControlPosition(this.tip); + + this.oldUp = this.up; + this.up = Vec3.normalize(Controller.getSpatialControlNormal(this.palm)); + + this.oldFront = this.front; + this.front = Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)); + + this.oldRight = this.right; + this.right = Vec3.normalize(Vec3.cross(this.front, this.up)); + + this.oldRotation = this.rotation; + this.rotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm)); + this.triggerValue = Controller.getTriggerValue(this.trigger); this.checkTrigger(); if (this.pressing) { - var particle = -1; - -// P P - Particle -// /| A - Palm -// / | d B - unit vector toward tip -// / | X - base of the perpendicular line -// A---X----->B d - distance fom axis -// x x - distance from A -// -// |X-A| = (P-A).B, in B unit -// X == A + ((P-A).B)B -// d = |P-X| in standard unit - - var A = this.palmPosition; - var B = Vec3.subtract(this.tipPosition, A); - var P = particlePosition; - - var x = Vec3.dot(Vec3.subtract(P, A), B); - var X = Vec3.sum(A, Vec3.multiply(B, x)); - var d = Vec3.length(Vec3.subtract(P, X)); - - - if (d < 0.5 && 0 < x && x < LASER_LENGTH_FACTOR) { - particle = particleTest; + if (this.checkParticle(particleTest1)) { + this.grab(particleTest1); } - Vec3.print("A = ", A); - Vec3.print("B = ", B); - Vec3.print("P = ", P); - Vec3.print("X = ", X); - print("d = " + d + ", x = " + x); - this.grab(particle); } if (!this.pressed && this.grabbing) { this.release(); } - + if(this.grabbing) { + this.oldParticleProperties = Particles.getParticleProperties(this.particleID); + } + this.moveLaser(); - this.moveParticle(); } this.cleanup = function () { Overlays.deleteOverlay(this.laser); + Overlays.deleteOverlay(this.ball); + Overlays.deleteOverlay(this.leftRight); + Overlays.deleteOverlay(this.topDown); } } @@ -164,20 +237,132 @@ var leftController = new controller(LEFT); var rightController = new controller(RIGHT); - -var particlePosition = MyAvatar.position; var particleRadius = 0.05; -var particleTest = Particles.addParticle({ position: particlePosition, - velocity: { x: 0, y: 0, z: 0}, - gravity: { x: 0, y: 0, z: 0}, - radius: particleRadius, - damping: 0.999, - color: { red: 255, green: 0, blue: 0 }, - lifetime: 100, - modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/models/heads/defaultAvatar_head.fst" - }) +var particleTest1 = Particles.addParticle({ position: { x: 0, y: 0, z:0 }, + velocity: { x: 0, y: 0, z: 0}, + gravity: { x: 0, y: 0, z: 0}, + radius: particleRadius, + color: { red: 0, green: 0, blue: 255 }, + modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/models/heads/defaultAvatar_head.fst", + lifetime: 600, + }) +var particleTest2 = Particles.addParticle({ position: { x: 0, y: 0, z:0 }, + velocity: { x: 0, y: 0, z: 0}, + gravity: { x: 0, y: 0, z: 0}, + radius: particleRadius, + color: { red: 0, green: 0, blue: 255 }, + alpha: 0.2, + lifetime: 600, + }) +var diff = { x: 0, y: 0, z: 0 }; +function moveParticles() { + if (leftController.grabbing) { + if (rightController.grabbing) { + + var newPosition = Vec3.sum(leftController.palmPosition, + Vec3.multiply(leftController.front, leftController.x)); + newPosition = Vec3.sum(newPosition, + Vec3.multiply(leftController.up, leftController.y)); + newPosition = Vec3.sum(newPosition, + Vec3.multiply(leftController.right, leftController.z)); + + var rotation = Quat.multiply(leftController.rotation, + Quat.inverse(leftController.oldRotation)); + rotation = Quat.multiply(rotation, Particles.getParticleProperties(leftController.particleID).modelRotation); + + Particles.editParticle(leftController.particleID, { + position: newPosition, + modelRotation: rotation, + lifetime: 600 + }); + Particles.editParticle(particleTest2, { + position: newPosition, + lifetime: 600 + }); + + return; + var leftVector = Vec3.sum(Vec3.multiply(leftController.front, leftController.x), + Vec3.multiply(leftController.up, leftController.y)); + leftVector = Vec3.sum(leftVector, + Vec3.multiply(leftController.right, leftController.z)); + + var rightVector = Vec3.sum(Vec3.multiply(rightController.front, rightController.x), + Vec3.multiply(rightController.up, rightController.y)); + rightVector = Vec3.sum(rightVector, + Vec3.multiply(rightController.right, rightController.z)); + + var newPosition = Vec3.sum(Vec3.sum(leftController.palmPosition, leftVector), + Vec3.sum(rightController.palmPosition, rightVector)); + newPosition = Vec3.multiply(newPosition, 0.5); + + + var rotantion = Quat.multiply(MyAvatar.orientation, + Quat.inverse(leftController.oldRotation)); + rotation = Quat.multiply(rotation, Particles.getParticleProperties(leftController.particleID).modelRotation); + + Particles.editParticle(leftController.particleID, { + position: newPosition, + //modelRotation: rotation, + lifetime: 600 + }); + Particles.editParticle(particleTest2, { + position: newPosition, + lifetime: 600 + }); + leftController.checkParticle(leftController.particleID); + rightController.checkParticle(rightController.particleID); + return; + } else { + var newPosition = Vec3.sum(leftController.palmPosition, + Vec3.multiply(leftController.front, leftController.x)); + newPosition = Vec3.sum(newPosition, + Vec3.multiply(leftController.up, leftController.y)); + newPosition = Vec3.sum(newPosition, + Vec3.multiply(leftController.right, leftController.z)); + + var rotation = Quat.multiply(leftController.rotation, + Quat.inverse(leftController.oldRotation)); + rotation = Quat.multiply(rotation, Particles.getParticleProperties(leftController.particleID).modelRotation); + + Particles.editParticle(leftController.particleID, { + position: newPosition, + modelRotation: rotation, + lifetime: 600 + }); + Particles.editParticle(particleTest2, { + position: newPosition, + lifetime: 600 + }); + } + } + + + if (rightController.grabbing) { + var newPosition = Vec3.sum(rightController.palmPosition, + Vec3.multiply(rightController.front, rightController.x)); + newPosition = Vec3.sum(newPosition, + Vec3.multiply(rightController.up, rightController.y)); + newPosition = Vec3.sum(newPosition, + Vec3.multiply(rightController.right, rightController.z)); + + var rotation = Quat.multiply(rightController.rotation, + Quat.inverse(rightController.oldRotation)); + rotation = Quat.multiply(rotation, Particles.getParticleProperties(rightController.particleID).modelRotation); + + Particles.editParticle(rightController.particleID, { + position: newPosition, + modelRotation: rotation, + lifetime: 600 + }); + Particles.editParticle(particleTest2, { + position: newPosition, + lifetime: 600 + }); + } +} + function checkController(deltaTime) { var numberOfButtons = Controller.getNumberOfButtons(); var numberOfTriggers = Controller.getNumberOfTriggers(); @@ -186,24 +371,52 @@ function checkController(deltaTime) { // this is expected for hydras if (!(numberOfButtons==12 && numberOfTriggers == 2 && controllersPerTrigger == 2)) { - print("no hydra connected?"); + //print("no hydra connected?"); return; // bail if no hydra } leftController.update(); rightController.update(); + moveParticles(); ///// TEMP /////// - var createButtonPressed = Controller.isButtonPressed(3) || Controller.isButtonPressed(9); + if (!particleTest1.isKnownID) { + var identify = Particles.identifyParticle(particleTest1); + if (!identify.isKnownID) { + print("Unknown ID (temp)"); + return; + } + } + var createButtonPressed = Controller.isButtonPressed(3); if (createButtonPressed) { - particlePosition = MyAvatar.position; + var position = MyAvatar.position; var forwardVector = Quat.getFront(MyAvatar.orientation); - particlePosition = Vec3.sum(particlePosition, Vec3.multiply(forwardVector, 2)); - Particles.editParticle(particleTest, { - position: particlePosition, - lifetime: 100000 + position = Vec3.sum(position, Vec3.multiply(forwardVector, 2)); + Particles.editParticle(particleTest1, { + position: position, + modelRotation: Quat.fromVec3Radians({ x: 0, y: 0 , z: 0 }), + lifetime: 600 + }); + Particles.editParticle(particleTest2, { + position: position, + lifetime: 600 + }); + } + createButtonPressed = Controller.isButtonPressed(9); + if (createButtonPressed) { + var position = MyAvatar.position; + var forwardVector = Quat.getFront(MyAvatar.orientation); + position = Vec3.sum(position, Vec3.multiply(forwardVector, 2)); + Particles.editParticle(particleTest1, { + position: position, + modelRotation: Quat.fromVec3Radians({ x: 0, y: 0 , z: 0 }), + lifetime: 600 + }); + Particles.editParticle(particleTest2, { + position: position, + lifetime: 600 }); } ////////////////////////////////// @@ -212,7 +425,8 @@ function checkController(deltaTime) { function scriptEnding() { leftController.cleanup(); rightController.cleanup(); - Particles.deleteParticle(particleTest); + Particles.deleteParticle(particleTest1); + Particles.deleteParticle(particleTest2); } Script.scriptEnding.connect(scriptEnding); diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index e5f31fa1be..c2ac92f45c 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -33,9 +33,9 @@ void Sphere3DOverlay::render() { glDisable(GL_LIGHTING); glPushMatrix(); - glTranslatef(_position.x + _size * 0.5f, - _position.y + _size * 0.5f, - _position.z + _size * 0.5f); + glTranslatef(_position.x, + _position.y, + _position.z); glLineWidth(_lineWidth); const int slices = 15; if (_isSolid) { From 0b4516e09b6efcfe92b584703f317fd707d75514 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 2 May 2014 15:32:29 -0700 Subject: [PATCH 27/33] editModels,js switched to use actual models instead of particles. --- examples/editModels.js | 223 ++++++++++++----------------------------- 1 file changed, 64 insertions(+), 159 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 5918818800..50b0137c4f 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -11,12 +11,11 @@ var LASER_WIDTH = 4; var LASER_COLOR = { red: 255, green: 0, blue: 0 }; -var LASER_LENGTH_FACTOR = 1; +var LASER_LENGTH_FACTOR = 1.5; var LEFT = 0; var RIGHT = 1; - function controller(wichSide) { this.side = wichSide; this.palm = 2 * wichSide; @@ -47,8 +46,7 @@ function controller(wichSide) { this.pressing = false; // is trigger being pressed (is pressed now but wasn't previously) this.grabbing = false; - this.particleID = 0; - this.oldParticleProperties; + this.modelID; this.laser = Overlays.addOverlay("line3d", { position: this.palmPosition, @@ -87,24 +85,23 @@ function controller(wichSide) { - this.grab = function (particle) { - if (!particle.isKnownID) { - var identify = Particles.identifyParticle(particle); + this.grab = function (modelID) { + if (!modelID.isKnownID) { + var identify = Models.identifyModel(modelID); if (!identify.isKnownID) { + print("Unknown ID " + identify.id + "(grab)"); return; } + modelID = identify; } - print("Grabbing"); + print("Grabbing " + modelID.id); this.grabbing = true; - this.particleID = identify; - - this.oldParticleProperties = Particles.getParticleProperties(this.particleID); + this.modelID = modelID; } this.release = function () { this.grabbing = false; - this.particleID = -1; - this.oldParticleProperties = { }; + this.modelID = 0; } this.checkTrigger = function () { @@ -146,15 +143,16 @@ function controller(wichSide) { }); } - this.checkParticle = function (particle) { - if (!particle.isKnownID) { - var identify = Particles.identifyParticle(particle); + this.checkModel = function (modelID) { + if (!modelID.isKnownID) { + var identify = Models.identifyModel(modelID); if (!identify.isKnownID) { - print("Unknown ID (checkParticle)"); + print("Unknown ID " + identify.id + "(checkModel)"); return; } + modelID = identify; } - // P P - Particle + // P P - Model // /| A - Palm // / | d B - unit vector toward tip // / | X - base of the perpendicular line @@ -167,7 +165,7 @@ function controller(wichSide) { var A = this.palmPosition; var B = this.front; - var P = Particles.getParticleProperties(particle).position; + var P = Models.getModelProperties(modelID).position; this.x = Vec3.dot(Vec3.subtract(P, A), B); this.y = Vec3.dot(Vec3.subtract(P, A), this.up); @@ -179,10 +177,9 @@ function controller(wichSide) { // Vec3.print("B: ", B); // Vec3.print("Particle pos: ", P); // print("d: " + d + ", x: " + this.x); - if (d < 0.5 && 0 < this.x && this.x < LASER_LENGTH_FACTOR) { + if (d < Models.getModelProperties(modelID).radius && 0 < this.x && this.x < LASER_LENGTH_FACTOR) { return true; } - return false; } @@ -209,18 +206,22 @@ function controller(wichSide) { this.checkTrigger(); if (this.pressing) { - if (this.checkParticle(particleTest1)) { - this.grab(particleTest1); + Vec3.print("Looking at: ", this.palmPosition); + var foundModels = Models.findModels(this.palmPosition, LASER_LENGTH_FACTOR); + for (var i = 0; i < foundModels.length; i++) { + print("Model found ID (" + foundModels[i].id + ")"); + if (this.checkModel(foundModels[i])) { + if (this.grab(foundModels[i])) { + return; + } + } } } if (!this.pressed && this.grabbing) { + // release if trigger not pressed anymore. this.release(); } - - if(this.grabbing) { - this.oldParticleProperties = Particles.getParticleProperties(this.particleID); - } this.moveLaser(); } @@ -236,83 +237,39 @@ function controller(wichSide) { var leftController = new controller(LEFT); var rightController = new controller(RIGHT); - -var particleRadius = 0.05; -var particleTest1 = Particles.addParticle({ position: { x: 0, y: 0, z:0 }, - velocity: { x: 0, y: 0, z: 0}, - gravity: { x: 0, y: 0, z: 0}, - radius: particleRadius, - color: { red: 0, green: 0, blue: 255 }, - modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/models/heads/defaultAvatar_head.fst", - lifetime: 600, - }) -var particleTest2 = Particles.addParticle({ position: { x: 0, y: 0, z:0 }, - velocity: { x: 0, y: 0, z: 0}, - gravity: { x: 0, y: 0, z: 0}, - radius: particleRadius, - color: { red: 0, green: 0, blue: 255 }, - alpha: 0.2, - lifetime: 600, - }) - - -var diff = { x: 0, y: 0, z: 0 }; -function moveParticles() { +function moveModels() { if (leftController.grabbing) { if (rightController.grabbing) { + var properties = Models.getModelProperties(leftController.modelID); - var newPosition = Vec3.sum(leftController.palmPosition, - Vec3.multiply(leftController.front, leftController.x)); - newPosition = Vec3.sum(newPosition, - Vec3.multiply(leftController.up, leftController.y)); - newPosition = Vec3.sum(newPosition, - Vec3.multiply(leftController.right, leftController.z)); + var oldLeftPoint = Vec3.sum(leftController.oldPalmPosition, Vec3.multiply(leftController.oldFront, leftController.x)); + var oldRightPoint = Vec3.sum(rightController.oldPalmPosition, Vec3.multiply(rightController.oldFront, rightController.x)); + var oldMiddle = Vec3.multiply(Vec3.sum(oldLeftPoint, oldRightPoint), 0.5); + var oldLength = Vec3.length(Vec3.subtract(oldLeftPoint, oldRightPoint)); + + + var leftPoint = Vec3.sum(leftController.palmPosition, Vec3.multiply(leftController.front, leftController.x)); + var rightPoint = Vec3.sum(rightController.palmPosition, Vec3.multiply(rightController.front, rightController.x)); + + var middle = Vec3.multiply(Vec3.sum(leftPoint, rightPoint), 0.5); + var length = Vec3.length(Vec3.subtract(leftPoint, rightPoint)); + + var ratio = length / oldLength; + + var newPosition = Vec3.sum(middle, + Vec3.multiply(Vec3.subtract(properties.position, oldMiddle), ratio)); + Vec3.print("Ratio : " + ratio + " New position: ", newPosition); var rotation = Quat.multiply(leftController.rotation, Quat.inverse(leftController.oldRotation)); - rotation = Quat.multiply(rotation, Particles.getParticleProperties(leftController.particleID).modelRotation); + rotation = Quat.multiply(rotation, properties.modelRotation); - Particles.editParticle(leftController.particleID, { - position: newPosition, - modelRotation: rotation, - lifetime: 600 - }); - Particles.editParticle(particleTest2, { - position: newPosition, - lifetime: 600 - }); - - return; - var leftVector = Vec3.sum(Vec3.multiply(leftController.front, leftController.x), - Vec3.multiply(leftController.up, leftController.y)); - leftVector = Vec3.sum(leftVector, - Vec3.multiply(leftController.right, leftController.z)); - - var rightVector = Vec3.sum(Vec3.multiply(rightController.front, rightController.x), - Vec3.multiply(rightController.up, rightController.y)); - rightVector = Vec3.sum(rightVector, - Vec3.multiply(rightController.right, rightController.z)); - - var newPosition = Vec3.sum(Vec3.sum(leftController.palmPosition, leftVector), - Vec3.sum(rightController.palmPosition, rightVector)); - newPosition = Vec3.multiply(newPosition, 0.5); - - - var rotantion = Quat.multiply(MyAvatar.orientation, - Quat.inverse(leftController.oldRotation)); - rotation = Quat.multiply(rotation, Particles.getParticleProperties(leftController.particleID).modelRotation); - - Particles.editParticle(leftController.particleID, { + Models.editModel(leftController.modelID, { position: newPosition, //modelRotation: rotation, - lifetime: 600 + radius: properties.radius * ratio }); - Particles.editParticle(particleTest2, { - position: newPosition, - lifetime: 600 - }); - leftController.checkParticle(leftController.particleID); - rightController.checkParticle(rightController.particleID); + return; } else { var newPosition = Vec3.sum(leftController.palmPosition, @@ -324,17 +281,13 @@ function moveParticles() { var rotation = Quat.multiply(leftController.rotation, Quat.inverse(leftController.oldRotation)); - rotation = Quat.multiply(rotation, Particles.getParticleProperties(leftController.particleID).modelRotation); + rotation = Quat.multiply(rotation, + Models.getModelProperties(leftController.modelID).modelRotation); - Particles.editParticle(leftController.particleID, { - position: newPosition, - modelRotation: rotation, - lifetime: 600 - }); - Particles.editParticle(particleTest2, { - position: newPosition, - lifetime: 600 - }); + Models.editModel(leftController.modelID, { + position: newPosition, + modelRotation: rotation + }); } } @@ -349,17 +302,13 @@ function moveParticles() { var rotation = Quat.multiply(rightController.rotation, Quat.inverse(rightController.oldRotation)); - rotation = Quat.multiply(rotation, Particles.getParticleProperties(rightController.particleID).modelRotation); + rotation = Quat.multiply(rotation, + Models.getModelProperties(rightController.modelID).modelRotation); - Particles.editParticle(rightController.particleID, { - position: newPosition, - modelRotation: rotation, - lifetime: 600 - }); - Particles.editParticle(particleTest2, { - position: newPosition, - lifetime: 600 - }); + Models.editModel(rightController.modelID, { + position: newPosition, + modelRotation: rotation + }); } } @@ -377,56 +326,12 @@ function checkController(deltaTime) { leftController.update(); rightController.update(); - moveParticles(); - - - - ///// TEMP /////// - if (!particleTest1.isKnownID) { - var identify = Particles.identifyParticle(particleTest1); - if (!identify.isKnownID) { - print("Unknown ID (temp)"); - return; - } - } - var createButtonPressed = Controller.isButtonPressed(3); - if (createButtonPressed) { - var position = MyAvatar.position; - var forwardVector = Quat.getFront(MyAvatar.orientation); - position = Vec3.sum(position, Vec3.multiply(forwardVector, 2)); - Particles.editParticle(particleTest1, { - position: position, - modelRotation: Quat.fromVec3Radians({ x: 0, y: 0 , z: 0 }), - lifetime: 600 - }); - Particles.editParticle(particleTest2, { - position: position, - lifetime: 600 - }); - } - createButtonPressed = Controller.isButtonPressed(9); - if (createButtonPressed) { - var position = MyAvatar.position; - var forwardVector = Quat.getFront(MyAvatar.orientation); - position = Vec3.sum(position, Vec3.multiply(forwardVector, 2)); - Particles.editParticle(particleTest1, { - position: position, - modelRotation: Quat.fromVec3Radians({ x: 0, y: 0 , z: 0 }), - lifetime: 600 - }); - Particles.editParticle(particleTest2, { - position: position, - lifetime: 600 - }); - } - ////////////////////////////////// + moveModels(); } function scriptEnding() { leftController.cleanup(); rightController.cleanup(); - Particles.deleteParticle(particleTest1); - Particles.deleteParticle(particleTest2); } Script.scriptEnding.connect(scriptEnding); From 48facce85ff0b94c6c669f2a137814b64d8d465c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 2 May 2014 15:34:07 -0700 Subject: [PATCH 28/33] coding standard --- libraries/script-engine/src/Vec3.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index 3dce565fa1..5a3eeca7be 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -24,7 +24,7 @@ class Vec3 : public QObject { Q_OBJECT - public slots: +public slots: glm::vec3 cross(const glm::vec3& v1, const glm::vec3& v2); float dot(const glm::vec3& v1, const glm::vec3& v2); glm::vec3 multiply(const glm::vec3& v1, float f); From 53eddab43ce53c51c56b6d4f491362b5e120cb7f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 2 May 2014 15:52:50 -0700 Subject: [PATCH 29/33] 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 30/33] 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 31/33] 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 32/33] 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 33/33] 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); } }