From 8a0136efe1b6fcdcd47d03363ab534a89159169f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 18 Feb 2014 13:21:32 -0800 Subject: [PATCH] more work on authentication, show login window when domain asks --- domain-server/src/DomainServer.cpp | 10 +++--- domain-server/src/DomainServer.h | 2 +- interface/src/Application.cpp | 8 +++-- interface/src/Menu.cpp | 8 +++-- interface/src/Menu.h | 4 +-- libraries/shared/src/AccountManager.cpp | 31 +++++++++++++----- libraries/shared/src/AccountManager.h | 28 +++++++++++++--- libraries/shared/src/DomainInfo.cpp | 4 +-- libraries/shared/src/DomainInfo.h | 10 +++--- libraries/shared/src/NodeList.cpp | 21 ++++++++++-- libraries/shared/src/NodeList.h | 2 ++ libraries/shared/src/OAuthAccessToken.cpp | 40 +++++++++++++++++++++++ libraries/shared/src/OAuthAccessToken.h | 32 ++++++++++++++++++ 13 files changed, 165 insertions(+), 35 deletions(-) create mode 100644 libraries/shared/src/OAuthAccessToken.cpp create mode 100644 libraries/shared/src/OAuthAccessToken.h diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index bccb5d4ac0..9e73bd6260 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -41,7 +41,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : _staticAssignmentHash(), _assignmentQueue(), _hasCompletedRestartHold(false), - _nodeAuthenticationHostname(DEFAULT_NODE_AUTH_URL) + _nodeAuthenticationURL(DEFAULT_NODE_AUTH_URL) { const char CUSTOM_PORT_OPTION[] = "-p"; const char* customPortString = getCmdOption(argc, (const char**) argv, CUSTOM_PORT_OPTION); @@ -65,9 +65,9 @@ DomainServer::DomainServer(int argc, char* argv[]) : const QString NO_AUTH_OPTION = "--noAuth"; const QString CUSTOM_AUTH_OPTION = "--customAuth"; if ((argumentIndex = argumentList.indexOf(NO_AUTH_OPTION) != -1)) { - _nodeAuthenticationHostname = QUrl(); + _nodeAuthenticationURL = QUrl(); } else if ((argumentIndex = argumentList.indexOf(CUSTOM_AUTH_OPTION)) != -1) { - _nodeAuthenticationHostname = QUrl(argumentList.value(argumentIndex + 1)); + _nodeAuthenticationURL = QUrl(argumentList.value(argumentIndex + 1)); } NodeList* nodeList = NodeList::createInstance(NodeType::DomainServer, domainServerPort); @@ -254,14 +254,14 @@ void DomainServer::readAvailableDatagrams() { QUuid nodeUUID = uuidFromPacketHeader(receivedPacket); - if (!_nodeAuthenticationHostname.isEmpty() && + if (!_nodeAuthenticationURL.isEmpty() && (nodeUUID.isNull() || !nodeList->nodeWithUUID(nodeUUID))) { // this is a node we do not recognize and we need authentication - ask them to do so // by providing them the hostname they should authenticate with QByteArray authenticationRequestPacket = byteArrayWithPopluatedHeader(PacketTypeDomainServerAuthRequest); QDataStream authPacketStream(&authenticationRequestPacket, QIODevice::Append); - authPacketStream << _nodeAuthenticationHostname; + authPacketStream << _nodeAuthenticationURL; qDebug() << "Asking node at" << senderSockAddr << "to authenticate."; diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 90dd41fa56..e6cf4ba6e6 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -59,7 +59,7 @@ private: bool _hasCompletedRestartHold; - QUrl _nodeAuthenticationHostname; + QUrl _nodeAuthenticationURL; private slots: void readAvailableDatagrams(); void addStaticAssignmentsBackToQueueAfterRestart(); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8327bae069..8bbbcc8f4b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -207,6 +207,9 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_voxels, SLOT(nodeKilled(SharedNodePointer))); connect(nodeList, &NodeList::uuidChanged, this, &Application::updateWindowTitle); + connect(&AccountManager::getInstance(), SIGNAL(authenticationRequiredForRootURL(const QUrl&)), + Menu::getInstance(), SLOT(showLoginForRootURL(const QUrl&))); + // read the ApplicationInfo.ini file for Name/Version/Domain information QSettings applicationInfo("resources/info/ApplicationInfo.ini", QSettings::IniFormat); @@ -4216,6 +4219,5 @@ void Application::takeSnapshot() { player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath())); player->play(); - Snapshot::saveSnapshot(_glWidget, AccountManager::getUsername(), _myAvatar->getPosition()); -} - + Snapshot::saveSnapshot(_glWidget, AccountManager::getInstance().getUsername(), _myAvatar->getPosition()); +} \ No newline at end of file diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index ee6be00db1..709ae5d4e8 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -89,7 +89,7 @@ Menu::Menu() : MenuOption::Login, 0, this, - SLOT(login()))); + SLOT(loginForCurrentDomain()))); addDisabledActionAndSeparator(fileMenu, "Scripts"); addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O, appInstance, SLOT(loadDialog())); @@ -737,7 +737,11 @@ void sendFakeEnterEvent() { const int QLINE_MINIMUM_WIDTH = 400; const float DIALOG_RATIO_OF_WINDOW = 0.30f; -void Menu::login() { +void Menu::loginForCurrentDomain() { + showLoginForRootURL(NodeList::getInstance()->getDomainInfo().getRootAuthenticationURL()); +} + +void Menu::showLoginForRootURL(const QUrl& rootURL) { QInputDialog loginDialog(Application::getInstance()->getWindow()); loginDialog.setWindowTitle("Login"); loginDialog.setLabelText("Username:"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 986034002d..4383b0885a 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -76,7 +76,6 @@ public: int getMaxVoxels() const { return _maxVoxels; } QAction* getUseVoxelShader() const { return _useVoxelShader; } - void handleViewFrustumOffsetKeyModifier(int key); // User Tweakable LOD Items @@ -102,6 +101,8 @@ public: void goToDomain(const QString newDomain); public slots: + void loginForCurrentDomain(); + void showLoginForRootURL(const QUrl& rootURL); void bandwidthDetails(); void voxelStatsDetails(); void lodTools(); @@ -114,7 +115,6 @@ public slots: private slots: void aboutApp(); - void login(); void editPreferences(); void goToDomain(); void goToLocation(); diff --git a/libraries/shared/src/AccountManager.cpp b/libraries/shared/src/AccountManager.cpp index 39105c3f72..861592df27 100644 --- a/libraries/shared/src/AccountManager.cpp +++ b/libraries/shared/src/AccountManager.cpp @@ -7,21 +7,34 @@ // #include +#include #include "PacketHeaders.h" #include "AccountManager.h" -QString AccountManager::_username = ""; +AccountManager& AccountManager::getInstance() { + static AccountManager sharedInstance; + return sharedInstance; +} -void AccountManager::processDomainServerAuthRequest(const QByteArray& packet) { - QDataStream authPacketStream(packet); - authPacketStream.skipRawData(numBytesForPacketHeader(packet)); +AccountManager::AccountManager() : + _username(), + _accessTokens(), + _networkAccessManager(NULL) +{ - // grab the hostname this domain-server wants us to authenticate with - QString authenticationHostname; - authPacketStream >> authenticationHostname; - - // check if we already have an access token associated with that hostname +} + +bool AccountManager::hasValidAccessTokenForRootURL(const QUrl &rootURL) { + OAuthAccessToken accessToken = _accessTokens.value(rootURL); + if (accessToken.token.isEmpty() || accessToken.isExpired()) { + // emit a signal so somebody can call back to us and request an access token given a username and password + qDebug() << "An access token is required for requests to" << qPrintable(rootURL.toString()); + emit authenticationRequiredForRootURL(rootURL); + return false; + } else { + return true; + } } \ No newline at end of file diff --git a/libraries/shared/src/AccountManager.h b/libraries/shared/src/AccountManager.h index dc32a7f616..fd498b2a91 100644 --- a/libraries/shared/src/AccountManager.h +++ b/libraries/shared/src/AccountManager.h @@ -10,15 +10,33 @@ #define __hifi__AccountManager__ #include +#include +#include +#include -class AccountManager { +#include "OAuthAccessToken.h" + +class AccountManager : public QObject { + Q_OBJECT public: - static void processDomainServerAuthRequest(const QByteArray& packet); + static AccountManager& getInstance(); - static const QString& getUsername() { return _username; } - static void setUsername(const QString& username) { _username = username; } + bool hasValidAccessTokenForRootURL(const QUrl& rootURL); + + const QString& getUsername() const { return _username; } + void setUsername(const QString& username) { _username = username; } + + void setNetworkAccessManager(QNetworkAccessManager* networkAccessManager) { _networkAccessManager = networkAccessManager; } +signals: + void authenticationRequiredForRootURL(const QUrl& rootURL); private: - static QString _username; + AccountManager(); + AccountManager(AccountManager const& other); // not implemented + void operator=(AccountManager const& other); // not implemented + + QString _username; + QMap _accessTokens; + QNetworkAccessManager* _networkAccessManager; }; #endif /* defined(__hifi__AccountManager__) */ diff --git a/libraries/shared/src/DomainInfo.cpp b/libraries/shared/src/DomainInfo.cpp index f580ed4763..863edba9ae 100644 --- a/libraries/shared/src/DomainInfo.cpp +++ b/libraries/shared/src/DomainInfo.cpp @@ -10,9 +10,9 @@ DomainInfo::DomainInfo() : _sockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)), - _requiresAuthentication(false), _connectionSecret(), - _registrationToken() + _registrationToken(), + _rootAuthenticationURL() { } diff --git a/libraries/shared/src/DomainInfo.h b/libraries/shared/src/DomainInfo.h index a0457796f2..72e5a853e4 100644 --- a/libraries/shared/src/DomainInfo.h +++ b/libraries/shared/src/DomainInfo.h @@ -11,6 +11,7 @@ #include #include +#include #include #include "HifiSockAddr.h" @@ -34,14 +35,15 @@ public: unsigned short getPort() const { return _sockAddr.getPort(); } - bool requiresAuthentication() const { return _requiresAuthentication; } - void setRequiresAuthentication(bool requiresAuthentication) { _requiresAuthentication = requiresAuthentication; } - const QUuid& getConnectionSecret() const { return _connectionSecret; } void setConnectionSecret(const QUuid& connectionSecret) { _connectionSecret = connectionSecret; } const QString& getRegistrationToken() const { return _registrationToken; } void setRegistrationToken(const QString& registrationToken); + + const QUrl& getRootAuthenticationURL() const { return _rootAuthenticationURL; } + void setRootAuthenticationURL(const QUrl& rootAuthenticationURL) { _rootAuthenticationURL = rootAuthenticationURL; } + private slots: void completedHostnameLookup(const QHostInfo& hostInfo); signals: @@ -49,9 +51,9 @@ signals: private: QString _hostname; HifiSockAddr _sockAddr; - bool _requiresAuthentication; QUuid _connectionSecret; QString _registrationToken; + QUrl _rootAuthenticationURL; }; #endif /* defined(__hifi__DomainInfo__) */ diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 5171c6721f..7fc2aa38f9 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -12,8 +12,10 @@ #include #include +#include #include +#include "AccountManager.h" #include "Assignment.h" #include "HifiSockAddr.h" #include "Logging.h" @@ -197,7 +199,7 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr } case PacketTypeDomainServerAuthRequest: { // the domain-server has asked us to auth via a data-server - + processDomainServerAuthRequest(packet); break; } @@ -471,7 +473,7 @@ void NodeList::sendDomainServerCheckIn() { // send a STUN request to figure it out sendSTUNRequest(); } else if (!_domainInfo.getIP().isNull() - && (!_domainInfo.requiresAuthentication() + && (_domainInfo.getRootAuthenticationURL().isEmpty() || !_sessionUUID.isNull() || !_domainInfo.getRegistrationToken().isEmpty()) ) { // construct the DS check in packet @@ -562,6 +564,21 @@ int NodeList::processDomainServerList(const QByteArray& packet) { return readNodes; } +void NodeList::processDomainServerAuthRequest(const QByteArray& packet) { + QDataStream authPacketStream(packet); + authPacketStream.skipRawData(numBytesForPacketHeader(packet)); + + // grab the hostname this domain-server wants us to authenticate with + QUrl authenticationRootURL; + authPacketStream >> authenticationRootURL; + + _domainInfo.setRootAuthenticationURL(authenticationRootURL); + + if (AccountManager::getInstance().hasValidAccessTokenForRootURL(authenticationRootURL)) { + // request a domain-server registration + } +} + void NodeList::sendAssignment(Assignment& assignment) { PacketType assignmentPacketType = assignment.getCommand() == Assignment::CreateCommand diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index 230cda6b54..7d947fec59 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -148,6 +148,8 @@ private: void clear(); + void processDomainServerAuthRequest(const QByteArray& packet); + NodeHash _nodeHash; QMutex _nodeHashMutex; QUdpSocket _nodeSocket; diff --git a/libraries/shared/src/OAuthAccessToken.cpp b/libraries/shared/src/OAuthAccessToken.cpp new file mode 100644 index 0000000000..add01caf1f --- /dev/null +++ b/libraries/shared/src/OAuthAccessToken.cpp @@ -0,0 +1,40 @@ +// +// OAuthAccessToken.cpp +// hifi +// +// Created by Stephen Birarda on 2/18/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#include "OAuthAccessToken.h" + +OAuthAccessToken::OAuthAccessToken() : + token(), + refreshToken(), + expiryTimestamp(), + tokenType() +{ + +} + +OAuthAccessToken::OAuthAccessToken(const OAuthAccessToken& otherToken) { + token = otherToken.token; + refreshToken = otherToken.refreshToken; + expiryTimestamp = otherToken.expiryTimestamp; + tokenType = otherToken.tokenType; +} + +OAuthAccessToken& OAuthAccessToken::operator=(const OAuthAccessToken& otherToken) { + OAuthAccessToken temp(otherToken); + swap(temp); + return *this; +} + +void OAuthAccessToken::swap(OAuthAccessToken& otherToken) { + using std::swap; + + swap(token, otherToken.token); + swap(refreshToken, otherToken.refreshToken); + swap(expiryTimestamp, otherToken.expiryTimestamp); + swap(tokenType, otherToken.tokenType); +} \ No newline at end of file diff --git a/libraries/shared/src/OAuthAccessToken.h b/libraries/shared/src/OAuthAccessToken.h new file mode 100644 index 0000000000..7dff5416d5 --- /dev/null +++ b/libraries/shared/src/OAuthAccessToken.h @@ -0,0 +1,32 @@ +// +// OAuthAccessToken.h +// hifi +// +// Created by Stephen Birarda on 2/18/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#ifndef __hifi__OAuthAccessToken__ +#define __hifi__OAuthAccessToken__ + +#include +#include + +class OAuthAccessToken : public QObject { + Q_OBJECT +public: + OAuthAccessToken(); + OAuthAccessToken(const OAuthAccessToken& otherToken); + OAuthAccessToken& operator=(const OAuthAccessToken& otherToken); + + bool isExpired() { return expiryTimestamp <= QDateTime::currentMSecsSinceEpoch(); } + + QString token; + QString refreshToken; + quint64 expiryTimestamp; + QString tokenType; +private: + void swap(OAuthAccessToken& otherToken); +}; + +#endif /* defined(__hifi__OAuthAccessToken__) */